Bir Veri Bilimi Projesinin Dört Temel Aşaması
Arkadaşlar, veri bilimi dünyası bazen karmaşık ve ucu bucağı olmayan bir yolculuk gibi görünebiliyor, değil mi? “Bir proje nereden başlar, nerede biter? Bir veri bilimci gün boyu gerçekten ne yapar?” gibi sorular hepimizin aklına geliyor.
İşte bu düşünceyle, bir veri bilimi projesinin özünü, fikirden değere giden serüveni 4 temel adıma indirgeyen bir sunum hazırladım. Gelin bu adımları birlikte keşfedelim.
Her şey doğru soruyu sormakla başlar. Bu aşama, projenin temelini attığımız ve okyanusta hangi yöne gideceğimizi belirlediğimiz, pusulamızı ayarladığımız yerdir. Yanlış bir soruya verilen en doğru cevap bile bizi hedefe götürmez.
İş Problemini Anlamak: Bu, sadece teknik bir adım değil, bir empati kurma sanatıdır. Paydaşlarla konuşarak, onların “gerçek” ihtiyacının ne olduğunu anlamaya çalışırız. Bazen bize “X’i tahmin et” derler, ama asıl ihtiyaçları “Y sürecini iyileştirmek” olabilir. Bu adımı atlamak, en büyük hatadır.
Veriyi Keşfetmek (EDA): Elimizdeki malzemeyi, yani veriyi ilk defa tanımaya başladığımız yerdir. Bir heykeltıraşın mermer bloğundaki çatlakları ve damarları incelemesi gibi, biz de veri setinin yapısını, eksik değerlerini, aykırı değerlerini ve temel istatistiklerini inceleriz. Bu adım, verinin bize fısıldadığı ilk ipuçlarını duyduğumuz yerdir.
Hipotezler Oluşturmak: Keşif aşamasında gördüklerimizden yola çıkarak ilk varsayımlarımızı ve test edilecek fikirlerimizi oluştururuz. “Acaba pazarlama kampanyaları, satışları gerçekten artırıyor mu?” veya “IT departmanındaki çalışanların maaşı, diğerlerinden daha mı yüksek?” gibi soruları hipotezlere dönüştürürüz.
Bu Aşamanın Ruhu: Merak, şüphecilik ve iyi bir dinleyici olmak.
Veriyi anladıktan sonra, onun içindeki desenleri kullanarak geleceği tahmin eden veya veriyi anlamlı gruplara ayıran sistemler kurarız. Bu, elimizdeki haritaya bakarak, henüz gitmediğimiz yerler hakkında öngörülerde bulunmaya benzer.
Algoritma Seçimi: Probleme en uygun makine öğrenmesi modelini seçtiğimiz yerdir. Bu, “Satışları mı tahmin edeceğiz (regresyon)? Müşterileri gruplara mı ayıracağız (kümeleme)? E-postanın spam olup olmadığını mı anlayacağız (sınıflandırma)?” sorusuna en uygun aleti seçmektir.
Model Eğitimi ve Doğrulama: Veri setimizi eğitim ve test olarak ikiye ayırarak modeli eğitir ve performansını objektif metriklerle ölçeriz. Bu, modelimizin sadece ezber yapmadığından, gerçekten “öğrendiğinden” emin olduğumuz kritik bir adımdır.
Model Optimizasyonu: Modelin daha iyi sonuçlar vermesi için parametre ayarları (hyperparameter tuning) yaparız. Bu, bir radyonun frekansını en net sesi alacak şekilde ince ayarlamaya benzer.
Bu Aşamanın Ruhu: Yaratıcılık, deneysellik ve matematiksel titizlik.
Fikirler ve modeller harikadır, ancak onları hayata geçiren şey, sağlam ve güvenilir bir koddur. Bu aşama, projenin planını, çalışan ve tekrarlanabilir bir motora dönüştürdüğümüz yerdir.
Kod Yazma: Analiz ve modelleme süreçlerini
R veya Python gibi dillerle, temiz, okunabilir
ve tekrar çalıştırılabilir bir şekilde kodlarız.
Pipeline Oluşturma: Veri temizlemeden model sonucunu üretmeye kadar olan tüm adımları otomatize eden akışlar tasarlarız. Bu, projenin tek bir butona basarak baştan sona çalışabilmesini sağlar ve “ama benim bilgisayarımda çalışıyordu” bahanelerini ortadan kaldırır.
Sürüm Kontrolü: Kodun gelişimini
Git gibi araçlarla takip ederek projenin
yönetilebilirliğini sağlarız. Bu, projenin “zaman makinesi” gibidir;
istediğimiz zaman geçmiş bir versiyona dönebilir veya farklı denemeleri
güvenle yapabiliriz.
Bu Aşamanın Ruhu: Disiplin, düzen ve zanaatkarlık.
Bu, tüm yolculuğun en önemli ve genellikle en çok ihmal edilen adımıdır. Dünyanın en iyi modelini de kursanız, bulgularınızı ve yarattığınız değeri başkalarına anlatamadığınız sürece projeniz bir değer yaratmaz.
Veri Görselleştirme: Karmaşık sonuçları, herkesin bir bakışta anlayabileceği, anlaşılır ve etkileyici grafiklere dönüştürme sanatıdır. Serimizin bir sonraki büyük macerası bu konu üzerine olacak!
Raporlama: Bulguları ve projenin işe olan etkisini özetleyen sunumlar veya raporlar hazırlarız. Burada hedef kitlemiz çok önemlidir; bir CEO’ya anlatacağımız hikaye ile bir mühendise anlatacağımız hikaye aynı değildir.
Değer Yaratma: Tüm bu teknik çalışmanın, şirketin veya kurumun hedeflerine nasıl katkı sağladığını net, rakamlarla ve somut örneklerle ortaya koyduğumuz yerdir.
Bu Aşamanın Ruhu: Empati, sadelik ve ikna kabiliyeti.
Peki, bu dört aşama arasında bir veri bilimci zamanını nasıl dağıtır? Genellikle yeni başlayanları şaşırtan bir gerçekle karşı karşıyayız:
Bir Veri Bilimi Projesinde Zaman Dağılımı
Gördüğünüz gibi arkadaşlar, başarılı bir veri bilimci; meraklı bir analist, yaratıcı bir modelleyici, disiplinli bir yazılımcı ve ikna edici bir hikaye anlatıcısıdır. Bu dört rol, bir projeyi fikirden değere taşıyan ve birbirinden ayrılmaz, vazgeçilmez bir bütünü oluşturur.
Şimdi bir örnek ile Keşif, Modelleme, Kodlama ve Sunum adımlarına tek tek bakalım.
Bu yolculukta en çok vakit geçirdiğimiz, belki de en heyecanlı durağın Keşif ve Analiz olduğundan bahsetmiştik.
Düşünün ki bir e-ticaret şirketinde çalışıyoruz ve yöneticimiz elinde bir Excel dosyasıyla geldi:
“İşte son 6 ayın müşteri şikayetleri. Buna bir bakın, ne yapabiliriz?” dedi.
Çoğumuzun ilk tepkisi hemen kod yazmaya ya da en havalı algoritmaları denemeye atlamak olabilir. Ancak ilk işimiz, okyanusun ortasında ne aradığımızı bilmek: Doğru soruyu sormak.
Müşteri Kaybını Anlamak: “Şikayet eden müşterilerimiz bizi terk mi ediyor?”
Operasyonel Sorunları Bulmak: “En çok hangi ürün veya kargo şirketi hakkında şikayet alıyoruz?”
Ürün Geliştirmek: “Müşteriler ürünümüzün hangi özelliğinden sürekli yakınıyor?”
👉 Biz bu yazıda, bu sorulardan ilkine odaklanalım:
Şikayetlerin müşteri memnuniyeti ve kaybı üzerindeki etkisini anlayabilir miyiz?
Bu soruyu aklımızın bir köşesine yazdık. Artık yola çıkabiliriz.
Şimdi elimizde veri var. Ama ham veri genellikle biraz… “dağınıktır”.
Analizimizi sabote edebilecek en büyük sorunlardan biri eksik veridir (NA).
Önce veri setimizde hiç eksik değer olup olmadığını kontrol edelim. Bunun en basit yolu, her sütundaki eksik değerleri saydırmaktır. Biz kodlarımızı R yazılım ile yazalım:
# 1. Veri setini yükle (UTF-8 kodlamasıyla)
veri <- read.csv("data/musteri_sikayetleri.csv")
# Sütunlardaki eksik değer sayısını kontrol edelim
colSums(is.na(veri))
## sikayet_id musteri_id sikayet_tarihi sikayet_kategorisi
## 0 0 0 0
## sikayet_metni
## 0
Bu komutun çıktısı her sütun için 0 ise, harika!
Hiç eksik verimiz yok demektir. Eğer sıfırdan farklı bir sayı görseydik, o sütun üzerinde ne yapacağımıza (satırları silmek, veriyi doldurmak vb.) karar vermemiz gerekecekti.
Neyse ki bizim verimiz temiz görünüyor! 🎉
Şimdi str() veya glimpse() komutlarıyla
verimizin yapısına bir göz atalım.
Her sütunun hangi tipte olduğunu bilmek, analiz
sırasında yanlışlık yapmamızı önler.
# Veri tiplerine göz atalım
#str(veri)
# Alternatif olarak dplyr paketi ile glimpse() yapalım. dplyr paketine alışalım.
library(dplyr)
glimpse(veri)
## Rows: 500
## Columns: 5
## $ sikayet_id <int> 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, …
## $ musteri_id <int> 1049, 1065, 1074, 1146, 1122, 1049, 1128, 1047, 102…
## $ sikayet_tarihi <chr> "2024-01-13", "2024-04-07", "2024-02-10", "2024-06-…
## $ sikayet_kategorisi <chr> "Teslimat Gecikmesi", "Ürün Hasarlı Geldi", "Ürün H…
## $ sikayet_metni <chr> "Teslimat tarihi geçti ama ürünüm ortada yok.", "Ür…
Çıktıda iki önemli nokta gözümüze çarpıyor:
sikayet_id ve musteri_id sayısal
(int) görünüyor, ama aslında onlar birer
etiket.Onlarla matematiksel işlem yapmayacağız, bu yüzden faktör (factor) veya karakter (character) tipine çevirmeliyiz.
sikayet_tarihi bir metin (chr) olarak
duruyor.Bunu R’ın anlayacağı Date formatına çevirmeliyiz ki zamanla ilgili analizler yapabilelim.
lubridate paketi, tarih ve saat
işlemleri için bir harikadır!
Hadi verimizi hem ID’ler hem de tarih sütunu açısından temizleyelim.
# Paketi yükleyelim
library(lubridate)
# Adım 3.1: Anlamsız ID'leri metin formatına çeviriyoruz
veri$sikayet_id <- as.character(veri$sikayet_id)
veri$musteri_id <- as.character(veri$musteri_id)
# Adım 3.2: Tarih formatındaki metni, gerçek bir 'Date' objesine çeviriyoruz
# ymd() fonksiyonu "Yıl-Ay-Gün" formatını otomatik olarak tanır
veri$sikayet_tarihi <- ymd(veri$sikayet_tarihi)
# Son bir kontrol: veri tipleri doğru mu?
print("Veri tipleri düzeltildikten sonraki son durum:")
## [1] "Veri tipleri düzeltildikten sonraki son durum:"
glimpse(veri)
## Rows: 500
## Columns: 5
## $ sikayet_id <chr> "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", …
## $ musteri_id <chr> "1049", "1065", "1074", "1146", "1122", "1049", "11…
## $ sikayet_tarihi <date> 2024-01-13, 2024-04-07, 2024-02-10, 2024-06-19, 20…
## $ sikayet_kategorisi <chr> "Teslimat Gecikmesi", "Ürün Hasarlı Geldi", "Ürün H…
## $ sikayet_metni <chr> "Teslimat tarihi geçti ama ürünüm ortada yok.", "Ür…
Grafiğimizin düzgün gözükmesi için ufak bir değişiklik
# dplyr ve stringr kütüphanelerinin yüklü olduğundan emin olalım
# (genellikle tidyverse içinde gelirler)
library(dplyr)
library(stringr)
# "sikayet_kategorisi" sütunundaki Türkçe karakter sorununu düzeltiyoruz
veri <- veri %>%
mutate(sikayet_kategorisi = str_replace(sikayet_kategorisi, "İade Süreci", "Iade Sureci")) %>%
mutate(sikayet_kategorisi = str_replace(sikayet_kategorisi, "Ürün Hasarlı Geldi", "Urun Hasarli Geldi"))
# Değişikliğin başarılı olup olmadığını kontrol edelim.
# Bu komut, sütundaki tüm benzersiz kategorileri listeler.
print("Kategori isimleri güncellendi mi?")
## [1] "Kategori isimleri güncellendi mi?"
unique(veri$sikayet_kategorisi)
## [1] "Teslimat Gecikmesi" "Urun Hasarli Geldi" "Fatura Sorunu"
## [4] "Teknik Destek" "Iade Sureci"
Binlerce satır veriye bakarak bir desen görmek imkansıza
yakındır.
Ama iyi bir grafik, binlerce satırın anlatamadığı hikayeyi tek
bir bakışta anlatabilir.
Haydi, şikayet verilerimizi konuşturmaya başlayalım!
İlk olarak, şikayetlerin hangi kategorilerde toplandığını merak
ederiz.
“Fatura”, “Teslimat Gecikmesi”, “Ürün Hasarlı Geldi”, “Teknik Destek”
gibi kategorilerimiz olduğunu varsayalım.
# Gerekli paket
library(ggplot2)
ggplot(veri, aes(x = fct_infreq(sikayet_kategorisi))) +
geom_bar(fill = "#FF6347") +
labs(title = "Sikayetlerin Kategorilere Gore Dagilimi",
x = "Sikayet Kategorisi",
y = "Sikayet Sayisi") +
theme_minimal() +
coord_flip() # Kategoriler uzunsa yatay göstermek daha okunaklı olur
Aha! Grafiğe baktığımızda, “Teslimat Gecikmesi”nin
açık ara en büyük sorun olduğunu gördük.
İşte bu, odaklanmamız gereken ilk yer olabilir.
Peki bu şikayetler hep mi böyleydi, yoksa belli dönemlerde mi
artıyor?
Özel bir kampanya dönemi veya yeni bir kargo firmasıyla anlaştığımız
tarih işleri karıştırmış olabilir mi?
Bunu anlamak için şikayetleri zamana yayarak
inceleyelim.
library(lubridate)
veri %>%
mutate(sikayet_tarihi = as.Date(sikayet_tarihi)) %>%
count(sikayet_tarihi) %>%
ggplot(aes(x = sikayet_tarihi, y = n)) +
geom_line(color = "#4682B4") +
labs(title = "Zaman Icinde Gunluk Sikayet Sayisi",
x = "Tarih",
y = "Sikayet Sayisi") +
theme_minimal()
Bazen kategoriler yeterli olmaz.
Müşterilerin kendi cümlelerinde en çok hangi kelimeleri kullandığını görmek, sorunun kökünü anlamamıza yardımcı olabilir.
Şikayet metinlerinden bir kelime bulutu oluşturalım.
library(wordcloud)
library(tidytext)
library(dplyr)
# 1. Kendi basit Türkçe stop words listemizi oluşturuyoruz
turkce_stop_words <- tibble(word = c("bir", "ve", "ile", "ama", "için", "gibi", "olarak",
"da", "de", "bu", "çok", "daha", "kadar", "ben",
"ne", "var", "yok", "sonra", "önce", "tarafından"))
# 2. Kelimeleri ayırıp, Türkçe stop words listemizi çıkarıyoruz
sikayet_metinleri <- veri %>%
# Veri setinizde metin içeren sütunun adının 'sikayet_metni' olduğundan emin olun
unnest_tokens(word, sikayet_metni) %>%
anti_join(turkce_stop_words) %>%
count(word, sort = TRUE)
# 3. Kelime bulutunu oluşturuyoruz
wordcloud(words = sikayet_metinleri$word,
freq = sikayet_metinleri$n,
max.words = 100,
random.order = FALSE,
rot.per = 0.35,
colors = brewer.pal(8, "Dark2"))
Şikayet Metinlerinde En Sık Geçen Kelimeler
Bu kelime bulutu, bize şikayet eden müşterinin adeta bir portresini
çiziyor.
En büyük kelimeler olan “bekliyorum”,
“istiyorum” ve “acil”,
müşterilerimizin sabırsızlıkla bir çözüm aradığını ve sürecin bir
yerinde takılıp kaldığını gösteriyor.
Peki ne hakkında bekliyorlar? İkinci plandaki kelimeler sorunun kaynağını işaret ediyor: “ürün”, “cihaz”, “defolu” ve “kırık”.
Anlıyoruz ki, şikayetlerin büyük bir kısmı fiziksel ürün problemleriyle ilgili.
Tüm bu süreçte en çok aradıkları şey ise “bilgi”. Müşteriler, ne yapacaklarını veya süreçlerinin ne durumda olduğunu öğrenmek istiyor.
Özetle, müşterilerimiz bize diyor ki:
“Defolu veya kırık ürünümle ilgili sizden acil bir çözüm ve bilgi bekliyorum, ama hala bir sonuç alamadım.”
Bu bulut, operasyonel olarak hangi noktalara odaklanmamız gerektiğini net bir şekilde ortaya koyuyor.
Şimdi ‘Modelleme’ durağına geçiyoruz. Burada artık fotoğraf çekmeyi bırakıp, geleceği öngören bir dürbün inşa etmeye çalışacağız. En kritik sorumuz şu: Hangi müşterilerin bizi terk etme riski taşıdığını önceden tahmin edebilir miyiz?
Fark ettiğimiz ilk şey: Elimizdeki şikayet verisi tek başına kimin gittiğini, kimin kaldığını söylemiyor. Bu, bir tahmin modeli kurmak için yeterli değil.
Çözüm? Tıpkı bir dedektifin farklı ipuçlarını birleştirmesi gibi, biz
de farklı bir kaynaktan gelen ikinci bir veri setine ihtiyaç duyuyoruz:
musteri_bilgileri.csv. Bu set, müşterilerin üyelik süresi,
aylık harcamaları ve en önemlisi
terk_durumu (churn) bilgisini
içeriyor.
Şimdi bu iki hazine haritasını birleştirelim!
# Gerekli kütüphaneleri yüklüyoruz
library(dplyr)
# Önceki bölümden gelen veriyi ve yeni müşteri verisini yüklüyoruz
# NOT: Bu dosyaların projenizin 'data' klasöründe olduğunu varsayıyoruz.
sikayetler <- read.csv("data/musteri_sikayetleri.csv")
musteri_bilgileri <- read.csv("data/musteri_bilgileri.csv")
# İki veri setini musteri_id üzerinden birleştiriyoruz
# Tıpkı Excel'deki VLOOKUP gibi!
birlesik_veri <- left_join(sikayetler, musteri_bilgileri, by = "musteri_id")
# Birleştirilmiş veriye hızlıca bir göz atalım
glimpse(birlesik_veri)
## Rows: 500
## Columns: 8
## $ sikayet_id <int> 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, …
## $ musteri_id <int> 1049, 1065, 1074, 1146, 1122, 1049, 1128, 1047, 102…
## $ sikayet_tarihi <chr> "2024-01-13", "2024-04-07", "2024-02-10", "2024-06-…
## $ sikayet_kategorisi <chr> "Teslimat Gecikmesi", "Ürün Hasarlı Geldi", "Ürün H…
## $ sikayet_metni <chr> "Teslimat tarihi geçti ama ürünüm ortada yok.", "Ür…
## $ uyelik_suresi_ay <int> 35, 33, 9, 20, 42, 35, 46, 4, 45, 29, 19, 46, 8, 27…
## $ aylik_harcama <dbl> 299.56, 171.37, 105.49, 122.19, 188.13, 299.56, 468…
## $ terk_durumu <int> 1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 0, 1, 0, 1, 1, 1, …
Harika! Artık tek bir veri setinde hem şikayet detayları hem de müşterinin bizi terk edip etmediği bilgisi (terk_durumu, 1=Terk Etti, 0=Kalmaya Devam Etti) var.
Modeller sayılarla konuşur. Onlara en anlamlı sayıları bizim vermemiz gerekir. Şu anki verimizde her şikayet ayrı bir satır. Modelin işini kolaylaştırmak için, her müşteriye özel bir özet çıkaralım. Mesela her müşterinin toplam şikayet sayısını hesaplayalım.
# Her müşterinin toplam şikayet sayısını ve diğer bilgilerini özetliyoruz
musteri_ozet <- birlesik_veri %>%
group_by(musteri_id, uyelik_suresi_ay, aylik_harcama, terk_durumu) %>%
summarise(toplam_sikayet_sayisi = n(), .groups = 'drop')
# n() fonksiyonu grup içindeki satırları sayar
# Yeni özet verimize göz atalım
glimpse(musteri_ozet)
## Rows: 146
## Columns: 5
## $ musteri_id <int> 1001, 1002, 1003, 1004, 1005, 1006, 1007, 1008, …
## $ uyelik_suresi_ay <int> 37, 1, 25, 10, 36, 18, 47, 24, 7, 36, 25, 37, 46…
## $ aylik_harcama <dbl> 402.85, 107.99, 108.09, 82.51, 73.91, 289.34, 10…
## $ terk_durumu <int> 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, …
## $ toplam_sikayet_sayisi <int> 4, 4, 7, 1, 4, 4, 1, 3, 3, 6, 3, 5, 3, 3, 3, 2, …
Artık her müşteri için tek bir satırımız var ve bu satırda “çok şikayet eden daha mı çabuk gider?” hipotezini test etmemizi sağlayacak toplam_sikayet_sayisi adında güçlü bir özelliğimiz var.
Müşterileri iki kategoriden birine (“terk etti” / “terk etmedi”) ayırmaya çalıştığımız için bu bir sınıflandırma problemidir. Bu görev için, sonuçları kolayca yorumlanabilen Lojistik Regresyon modelini kullanacağız. Çünkü bu model, bir müşterinin terk etme olasılığını hesaplamak için harika bir başlangıç noktasıdır.
Arkadaşlar, makine öğrenmesi derya deniz bir konu. Onlarca farklı ve güçlü algoritma var. Biz bu seride, konunun özünü en anlaşılır şekilde kavramak için bir başlangıç yapıyoruz. Lojistik Regresyon, sınıflandırma problemlerini anlamak için en temel, yorumlaması en kolay ve en harika başlangıç noktasıdır.
Şimdi eğitim setimizi kullanarak geleceği tahmin edecek modelimizi inşa etme zamanı!
# Lojistik Regresyon modelini kuruyoruz
# terk_durumu'nu diğer değişkenlere göre tahmin etmeye çalışıyoruz
model <- glm(terk_durumu ~ toplam_sikayet_sayisi + uyelik_suresi_ay + aylik_harcama,
data = egitim_seti,
family = "binomial") # Sınıflandırma için "binomial" ailesini seçiyoruz
# Modelin özetini inceleyelim
summary(model)
##
## Call:
## glm(formula = terk_durumu ~ toplam_sikayet_sayisi + uyelik_suresi_ay +
## aylik_harcama, family = "binomial", data = egitim_seti)
##
## Coefficients:
## Estimate Std. Error z value Pr(>|z|)
## (Intercept) -0.504635 0.761992 -0.662 0.508
## toplam_sikayet_sayisi 0.644381 0.158996 4.053 5.06e-05 ***
## uyelik_suresi_ay -0.069209 0.017569 -3.939 8.17e-05 ***
## aylik_harcama -0.001114 0.001816 -0.613 0.540
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## (Dispersion parameter for binomial family taken to be 1)
##
## Null deviance: 158.41 on 116 degrees of freedom
## Residual deviance: 120.05 on 113 degrees of freedom
## AIC: 128.05
##
## Number of Fisher Scoring iterations: 4
Hikayeyi Okumak: Modelin özetini istatistiksel terimlere boğulmadan yorumlayalım:
toplam_sikayet_sayisi: Yanındaki üç
yıldız (***) bize “Hey, ben çok önemliyim!” diyor.
Katsayısı pozitif, yani toplam şikayet sayısı arttıkça,
müşterinin terk etme olasılığı da anlamlı bir şekilde
artıyor!
uyelik_suresi_ay: Bu da çok önemli
(***). Ama katsayısı negatif. Yani üyelik süresi
uzadıkça, müşterinin terk etme olasılığı azalıyor. Bu çok
mantıklı, sadık müşterilerimiz bizimle kalıyor!
aylik_harcama: Yanında yıldız yok.
Yani modelimize göre, bir müşterinin aylık harcaması, terk edip etmemesi
üzerinde anlamlı bir etkiye sahip değil.
İşte bu, veriyle kanıtlanmış içgörüdür!
Peki inşa ettiğimiz bu “tahmin dürbünü” ne kadar net gösteriyor? Bunu anlamak için modelimizi daha önce hiç görmediği test seti ile sınava sokalım. Önce, modelimizin tahminleri ile gerçek sonuçları karşılaştıran meşhur Karışıklık Matrisi’ni (Confusion Matrix) oluşturalım ve ardından tüm kritik notlarını hesaplayalım.
# Test seti üzerinde tahminler yapıyoruz
tahmin_olasikliklari <- predict(model, newdata = test_seti, type = "response")
# Olasılıkları 0.5 eşiğine göre "Terk Etti" (1) veya "Etmedi" (0) olarak sınıflandırıyoruz
tahmin_siniflari <- ifelse(tahmin_olasikliklari > 0.5, 1, 0)
# Karışıklık Matrisi'ni oluşturalım (Hata önlemek için faktör seviyeleriyle)
karne <- table(Gercek = factor(test_seti$terk_durumu, levels=c(0,1)),
Tahmin = factor(tahmin_siniflari, levels=c(0,1)))
print("Karne (0.5 Eşiği ile):")
## [1] "Karne (0.5 Eşiği ile):"
print(karne)
## Tahmin
## Gercek 0 1
## 0 12 2
## 1 3 12
# Şimdi bu karneden temel notlarımızı hesaplayalım
dogruluk <- sum(diag(karne)) / sum(karne) # Accuracy
kesinlik <- karne[2,2] / sum(karne[,2]) # Precision: TP / (TP + FP)
duyarlilik <- karne[2,2] / sum(karne[2,]) # Recall: TP / (TP + FN)
ozgulluk <- karne[1,1] / sum(karne[1,]) # Specificity: TN / (TN + FP)
# Sonuçları tablo gibi daha okunaklı sunalım
metrikler <- data.frame(
Metrik = c("Doğruluk (Accuracy)", "Kesinlik (Precision)", "Duyarlılık (Recall)",
"Özgüllük (Specificity)"),
Deger = c(dogruluk, kesinlik, duyarlilik, ozgulluk)
)
print("Model Performans Metrikleri:")
## [1] "Model Performans Metrikleri:"
print(knitr::kable(metrikler, digits = 3))
##
##
## |Metrik | Deger|
## |:----------------------|-----:|
## |Doğruluk (Accuracy) | 0.828|
## |Kesinlik (Precision) | 0.857|
## |Duyarlılık (Recall) | 0.800|
## |Özgüllük (Specificity) | 0.857|
Karneyi Yorumlayalım:
Arkadaşlar, bu tablo bize en baştan itibaren çok daha dengeli ve güçlü bir modelin resmini çiziyor.
Doğruluk (Accuracy) - %82.8:
Modelimizin genel başarısı oldukça yüksek.
Kesinlik (Precision) - %85.7:”Bu
müşteri gidecek” dediğimizde, yüksek bir ihtimalle haklıyız. Bu, kaynak
israfını önler.
Özgüllük (Specificity) - %85.7:Kalacak
müşterileri doğru tahmin etme konusunda da çok başarılıyız. Sadık
müşterilerimizi gereksiz kampanyalarla rahatsız etmeyiz.
Duyarlılık (Recall) - %80.0:İşte en
güzel gelişme bu! Modelimiz, standart ayarlarıyla bile, terk edecek her
10 müşteriden 8’ini yakalamayı başarıyor! Bu, ana iş hedefimiz olan
“kayıp önleme” konusunda bizi en baştan güçlü bir konuma
getiriyor.
Sonuç: Modelimiz, standart haliyle bile iş hedefimize oldukça iyi hizmet eden, dengeli ve güçlü bir başlangıç noktası sunuyor. Peki, bu dengeyi stratejik olarak daha da iyileştirebilir miyiz?
Modelimiz, standart 0.5 eşiğiyle bile iş hedefimiz olan kayıp önleme konusunda oldukça başarılı (%80 Recall). Peki, bu dengeyi stratejik olarak daha da iyileştirebilir miyiz?
Şirket stratejimiz, “ne pahasına olursa olsun müşteri kaybetmemek” üzerine kurulu olabilir. Veya tam tersi, “pazarlama bütçesini en verimli şekilde kullanmak” öncelikli olabilir. Elimizdeki bu tablo, bir veri bilimcinin teknik bir analisti stratejik bir danışmana dönüştürdüğü yerdir. Gelin farklı senaryoları inceleyelim:
# Farklı eşik değerleri için metrikleri saklayacak bir data frame oluşturalım
esik_degerleri <- seq(0.25, 0.6, by = 0.05)
sonuclar_df <- data.frame(Esik=numeric(), Dogruluk=numeric(), Kesinlik=numeric(),
Duyarlilik=numeric(), Ozgulluk=numeric())
for (esik in esik_degerleri) {
tahminler <- ifelse(tahmin_olasikliklari > esik, 1, 0)
karne <- table(Gercek = factor(test_seti$terk_durumu, levels=c(0,1)),
Tahmin = factor(tahminler, levels=c(0,1)))
# Tüm metrikleri hesapla
dogruluk <- sum(diag(karne)) / sum(karne)
kesinlik <- karne[2,2] / sum(karne[,2])
duyarlilik <- karne[2,2] / sum(karne[2,])
ozgulluk <- karne[1,1] / sum(karne[1,])
sonuclar_df <- rbind(sonuclar_df, data.frame(Esik=esik, Dogruluk=dogruluk,
Kesinlik=kesinlik, Duyarlilik=duyarlilik, Ozgulluk=ozgulluk))
}
sonuclar_df[is.na(sonuclar_df)] <- 0
print("Farklı Eşik Değerleri için Metriklerin Değişimi:")
## [1] "Farklı Eşik Değerleri için Metriklerin Değişimi:"
print(knitr::kable(sonuclar_df, digits = 3))
##
##
## | Esik| Dogruluk| Kesinlik| Duyarlilik| Ozgulluk|
## |----:|--------:|--------:|----------:|--------:|
## | 0.25| 0.690| 0.636| 0.933| 0.429|
## | 0.30| 0.724| 0.667| 0.933| 0.500|
## | 0.35| 0.793| 0.737| 0.933| 0.643|
## | 0.40| 0.828| 0.812| 0.867| 0.786|
## | 0.45| 0.828| 0.812| 0.867| 0.786|
## | 0.50| 0.828| 0.857| 0.800| 0.857|
## | 0.55| 0.828| 0.857| 0.800| 0.857|
## | 0.60| 0.793| 0.846| 0.733| 0.857|
Stratejik Seçim:
Bu tablo, bize farklı iş stratejileri için en uygun ayarı seçme imkanı tanıyor.
Senaryo 1: Dengeli Performans (Mevcut Durum - Eşik 0.50)
Mevcut ayarımızda Duyarlılık (Recall) %80, Kesinlik (Precision) ise %85.7 seviyesinde. Bu, her iki metrik arasında harika bir denge kurduğumuz anlamına gelir. Eğer hem müşteri kaybını önlemek hem de pazarlama bütçesini verimli kullanmak eşit derecede önemliyse, bu ayar zaten çok iyi bir başlangıç noktasıdır.
Senaryo 2: Agresif Kayıp Önleme (Recall Odaklı - Eşik 0.35)
Eğer şirket için tek bir müşteriyi bile kaybetmenin maliyeti çok yüksekse, hedefimiz Recall’ı maksimize etmektir. Eşiği 0.35’e çektiğimizde:
Duyarlılık (Recall) %93.3’e fırlıyor! Neredeyse terk edecek tüm müşterileri yakalıyoruz. Bu, kayıp önleme açısından bir zaferdir.
Bedeli: Kesinlik (Precision) %73.7’ye düşüyor. Yani artık “riskli” diye etiketlediğimiz her 4 müşteriden 1’den fazlası aslında kalacak olan müşteri. Bu, pazarlama bütçesinin bir kısmının yanlış hedeflere gideceği anlamına gelir.
Senaryo 3: Stratejik Tatlı Nokta (Dengeli İyileştirme - Eşik 0.40)
Peki, Recall’ı anlamlı bir şekilde artırırken, Kesinlik’ten çok fazla ödün vermeyeceğimiz bir “tatlı nokta” var mı? Tabloya göre kesinlikle var! Eşiği 0.40’a çektiğimizde:
Duyarlılık (Recall) %80’den %86.7’ye yükseliyor. Bu, ana hedefimiz için kayda değer bir iyileşme.
Kesinlik (Precision) ise %85.7’den sadece %81.2’ye düşüyor. Bu, kazancımıza kıyasla çok makul bir kayıp.
Bu tamamen bir iş kararıdır. Ancak bu analiz, 0.40 eşiğinin, ana hedefimiz olan kayıp önlemede bize önemli bir avantaj sağlarken, pazarlama verimliliğini de yüksek seviyede tutan en stratejik nokta olduğunu gösteriyor. Biz de iş hedefimiz doğrultusunda bu bilinçli adımı atarak modelimizi standart bir modelden, stratejik bir araca dönüştürmüş oluyoruz.
Peki Sadece Lojistik Regresyon mu?
Bu noktada aklınıza şu soru gelmiş olabilir: “Bu işin tek yolu Lojistik Regresyon mu? Ya daha iyi sonuç veren başka bir yöntem varsa?”
Bu harika bir soru ve sizi daha iyi bir veri bilimci yapacak olan merak tam olarak budur! Makine öğrenmesi, içinde Karar Ağaçları (Decision Trees), Rastgele Orman (Random Forest), Destek Vektör Makineleri (SVM) gibi onlarca farklı ve güçlü aletin bulunduğu zengin bir alet çantası gibidir.
Gerçek bir projede, genellikle birden fazla modeli dener, onları birbiriyle yarıştırır ve problemimize en iyi çözümü sunan “şampiyon modeli” seçeriz. Hatta bu daha gelişmiş modellerin, bizim yaptığımız eşik ayarından çok daha karmaşık ve etkili “hyperparameter tuning” (ince ayar) imkanları vardır.
saveRDS(model, file = "data/churn_model.rds")
print("Model 'churn_model.rds' adıyla başarıyla kaydedildi.")
## [1] "Model 'churn_model.rds' adıyla başarıyla kaydedildi."
write.csv(musteri_ozet, "data/musteri_ozet.csv", row.names = FALSE)
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:
# Gerekli kütüphaneleri yükleyen fonksiyon
kutuphaneleri_yukle <- function() {
library(dplyr)
library(caret)
}
# Yeni veriyi modele hazır hale getiren fonksiyon
veri_hazirla <- function(sikayetler_df, musteri_bilgileri_df) {
# Verileri birleştir
birlesik_veri <- left_join(
sikayetler_df,
musteri_bilgileri_df,
by = "musteri_id"
)
# Müşteri bazlı özet oluştur
musteri_ozet <- birlesik_veri %>%
group_by(musteri_id, uyelik_suresi_ay, aylik_harcama) %>%
summarise(
toplam_sikayet_sayisi = n(),
.groups = "drop"
)
return(musteri_ozet)
}
# Tahmin yapan fonksiyon
tahmin_yap <- function(model, hazir_veri) {
# Tahmin olasılıklarını üret
tahmin_olasikliklari <- predict(
model,
newdata = hazir_veri,
type = "response"
)
# Stratejik eşik
en_iyi_esik <- 0.40
tahmin_siniflari <- ifelse(
tahmin_olasikliklari > en_iyi_esik,
"Terk Edecek",
"Kalacak"
)
# Sonuç tablosu
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 = "data/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.
# Adım 1: Fonksiyonları yükle
source("fonksiyonlar.R")
# Adım 2: Kütüphaneleri yükle
kutuphaneleri_yukle()
# Adım 3: Eğitilmiş modeli yükle
print("Model yükleniyor...")
model <- readRDS("data/churn_model.rds")
# Adım 4: Yeni verileri oku
print("Yeni veriler hazırlanıyor...")
sikayetler_df <- read.csv(
"data/yeni_sikayetler.csv"
)
musteri_bilgileri_df <- read.csv(
"data/yeni_musteri_bilgileri.csv"
)
# Adım 5: Veriyi hazırla
hazir_veri <- veri_hazirla(
sikayetler_df = sikayetler_df,
musteri_bilgileri_df = musteri_bilgileri_df
)
# Adım 6: Tahmin üret
print("Tahminler yapılıyor...")
tahmin_sonuclari <- tahmin_yap(
model = model,
hazir_veri = hazir_veri
)
# Adım 7: Sonuçları kaydet
write.csv(
tahmin_sonuclari,
"data/tahmin_sonuclari.csv",
row.names = FALSE
)
print("İşlem tamamlandı!")
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.
Arkadaşlar, 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.
Birlikte veriyi keşfettik, geleceği
modelledik ve bu modeli çalışan bir motora
dönüştürdük. Motorumuz her gün bize risk altındaki müşterilerin
bir listesini (tahmin_sonuclari.csv) veriyor.
Bu bölümde, veri bilimcinin en önemli yeteneklerinden birini, verinin hikaye anlatıcılığı sanatını keşfedeceğiz. Teknik bir çıktıyı, kararları yönlendiren, ilham veren ve aksiyon yaratan bir hikayeye dönüştüreceğiz.
Tek bir sunum veya rapor herkes için uygun değildir. Hikayemizi anlatmadan önce, dinleyicilerimizin kim olduğunu ve ne duymak istediklerini anlamalıyız. Her paydaşın farklı bir “dilde” konuştuğunu ve farklı sorular sorduğunu unutmamalıyız:
CEO / Üst Yönetim:
Odak Noktası: “Büyük resim” ve stratejik etki.
Duymak İstedikleri: Projenin finansal etkisini (Ne kadar müşteri kurtaracağız? Bize ne kadara mal olacak?), stratejik önemini ve genel başarıyı.
Bizim Görevimiz: Karmaşık metriklerden kaçınarak net sonuçlar ve uygulanabilir öneriler sunmak.
Pazarlama Departmanı:
Odak Noktası: “Aksiyon listesi” ve operasyonel verimlilik.
Sordukları Soru: “Tamam, şimdi ne yapmamız gerekiyor?”
Bizim Görevimiz: Hangi müşterilere, hangi kanaldan, ne tür bir kampanya ile ulaşmaları gerektiğini gösteren, önceliklendirilmiş ve somut listeler sunmak.
Bu bölümde, hikayemizi Yönetim ve Pazarlama kitlelerinin dillerine çevirerek, projemizin yarattığı değeri en etkili şekilde onlara sunacağız.
Şimdi, Bölüm 3’te motorumuzun ürettiği o değerli
tahmin_sonuclari.csv dosyasını alıp, sıkıcı bir tablodan
çıkararak, farklı paydaşlarımızın bir bakışta anlayabileceği, güçlü ve
ikna edici görsellere dönüştürme zamanı.
Bu görev için R’ın görselleştirme devi olan
ggplot2 paketini kullanacağız. Unutmayın,
amacımız sadece güzel grafikler çizmek değil, doğru sorulara
cevap veren ve karar almayı kolaylaştıran
görseller yaratmaktır.
# Gerekli kütüphaneleri yüklüyoruz
library(dplyr)
library(ggplot2)
# Bölüm 3'ten gelen tahmin sonuçlarını ve müşteri bilgilerini okuyalım
# Bu dosyaların projenizin ana dizininde olduğunu varsayıyoruz
tahminler <- read.csv("data/tahmin_sonuclari.csv")
musteri_detaylari <- read.csv("data/yeni_musteri_bilgileri.csv")
# İki dosyayı birleştirerek daha zengin bir veri seti oluşturalm
# Bu sayede tahmin sonuçlarımıza, müşterinin harcama bilgisi gibi
# raporlama için kritik olan detayları eklemiş oluyoruz.
rapor_verisi <- left_join(tahminler, musteri_detaylari, by = "musteri_id")
# Oluşturduğumuz nihai rapor verisinin ilk 6 satırına bir göz atalım
# head() fonksiyonu, bir tablonun nasıl göründüğünü anlamak için harikadır.
print("Raporlama için Hazırlanan Veri Setinin İlk 6 Satırı:")
## [1] "Raporlama için Hazırlanan Veri Setinin İlk 6 Satırı:"
head(rapor_verisi)
## musteri_id terk_etme_olasiligi tahmin_durumu uyelik_suresi_ay aylik_harcama
## 1 1501 0.396 Kalacak 5 194.17
## 2 1502 0.262 Kalacak 24 144.06
## 3 1503 0.132 Kalacak 28 79.09
## 4 1504 0.268 Kalacak 11 345.00
## 5 1505 0.318 Kalacak 25 413.52
## 6 1506 0.674 Terk Edecek 6 258.52
Bu grafik, üst yönetime tek bir bakışta riskin nerede yoğunlaştığını gösterir. Amacı, karmaşık sayılar yerine, “Sorunun büyüklüğü ne?” ve “Nerede odaklanmalıyız?” sorularına anında görsel bir cevap vermektir.
ggplot(rapor_verisi, aes(x = terk_etme_olasiligi)) +
geom_histogram(bins = 20, fill = "#FF6347", color = "white", alpha = 0.8) +
geom_vline(xintercept = 0.40, color = "darkred", linetype = "dashed", size = 1.2) +
labs(title = "Yeni Müsteriler Arasindaki Terk Etme Riski Dagilimi",
x = "Terk Etme Olasiligi",
y = "Musteri Sayisi",
caption = "Kirmizi cizgi, 'riskli' olarak isaretledigimiz 0.40 esigini gostermektedir.") +
theme_minimal(base_size = 12)
Bu grafik CEO’ya ne söyler?
“Müşterilerimizin önemli bir kısmı, bizim belirlediğimiz kırmızı risk çizgisinin sağ tarafında, yani yüksek risk bölgesinde yoğunlaşmış durumda. Acil aksiyon almamız gereken somut bir grup var.”
Arkadaşlar, yukarıdaki grafik basit bir çubuk grafiği gibi görünse de, içinde bir CEO’nun stratejik karar vermesi için gereken tüm bilgiyi barındırır. Gelin bu grafiği bir dedektif gibi inceleyelim ve bir yöneticinin zihninde hangi düşünceleri tetiklediğini görelim.
X-Ekseni (Risk Metresi): Bu yatay çizgi, bir
müşterinin terk etme riskini 0’dan 1’e kadar
ölçer. Sağa doğru gittikçe risk artar.
Y-Ekseni (Müşteri Sayısı): Dikey çizgi, belirli bir risk seviyesinde kaç tane müşterimiz olduğunu sayar.
Kırmızı Çizgi (Stratejik Sınır): Bu, bizim Bölüm
2’de belirlediğimiz “artık bu müşteri risklidir” dediğimiz
0.40’lık sınırdır. Bu çizginin sağı,
aksiyon alınacak tehlikeli bölgedir.
Bir CEO bu grafiğe baktığında çubukların yüksekliğini saymaz, büyük resmi görür:
Problemin Büyüklüğü: CEO, kırmızı çizginin sağındaki tüm çubukların toplam yüksekliğine bakar ve anında şunu anlar: “Modelimiz, yeni gelen müşterilerden 10 tanesinin bizi terk etme riski taşıdığını söylüyor.” Bu, problemin boyutunu somut bir sayıya indirger.
Aciliyetin Seviyesi: CEO, sadece toplam sayıya
değil, çubukların dağılımına da bakar. En sağdaki, %70-80
riskli çubukları görür ve düşünür: “Bu risk sadece sınırda değil,
bazılarının durumu çok kritik. Bu bir ‘belki’ sorunu
değil, ‘yakında olacak’ bir sorun.”
Bu görsel bilgi, CEO’nun zihninde bir dizi stratejik kararı tetikler:
Kaynak Planlaması: “Demek ki, şu an masada acil ilgilenmemiz gereken 10 müşteri var. Pazarlama ekibinin bu 10 müşteriye yönelik bir kampanya hazırlaması gerekiyor. Bu kampanyanın maliyeti ne olur?”
Potansiyel Kazanç (ROI) Analizi: “Eğer bu 10 müşteriden sadece yarısını bile kurtarabilirsek, bu bize aylık X kadar ciro kazandırır. Bu yatırım yapmaya değer mi?”
Sorumluluk Atama: “Pazarlama Direktörü’nden, bu 10 kişilik gruba özel bir eylem planı hazırlamasını ve bir hafta içinde sonuçları bana sunmasını isteyeceğim.”
Gördüğünüz gibi, doğru tasarlanmış tek bir grafik; bir durumu ölçülebilir hale getirir, bir aciliyet hissi yaratır ve üst yönetimin kaynakları doğru yere yönlendirmesini sağlar. Bu, veri biliminin en güçlü yönlerinden biridir.
Bu bölümü bitirdiğimize göre, şimdi aynı detay ve derinlikle Pazarlama Direktörünün dünyasına geçmeye hazırız.
Bu grafik, pazarlama ekibine “Kime odaklanmalıyız?” sorusunun cevabını verir.
# Sadece "Terk Edecek" olarak tahmin edilenleri filtreleyelim
riskli_musteriler <- rapor_verisi %>% filter(tahmin_durumu == "Terk Edecek")
ggplot(riskli_musteriler, aes(x = aylik_harcama, y = terk_etme_olasiligi)) +
geom_point(aes(color = terk_etme_olasiligi, size = aylik_harcama), alpha = 0.7) +
scale_color_gradient(low = "orange", high = "red") +
labs(title = "Oncelik Verilecek Riskli Musteriler",
x = "Aylik Harcama (Deger)",
y = "Terk Etme Olasiligi (Risk)",
color = "Risk Skoru",
size = "Musteri Degeri") +
theme_minimal(base_size = 14)
Bu grafik Pazarlama Direktörüne ne söyler?
“Grafiğin sağ üst köşesindeki müşteriler, hem bize en çok parayı kazandıran (yüksek harcama) hem de terk etme riski en yüksek olanlar. Müdahale kampanyamıza ilk olarak bu müşterilerden başlamalıyız!”
Bu grafik, risk altındaki müşterilerimizi üç önemli boyutta aynı anda görmemizi sağlar:
X-Ekseni (Müşterinin Değeri): Bir müşteri ne
kadar sağda ise, şirketimize o kadar çok para
kazandırıyor demektir (Aylık Harcama).
Y-Ekseni (Müşterinin Riski): Bir müşteri ne
kadar yukarıda ise, onu kaybetme olasılığımız o kadar
yüksektir (Terk Etme Olasılığı).
Noktaların Boyutu ve Rengi: Bu iki özellik, X ve Y eksenindeki bilgiyi görsel olarak pekiştirir. Noktaların büyüklüğü müşterinin değerini, kırmızıya dönen rengi ise riskinin yüksekliğini gösterir.
Bu grafiğe bakan bir Pazarlama Direktörü, müşterileri anında zihninde dört ana segmente ayırır ve her biri için farklı bir aksiyon planı düşünür:
Kimler Bunlar? Hem bize en çok parayı kazandıran (yüksek harcama) hem de terk etme riski en yüksek olan müşteriler. Grafikteki büyük ve kırmızı noktalar.
Direktörün Düşüncesi: “Bunlar bizim ‘yüksek değerli, yüksek riskli’ mücevherlerimiz. Onları kaybetmek, sadece bir müşteri kaybetmek değil, ciromuzda ciddi bir delik açmak demektir. Standart bir kampanya yeterli olmaz.”
Aksiyon Planı: “Bu 3-4 müşteri için hemen özel müşteri temsilcilerimizi devreye sokalım. Birebir arayıp sorunlarını dinlesinler ve onlara özel, yüksek değerli bir teklif (örn: ücretsiz ek hizmet, büyük bir indirim) sunsunlar. Bu grubun maliyeti yüksek olabilir ama potansiyel kaybın yanında bu bir hiç.”
Kimler Bunlar? Terk etme riskleri çok yüksek, ancak şirketimize katkıları daha az olan müşteriler. Grafikteki küçük ama kırmızı noktalar.
Direktörün Düşüncesi: “Bu müşterileri de kaybetmek istemeyiz, ancak her birine özel temsilci atamak maliyetli olur. Verimliliği yüksek tutmalıyız.”
Aksiyon Planı: “Bu grup için otomatize edilmiş, kişiselleştirilmiş bir e-posta veya SMS kampanyası başlatalım. Onlara ‘Sizi özledik, geri dönün!’ diyen standart bir indirim kodu gönderelim. Maliyeti düşük, potansiyel etkisi yüksek bir hamle.”
Kimler Bunlar? Bize iyi para kazandıran ama şu an için riskleri daha düşük olan müşteriler. (Bu grafikte bu gruptan çok yok, çünkü sadece risklileri filtreledik, ama gerçek bir analizde bu grup da olurdu).
Direktörün Düşüncesi: “Bu müşteriler şu an güvende görünüyor. Onları gereksiz ve agresif kampanyalarla rahatsız etmeyelim. Sadece ilişkileri sıcak tutalım.”
Aksiyon Planı: “Bu gruba standart sadakat programımız dahilinde küçük jestler (doğum günü kutlaması vb.) yapmaya devam edelim. Şu an için özel bir müdahaleye gerek yok.”
Kimler Bunlar? Hem riskleri hem de değerleri düşük olan grup
Direktörün Düşüncesi: “Bu grup şu anki önceliğimiz değil. Kaynaklarımızı ilk iki gruba odaklamalıyız.”
Aksiyon Planı: “Bu grup için şimdilik bir aksiyon alınmayacak. Standart pazarlama iletişimimiz devam etsin.”
Bu tek grafik, Pazarlama Departmanının tüm “elde tutma” stratejisini veriye dayalı, segmente edilmiş ve bütçe dostu bir şekilde planlamasını sağlamış oldu.
Artık tüm parçalarımız var: Problemi anlayan bir CEO ve aksiyon almaya hazır bir Pazarlama Direktörü. Şimdi, bu iki kitleyi aynı odada topladığımızı ve onlara 5 dakikalık bir sunum yaptığımızı hayal edelim. Hikayemiz, klasik 3 perdelik bir yapı izleyecek.
(Bir sunumun ilk slaytında olduğumuzu hayal edelim. Slaytın başlığı: “Müşteri Kaybı: Reaktif Savunmadan Proaktif Hücuma”. İşte o slaytı anlatırken söyleyeceklerimiz:)
“Değerli yönetim, değerli pazarlama ekibi,
Bildiğiniz gibi, en büyük zorluklarımızdan biri müşteri kaybıydı. Bu sorunu bugüne kadar reaktif yöntemlerle, yani müşteri gittikten sonra anlamaya çalışarak çözmeye çalıştık. Ancak bu, kanamayı durdurmak yerine sadece yarayı temizlemek gibiydi.
Bu yüzden, proaktif bir yaklaşım benimsemek için bir veri bilimi projesi başlattık. Amacımız basitti: Müşterileri kaybetmeden önce, kimin risk altında olduğunu bilimsel bir kesinlikle tespit etmek ve onlara özel olarak müdahale etmek.”
(İkinci slayta geçilir, Grafik 1: Riskli Müşteri Dağılımı ekrandadır)
“Bugün, bu amaca ulaştığımızı duyurmaktan mutluluk duyuyorum. Geliştirdiğimiz makine öğrenmesi motoru sayesinde, artık terk etme riski taşıyan müşterileri %86’nın üzerinde bir başarıyla (Recall) tespit edebiliyoruz.
Ekranda gördüğünüz bu grafik, sadece son bir haftada sistemimize giren yeni müşteriler arasındaki risk dağılımını gösteriyor. Kırmızı çizginin sağında kalanlar, motorumuzun ‘acil müdahale gerekli’ dediği müşteriler. Gördüğünüz gibi, şu anda bile 10 yeni müşterimiz bu yüksek risk bölgesinde. Problem, somut ve ölçülebilir bir şekilde karşımızda duruyor.”
(Üçüncü slayta geçilir, Grafik 2: Öncelik Verilecek Riskli Müşteriler ekrandadır)
“Peki, bu 10 müşterinin hepsi aynı mı? Hayır. Motorumuz bize sadece kimin riskli olduğunu değil, kime öncelik vermemiz gerektiğini de söylüyor.
Bu grafik, o 10 riskli müşteriyi, hem şirketimize getirdikleri değere (yatay eksen) hem de risk seviyelerine (dikey eksen) göre haritalandırıyor. Pazarlama ekibi için bu, bir hazine haritası niteliğinde.
Önerimiz net ve uygulanabilir:
Acil Aksiyon (VIP Grubu): Grafiğin sağ üst köşesinde gördüğünüz, hem en değerli hem de en riskli olan 3-4 müşterimiz için, özel müşteri temsilcilerimiz hemen devreye girmeli. Bu, kişisel ve yüksek değerli bir müdahale gerektiriyor.
Otomatize Aksiyon (Geniş Risk Grubu): Geriye kalan, daha düşük değerli ama yine de riskli olan diğer müşteriler için ise, daha az maliyetli, otomatize edilmiş bir e-posta kampanyası başlatabiliriz.
Bu projenin, bu stratejiyle, önümüzdeki çeyrekte müşteri kaybı oranımızı %15 düşürme potansiyeli olduğunu öngörüyoruz. İlk sonuçları ve kampanyanın etkisini bir ay içinde tekrar sizlere sunmak için sabırsızlanıyoruz.”
Keşfettik: Verinin içindeki desenleri ve sorunları anladık.
Modelledik: Geleceği tahmin eden bir beyin inşa ettik ve onu iş hedeflerimize göre stratejik olarak kalibre ettik.
Kodladık: Bu beyni, yorulmadan çalışan otomatize bir motora dönüştürdük.
Sunduk: Ve son olarak, bu motorun ürettiği değeri, herkesin anlayabileceği ikna edici bir hikayeye çevirdik.
Bu, bir veri bilimi projesinin uçtan uca yaşam döngüsüdür. Pusulamız bize yolu gösterdi, ama unutmayın ki bu sadece bir başlangıç. Veri bilimi, sürekli bir öğrenme ve keşif yolculuğudur.
Az önce anlatımını yaptğımız (pipeline.R), arka planda
tıkır tıkır çalışıyor. Peki teknik olmayan bir pazarlama yöneticisinin
veya bir satış temsilcisinin parmaklarının ucuna getirmek
isteseydik?
İşte bu bölümde, modelimizi bir komut satırı aracından çıkarıp, herkesin bir web tarayıcısı üzerinden etkileşime girebileceği, yaşayan bir interaktif uygulamaya dönüştüreceğiz. Bu uygulamanın adı: R Shiny!
Shiny’yi anlamanın en kolay yolu, onu bir arabaya benzetmektir:
Kullanıcı Arayüzü (UI - User Interface): Bu, arabanın “içidir”. Direksiyon, gaz pedalı, gösterge paneli… Kullanıcının gördüğü ve etkileşime girdiği her şey buradadır.
Sunucu (Server): Bu, arabanın “motorudur”. Siz gaza bastığınızda (UI’da bir düğmeye tıkladığınızda), motorun (Server) arka planda çalışıp tekerlekleri döndürmesi gibi, bizim R kodlarımız da burada çalışır ve sonuçları gösterge paneline (UI) gönderir.
Haydi, bu iki parçayı birleştirerek kendi veri bilimi arabamızı inşa edelim!
Öncelikle, Shiny’yi kullanmak için gerekli paketi yüklememiz gerekiyor. Bu komutu R konsolunda bir kez çalıştırmanız yeterli:
install.packages("shiny")
Lütfen dikkat:
Şimdi, projenizin ana dizininde app.R adında tek bir yeni R script dosyası oluşturun. Tüm Shiny kodumuz bu dosyanın içinde yaşayacak.
app.R dosyamızın ilk bölümü, uygulamamızın nasıl görüneceğini tanımlayan ui kısmıdır. Bu, bizim dijital vitrinimiz olacak.
Amacımız basit ve işlevsel bir tasarım yapmak:
Kullanıcının yeni şikayet ve müşteri dosyalarını yükleyebileceği bir alan.
Tahmin işlemini başlatacak bir düğme.
Sonuçların gösterileceği temiz bir tablo.
Haydi bu vitrini, Shiny’nin fonksiyonlarını kullanarak inşa edelim. Aşağıdaki kod, app.R dosyanızın ilk parçasını oluşturacak:
library(shiny)
# Kullanıcı Arayüzünü Tanımla (UI)
ui <- fluidPage(
# Uygulama Başlığı
titlePanel("Müşteri Kaybı Tahmin Motoru"),
# Arayüzü yan menü ve ana panel olarak ikiye ayır
sidebarLayout(
# Yan Menü (Kontrollerin olduğu yer)
sidebarPanel(
h4("1. Yeni Veri Dosyalarını Yükleyin"),
# Yeni şikayet ve müşteri dosyalarını yüklemek için bir düğme
fileInput("yeni_veriler", "Şikayet ve Müşteri CSV Dosyalarını Seçin:",
multiple = TRUE, # Birden fazla dosya yüklemeye izin ver
accept = c(".csv")),
h4("2. Tahminleri Başlatın"),
# Tahmin işlemini tetikleyecek aksiyon düğmesi
actionButton("tahmin_et_butonu", "Tahminleri Oluştur!", icon = icon("rocket")),
br(),br(),
p("Not: Lütfen 'yeni_sikayetler.csv' ve 'yeni_musteri_bilgileri.csv' dosyalarını yükleyin.")
),
# Ana Panel (Sonuçların gösterileceği yer)
mainPanel(
h3("Tahmin Sonuçları"),
# Sonuç tablosunun gösterileceği alan
dataTableOutput("sonuc_tablosu")
)
)
)
Gördüğünüz gibi, UI kısmında HTML/CSS bilmemize gerek kalmadı. Shiny, titlePanel, fileInput, actionButton gibi basit R fonksiyonlarıyla tüm görsel arayüzü bizim için, sihirli bir şekilde oluşturuyor.
Şimdi geldik en heyecanlı kısma. Bölüm 3’te yazdığımız o harika fonksiyonları ve kaydettiğimiz modeli, bu arayüze nasıl bağlayacağız? app.R dosyasının ikinci bölümü olan server kısmı tam olarak bu işi yapar.
Not:
En iyi pratik, model ve fonksiyonlar gibi ağır dosyaları, uygulama her çalıştığında değil, sadece bir kez, uygulama başlarken yüklemektir. Bu, uygulamanızın çok daha hızlı çalışmasını sağlar. Aşağıdaki kodda bunu nasıl yaptığımıza dikkat edin.
# app.R dosyasının devamı...
source("fonksiyonlar.R")
model <- readRDS("data/churn_model.rds")
kutuphaneleri_yukle()
# --------------------------------------------------------------------------
server <- function(input, output) {
observeEvent(input$tahmin_et_butonu, {
req(input$yeni_veriler)
# Kullanıcının 2 dosya yüklediğinden emin olalım
req(nrow(input$yeni_veriler) == 2)
# Yüklenen dosya bilgilerini al
yuklenen_dosyalar <- input$yeni_veriler
# Hangi dosyanın hangisi olduğunu isimlerinden bulmaya çalışalım
sikayet_dosya_index <- which(grepl("sikayet", yuklenen_dosyalar$name))
musteri_dosya_index <- which(grepl("musteri", yuklenen_dosyalar$name))
# Dosyaları doğru yollardan OKU
sikayet_df <- read.csv(yuklenen_dosyalar$datapath[sikayet_dosya_index])
musteri_df <- read.csv(yuklenen_dosyalar$datapath[musteri_dosya_index])
# --- İŞTE SİHİR BURADA: YENİ FONKSİYONUMUZU DOĞRU ARGÜMANLARLA ÇAĞIRIYORUZ! ---
# 1. Veriyi Hazırla (Artık data frame'leri gönderiyoruz)
hazir_veri <- veri_hazirla(sikayetler_df = sikayet_df,
musteri_bilgileri_df = musteri_df)
# 2. Tahminleri Yap
tahmin_sonuclari <- tahmin_yap(model = model, hazir_veri = hazir_veri)
# -------------------------------------------------------------------------
# Sonuçları ara yüzdeki tabloya gönder
output$sonuc_tablosu <- renderDataTable({
tahmin_sonuclari
})
})
}
İşte bu, az önce yaptığımız o harika mühendislik çalışmasının meyvesi! Tüm karmaşık mantık, zaten fonksiyonlar.R dosyamızın içinde olduğu için, server kısmındaki kodumuz inanılmaz derecede temiz ve kısa oldu.
Artık hem “vitrinimiz” (UI) hem de “motorumuz” (Server) hazır. app.R dosyasının en sonuna, bu iki parçayı birleştiren ve uygulamayı başlatan sihirli komutu ekliyoruz.
# app.R dosyasının sonu
# UI ve Server'ı birleştirip Shiny uygulamasını çalıştır
shinyApp(ui = ui, server = server)
app.R dosyanızın tamamı artık hazır. RStudio’da bu dosyayı açtığınızda, sağ üst köşede “Run App” adında bir düğme göreceksiniz. Bu düğmeye bastığınızda, modeliniz canlanacak ve web tarayıcınızda interaktif uygulamanız açılacaktır!
Artık bir pazarlama yöneticisi, hiçbir R kodu bilmeden, kendi bilgisayarından yeni müşteri listelerini yükleyip, “Tahmin Et!” düğmesine basarak anında risk altındaki müşterileri görebilir.
Haydi Deneyelim.
Karşımıza uygulama geldi
yeni_sikayetler.csv ve
yeni_musteri_bilgileri.csv’yi aynı anda seçip
yükleyelim.
tahmim et butonunu tıklayalım.
Ve sonuçlar karşımızda
İşte bu, veri biliminin yarattığı değeri herkes için erişilebilir kılmaktır!
Bu bonus bölümde, Bölüm 3’te inşa ettiğimiz komut satırı motorunu alıp, onu yaşayan, interaktif ve iş değeri yaratan bir web uygulaması prototipine dönüştürdük. Bir veri bilimcinin alet çantasındaki en güçlü ve en etkileyici yeteneklerden birinin, yani Shiny’nin gücüne tanıklık ettik.
Bu Bir Simülasyon, Gerçek Bir “Deployment” Değil!
Arkadaşlar, burada çok önemli bir ayrımın altını çizmek isterim. Yaptığımız bu harika Shiny uygulaması, bir modelin canlı bir ortama nasıl alınabileceğinin küçük ölçekli bir provasıdır. Kendi bilgisayarımızda harika çalışıyor ve konsepti mükemmel bir şekilde kanıtlıyor.
Ancak, bu uygulamayı yüzlerce kişinin aynı anda kullanacağı, gerçek bir sunucuya koyma süreci, MLOps (Makine Öğrenmesi Operasyonları) ve Yazılım Mühendisliği alanlarına giren, kendi içinde çok derin ve komplike bir konudur. Gerçek bir “deployment” (canlıya alma) sürecinde, şu gibi onlarca ek soruyla karşılaşırız:
Elbette bir veri bilimcinin MLOps uzmanı olması beklenmez. Ancak bu süreçlerin varlığından haberdar olması, bir modelin sadece bir analizden ibaret olmadığını anlaması, projenin başından itibaren mühendislik ekipleriyle daha sağlıklı bir iletişim kurmasını sağlar.
Kısacası, biz bu bölümde arabamızın harika bir prototipini inşa ettik. Onu otoyola çıkarmak ve binlerce kilometre yol yapmasını sağlamak ise, bambaşka bir mühendislik disiplininin uzmanlık alanıdır.