Neden müşteri kaybı?
Bu projede telekom sektöründeki churn problemini ele aldım: mevcut müşteri hizmeti kullanmaya devam mı edecek, yoksa yakın zamanda ayrılacak mı? Staj yapacağım şirketin telekom ve müşteri deneyimi problemleri üzerinde çalışması, öğrendiğim derin öğrenme yaklaşımını daha kurumsal bir senaryoda denemem için iyi bir motivasyon oldu.
Başlangıç noktası IBM Telco Customer Churn veri setiydi. Yedi binden fazla müşterinin sözleşme tipi, internet hizmeti, ödeme yöntemi, aylık ücret ve hizmet süresi gibi bilgileri bulunuyor. Hedef sütunu ise müşterinin ayrılıp ayrılmadığını gösteriyor.
Veriyi modele hazırlamak
İlk engel model değil veriydi. Sayısal olması gereken TotalCharges alanında boş değerler vardı. Bunların yeni kaydolmuş ve henüz ödeme geçmişi oluşmamış müşterilere ait olduğunu gördüm; alanı sayıya çevirip boş değerleri sıfırla tamamladım.
Sözleşme tipi ve internet hizmeti gibi kategorik alanları one-hot encoding ile ayrı 0/1 sütunlarına dönüştürdüm. Böylece başlangıçtaki 19 özellik, modelin okuyabildiği yaklaşık 30 sayısal girdiye çıktı. Eğitim ve tahmin sırasında aynı sütun sırasını korumak, daha sonra yakaladığım önemli bir hatanın da merkezindeydi.
Dengesiz sınıflar ve doğru metrik
Veride müşterilerin yalnızca yaklaşık yüzde 26'sı ayrılıyor. Model herkese “kalacak” dese yüksek bir accuracy elde edebilir, ancak ayrılacak müşterilerin hiçbirini bulamaz. Bu yüzden başarıyı yalnızca doğrulukla ölçmek yerine recall, F1-score ve ROC-AUC üzerinden değerlendirdim.
PyTorch tarafında pozitif sınıfa daha yüksek ağırlık veren pos_weight kullandım. Modelin ayrılan müşteriyi kaçırmasının cezası böylece arttı. Kurduğum ağ 30 girdi, 64 ve 32 nöronlu iki gizli katman ve tek bir çıktıdan oluşuyor.
- ROC-AUC: 0.839
- Recall: yüzde 79, yani ayrılacak her beş müşterinin yaklaşık dördünü yakalıyor.
- F1-score: yüzde 62
Daha çok veri her zaman daha iyi mi?
Aynı yaklaşımı 51 bin satırlık Cell2Cell veri setinde de denedim. Beklentim daha büyük verinin skoru yükseltmesiydi, ancak ROC-AUC yaklaşık 0.63'e düştü. Bu karşılaştırma, satır sayısından önce özelliklerin hedef davranışı ne kadar iyi açıkladığına bakmam gerektiğini gösterdi.
Küçük ama güçlü sinyal taşıyan veri, büyük fakat churn davranışını yeterince açıklamayan bir veri setinden daha yararlı olabilir.
Kod yapısı ve web arayüzü
İlk sürüm tek bir Python dosyasındaydı. Daha sonra veri temizleme, model, eğitim, değerlendirme ve sonuç yorumlama sorumluluklarını ayrı modüllere böldüm. Bu yapı yeni bir model denemek istediğimde veri ve arayüz koduna dokunmadan ilerlememi sağladı.
Web arayüzünde müşteri bilgileri forma giriliyor; model ayrılma riskini hesaplayıp sonucu yüksek veya düşük risk olarak gösteriyor. Aynı modeli sunucusuz çalıştırmak için hesaplamayı JavaScript'e de aktardım ve Python çıktısıyla karşılaştırdım.
Sessiz çalışan tahmin hatası
Python ve JavaScript sonuçları aynı olmayınca tek satırlık tahmin akışını inceledim. Bir müşteriyi get_dummies ile tek başına dönüştürmek, o satırda bulunmayan kategorilerin sütunlarını yanlış kuruyordu. Kod hata vermiyor, yalnızca sessizce farklı bir tahmin üretiyordu.
Çözüm, eğitimde oluşturulan özellik şemasını kaydedip her tahminde aynı sütunları ve aynı sırayı zorunlu kılmak oldu. Bu proje bana en riskli hataların çoğu zaman programı çökertmeyenler olduğunu; bağımsız iki uygulamanın aynı girdide aynı sonucu vermesini test etmenin güçlü bir kontrol olduğunu öğretti.
Sonuç
Churn modeli yalnızca bir sinir ağı denemesi olmaktan çıktı; veri temizleme, dengesiz sınıflar, doğru metrik seçimi, modüler kod ve istemciler arası sonuç tutarlılığını birlikte ele aldığım bir ürün prototipine dönüştü. Bir sonraki adımda model açıklamalarını daha sistematik hâle getirmek ve farklı algoritmaları aynı test düzeninde karşılaştırmak istiyorum.
SONRAKİ YAZIYOLOv8 ile kapalı alanda sigara tespiti