Dostlara Bir Not:
Bölüm 2’ye gösterdiğiniz ilgiye ve yapıcı geri bildirimler için çok teşekkür ederim! Özellikle, kurduğumuz modelin Odds Ratio ve Marjinal Etkiler gibi daha derin yorumlama teknikleri hakkında gelen tavsiyeler, bizi ileriki paylaşımlar için motive etti. Merak etmeyin, bu değerli geri bildirimleri not ettik! Bu konulara özel, “İleri Seviye Model Yorumlama” adında yepyeni bir serinin tohumlarını şimdiden attık.
Şimdilik, mevcut serimizin akışına sadık kalarak, Bölüm 2’de tasarladığımız o akıllı prototipi, şimdi sağlam ve otomatize bir “motora” dönüştürmeye odaklanalım. Kemerleri bağlayın, atölyenin en teknik bölümüne giriyoruz!
Dostlar, Bölüm 2’de kullandığımız R Markdown (.Rmd
)
dosyası, analiz yapmak, notlar almak ve bir hikaye anlatmak için
harikaydı. Tıpkı bir sanatçının eskiz defteri gibiydi. Ancak bir
fabrikanın üretim hattı, eskiz defterleriyle çalışmaz; temiz, verimli ve
hatasız çalışan mühendislik planlarına ihtiyaç
duyar.
Bizim mühendislik planımız ise temiz bir R Script
(.R
) dosyası olacak. Bu dosyada sadece ve sadece
motorumuzun çalışması için gerekli olan kemik kodlar yer alacak. Notlar,
denemeler ve çıktılar olmayacak. Bu, “analiz” ile “üretim” dünyasını
ayırmanın ilk ve en önemli adımıdır.
Uzun, baştan sona akan bir kod yazmak yerine, kodumuzu yeniden kullanılabilir ve test edilebilir parçalara ayıracağız. İşte bu parçalara fonksiyon diyoruz. Bu, kodumuzu inanılmaz derecede temiz, okunaklı ve yönetilebilir hale getirecek.
Haydi, projemizin ana adımlarını fonksiyonlara dönüştürelim. Yeni bir
R Script dosyası (fonksiyonlar.R
) açıp içine bu
fonksiyonları yazıyoruz:
# fonksiyonlar.R
# Gerekli kütüphaneleri yükleyen bir fonksiyon
kutuphaneleri_yukle <- function() {
library(dplyr)
library(caret)
}
# Yeni veriyi modele hazır hale getiren fonksiyon
veri_hazirla <- function(yeni_sikayet_dosyasi, yeni_musteri_dosyasi) {
# Verileri oku
sikayetler <- read.csv(yeni_sikayet_dosyasi)
musteri_bilgileri <- read.csv(yeni_musteri_dosyasi)
# Bölüm 2'de yaptığımız adımları tekrarla
birlesik_veri <- left_join(sikayetler, musteri_bilgileri, by = "musteri_id")
musteri_ozet <- birlesik_veri %>%
group_by(musteri_id, uyelik_suresi_ay, aylik_harcama) %>% # Artık terk_durumu yok!
summarise(toplam_sikayet_sayisi = n(), .groups = 'drop')
return(musteri_ozet)
}
# Tahminleri üreten ve sonuçları hazırlayan fonksiyon
tahmin_yap <- function(model, hazir_veri) {
# Olasılıkları tahmin et
tahmin_olasikliklari <- predict(model, newdata = hazir_veri, type = "response")
# Bölüm 2'de bulduğumuz en iyi stratejik eşiği (0.40) kullanalım!
en_iyi_esik <- 0.40
tahmin_siniflari <- ifelse(tahmin_olasikliklari > en_iyi_esik, "Terk Edecek", "Kalacak")
# Sonuçları güzel bir tabloya dönüştür
sonuclar <- data.frame(
musteri_id = hazir_veri$musteri_id,
terk_etme_olasiligi = round(tahmin_olasikliklari, 3),
tahmin_durumu = tahmin_siniflari
)
return(sonuclar)
}
Her tahmin yapmak istediğimizde, modeli sıfırdan eğitmek istemeyiz. Bu yüzden Bölüm 2’de eğittiğimiz o değerli modeli, daha sonra kullanmak üzere diskimize kaydediyoruz.
# Bu kodu Bölüm 2'deki RMD dosyanızın sonunda bir kez çalıştırmanız yeterli!
# model <- glm(...) # Modelinizi eğittiniz...Bölüm2'de bu işlemi gerçekleştirmiştik.
# saveRDS(model, file = "churn_model.rds")
Artık churn_model.rds adında, içinde tüm bilgeliği barındıran sihirli bir dosyamız var.
Şimdi tüm parçaları bir araya getiren “orkestra şefi” script’imizi (pipeline.R) yazalım. Bu script, bizim tam otomatize “motorumuz” olacak! Bu bizim ikinci R script dosyamız olacak.
# pipeline.R
# Adım 1: Fonksiyonlarımızı R ortamına çağır
source("fonksiyonlar.R")
# Adım 2: Gerekli kütüphaneleri yükle
kutuphaneleri_yukle()
# Adım 3: Eğitilmiş modelimizi diskten hafızaya yükle
print("Model yukleniyor...")
model <- readRDS("churn_model.rds")
# Adım 4: Yeni gelen veriyi hazırla (Normalde bu dosyalar her gün güncellenir)
print("Yeni veriler hazırlaniyor...")
hazir_veri <- veri_hazirla(yeni_sikayet_dosyasi = "data/yeni_sikayetler.csv",
yeni_musteri_dosyasi = "data/yeni_musteri_bilgileri.csv")
# Adım 5: Tahminleri üret
print("Tahminler yapiliyor...")
tahmin_sonuclari <- tahmin_yap(model = model, hazir_veri = hazir_veri)
# Adım 6: Sonuçları kaydet
write.csv(tahmin_sonuclari, "tahmin_sonuclari.csv", row.names = FALSE)
print("Islem tamamlandi! 'tahmin_sonuclari.csv' dosyasi olusturuldu.")
İşte bu kadar! Artık pipeline.R script’ini her çalıştırdığımızda, tüm süreç otomatik olarak işleyecek ve bize risk altındaki yeni müşterilerin listesini verecek.
Dostlar, motorumuz pipeline.R
başarıyla çalıştı ve bize
en önemli çıktıyı verdi: tahmin_sonuclari.csv
dosyası! Bu
dosya, modelimizin daha önce hiç görmediği yeni müşteriler hakkında ne
düşündüğünü gösteriyor.
Peki, bu tablodaki “Terk Edecek” ve “Kalacak” etiketleri tam olarak nereden geliyor? Sınır değeri neydi ve neden oydu?
Stratejimizi Hatırlayalım: Bölüm 2’deki en kritik
anı hatırlayın. Standart 0.5
eşiğinin, bizim ana hedefimiz
olan “kayıp önleme” (yüksek Recall) için yeterince iyi olmadığına karar
vermiştik. Yaptığımız analiz sonucunda, Duyarlılık (Recall) oranını
%80’den %86.7’ye çıkaran, buna karşılık Kesinlik (Precision) oranından
sadece makul bir ödün veren “Stratejik Tatlı Nokta”
olarak 0.40
eşiğini seçmiştik.
Yani kuralımız basit: Bir müşterinin terk etme olasılığı 0.40’tan büyükse, onu “riskli” olarak işaretle!
Şimdi, bu kuralın yeni sonuçlarımız üzerinde nasıl çalıştığını somut bir tabloyla görelim:
Müşteri ID | Terk Etme Olasılığı | Kural: > 0.40? | Tahmin Durumu | Yorum |
---|---|---|---|---|
1501 | 0.396 | Hayır | Kalacak | Doğru, 0.40’tan küçük. |
1502 | 0.262 | Hayır | Kalacak | Doğru, 0.40’tan küçük. |
1506 | 0.674 | Evet | Terk Edecek | Doğru, 0.40’tan büyük. |
1508 | 0.482 | Evet | Terk Edecek | Doğru, 0.40’tan büyük. |
1509 | 0.490 | Evet | Terk Edecek | Doğru, 0.40’tan büyük. |
1515 | 0.515 | Evet | Terk Edecek | Doğru, 0.40’tan büyük. |
1521 | 0.447 | Evet | Terk Edecek | Doğru, 0.40’tan büyük. |
1524 | 0.493 | Evet | Terk Edecek | Doğru, 0.40’tan büyük. |
1530 | 0.793 | Evet | Terk Edecek | Doğru, 0.40’tan büyük. |
İşte stratejimizin gücü tam olarak burada ortaya çıkıyor!
Tablodaki 1508
, 1509
, 1521
ve
1524
gibi müşterilere dikkat edin. Bu müşterilerin terk
etme olasılıkları %40 ile %50 arasında. Eğer standart 0.5
eşiğini kullansaydık, bu kritik müşterileri “riskli” olarak
etiketlemeyecek ve potansiyel olarak gözden
kaçıracaktık! Ancak Bölüm 2’de aldığımız bilinçli karar
sayesinde, motorumuz bu “gri bölgedeki” müşterileri başarıyla tespit
edebiliyor.
Bu tablo, Bölüm 2’de aldığımız teorik ve stratejik bir kararın, Bölüm 3’te kurduğumuz motor sayesinde nasıl pratiğe döküldüğünün ve gerçek iş değeri yarattığının en somut kanıtıdır.
Peki ya bu sonuçları bir CSV dosyasında görmek yerine, bir pazarlama yöneticisinin kullanabileceği interaktif bir web arayüzünde (dashboard) sunmak isteseydik? İşte o zaman R Shiny gibi araçlar devreye girer. Bu, modelimizi bir “vitrine” koymanın en şık yoludur. Belki de bu, serimizin sonunda bizi bekleyen sürpriz bir bonus durağı olur, ne dersiniz?
Bu önemli duraktan ayrılırken, sadece kod yazmadık; bir fikri, yaşayan bir sisteme dönüştürmenin temelini attık. Gelin, fabrikada attığımız adımları ve ne anlama geldiklerini bir kez daha hatırlayalım:
Atölyeyi Fabrikadan Ayırdık (Analizden Üretime
Geçiş): İlk işimiz, Bölüm 2’deki notlarımızla, denemelerimizle
dolu, dağınık ama yaratıcı “eskiz defterimizi” (.Rmd
dosyası) bir kenara koymak oldu. Onun yerine, içinde sadece motorun
çalışması için gereken, hatasız ve net komutların olduğu bir
“mühendislik planı” (.R
dosyası) hazırladık. Bu, bir
projenin sürdürülebilirliği için en kritik adımdır: Keşif ve üretim
süreçlerini birbirinden ayırmak.
Montaj Hattı İstasyonları Kurduk (Fonksiyonların
Gücü): Tüm işi tek bir uzun hatta yapmak yerine, fabrikamızı
modüler istasyonlara böldük. “Veri Hazırlama İstasyonu”
(veri_hazirla()
fonksiyonu) ve “Tahmin Yapma İstasyonu”
(tahmin_yap()
fonksiyonu) gibi. Bu sayede, gelecekte bir
adımda değişiklik yapmak istediğimizde tüm fabrikayı durdurmak yerine
sadece ilgili istasyonu güncellememiz yeterli olacak. Bu, kodumuza
esneklik, okunabilirlik ve bakım kolaylığı
kazandırdı.
En Değerli Parçamızı Mühürledik (Modeli
Kaydetme): Bölüm 2’de onca emekle ürettiğimiz o akıllı “beyni”,
yani modelimizi, her seferinde yeniden üretmek zorunda kalmamak için
özel bir kasaya (churn_model.rds
) kilitleyip mühürledik.
Artık fabrikamız, bu hazır ve test edilmiş beyni raftan alıp anında
kullanabilir. Bu, bize hız ve tutarlılık
kazandırdı.
Montaj Hattını Çalıştırdık (Üretim Pipeline’ı):
Tüm bu istasyonları ve hazır parçaları, doğru sırayla çalıştıran bir
“ana şalter” (pipeline.R
script’i) oluşturduk. Bu şalteri
indirdiğimizde, yeni ham veriler bir uçtan giriyor, tüm istasyonlardan
geçiyor ve diğer uçtan “riskli müşteriler” listesi olarak çıkıyor. Artık
bir prototipimiz değil, otomatize, tekrarlanabilir ve güvenilir
bir motorumuz var.
Stratejimizin Çalıştığını Kanıtladık (Sonuçları
Yorumlama): Son olarak, motorumuzun ürettiği çıktıları
inceledik. Bölüm 2’de aldığımız o stratejik 0.40 eşiği
kararının, yeni müşteriler üzerinde tam olarak beklediğimiz gibi
çalıştığını ve potansiyel müşteri kayıplarını başarıyla yakaladığını
somut kanıtlarla gördük. Bu, teorinin pratiğe döküldüğü ve projemizin
gerçek iş değeri yarattığı andı.
Artık elimizde sadece çalışan bir kod parçası değil; test edilmiş, iş mantığıyla optimize edilmiş ve bir sonraki adıma hazır, akıllı bir prototip var.
Peki sırada ne var?
Motorumuz her gün tıkır tıkır çalışıyor ve bize risk altındaki müşterilerin bir listesini veriyor. Peki, bu teknik çıktıyı pazarlama direktörüne veya CEO’ya nasıl sunacağız? Bu rakamları, aksiyon alınabilir içgörülere, etkileyici grafiklere ve ikna edici bir hikayeye nasıl dönüştürürüz?
İşte bu soruların cevabı, yolculuğumuzun son ve en önemli durağı olan İletişim ve Sunum’da!