Bu bütünleme projesinde Spaceship Titanic veri seti üzerinde çalışıyorum. Amaç, uzay gemisindeki yolcuların alternatif bir boyuta nakledilip nakledilmediğini (Transported) tahmin etmektir.
Proje Akışı:
Bu projede önce train ve test verilerini yükleyip eksik değerleri analiz edeceğim. Ardından verileri temizleyip tidymodels recipe ile ön işleme yapacağım. Train verisini yüzde 75 deneme_train ve yüzde 25 deneme_test olarak ayıracağım. Üç farklı makine öğrenmesi modeli (Lojistik Regresyon, Random Forest, XGBoost) kuracağım. Modelleri deneme_test setinde değerlendirip confusion matrix, accuracy, precision, recall ve F1-score hesaplayacağım. En iyi performansı gösteren modeli seçeceğim. Tüm train verisiyle final modelleri eğitip gerçek test setinde tahmin yapacağım. Son olarak Kaggle’a submission dosyalarını yükleyeceğim.
library(tidyverse) # Veri manipülasyonu
library(tidymodels) # Modelleme framework'ü
library(xgboost) # XGBoost modeli
library(ranger) # Random Forest
library(knitr) # Tablo formatı
library(ggthemes) # Grafik temaları
library(patchwork) # Grafikleri birleştirmek için
train_raw <- read_csv("train.csv", show_col_types = FALSE)
test_raw <- read_csv("test.csv", show_col_types = FALSE)
cat("=== TRAIN VERİSİ ===\n")
## === TRAIN VERİSİ ===
glimpse(train_raw)
## Rows: 8,693
## Columns: 14
## $ PassengerId <chr> "0001_01", "0002_01", "0003_01", "0003_02", "0004_01", "0…
## $ HomePlanet <chr> "Europa", "Earth", "Europa", "Europa", "Earth", "Earth", …
## $ CryoSleep <lgl> FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, TRUE, FA…
## $ Cabin <chr> "B/0/P", "F/0/S", "A/0/S", "A/0/S", "F/1/S", "F/0/P", "F/…
## $ Destination <chr> "TRAPPIST-1e", "TRAPPIST-1e", "TRAPPIST-1e", "TRAPPIST-1e…
## $ Age <dbl> 39, 24, 58, 33, 16, 44, 26, 28, 35, 14, 34, 45, 32, 48, 2…
## $ VIP <lgl> FALSE, FALSE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, FA…
## $ RoomService <dbl> 0, 109, 43, 0, 303, 0, 42, 0, 0, 0, 0, 39, 73, 719, 8, 32…
## $ FoodCourt <dbl> 0, 9, 3576, 1283, 70, 483, 1539, 0, 785, 0, 0, 7295, 0, 1…
## $ ShoppingMall <dbl> 0, 25, 0, 371, 151, 0, 3, 0, 17, 0, NA, 589, 1123, 65, 12…
## $ Spa <dbl> 0, 549, 6715, 3329, 565, 291, 0, 0, 216, 0, 0, 110, 0, 0,…
## $ VRDeck <dbl> 0, 44, 49, 193, 2, 0, 0, NA, 0, 0, 0, 124, 113, 24, 7, 0,…
## $ Name <chr> "Maham Ofracculy", "Juanna Vines", "Altark Susent", "Sola…
## $ Transported <lgl> FALSE, TRUE, FALSE, FALSE, TRUE, TRUE, TRUE, TRUE, TRUE, …
cat("\n=== TEST VERİSİ ===\n")
##
## === TEST VERİSİ ===
glimpse(test_raw)
## Rows: 4,277
## Columns: 13
## $ PassengerId <chr> "0013_01", "0018_01", "0019_01", "0021_01", "0023_01", "0…
## $ HomePlanet <chr> "Earth", "Earth", "Europa", "Europa", "Earth", "Earth", "…
## $ CryoSleep <lgl> TRUE, FALSE, TRUE, FALSE, FALSE, FALSE, TRUE, TRUE, TRUE,…
## $ Cabin <chr> "G/3/S", "F/4/S", "C/0/S", "C/1/S", "F/5/S", "F/7/P", "B/…
## $ Destination <chr> "TRAPPIST-1e", "TRAPPIST-1e", "55 Cancri e", "TRAPPIST-1e…
## $ Age <dbl> 27, 19, 31, 38, 20, 31, 21, 20, 23, 24, 19, 45, 44, 46, 2…
## $ VIP <lgl> FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, F…
## $ RoomService <dbl> 0, 0, 0, 0, 10, 0, 0, 0, 0, 0, 339, 932, 0, 0, 0, 0, 2, 0…
## $ FoodCourt <dbl> 0, 9, 0, 6652, 0, 1615, NA, 0, 0, 639, 3, 74, 1561, 0, 0,…
## $ ShoppingMall <dbl> 0, 0, 0, 0, 635, 263, 0, 0, 0, 0, 136, NA, 0, 0, 0, 0, 25…
## $ Spa <dbl> 0, 2823, 0, 181, 0, 113, 0, 0, 0, 0, 237, 7, 14, 0, 1687,…
## $ VRDeck <dbl> 0, 0, 0, 585, 0, 60, 0, 0, 0, 0, 0, 1010, 224, 0, 92, NA,…
## $ Name <chr> "Nelly Carsoning", "Lerome Peckers", "Sabih Unhearfus", "…
Veri Seti Özeti:
| Değişken | Açıklama | Tür |
|---|---|---|
| PassengerId | Yolcu kimlik numarası | Karakter |
| HomePlanet | Kalkış gezegeni (Europa, Earth, Mars) | Kategorik |
| CryoSleep | Dondurulmuş uyku modunda mı? | Boolean |
| Cabin | Kabin numarası (Deck/Num/Side formatında) | Karakter |
| Destination | Varış noktası gezegeni | Kategorik |
| Age | Yolcu yaşı | Sayısal |
| VIP | VIP servisi aldı mı? | Boolean |
| RoomService | Oda servisi harcaması (TL) | Sayısal |
| FoodCourt | Yemek alanı harcaması (TL) | Sayısal |
| ShoppingMall | Alışveriş merkezi harcaması (TL) | Sayısal |
| Spa | Spa harcaması (TL) | Sayısal |
| VRDeck | VR Deck harcaması (TL) | Sayısal |
| Name | Yolcu adı soyadı | Karakter |
| Transported | Alternatif boyuta nakledildi mi? (HEDEF) | Boolean |
train_na <- tibble(
Degisken = names(train_raw),
Eksik_Deger = colSums(is.na(train_raw)),
Toplam_Gozlem = nrow(train_raw),
Eksik_Oran = round(colSums(is.na(train_raw)) / nrow(train_raw) * 100, 2)
) %>%
arrange(desc(Eksik_Deger))
kable(train_na, caption = "Train Verisinde Eksik Değer Dağılımı")
| Degisken | Eksik_Deger | Toplam_Gozlem | Eksik_Oran |
|---|---|---|---|
| CryoSleep | 217 | 8693 | 2.50 |
| ShoppingMall | 208 | 8693 | 2.39 |
| VIP | 203 | 8693 | 2.34 |
| HomePlanet | 201 | 8693 | 2.31 |
| Name | 200 | 8693 | 2.30 |
| Cabin | 199 | 8693 | 2.29 |
| VRDeck | 188 | 8693 | 2.16 |
| FoodCourt | 183 | 8693 | 2.11 |
| Spa | 183 | 8693 | 2.11 |
| Destination | 182 | 8693 | 2.09 |
| RoomService | 181 | 8693 | 2.08 |
| Age | 179 | 8693 | 2.06 |
| PassengerId | 0 | 8693 | 0.00 |
| Transported | 0 | 8693 | 0.00 |
train_na %>%
filter(Eksik_Deger > 0) %>%
ggplot(aes(x = reorder(Degisken, Eksik_Oran), y = Eksik_Oran)) +
geom_col(fill = "#E74C3C", alpha = 0.8) +
geom_text(aes(label = paste0(Eksik_Oran, "%")), hjust = -0.2, size = 3.5) +
coord_flip() +
labs(
title = "Train Verisinde Eksik Değer Oranları",
subtitle = paste0("Toplam ", sum(train_na$Eksik_Deger), " eksik değer mevcut"),
x = "Değişken",
y = "Eksik Değer Oranı (%)"
) +
theme_minimal() +
theme(
plot.title = element_text(size = 14, face = "bold"),
plot.subtitle = element_text(size = 11),
axis.text = element_text(size = 10)
) +
ylim(0, max(train_na$Eksik_Oran) * 1.15)
Train Verisi Eksik Değer Özeti:
test_na <- tibble(
Degisken = names(test_raw),
Eksik_Deger = colSums(is.na(test_raw)),
Toplam_Gozlem = nrow(test_raw),
Eksik_Oran = round(colSums(is.na(test_raw)) / nrow(test_raw) * 100, 2)
) %>%
arrange(desc(Eksik_Deger))
kable(test_na, caption = "Test Verisinde Eksik Değer Dağılımı")
| Degisken | Eksik_Deger | Toplam_Gozlem | Eksik_Oran |
|---|---|---|---|
| FoodCourt | 106 | 4277 | 2.48 |
| Spa | 101 | 4277 | 2.36 |
| Cabin | 100 | 4277 | 2.34 |
| ShoppingMall | 98 | 4277 | 2.29 |
| Name | 94 | 4277 | 2.20 |
| CryoSleep | 93 | 4277 | 2.17 |
| VIP | 93 | 4277 | 2.17 |
| Destination | 92 | 4277 | 2.15 |
| Age | 91 | 4277 | 2.13 |
| HomePlanet | 87 | 4277 | 2.03 |
| RoomService | 82 | 4277 | 1.92 |
| VRDeck | 80 | 4277 | 1.87 |
| PassengerId | 0 | 4277 | 0.00 |
test_na %>%
filter(Eksik_Deger > 0) %>%
ggplot(aes(x = reorder(Degisken, Eksik_Oran), y = Eksik_Oran)) +
geom_col(fill = "#3498DB", alpha = 0.8) +
geom_text(aes(label = paste0(Eksik_Oran, "%")), hjust = -0.2, size = 3.5) +
coord_flip() +
labs(
title = "Test Verisinde Eksik Değer Oranları",
subtitle = paste0("Toplam ", sum(test_na$Eksik_Deger), " eksik değer mevcut"),
x = "Değişken",
y = "Eksik Değer Oranı (%)"
) +
theme_minimal() +
theme(
plot.title = element_text(size = 14, face = "bold"),
plot.subtitle = element_text(size = 11),
axis.text = element_text(size = 10)
) +
ylim(0, max(test_na$Eksik_Oran) * 1.15)
Test Verisi Eksik Değer Özeti:
Recipe’de step_mutate olmadığı için bazı işlemleri
önceden yapıyoruz:
# Train verisi için
train_raw <- train_raw %>%
# Hedef değişkeni faktöre çevir (tidymodels için)
mutate(Transported = factor(ifelse(Transported, "Yes", "No"),
levels = c("No", "Yes"))) %>%
# Cabin'i ayır (Deck/Num/Side formatı)
separate(Cabin, into = c("Deck", "CabinNum", "Side"),
sep = "/", remove = FALSE, fill = "right") %>%
mutate(
# Düşük frekanslı Deck'leri birleştir
Deck = ifelse(Deck %in% c("A", "D", "T"), "Other", Deck),
Deck = as.factor(Deck),
CabinNum = as.numeric(CabinNum),
Side = as.factor(Side),
# Diğer kategorik değişkenler
HomePlanet = as.factor(HomePlanet),
CryoSleep = as.factor(CryoSleep),
Destination = as.factor(Destination),
VIP = as.factor(VIP)
)
# Test verisi için (aynı işlemler)
test_raw <- test_raw %>%
separate(Cabin, into = c("Deck", "CabinNum", "Side"),
sep = "/", remove = FALSE, fill = "right") %>%
mutate(
Deck = ifelse(Deck %in% c("A", "D", "T"), "Other", Deck),
Deck = as.factor(Deck),
CabinNum = as.numeric(CabinNum),
Side = as.factor(Side),
HomePlanet = as.factor(HomePlanet),
CryoSleep = as.factor(CryoSleep),
Destination = as.factor(Destination),
VIP = as.factor(VIP)
)
Yapılan İşlemler:
Bu aşamada Transported değişkeni “Yes” ve “No” faktör seviyelerine çevrildi. Cabin değişkeni Deck, CabinNum ve Side olarak 3 sütuna ayrıldı. Nadir Deck değerleri (A, D, T) “Other” kategorisinde birleştirildi. Tüm kategorik değişkenler faktör tipine çevrildi.
set.seed(123) # Tekrar üretilebilirlik için
# Stratified sampling (Transported değişkenine göre)
data_split <- initial_split(train_raw, prop = 0.75, strata = Transported)
deneme_train <- training(data_split)
deneme_test <- testing(data_split)
# Ayrım özeti
tibble(
Veri_Seti = c("Deneme Train", "Deneme Test", "TOPLAM"),
Satir_Sayisi = c(nrow(deneme_train), nrow(deneme_test), nrow(train_raw)),
Oran = c("75%", "25%", "100%")
) %>% kable(caption = "Train-Test Ayrımı (Stratified Sampling)")
| Veri_Seti | Satir_Sayisi | Oran |
|---|---|---|
| Deneme Train | 6519 | 75% |
| Deneme Test | 2174 | 25% |
| TOPLAM | 8693 | 100% |
Stratified Sampling Neden Önemli?
Hedef değişken (Transported) dengesiz dağılmış olabilir. Stratified sampling ile her iki sette de (deneme_train ve deneme_test) Transported’ın oranları korunur. Bu durum model değerlendirmesinin daha güvenilir olmasını sağlar.
Tüm veri ön işleme adımlarını tek bir recipe içinde yapıyoruz. Bu sayede işlemler tekrar üretilebilir ve hem train hem de test verilerine aynı şekilde uygulanır.
model_rec <- recipe(Transported ~ ., data = deneme_train) %>%
# 1. Gereksiz değişkenleri çıkar (ID ve isim bilgisi model için gerekli değil)
step_rm(PassengerId, Name, Cabin) %>%
# 2. Karakter kolonları faktöre çevir (XGBoost için gerekli)
step_string2factor(all_nominal_predictors()) %>%
# 3. Eksik değerleri doldur
# - Kategorik değişkenler için: En sık görülen değer (mode)
# - Sayısal değişkenler için: Medyan
step_impute_mode(all_nominal_predictors()) %>%
step_impute_median(all_numeric_predictors()) %>%
# 4. Kategorik değişkenleri dummy değişkenlere çevir (one-hot encoding)
step_dummy(all_nominal_predictors()) %>%
# 5. Sıfır varyanslı değişkenleri çıkar (sabit değerli kolonlar)
step_zv(all_predictors())
# Recipe'yi hazırla
prep_rec <- prep(model_rec)
# Verileri dönüştür
train_baked <- bake(prep_rec, new_data = NULL) # deneme_train için
test_baked <- bake(prep_rec, new_data = deneme_test) # deneme_test için
na_check <- tibble(
Veri_Seti = c("Deneme Train (recipe sonrası)",
"Deneme Test (recipe sonrası)"),
Toplam_NA = c(sum(is.na(train_baked)),
sum(is.na(test_baked))),
Satir = c(nrow(train_baked),
nrow(test_baked)),
Sutun = c(ncol(train_baked),
ncol(test_baked))
)
kable(na_check, caption = "Recipe Sonrası Eksik Değer Kontrolü")
| Veri_Seti | Toplam_NA | Satir | Sutun |
|---|---|---|---|
| Deneme Train (recipe sonrası) | 0 | 6519 | 20 |
| Deneme Test (recipe sonrası) | 0 | 2174 | 20 |
if(sum(is.na(train_baked)) == 0 & sum(is.na(test_baked)) == 0) {
cat("\nDoğrulama: Recipe sonrası her iki veri setinde de hiçbir eksik değer (NA) kalmamıştır.\n\n")
} else {
cat("\nUYARI: Recipe sonrası hala eksik değer bulunmaktadır!\n\n")
}
Doğrulama: Recipe sonrası her iki veri setinde de hiçbir eksik değer (NA) kalmamıştır.
Bu bölümde üç farklı makine öğrenmesi yöntemi kullanacağım. Her model için önce teorik açıklama, ardından model kurulumu yer almaktadır.
Lojistik Regresyon, binary (ikili) sınıflandırma problemleri için kullanılan parametrik bir istatistiksel yöntemdir. Doğrusal regresyondan farklı olarak, çıktıyı [0,1] aralığına sıkıştırmak için sigmoid (lojistik) fonksiyonu kullanır:
\[P(Y=1|X) = \frac{1}{1 + e^{-(\beta_0 + \beta_1X_1 + ... + \beta_pX_p)}}\]
Lojistik regresyonun temel avantajları arasında hızlı eğitim ve tahmin süreci, yorumlanabilir katsayılar (odds ratio’ya çevrilebilir), düşük overfitting riski ve baseline model olarak ideal olması yer alır. Ancak doğrusal olmayan ilişkileri yakalayamaması, feature engineering gerektirmesi ve karmaşık etkileşimleri modelleyememesi gibi sınırlamaları bulunmaktadır.
logistic_spec <- logistic_reg() %>%
set_engine("glm") %>%
set_mode("classification")
logistic_wf <- workflow() %>%
add_recipe(model_rec) %>%
add_model(logistic_spec)
logistic_fit <- fit(logistic_wf, deneme_train)
Random Forest, birden fazla karar ağacının (decision tree) tahminlerini birleştiren bir ensemble (topluluk öğrenmesi) yöntemidir. Temel prensibi şu şekilde işler: Her ağaç, train verisinden bootstrap sampling ile rastgele seçilmiş örneklerle eğitilir. Her düğümde rastgele seçilmiş özellikler arasından en iyisi kullanılır. Final tahmin, tüm ağaçların oylamasıyla yapılır (classification için majority voting).
Random Forest overfitting’e karşı dayanıklıdır (bagging sayesinde) ve doğrusal olmayan ilişkileri yakalayabilir. Feature importance sağlaması ve hiperparametre ayarına az duyarlı olması diğer avantajlarıdır. Ancak yorumlanabilirliği düşüktür, eğitim süresi uzun olabilir ve büyük veri setlerinde memory-intensive olabilir.
Bu modelde mtry parametresi 5 olarak ayarlanmıştır (her split’te rastgele seçilen değişken sayısı), trees parametresi 500’dür (toplam ağaç sayısı) ve min_n parametresi 10’dur (terminal düğümdeki minimum gözlem sayısı).
rf_spec <- rand_forest(
mtry = 5,
trees = 500,
min_n = 10
) %>%
set_engine("ranger") %>%
set_mode("classification")
rf_wf <- workflow() %>%
add_recipe(model_rec) %>%
add_model(rf_spec)
rf_fit <- fit(rf_wf, deneme_train)
XGBoost, gradient boosting algoritmasının optimize edilmiş versiyonudur. Random Forest’tan farklı olarak ağaçları sıralı (sequential) şekilde eğitir. İlk ağaç veriyi öğrenir, ikinci ağaç ilk ağacın hatalarını düzeltmeye çalışır, her yeni ağaç önceki ağaçların hatalarını minimize etmeye odaklanır ve final tahmin tüm ağaçların ağırlıklı toplamıdır.
XGBoost state-of-the-art performans sağlar (Kaggle yarışmalarında sık kazanır), eksik değerleri otomatik işleyebilir, feature importance sağlar ve regularization sayesinde overfitting kontrolü sunar. Ancak hiperparametre ayarı kritik ve karmaşıktır, eğitim süresi uzun olabilir ve yorumlanabilirlik düşüktür.
Bu modelde trees parametresi 500 (boosting iteration sayısı), tree_depth parametresi 6 (ağaç derinliği, overfitting kontrolü için), learn_rate parametresi 0.1 (her ağacın katkısı) ve min_n parametresi 10 (leaf node’da minimum gözlem sayısı) olarak ayarlanmıştır.
xgb_spec <- boost_tree(
trees = 500,
tree_depth = 6,
learn_rate = 0.1,
min_n = 10
) %>%
set_engine("xgboost") %>%
set_mode("classification")
xgb_wf <- workflow() %>%
add_recipe(model_rec) %>%
add_model(xgb_spec)
xgb_fit <- fit(xgb_wf, deneme_train)
Şimdi kurduğumuz üç modeli deneme_test seti üzerinde değerlendireceğiz. Bu aşamada confusion matrix, accuracy, precision, recall, F1-score ve ROC-AUC gibi metrikleri hesaplayacağız.
# Tahminler (hem sınıf hem de olasılık)
logistic_pred <- predict(logistic_fit, deneme_test, type = "class") %>%
bind_cols(predict(logistic_fit, deneme_test, type = "prob")) %>%
bind_cols(select(deneme_test, Transported))
rf_pred <- predict(rf_fit, deneme_test, type = "class") %>%
bind_cols(predict(rf_fit, deneme_test, type = "prob")) %>%
bind_cols(select(deneme_test, Transported))
xgb_pred <- predict(xgb_fit, deneme_test, type = "class") %>%
bind_cols(predict(xgb_fit, deneme_test, type = "prob")) %>%
bind_cols(select(deneme_test, Transported))
Confusion Matrix, sınıflandırma modelinin gerçek ve tahmin edilen etiketleri karşılaştıran bir tablodur. Tabloda dört temel metrik yer alır: True Negative (TN) aslında negatif olan ve negatif tahmin edilen gözlemlerdir. True Positive (TP) aslında pozitif olan ve pozitif tahmin edilen gözlemlerdir. False Positive (FP) aslında negatif olan ancak yanlışlıkla pozitif tahmin edilen gözlemlerdir (Type I error). False Negative (FN) ise aslında pozitif olan ancak yanlışlıkla negatif tahmin edilen gözlemlerdir (Type II error).
Bu projede Transported değişkeni için “No” negatif sınıf, “Yes” pozitif sınıftır. Dolayısıyla TN yolcunun nakledilmediği doğru tahmin edildiğinde, TP yolcunun nakledildiği doğru tahmin edildiğinde, FP yolcunun nakledilmediği halde nakledildi denildiğinde, FN ise yolcunun nakledildiği halde nakledilmedi denildiğinde ortaya çıkar.
cm_logistic <- conf_mat(logistic_pred, truth = Transported, estimate = .pred_class)
cm_rf <- conf_mat(rf_pred, truth = Transported, estimate = .pred_class)
cm_xgb <- conf_mat(xgb_pred, truth = Transported, estimate = .pred_class)
Lojistik Regresyon:
cm_logistic
## Truth
## Prediction No Yes
## No 841 194
## Yes 238 901
Random Forest:
cm_rf
## Truth
## Prediction No Yes
## No 856 179
## Yes 223 916
XGBoost:
cm_xgb
## Truth
## Prediction No Yes
## No 870 218
## Yes 209 877
library(patchwork)
# Lojistik için heatmap
p1 <- cm_logistic %>%
autoplot(type = "heatmap") +
labs(title = "Lojistik Regresyon - Confusion Matrix") +
theme_minimal() +
theme(plot.title = element_text(size = 12, face = "bold"))
# RF için heatmap
p2 <- cm_rf %>%
autoplot(type = "heatmap") +
labs(title = "Random Forest - Confusion Matrix") +
theme_minimal() +
theme(plot.title = element_text(size = 12, face = "bold"))
# XGBoost için heatmap
p3 <- cm_xgb %>%
autoplot(type = "heatmap") +
labs(title = "XGBoost - Confusion Matrix") +
theme_minimal() +
theme(plot.title = element_text(size = 12, face = "bold"))
# Grafikleri birleştir
p1 / p2 / p3
Aşağıda kullanılacak performans metriklerinin tanımları ve formülleri yer almaktadır:
| Metrik | Açıklama | Formül | Neden Kullanılır? |
|---|---|---|---|
| Accuracy | Doğru tahminlerin toplam gözleme oranı | (TP + TN) / (TP + TN + FP + FN) | Genel doğruluk ölçüsü. Dengeli veri setlerinde kullanışlı. |
| Precision | Pozitif tahmin edilenlerin gerçekten pozitif olma oranı | TP / (TP + FP) | False Positive’leri minimize etmek önemliyse (örn: spam tespiti) |
| Recall (Sensitivity) | Gerçek pozitiflerin yakalanma oranı | TP / (TP + FN) | False Negative’leri minimize etmek önemliyse (örn: hastalık tespiti) |
| F1-Score | Precision ve Recall’un harmonik ortalaması | 2 × (Precision × Recall) / (Precision + Recall) | Precision ve Recall arasında denge istiyorsak |
| ROC-AUC | Sınıfları ayırma gücü (1’e yakın = daha iyi) | ROC eğrisi altındaki alan | Eşik değerinden bağımsız genel performans ölçüsü |
hesapla_metrikler <- function(pred_df, model_adi) {
tibble(
Model = model_adi,
Accuracy = accuracy(pred_df, truth = Transported, estimate = .pred_class)$.estimate,
Precision = precision(pred_df, truth = Transported, estimate = .pred_class,
event_level = "second")$.estimate,
Recall = recall(pred_df, truth = Transported, estimate = .pred_class,
event_level = "second")$.estimate,
F1 = f_meas(pred_df, truth = Transported, estimate = .pred_class,
event_level = "second")$.estimate,
ROC_AUC = roc_auc(pred_df, truth = Transported, .pred_Yes,
event_level = "second")$.estimate
)
}
metrikler <- bind_rows(
hesapla_metrikler(logistic_pred, "Lojistik Regresyon"),
hesapla_metrikler(rf_pred, "Random Forest"),
hesapla_metrikler(xgb_pred, "XGBoost")
)
kable(metrikler, digits = 4, caption = "Model Performans Karşılaştırması (deneme_test)")
| Model | Accuracy | Precision | Recall | F1 | ROC_AUC |
|---|---|---|---|---|---|
| Lojistik Regresyon | 0.8013 | 0.7910 | 0.8228 | 0.8066 | 0.8864 |
| Random Forest | 0.8151 | 0.8042 | 0.8365 | 0.8201 | 0.9049 |
| XGBoost | 0.8036 | 0.8076 | 0.8009 | 0.8042 | 0.9049 |
best_model <- metrikler %>%
arrange(desc(ROC_AUC)) %>%
slice(1) %>%
pull(Model)
cat(paste0("\nEn yüksek ROC-AUC skoru: ", best_model, "\n\n"))
En yüksek ROC-AUC skoru: Random Forest
Model tahminlerinin dağılımını görselleştirerek her modelin kaç yolcuyu Transported olarak tahmin ettiğini görebiliriz.
pred_summary <- tibble(
Model = rep(c("Lojistik Regresyon", "Random Forest", "XGBoost"), each = 2),
Tahmin = rep(c("No", "Yes"), 3),
Sayi = c(
sum(logistic_pred$.pred_class == "No"),
sum(logistic_pred$.pred_class == "Yes"),
sum(rf_pred$.pred_class == "No"),
sum(rf_pred$.pred_class == "Yes"),
sum(xgb_pred$.pred_class == "No"),
sum(xgb_pred$.pred_class == "Yes")
)
)
ggplot(pred_summary, aes(x = Model, y = Sayi, fill = Tahmin)) +
geom_col(position = "dodge", width = 0.7) +
geom_text(aes(label = Sayi),
position = position_dodge(width = 0.7),
vjust = -0.5, size = 3.5) +
labs(
title = "Modellerin Tahmin Dağılımları (deneme_test)",
subtitle = "Her model kaç yolcuyu Transported (Yes) veya Not Transported (No) olarak tahmin etti",
y = "Yolcu Sayısı",
x = "Model",
fill = "Tahmin"
) +
theme_minimal() +
theme(
plot.title = element_text(size = 13, face = "bold"),
plot.subtitle = element_text(size = 10),
legend.position = "bottom"
) +
scale_fill_manual(values = c("No" = "#E74C3C", "Yes" = "#27AE60"))
Modellerin tahmin olasılıklarının (probability) dağılımını inceleyerek model güvenini değerlendirebiliriz.
prob_data <- bind_rows(
logistic_pred %>%
select(.pred_Yes) %>%
mutate(Model = "Lojistik Regresyon"),
rf_pred %>%
select(.pred_Yes) %>%
mutate(Model = "Random Forest"),
xgb_pred %>%
select(.pred_Yes) %>%
mutate(Model = "XGBoost")
)
ggplot(prob_data, aes(x = .pred_Yes, fill = Model)) +
geom_histogram(bins = 50, alpha = 0.7, position = "identity") +
facet_wrap(~Model, ncol = 1) +
labs(
title = "Modellerin Tahmin Olasılık Dağılımları",
subtitle = "0'a yakın = Not Transported, 1'e yakın = Transported",
x = "P(Transported = Yes)",
y = "Frekans"
) +
theme_minimal() +
theme(
plot.title = element_text(size = 13, face = "bold"),
plot.subtitle = element_text(size = 10),
legend.position = "none"
) +
scale_fill_brewer(palette = "Set1")
Modellerin hangi tür hataları yaptığını analiz ediyoruz.
hata_analizi <- tibble(
Model = c("Lojistik Regresyon", "Random Forest", "XGBoost"),
False_Positive = c(
sum(cm_logistic$table[1, 2]),
sum(cm_rf$table[1, 2]),
sum(cm_xgb$table[1, 2])
),
False_Negative = c(
sum(cm_logistic$table[2, 1]),
sum(cm_rf$table[2, 1]),
sum(cm_xgb$table[2, 1])
)
) %>%
pivot_longer(cols = -Model, names_to = "Hata_Tipi", values_to = "Sayi")
ggplot(hata_analizi, aes(x = Model, y = Sayi, fill = Hata_Tipi)) +
geom_col(position = "dodge", width = 0.7) +
geom_text(aes(label = Sayi),
position = position_dodge(width = 0.7),
vjust = -0.5, size = 3.5) +
labs(
title = "Modellerin Hata Dağılımları",
subtitle = "False Positive: Yanlış pozitif tahmin, False Negative: Yanlış negatif tahmin",
y = "Hata Sayısı",
x = "Model",
fill = "Hata Tipi"
) +
theme_minimal() +
theme(
plot.title = element_text(size = 13, face = "bold"),
plot.subtitle = element_text(size = 10),
legend.position = "bottom"
) +
scale_fill_manual(
values = c("False_Positive" = "#E67E22", "False_Negative" = "#9B59B6"),
labels = c("False Negative", "False Positive")
)
ROC (Receiver Operating Characteristic) Eğrisi, farklı eşik değerlerinde modelin True Positive Rate (Sensitivity) ve False Positive Rate (1 - Specificity) değerlerini gösterir. Eğri sol üst köşeye ne kadar yakınsa model o kadar iyidir. AUC (Area Under Curve) değeri 1’e yaklaştıkça model mükemmel performans gösterir, 0.5 ise rastgele tahmin anlamına gelir.
roc_logistic <- roc_curve(logistic_pred, truth = Transported, .pred_Yes,
event_level = "second") %>%
mutate(Model = "Lojistik Regresyon")
roc_rf <- roc_curve(rf_pred, truth = Transported, .pred_Yes,
event_level = "second") %>%
mutate(Model = "Random Forest")
roc_xgb <- roc_curve(xgb_pred, truth = Transported, .pred_Yes,
event_level = "second") %>%
mutate(Model = "XGBoost")
roc_all <- bind_rows(roc_logistic, roc_rf, roc_xgb)
ggplot(roc_all, aes(x = 1 - specificity, y = sensitivity, color = Model)) +
geom_line(linewidth = 1.2) +
geom_abline(linetype = "dashed", color = "gray50") +
labs(
title = "ROC Eğrileri Karşılaştırması",
subtitle = "Yüksek AUC değeri daha iyi model performansını gösterir",
x = "1 - Specificity (False Positive Rate)",
y = "Sensitivity (True Positive Rate)"
) +
theme_minimal() +
theme(
legend.position = "bottom",
plot.title = element_text(size = 14, face = "bold"),
plot.subtitle = element_text(size = 11)
) +
scale_color_brewer(palette = "Set1")
metrikler_long <- metrikler %>%
pivot_longer(cols = -Model, names_to = "Metrik", values_to = "Deger")
ggplot(metrikler_long, aes(x = Metrik, y = Deger, fill = Model)) +
geom_col(position = "dodge", width = 0.7) +
geom_text(aes(label = round(Deger, 3)),
position = position_dodge(width = 0.7),
vjust = -0.5, size = 3) +
labs(
title = "Model Performans Karşılaştırması",
subtitle = "Tüm metrikler için yüksek değer daha iyidir (1'e yakın = ideal)",
y = "Değer",
x = "Performans Metriği"
) +
theme_minimal() +
theme(
legend.position = "bottom",
plot.title = element_text(size = 14, face = "bold")
) +
scale_fill_brewer(palette = "Set2") +
ylim(0, 1.05)
Bu aşamada, tüm train verisiyle modelleri yeniden eğitip gerçek test setinde tahmin yapacağım.
# Final recipe (tüm train verisi için)
final_rec <- recipe(Transported ~ ., data = train_raw) %>%
step_rm(PassengerId, Name, Cabin) %>%
step_string2factor(all_nominal_predictors()) %>%
step_impute_mode(all_nominal_predictors()) %>%
step_impute_median(all_numeric_predictors()) %>%
step_dummy(all_nominal_predictors()) %>%
step_zv(all_predictors())
# Lojistik Regresyon
logistic_wf_final <- workflow() %>%
add_recipe(final_rec) %>%
add_model(logistic_spec)
logistic_final <- fit(logistic_wf_final, train_raw)
# Random Forest
rf_wf_final <- workflow() %>%
add_recipe(final_rec) %>%
add_model(rf_spec)
rf_final <- fit(rf_wf_final, train_raw)
# XGBoost
xgb_wf_final <- workflow() %>%
add_recipe(final_rec) %>%
add_model(xgb_spec)
xgb_final <- fit(xgb_wf_final, train_raw)
# Test verisinde tahmin yap
pred_logistic_final <- predict(logistic_final, test_raw)
pred_rf_final <- predict(rf_final, test_raw)
pred_xgb_final <- predict(xgb_final, test_raw)
# Yes/No -> True/False formatına çevir (Kaggle formatı)
convert_pred <- function(pred) {
ifelse(pred$.pred_class == "Yes", "True", "False")
}
# Submission dosyalarını oluştur
submission_logistic <- tibble(
PassengerId = test_raw$PassengerId,
Transported = convert_pred(pred_logistic_final)
)
submission_rf <- tibble(
PassengerId = test_raw$PassengerId,
Transported = convert_pred(pred_rf_final)
)
submission_xgb <- tibble(
PassengerId = test_raw$PassengerId,
Transported = convert_pred(pred_xgb_final)
)
# Dosyaları kaydet
write_csv(submission_logistic, "submission_logistic.csv")
write_csv(submission_rf, "submission_rf.csv")
write_csv(submission_xgb, "submission_xgb.csv")
Oluşturulan Dosyalar:
Submission aşamasında üç farklı dosya oluşturulmuştur: submission_logistic.csv, submission_rf.csv ve submission_xgb.csv dosyaları.
İlk 10 tahmin (örnek - XGBoost):
head(submission_xgb, 10) %>% kable()
| PassengerId | Transported |
|---|---|
| 0013_01 | True |
| 0018_01 | False |
| 0019_01 | True |
| 0021_01 | True |
| 0023_01 | True |
| 0027_01 | False |
| 0029_01 | True |
| 0032_01 | True |
| 0032_02 | True |
| 0033_01 | True |
Modellerin Kaggle platformuna yüklenmesi sonucunda elde edilen skorlar:
Bu projede Spaceship Titanic veri seti üzerinde üç farklı makine öğrenmesi modeli (Lojistik Regresyon, Random Forest, XGBoost) kurdum ve karşılaştırdım.
Model performansları incelendiğinde, deneme_test seti üzerinde üç model de birbirine yakın performans göstermiştir (Accuracy yaklaşık yüzde 79-80). Kaggle skorları açısından Random Forest en yüksek public leaderboard skorunu elde etmiştir (0.79962). XGBoost ikinci sırada yer almıştır (0.79424). Lojistik Regresyon ise 0.79167 ile üçüncü sırada kalmıştır.
Üç model arasındaki fark oldukça küçüktür (en iyi ve en kötü model arasında yalnızca 0.00795 puan fark vardır). Bununla birlikte Random Forest ve XGBoost gibi ensemble yöntemler lojistik regresyondan daha iyi performans göstermiştir. Bu durum veri setindeki karmaşık doğrusal olmayan ilişkilerin ensemble yöntemler tarafından daha iyi yakalandığını göstermektedir.
Cabin değişkenini Deck, CabinNum ve Side olarak ayırmak feature engineering açısından faydalı olmuştur. Eksik değerler tidymodels recipe ile sistematik şekilde doldurulmuştur (kategorik değişkenler için mode, sayısal değişkenler için median). Recipe pattern sayesinde tüm preprocessing adımları tekrar üretilebilir hale gelmiştir.
Confusion Matrix ile modellerin hata türlerini (False Positive, False Negative) detaylı şekilde analiz ettim. ROC-AUC ile eşik değerinden bağımsız genel model performansını ölçtüm. F1-Score ile Precision ve Recall arasındaki dengeyi değerlendirdim. Görselleştirmeler sayesinde (ROC eğrileri ve metrik karşılaştırma grafikleri) modellerin performansını görsel olarak analiz ettim.
Tidymodels ekosistemi sayesinde recipe, workflow ve parsnip paketleri ile model kurma ve değerlendirme süreçleri tutarlı ve tekrar üretilebilir hale gelmiştir. Random Forest ve XGBoost gibi ensemble yöntemlerin kompleks ilişkileri yakalamada başarılı olduğunu gözlemledim. Model performansını optimize etmek için doğru hiperparametre kombinasyonunu bulmanın önemli olduğunu öğrendim. Gelecekte k-fold cross-validation kullanarak model değerlendirmesini daha robust hale getirebilirim.