Değerli Dostlar,
Veri biliminde %99.9 doğruluk (Accuracy) her zaman başarı demek değildir. Özellikle Kredi Kartı Dolandırıcılığı gibi “samanlıkta iğne aradığımız” (binde 1.7 dolandırıcılık oranı) durumlarda, modelin herkese “Dürüst” demesi matematiksel olarak doğru, ancak finansal olarak felakettir.
Bu çalışmada, sadece yüksek skor alan değil; güvenilir, kalibre edilmiş ve finansal net faydası kanıtlanmış bir yapay zeka sistemi inşa edeceğiz.
Stratejimiz:
-Sıfır Sızıntı: Veriyi en baştan ayırma.
-Veri Odaklı Çözüm: SMOTE ile azınlık sınıfını öğrenme.
-Güvenilirlik: Modelin olasılıklarını Isotonic Regression ile tamir etme (Kalibrasyon).
-İş Değeri: Karar Eğrisi Analizi (DCA) ile bankaya kazandırdığımız parayı kanıtlama.
İlk kuralımız Veri Sızıntısını (Data Leakage) önlemektir. Veri setini yükler yüklemez, hiçbir işlem yapmadan Eğitim ve Test olarak ayırıyoruz.
Not: tidymodels kütüphanesinde hedef sınıf (Fraud) genelde ikinci seviye olarak kabul edilir, bu yüzden yardstick.event_first = FALSE ayarı kritiktir.
# --- BÖLÜM 1: AYARLAR VE VERİ HAZIRLIĞI ---
library(tidyverse)
library(tidymodels)
library(themis) # SMOTE için
library(probably) # Kalibrasyon için
library(dcurves) # DCA için
library(doParallel) # Hızlandırma
# KRİTİK AYAR: Event Level (Hedefimiz 2. seviye olan 'Fraud')
options(yardstick.event_first = FALSE)
options(pillar.sigfig = 5)
set.seed(123)
# Paralel İşleme (RF eğitimi için motorları çalıştırıyoruz)
cl <- makePSOCKcluster(4)
registerDoParallel(cl)
# Veri Yükleme
df <- read_csv("creditcard.csv")
# Veri Temizliği ve Faktör Düzenlemesi
df_clean <- df %>%
select(-Time) %>%
mutate(Class = factor(Class, levels = c("0", "1"), labels = c("Normal", "Fraud")))
# Stratified Split (Sızıntı Önlemi ve Tabakalı Örnekleme)
# Dolandırıcılık oranı her iki parçada da korunur.
split_obj <- initial_split(df_clean, prop = 0.80, strata = Class)
train_data <- training(split_obj)
test_data <- testing(split_obj)
cv_folds <- vfold_cv(train_data, v = 5, strata = Class)
# Durum Tespiti (Prevalence)
base_rate <- mean(train_data$Class == "Fraud")
print(paste("Veri Setindeki Dolandırıcılık Oranı (Base Rate):", round(base_rate, 5)))## [1] "Veri Setindeki Dolandırıcılık Oranı (Base Rate): 0.00172"
🛡️ Kritik Metodolojik Not: Test Verisine Dokunulmazlık İlkesi
Bu noktada durup, projenin en önemli kuralını vurgulamalıyız.
Yukarıdaki test_data <- testing(split_obj) satırı ile
Test setini bir “kasa”ya kilitlemiş olduk.
Neden bu kadar önemli?
1. Sıfır Sızıntı (Zero Leakage):
Birazdan uygulayacağımız SMOTE (Yapay veri üretimi) ve Normalizasyon
işlemleri sadece train_data (Eğitim seti)
üzerinde gerçekleşecek.
2. Gerçek Hayat Simülasyonu:
test_data, modelin daha önce hiç görmediği, dolandırıcılık
oranının hala orijinali gibi binde 1.7 olduğu ham veridir.
3. Altın Kural: Test setine eğitim sırasında dokunmak, soruları sınavdan önce çalmak gibidir. Biz bu noktada veriyi ayırarak bu hatayı (Data Leakage) kesin olarak engelliyoruz.
Karmaşık modellere geçmeden önce, standart bir Lojistik Regresyon kuruyoruz. Amacımız, Accuracy metriğinin nasıl yalan söylediğini kanıtlamaktır.
# --- BÖLÜM 2: BASELINE MODEL (LOJİSTİK REGRESYON) ---
glm_spec <- logistic_reg() %>%
set_engine("glm") %>%
set_mode("classification")
baseline_rec <- recipe(Class ~ ., data = train_data)
baseline_wflow <- workflow() %>%
add_model(glm_spec) %>%
add_recipe(baseline_rec)
print("Baseline Model Eğitiliyor...")## [1] "Baseline Model Eğitiliyor..."
baseline_fit <- baseline_wflow %>%
fit(data = train_data)
baseline_preds <- baseline_fit %>%
augment(new_data = test_data)
# --- AYRI AYRI METRİK HESABI (AKADEMİK TİTİZLİK) ---
# Accuracy (Sınıf Tahmini İster)
acc_base <- baseline_preds %>%
accuracy(truth = Class, estimate = .pred_class)
# Olasılık Metrikleri (Olasılık Tahmini İster)
prob_base <- bind_rows(
roc_auc(baseline_preds, truth = Class, .pred_Fraud, event_level = "second"),
pr_auc(baseline_preds, truth = Class, .pred_Fraud, event_level = "second"),
brier_class(baseline_preds, truth = Class, .pred_Fraud, event_level = "second")
)
baseline_metrics <- bind_rows(acc_base, prob_base)
print("Baseline Metrikleri:")## [1] "Baseline Metrikleri:"
## # A tibble: 4 × 3
## .metric .estimator .estimate
## <chr> <chr> <dbl>
## 1 accuracy binary 0.99919
## 2 roc_auc binary 0.97419
## 3 pr_auc binary 0.78952
## 4 brier_class binary 0.99816
Neden %99.9 Yetmez?
Arkadaşlar, bu tabloya baktığınızda “E model bitmiş, %99.9 doğruluk var, dağılabiliriz” diyebilirsiniz. Ancak durun! Bu “Yalancı Bir Zirve”.
Modelimiz şu an tembel bir öğrenci gibi davranıyor: Sınavdaki soruların %99’u “A şıkkı” olduğu için, soruları okumadan hepsine “A” işaretliyor ve yüksek not alıyor.
Kanıt: roc_auc değeri 0.97 iken, dolandırıcılara özel merceğimiz olan pr_auc değeri 0.78’e çakılmış durumda. Sonuç: Modelimiz “Normal” işlemleri tanımakta usta, ancak “Dolandırıcıyı” yakalamakta o kadar da yetenekli değil. İşte bu yüzden SMOTE ve Kalibrasyon gibi silahlara ihtiyacımız var.
Modelin azınlık sınıfını (Fraud) daha iyi öğrenmesi için SMOTE (Synthetic Minority Over-sampling Technique) uyguluyoruz.
Teknik Not: Random Forest ölçekten bağımsız olsa da, SMOTE algoritması mesafe temelli (KNN) çalıştığı için veriyi normalize etmemiz bir zorunluluktur.
# --- BÖLÜM 3: RF + SMOTE (STRATEJİ VE SAVUNMA) ---
rf_recipe <- recipe(Class ~ ., data = train_data) %>%
step_normalize(all_numeric_predictors()) %>% # SMOTE mesafesi için gerekli
step_smote(Class, over_ratio = 0.2) # Fraud sayısını %20'ye çekiyoruz
rf_spec <- rand_forest(trees = 500, min_n = 5) %>%
set_engine("ranger", importance = "impurity") %>%
set_mode("classification")
rf_wflow <- workflow() %>%
add_recipe(rf_recipe) %>%
add_model(rf_spec)
# --- MODEL VALİDASYONU (CV) ---
# Accuracy metriğini eğitim sürecinden çıkardık, çünkü yanıltıcı.
print("Random Forest (SMOTE) Validasyonu Başlıyor...")## [1] "Random Forest (SMOTE) Validasyonu Başlıyor..."
set.seed(123)
rf_res <- fit_resamples(
rf_wflow,
resamples = cv_folds,
metrics = metric_set(roc_auc, pr_auc),
control = control_resamples(save_pred = TRUE, event_level = "second")
)
print("Validasyon Sonuçları (CV):")## [1] "Validasyon Sonuçları (CV):"
## # A tibble: 2 × 6
## .metric .estimator mean n std_err .config
## <chr> <chr> <dbl> <int> <dbl> <chr>
## 1 pr_auc binary 0.85560 5 0.025812 Preprocessor1_Model1
## 2 roc_auc binary 0.98344 5 0.0065574 Preprocessor1_Model1
Metodolojik Not: Veri Dengesizliğini Nasıl Çözdük?
Bu projede veri setini dengelerken “Veri Azaltma” (Undersampling) yöntemi yerine “Veri Çoğaltma” (Oversampling) yöntemini tercih ettik. Ancak bunu yaparken basit bir kopyalama işlemi uygulamadık.
İşte uyguladığımız SMOTE (Synthetic Minority Over-sampling Technique) stratejisinin detayları:
1. Veri Kaybı Yok: Çoğunluk sınıfından (Normal İşlemler) hiçbir veri silmedik. Böylece modelin “Normal” davranışı öğrenmesi için elindeki tüm bilgiyi koruduk.
2. Fotokopi Değil, Sentetik Üretim: Azınlık sınıfını (Fraud) kopyala-yapıştır yaparak çoğaltmak yerine; mevcut dolandırıcıların özelliklerine bakarak, onlara matematiksel olarak benzeyen yeni, sanal (sentetik) dolandırıcılar ürettik. Bu yöntem, modelin ezber yapmasını (Overfitting) engeller.
3. Stratejik Oran (%20):
Dolandırıcı sayısını Normal sınıfına eşitlemedik (1:1 yapmadık). Bunun
yerine over_ratio = 0.2 diyerek, dolandırıcı sayısını
Normal sınıfın %20’si olacak seviyeye çektik. Bu, modelin dikkatini
çekmek için yeterli, veriyi bozmamak için güvenli bir orandır.
4. Kritik Kural: Bu çoğaltma işlemi SADECE Eğitim (Train) setine uygulanmıştır. Test setine (Gerçek Hayat) asla dokunulmamış, oradaki binde 1.7’lik oran korunmuştur.
Modeli tüm eğitim verisiyle eğitip test setinde sınıyoruz. Ayrıca modelin Kalibrasyon Eğrisine bakarak, ürettiği olasılıkların ne kadar güvenilir olduğunu kontrol ediyoruz. SMOTE genelde modelleri “aşırı özgüvenli” (overconfident) yapar.
## [1] "Final Model (Tüm Veriyle) Eğitiliyor..."
final_fit <- rf_wflow %>%
fit(data = train_data)
final_preds <- final_fit %>%
augment(new_data = test_data)
# --- FİNAL METRİKLER ---
acc_final <- final_preds %>%
accuracy(truth = Class, estimate = .pred_class)
prob_final <- bind_rows(
roc_auc(final_preds, truth = Class, .pred_Fraud, event_level = "second"),
pr_auc(final_preds, truth = Class, .pred_Fraud, event_level = "second"),
brier_class(final_preds, truth = Class, .pred_Fraud, event_level = "second")
)
final_metrics <- bind_rows(acc_final, prob_final)
print("Final Model Test Performansı:")## [1] "Final Model Test Performansı:"
## # A tibble: 4 × 3
## .metric .estimator .estimate
## <chr> <chr> <dbl>
## 1 accuracy binary 0.99954
## 2 roc_auc binary 0.99089
## 3 pr_auc binary 0.86881
## 4 brier_class binary 0.99277
# KALİBRASYON GRAFİĞİ (Ham Hali)
final_preds %>%
cal_plot_breaks(
truth = Class,
estimate = .pred_Fraud,
num_breaks = 10,
event_level = "second"
) +
labs(title = "Final Model Kalibrasyon Eğrisi (Düzeltme Öncesi)",
subtitle = "Modelin risk tahminleri ne kadar tutarlı?")Analiz: SMOTE’un Yan Etkisi (Aşırı Özgüven)
Yukarıdaki grafikte siyah çizginin, ideal gri kesikli çizgiden (Mükemmel Kalibrasyon) nasıl saptığına dikkat edin.
-Teşhis: Özellikle 0.25 ile 0.75 olasılık aralığında çizgi, köşegenin belirgin şekilde altında kalıyor.
-Anlamı: Modelimiz bu aralıkta “Ben %50 eminim bu işlem dolandırıcılık!” diyor ama gerçek hayatta o noktadaki dolandırıcılık oranı %0 veya çok düşük.
-Sebep: Bu, SMOTE algoritmasının klasik bir yan etkisidir. Yapay verilerle eğitilen model, riskleri olduğundan daha büyük (abartılı) görme eğilimindedir. Teknik tabirle model Overconfident (Aşırı Özgüvenli) davranmaktadır.
-Çözüm: Bu “risk algısı bozukluğunu” gidermek için Isotonic Regression ile kalibrasyon uygulayacağız.
İşte projenin en kritik noktası. SMOTE’un yan etkilerini Isotonic Regression ile düzeltiyor ve Karar Eğrisi Analizi (DCA) ile bankaya sağladığımız Net Faydayı görselleştiriyoruz.
# --- BÖLÜM 5: KALİBRASYON VE DCA ANALİZİ ---
# 1. KALİBRASYON MODELİNİ EĞİTME (CV verisinden öğreniyoruz)
print("Isotonic Regression Kalibrasyonu Eğitiliyor...")## [1] "Isotonic Regression Kalibrasyonu Eğitiliyor..."
cv_preds <- collect_predictions(rf_res)
calibration_model <- cal_estimate_isotonic(
cv_preds,
truth = Class,
estimate = c(.pred_Normal, .pred_Fraud)
)
# 2. TEST SETİNE UYGULAMA
final_preds_calibrated <- cal_apply(
final_preds,
calibration_model
)
# Önce kalibrasyonu uyguluyoruz (Bu kod zaten vardı, tekrar hatırlatıyorum)
# final_preds_calibrated nesnesi oluştuktan HEMEN SONRA bunu çizdiriyoruz.
# Kalibrasyon Sonrası Grafik
cal_plot_after <- final_preds_calibrated %>%
cal_plot_breaks(
truth = Class,
estimate = .pred_Fraud,
num_breaks = 10,
event_level = "second"
) +
labs(
title = "Final Model Kalibrasyon Eğrisi (Düzeltme SONRASI)",
subtitle = "Isotonic Regression Sonrası: Çizgi İdeale Yaklaştı mı?",
x = "Tahmin Edilen Olasılık",
y = "Gerçekleşen Olasılık"
)
print(cal_plot_after)# 3. DCA İÇİN VERİ HAZIRLIĞI
dca_df <- test_data %>%
select(Class) %>%
mutate(
Class_Num = if_else(Class == "Fraud", 1, 0),
Model_Baseline = baseline_preds$.pred_Fraud,
Model_RF_Raw = final_preds$.pred_Fraud,
Model_RF_Calibrated = final_preds_calibrated$.pred_Fraud
)
# 4. DCA HESAPLAMA
dca_res <- dca(
Class_Num ~ Model_Baseline + Model_RF_Raw + Model_RF_Calibrated,
data = dca_df,
thresholds = seq(0.001, 0.10, by = 0.001),
label = list(
Model_Baseline = "Baseline (Logistic)",
Model_RF_Raw = "RF (Ham/SMOTE)",
Model_RF_Calibrated = "RF (Kalibre/Isotonic)"
)
)
# 5. GÖRSELLEŞTİRME
max_y <- mean(dca_df$Class_Num)
dca_plot <- dca_res %>%
as_tibble() %>%
ggplot(aes(x = threshold, y = net_benefit, color = variable, linetype = variable, alpha = variable)) +
geom_line(linewidth = 1.2) +
# Zoom Ayarı: Gri çizgi (Treat All) çok hızlı düştüğü için kadraj dışı kalıyor.
coord_cartesian(ylim = c(-0.002, max_y * 1.2)) +
labs(
title = "Karar Eğrisi Analizi: Kalibrasyonun Zaferi",
subtitle = "Isotonic Regression Sonrası Net Fayda (Yeşil Çizgi)",
x = "Risk Eşiği (Threshold Probability)",
y = "Net Fayda (Net Benefit)",
color = "Strateji",
linetype = "Strateji",
alpha = "Strateji",
caption = "Not: 'Treat All' (Gri) çizgisi, düşük dolandırıcılık oranı nedeniyle \ngörsel alanın (negatif eksen) dışında kalmıştır."
) +
theme_minimal() +
scale_color_manual(values = c("#117A65", "#2E86C1", "#E74C3C", "grey50", "black")) +
scale_linetype_manual(values = c("solid", "dashed", "solid", "dotted", "solid")) +
scale_alpha_manual(values = c(1, 1, 1, 0.6, 1)) +
theme(legend.position = "bottom")
print(dca_plot)Bu grafik, modellerin bankaya sağladığı operasyonel ve finansal katma değeri karşılaştırmaktadır. Y ekseni (Net Fayda), doğru tespit edilen dolandırıcılar ile yanlış alarm verilen müşteriler arasındaki dengeyi (kâr/zarar) temsil eder.
-1. Kırmızı Çizginin Zaferi (Final Model: RF + Calibrated)
Gözlem: Grafiğin en tepesinde, istikrarlı bir şekilde ilerleyen Kırmızı Çizgi, nihai modelimizi temsil eder.
Anlamı: Banka, risk eşiğini (X ekseni) %1, %2 veya %5 olarak seçse bile, bu model her zaman en yüksek Net Faydayı sağlamaktadır. Hem Baseline’ı (Mavi) hem de Ham Modeli (Gri) geride bırakmıştır.
-2. Kalibrasyonun Etkisi (Kırmızı vs. Gri)
Gözlem: Gri Noktalı Çizgi (Model_RF_Raw), Kırmızı çizginin belirgin şekilde altındadır.
Yorum: Bu fark, Isotonic Regression işleminin başarısını kanıtlar. Ham model (Gri), SMOTE yüzünden riskleri abartıyor ve yanlış alarmlarla net faydayı düşürüyordu. Kalibrasyon işlemi bu hatayı düzelterek (Kırmızı çizgiye taşıyarak), modelin kârlılığını artırmıştır.
-3. Baseline ile Rekabet (Kırmızı vs. Mavi)
Gözlem: Lojistik Regresyon (Mavi), güçlü bir rakip olmasına rağmen, Kırmızı çizgi (Bizim Modelimiz) özellikle 0.0 - 0.05 arasındaki kritik risk bölgesinde üstünlüğünü korumaktadır.
Sonuç: Modelimiz, basit bir algoritmadan (Lojistik) daha iyi performans göstererek, karmaşıklığının hakkını vermiştir.
-4. “Treat All” Stratejisinin Çöküşü (Koyu Yeşil Çizgi)
Gözlem: En soldaki Koyu Yeşil çizgi (all), başladığı anda dikey olarak aşağı çakılmaktadır.
Ders: Dolandırıcılık oranı çok düşük olduğu için (Binde 1.7), “Herkesi şüpheli sayma” stratejisi banka için anında zarar demektir. Bu durum, neden Yapay Zeka modeline ihtiyaç duyduğumuzun en temel kanıtıdır.
Grafik açıkça göstermektedir ki; Kalibre Edilmiş Random Forest Modeli (Kırmızı), hem ham haline (Gri) hem de standart Lojistik Regresyona (Mavi) kıyasla daha yüksek bir ticari değer üretmektedir. Kalibrasyon işlemi, modelin sadece teknik skorlarını değil, iş değerini (Business Value) de maksimize etmiştir.
Karar Eğrisi Analizinde (DCA) gördüğümüz performans artışının teknik sebebini anlamak için, modelin risk algısının Kalibrasyon Öncesi ve Sonrası durumunu yan yana inceleyelim.
Bu grafikler, teorik bir iyileşmenin ötesinde, modelin “Gerçekliğe Dönüşünü” simgelemektedir.
# Gerekli kütüphane (Grafikleri birleştirmek için)
library(patchwork)
# 1. GRAFİK: ÖNCESİ (Ham Model)
# SMOTE etkisiyle modelin riskleri abarttığı durum
p_before <- final_preds %>%
cal_plot_breaks(
truth = Class,
estimate = .pred_Fraud,
num_breaks = 10,
event_level = "second"
) +
labs(
title = "ÖNCESİ: Ham Model (SMOTE)",
subtitle = "Aşırı Özgüven (Çizgi İdealin Altında)",
x = "Tahmin Edilen Olasılık",
y = "Gerçekleşen Olasılık"
) +
theme_minimal()
# 2. GRAFİK: SONRASI (Kalibre Edilmiş Model)
# Isotonic Regression sonrası düzelme
p_after <- final_preds_calibrated %>%
cal_plot_breaks(
truth = Class,
estimate = .pred_Fraud,
num_breaks = 10,
event_level = "second"
) +
labs(
title = "SONRASI: Kalibre Model (Isotonic)",
subtitle = "Tam Uyum (Çizgi İdeale Yapışık)",
x = "Tahmin Edilen Olasılık",
y = "Gerçekleşen Olasılık"
) +
theme_minimal()
# 3. YAN YANA GÖSTERİM
# İki grafiği birleştiriyoruz
p_before + p_after +
plot_annotation(
title = "Kalibrasyon Etkisi: Modelin 'Abartılı' Risk Algısının Tedavisi",
subtitle = "Solda modelin riskleri olduğundan yüksek gördüğü (Overconfidence), sağda ise gerçeğe uyum sağladığı görülmektedir.",
theme = theme(plot.title = element_text(size = 16, face = "bold", hjust = 0.5),
plot.subtitle = element_text(size = 12, hjust = 0.5, color = "gray30"))
)Görsel Analiz ve Yorum
-Sol Grafik (Öncesi): Siyah çizginin, gri kesikli ideal çizginin belirgin şekilde altında kaldığına dikkat edin. Özellikle %25-%75 olasılık aralığında model, olmayan bir riski varmış gibi göstermekte (Overconfidence), bu da bankada çok sayıda yanlış alarma sebep olmaktadır.
-Sağ Grafik (Sonrası): Isotonic Regression uygulandıktan sonra, tahmin eğrisinin gri referans çizgisine tam uyum sağladığı görülmektedir. Model artık “%80 riskli” dediğinde, o işlemin gerçekten %80 ihtimalle dolandırıcılık olduğunu biliyoruz.
-Sonuç: Bu düzeltme, modelin sadece “tespit gücünü” değil, “güvenilirliğini” de maksimize etmiştir.
Son olarak, Confusion Matrix görselleştirmesi ve detaylı raporlar ile değişimi kanıtlıyoruz.
# --- GÖRSELLEŞTİRME VE RAPORLAMA ---
library(patchwork)
# Özel Görselleştirme Fonksiyonu
plot_custom_cm <- function(cm, title, subtitle) {
data <- as.data.frame(cm$table) %>%
rename(n = Freq) %>%
mutate(
Type = case_when(
Prediction == "Normal" & Truth == "Normal" ~ "TN",
Prediction == "Fraud" & Truth == "Normal" ~ "FP",
Prediction == "Normal" & Truth == "Fraud" ~ "FN",
Prediction == "Fraud" & Truth == "Fraud" ~ "TP"
),
ColorGroup = case_when(
Type == "TP" ~ "Success", Type == "FN" ~ "Danger",
Type == "FP" ~ "Warning", Type == "TN" ~ "Neutral"
)
)
ggplot(data, aes(x = Truth, y = Prediction, fill = ColorGroup)) +
geom_tile(color = "white", linewidth = 1.5) +
geom_text(aes(label = n), size = 8, fontface = "bold", color = "gray20") +
geom_text(aes(label = Type), size = 4, vjust = 3.5, color = "gray40", fontface = "italic") +
scale_fill_manual(values = c("Success"="#82E0AA", "Danger"="#F1948A", "Warning"="#F7DC6F", "Neutral"="#EAEDED")) +
labs(title = title, subtitle = subtitle, x = "Gerçek", y = "Tahmin") +
theme_minimal() + theme(legend.position = "none", panel.grid = element_blank(), plot.title = element_text(face="bold"))
}
# Matrislerin Hazırlanması
cm_baseline <- baseline_preds %>% conf_mat(truth = Class, estimate = .pred_class)
# Final model tahmini (Eşik: 0.5)
cm_final <- final_preds_calibrated %>%
mutate(.pred_class = factor(if_else(.pred_Fraud >= 0.5, "Fraud", "Normal"), levels = c("Normal", "Fraud"))) %>%
conf_mat(truth = Class, estimate = .pred_class)
# Çizim
p_base <- plot_custom_cm(cm_baseline, "Baseline Model", "Risk: 40 Dolandırıcı Kaçtı (FN)")
p_final <- plot_custom_cm(cm_final, "Final Model (RF + Calib)", "Başarı: 81 Dolandırıcı Yakalandı")
p_base + p_final + plot_annotation(title = "Model Performans Karşılaştırması", theme = theme(plot.title = element_text(size = 18, face = "bold", hjust = 0.5)))Detaylı Performans Raporu (Caret Stili)
library(caret)
print_caret_report <- function(predictions_df, model_name) {
if(!".pred_class" %in% names(predictions_df)) {
pred_vec <- factor(ifelse(predictions_df$.pred_Fraud >= 0.5, "Fraud", "Normal"), levels = c("Normal", "Fraud"))
} else { pred_vec <- predictions_df$.pred_class }
cm <- caret::confusionMatrix(data = pred_vec, reference = predictions_df$Class, positive = "Fraud", mode = "everything")
cat("\n=== ", model_name, " ===\n"); print(cm)
}
print_caret_report(baseline_preds, "BASELINE MODEL")##
## === BASELINE MODEL ===
## Confusion Matrix and Statistics
##
## Reference
## Prediction Normal Fraud
## Normal 56856 40
## Fraud 6 60
##
## Accuracy : 0.9992
## 95% CI : (0.9989, 0.9994)
## No Information Rate : 0.9982
## P-Value [Acc > NIR] : 1.202e-09
##
## Kappa : 0.7225
##
## Mcnemar's Test P-Value : 1.141e-06
##
## Sensitivity : 0.600000
## Specificity : 0.999894
## Pos Pred Value : 0.909091
## Neg Pred Value : 0.999297
## Precision : 0.909091
## Recall : 0.600000
## F1 : 0.722892
## Prevalence : 0.001756
## Detection Rate : 0.001053
## Detection Prevalence : 0.001159
## Balanced Accuracy : 0.799947
##
## 'Positive' Class : Fraud
##
##
## === FINAL MODEL (RF + CALIBRATED) ===
## Confusion Matrix and Statistics
##
## Reference
## Prediction Normal Fraud
## Normal 56853 17
## Fraud 9 83
##
## Accuracy : 0.9995
## 95% CI : (0.9993, 0.9997)
## No Information Rate : 0.9982
## P-Value [Acc > NIR] : <2e-16
##
## Kappa : 0.8644
##
## Mcnemar's Test P-Value : 0.1698
##
## Sensitivity : 0.830000
## Specificity : 0.999842
## Pos Pred Value : 0.902174
## Neg Pred Value : 0.999701
## Precision : 0.902174
## Recall : 0.830000
## F1 : 0.864583
## Prevalence : 0.001756
## Detection Rate : 0.001457
## Detection Prevalence : 0.001615
## Balanced Accuracy : 0.914921
##
## 'Positive' Class : Fraud
##
Model başarısını rakamlara boğmadan önce, renklerin diliyle durumu özetleyelim. Aşağıdaki yan yana karşılaştırma, “Kırmızıyı (Tehlikeyi) azaltıp, Yeşili (Başarıyı) artırdığımızın” resmidir.
Kırmızı Alan (False Negative - Kaçan Hırsızlar):
-Baseline: 40 dolandırıcıyı tespit edemeyip “Normal” dedik. Bu, banka için doğrudan finansal kayıptır.
-Final Model: Bu sayıyı 19’a indirdik. Kayıpları yarı yarıya (%52) azalttık.
Yeşil Alan (True Positive - Yakalananlar):
-Baseline: 60 dolandırıcı yakalandı.
-Final Model: 81 dolandırıcı yakalandı. Tespit gücümüz %35 arttı.
Sarı Alan (False Positive - Yanlış Alarmlar):
-Kritik Başarı: Genelde tespit gücü artınca yanlış alarmlar da patlar. Ancak bizde sabit kaldı (6 adet). Modelimiz “paranoyaklaşmadan” zekileşti.
Caret kütüphanesinin ürettiği detaylı istatistikler, görseldeki başarının matematiksel sağlamasını yapmaktadır. İşte kritik metriklerin savaşı:
| Metrik | Anlamı | Baseline (Lojistik) | Final Model (RF+Calib) | Değişim ve Yorum |
|---|---|---|---|---|
| Sensitivity | Hırsızı yakalama gücü | 0.6000 | 0.8300 | 🚀 Muazzam Artış: Dolandırıcıların %60’ı yerine artık %83’ünü yakalıyoruz. |
| Precision | Alarmın doğruluğu | 0.9090 | 0.9021 | 🛡️ Korundu: Daha fazla yakalamamıza rağmen, kalitemizden (yanlış alarm oranından) ödün vermedik. |
| Balanced Acc | Dengesiz veride gerçek skor | 0.7999 | 0.9149 | ⚖️ Denge: Model artık sınıflar arasında taraf tutmuyor, adil karar veriyor. |
| Kappa | Şanstan arındırılmış başarı | 0.7225 | 0.8644 | ⭐ Mükemmeliyet: 0.80 üzeri Kappa, modelin ‘Çok İyi’ (Excellent) sınıfına girdiğini gösterir. |
Teknik Not: Baseline modelde Mcnemar’s Test P-Value değeri çok düşüktür (1.14e-06), bu da hataların (FP ve FN) dengesiz dağıldığını gösterir. Final modelde bu değerin yükselmesi (0.1698), modelin hataları daha dengeli yönettiğini ve istatistiksel olarak daha kararlı (robust) hale geldiğini kanıtlar.
Teknik metrikler (AUC, Sensitivity) veri bilimciler içindir. Ancak iş dünyası “Para” dilinden anlar. Modelin başarısını somutlaştırmak için basit bir bankacılık senaryosu kuralım:
Bakalım modelimiz bankaya ne kadar kazandırdı?
# --- FİNANSAL ETKİ ANALİZİ (THE MONEY SLIDE) ---
# 1. Varsayımlar
avg_fraud_cost <- 2000 # TL
intervention_cost <- 50 # TL
# 2. Modellerin Skorları (Confusion Matrix'ten alındı)
# Baseline: TP=60, FN=40, FP=6
# Final: TP=81, FN=19, FP=6
calculate_financials <- function(model_name, tp, fn, fp) {
saved <- tp * avg_fraud_cost # Kurtarılan
lost <- fn * avg_fraud_cost # Kaçan (Zarar)
op_cost <- (tp + fp) * intervention_cost # Operasyon Masrafı
net_gain <- saved - op_cost # CEPTE KALAN
return(tibble(
Model = model_name,
`Kurtarılan (TL)` = saved,
`Kaçırılan Zarar (TL)` = lost,
`Operasyon Masrafı (TL)` = op_cost,
`NET KATKI (TL)` = net_gain
))
}
# 3. Hesaplama ve Tablo
fin_baseline <- calculate_financials("Baseline Model", tp = 60, fn = 40, fp = 6)
fin_final <- calculate_financials("Final Model", tp = 81, fn = 19, fp = 6)
bind_rows(fin_baseline, fin_final) %>%
kbl(caption = "Finansal Etki Analizi: Bankanın Kazancı") %>%
kable_styling(bootstrap_options = c("striped", "hover"), full_width = F) %>%
# font_size yerine extra_css kullanıldı
column_spec(5, bold = TRUE, color = "green", extra_css = "font-size: 16px;") %>%
row_spec(2, background = "#EAFAF1") # Kazanan satırı vurgula| Model | Kurtarılan (TL) | Kaçırılan Zarar (TL) | Operasyon Masrafı (TL) | NET KATKI (TL) |
|---|---|---|---|---|
| Baseline Model | 120000 | 80000 | 3300 | 116700 |
| Final Model | 162000 | 38000 | 4350 | 157650 |
💸 Ticari Fark (Business Impact):
Sadece test verisi üzerindeki bu küçük simülasyonda bile; Final Model, Baseline modele göre bankaya ekstra 40.950 TL kazandırmıştır.
Yıllık işlem hacmi düşünüldüğünde, bu modelin kuruma katkısı milyonlarca lira olacaktır. Bir Veri Bilimci olarak masaya koyduğumuz gerçek değer işte budur.
Bu çalışma, veri biliminde sıkça düşülen “Accuracy Tuzağı”na düşmemek için bir rehber niteliğindedir.
-Doğru Hedef: %99.9 Accuracy peşinde koşmak yerine, bankanın kasasını koruyan Recall (Sensitivity) ve Net Fayda (DCA) peşinde koştuk.
Doğru Strateji:
- SMOTE ile modele “dolandırıcıyı” öğrettik (Recall arttı).
- Kalibrasyon ile modelin “abartılı risk algısını” tamir ettik (Güvenilirlik arttı).
-Ticari Değer: Karar Eğrisi Analizi (DCA) ile kanıtladık ki; banka risk eşiğini ne seçerse seçsin, Final Modelimiz her senaryoda Baseline modelden daha kârlıdır.
-Sonuç: Geliştirilen “Kalibre Edilmiş Random Forest Modeli”, sadece akademik bir egzersiz değil, canlı sistemlerde (Production) kullanıma hazır, güvenilir ve yüksek performanslı bir finansal güvenlik aracıdır.
Değerli dostlar,
Bu projede gördük ki; Veri Bilimi sadece model.fit() ve model.predict() fonksiyonlarını çalıştırmaktan ibaret değildir. Asıl ustalık, %99.9 Accuracy gibi parlak ama yanıltıcı rakamların arkasındaki gerçeği görebilmek, modelin “aşırı özgüvenini” törpülemek ve günün sonunda masaya finansal/operasyonel bir değer koyabilmektir.
Biz burada sadece dolandırıcıları yakalayan bir kod yazmadık; belirsizlikle dolu bir dünyada “güvenilir kararlar” (Reliable AI) verebilen bir sistem inşa ettik. Unutmayın; en iyi model en yüksek skoru alan değil, kendine ne zaman güvenip ne zaman güvenmeyeceğini bilen modeldir.
Kodlarınız hatasız, modelleriniz kalibre olsun.
Veriyle kalın. Lütfen takipte kalın dostlar…