Spaceship Titanic Project

Author

Esma Gülmez

Spaceship Titanic – Makine Öğrenmesi Projesi Raporu

1. Giriş

Bu projede, Kaggle platformunda yer alan Spaceship Titanic adlı sınıflandırma problemi ele alınmıştır. Amaç, kurgusal bir uzay gemisinde seyahat eden yolcuların bir anomali sonucunda alternatif bir boyuta taşınıp taşınmadığını tahmin eden bir makine öğrenmesi modeli geliştirmektir.

Her ne kadar veri seti kurgusal olsa da, proje kapsamında uygulanan veri temizleme, ön işleme, modelleme ve değerlendirme adımları gerçek hayattaki veri bilimi problemleriyle büyük ölçüde benzerlik göstermektedir. Bu çalışma ile R programlama dili kullanılarak uçtan uca bir makine öğrenmesi sürecinin sistematik şekilde uygulanması hedeflenmiştir.
1.1. Kaggle ve Veri Seti Tanıtımı

Kaggle, veri bilimi ve makine öğrenmesi alanında yarışmaların, veri setlerinin ve örnek çözümlerin paylaşıldığı çevrimiçi bir platformdur. Spaceship Titanic yarışmasında katılımcılara iki ana veri seti sunulmaktadır:

  • Train veri seti: 8693 gözlem ve 14 değişken

  • Test veri seti: 4277 gözlem ve 13 değişken

Train veri setinde hedef değişken Transported olup, yolcunun alternatif bir boyuta taşınıp taşınmadığını (True/False) göstermektedir. Diğer değişkenler demografik bilgiler, kabin bilgileri ve yolcunun gemi içindeki harcama alışkanlıklarını içermektedir.

1.2.Kullanılan Paketler

library(tidymodels)
── Attaching packages ────────────────────────────────────── tidymodels 1.4.1 ──
✔ broom        1.0.11     ✔ recipes      1.3.1 
✔ dials        1.4.2      ✔ rsample      1.3.1 
✔ dplyr        1.1.4      ✔ tailor       0.1.0 
✔ ggplot2      4.0.1      ✔ tidyr        1.3.2 
✔ infer        1.1.0      ✔ tune         2.0.1 
✔ modeldata    1.5.1      ✔ workflows    1.3.0 
✔ parsnip      1.4.1      ✔ workflowsets 1.1.1 
✔ purrr        1.2.0      ✔ yardstick    1.3.2 
── Conflicts ───────────────────────────────────────── tidymodels_conflicts() ──
✖ purrr::discard() masks scales::discard()
✖ dplyr::filter()  masks stats::filter()
✖ dplyr::lag()     masks stats::lag()
✖ recipes::step()  masks stats::step()
library(tidyverse)
── Attaching core tidyverse packages ──────────────────────── tidyverse 2.0.0 ──
✔ forcats   1.0.1     ✔ stringr   1.6.0
✔ lubridate 1.9.4     ✔ tibble    3.3.0
✔ readr     2.1.6     
── Conflicts ────────────────────────────────────────── tidyverse_conflicts() ──
✖ readr::col_factor() masks scales::col_factor()
✖ purrr::discard()    masks scales::discard()
✖ dplyr::filter()     masks stats::filter()
✖ stringr::fixed()    masks recipes::fixed()
✖ dplyr::lag()        masks stats::lag()
✖ readr::spec()       masks yardstick::spec()
ℹ Use the conflicted package (<http://conflicted.r-lib.org/>) to force all conflicts to become errors
library(skimr)
library(ranger)
library(ggplot2)

1.3.Veri Setinin Yüklenmesi

train <- read.csv("train.csv")
test  <- read.csv("test.csv")
sub0  <- read.csv("sample_submission.csv")

1.4.Veri Ön İnceleme

skim(train)
Data summary
Name train
Number of rows 8693
Number of columns 14
_______________________
Column type frequency:
character 8
numeric 6
________________________
Group variables None

Variable type: character

skim_variable n_missing complete_rate min max empty n_unique whitespace
PassengerId 0 1 7 7 0 8693 0
HomePlanet 0 1 0 6 201 4 0
CryoSleep 0 1 0 5 217 3 0
Cabin 0 1 0 8 199 6561 0
Destination 0 1 0 13 182 4 0
VIP 0 1 0 5 203 3 0
Name 0 1 0 18 200 8474 0
Transported 0 1 4 5 0 2 0

Variable type: numeric

skim_variable n_missing complete_rate mean sd p0 p25 p50 p75 p100 hist
Age 179 0.98 28.83 14.49 0 19 27 38 79 ▂▇▅▂▁
RoomService 181 0.98 224.69 666.72 0 0 0 47 14327 ▇▁▁▁▁
FoodCourt 183 0.98 458.08 1611.49 0 0 0 76 29813 ▇▁▁▁▁
ShoppingMall 208 0.98 173.73 604.70 0 0 0 27 23492 ▇▁▁▁▁
Spa 183 0.98 311.14 1136.71 0 0 0 59 22408 ▇▁▁▁▁
VRDeck 188 0.98 304.85 1145.72 0 0 0 46 24133 ▇▁▁▁▁

Veri seti skim() fonksiyonu kullanılarak incelenmiştir. Bu inceleme sonucunda bazı sayısal değişkenlerde (Age, RoomService, FoodCourt vb.) eksik gözlemler olduğu tespit edilmiştir.

Eksik veriler, modelleme sürecinde sorun oluşturmaması için recipe yapısı içerisinde uygun yöntemlerle doldurulmuştur. Bu yaklaşım sayesinde veri sızıntısı (data leakage) engellenmiş ve tüm ön işleme adımları yalnızca eğitim verisi üzerinden öğrenilmiştir.

Ön işleme sonrası hem eğitim hem test veri setlerinde hiçbir eksik (NA) değer kalmadığı açıkça doğrulanmıştır.

2.Veri Ön İşleme ve Özellik Mühendisliği

2.1.PassengerId içinden grup numarasını çıkarma

train$Group <- as.integer(substr(train$PassengerId, 1, 4))
test$Group  <- as.integer(substr(test$PassengerId, 1, 4))

PassengerId değişkeni kullanılarak yolcuların aynı grup içerisinde seyahat edip etmedikleri belirlenmiştir. Grup bilgisinin, yolcuların benzer koşullardan etkilenmiş olma ihtimalini yansıtacağı varsayılmıştır.

2.2.Cabin Değişkeninin Ayrıştırılması

library(tidyr)
library(dplyr)

train <- train %>%
  separate(Cabin, into = c("Deck", "CabinNum", "Side"),
           sep = "/", fill = "right")

test <- test %>%
  separate(Cabin, into = c("Deck", "CabinNum", "Side"),
           sep = "/", fill = "right")

train$CabinNum <- as.numeric(train$CabinNum)
test$CabinNum  <- as.numeric(test$CabinNum)

Model performansını artırmak amacıyla bazı yeni değişkenler türetilmiştir:

  • PassengerId → Group:
    PassengerId içindeki grup bilgisi ayrıştırılarak yolcuların birlikte seyahat edip etmedikleri hakkında bilgi elde edilmiştir.

  • Cabin → Deck / CabinNum / Side:
    Kabin bilgisi üç ayrı bileşene ayrılarak gemi içindeki konum bilgisinin modele daha anlamlı şekilde dahil edilmesi sağlanmıştır.

Bu adımlar, modelin veri setindeki yapısal bilgileri daha iyi öğrenebilmesi amacıyla uygulanmıştır.

2.3.Eğitim ve Test Veri Setinin Oluşturulması

set.seed(123)

library(rsample)

split_obj <- initial_split(train, prop = 0.75, strata = Transported)
deneme_train <- training(split_obj)
deneme_test  <- testing(split_obj)

nrow(deneme_train)
nrow(deneme_test)

prop.table(table(train$Transported))
prop.table(table(deneme_train$Transported))
prop.table(table(deneme_test$Transported))

Temizlenmiş train veri seti, set.seed(123) kullanılarak tekrar üretilebilir şekilde:

  • %75 deneme_train

  • %25 deneme_test

olarak ayrılmıştır. Ayrım sırasında hedef değişken (Transported) için stratified split uygulanmış ve sınıf oranlarının her iki alt veri setinde de korunması sağlanmıştır.

3.Veri Ön İşleme (Recipe)

library(recipes)

rec <- recipe(Transported ~ ., data = deneme_train) %>%
  step_rm(PassengerId, Name) %>%
  step_impute_median(all_numeric_predictors()) %>%
  step_impute_mode(all_nominal_predictors()) %>%
  step_dummy(all_nominal_predictors()) %>%
  step_zv(all_predictors()) %>%      # sıfır varyanslı değişkenleri kaldır
  step_normalize(all_numeric_predictors())

prep_rec <- prep(rec)

train_baked <- bake(prep_rec, new_data = NULL)
test_baked  <- bake(prep_rec, new_data = deneme_test)

sum(is.na(train_baked))
sum(is.na(test_baked))

Tüm ön işleme adımları tidymodels çatısı altında recipe kullanılarak gerçekleştirilmiştir. Uygulanan adımlar şunlardır:

  • Eksik değerlerin doldurulması (sayısal ve kategorik)

  • Kategorik değişkenler için:

    • bilinmeyen seviyelerin tanımlanması

    • Nadir kategorilerin “other” sınıfında toplanması

    • Dummy (one-hot) encoding uygulanması

  • Sayısal değişkenlerin normalize edilmesi

  • Varyansı sıfır olan değişkenlerin (step_zv) modelden çıkarılması

Bu yapı sayesinde tüm modeller aynı ve adil bir ön işleme süreciyle eğitilmiştir.

3.1.Temizlenmiş Veri Setleri

Ham veri seti, modelleme öncesinde tidymodels altyapısı içerisinde tanımlanan recipe adımı kullanılarak temizlenmiştir. Bu aşamada sayısal değişkenlerde bulunan eksik gözlemler ortalama (mean) yöntemi ile doldurulmuş, kategorik değişkenlerdeki eksik değerler ise en sık gözlenen kategori (mode) ile tamamlanmıştır.

Ayrıca kategorik değişkenler one-hot encoding yöntemiyle sayısal forma dönüştürülmüş ve modelleme sürecinde veri sızıntısını önlemek amacıyla tüm ön işleme adımları yalnızca eğitim verisi üzerinde öğrenilmiştir. Böylece modelleme aşamasında kullanılan veri setleri, eksik değerlerden arındırılmış ve algoritmalar için uygun hale getirilmiştir.

# Temizlenmiş veri setlerinde NA kontrolü
colSums(is.na(train_baked))
colSums(is.na(test_baked))

Yukarıdaki çıktılar incelendiğinde, recipe adımı sonrasında hem eğitim hem de test veri setlerinde hiçbir değişkende eksik (NA) değer kalmadığı açıkça görülmektedir. Bu durum, ön işleme sürecinin başarıyla tamamlandığını ve modelleme için temiz veri setleri elde edildiğini göstermektedir.

4.Modellerin Kurulması ve Değerlendirilmesi

Çalışmada üç farklı makine öğrenmesi yöntemi kullanılmıştır:

  1. Logistic Regression

  2. Random Forest

  3. XGBoost

Her model, aynı recipe yapısı kullanılarak deneme_train veri seti üzerinde eğitilmiş ve deneme_test veri seti üzerinde test edilmiştir.

Modellerin başarısını değerlendirmek için kullanılan Hata Matrisi (Confusion Matrix) bileşenleri şu şekildedir:

  • True Positive (TP): Alternatif boyuta taşınan ve modelin doğru tahmin ettiği yolcular.

  • True Negative (TN): Taşınmayan ve modelin doğru tahmin ettiği yolcular.

  • False Positive (FP): Aslında taşınmayan ancak modelin taşındı dediği yolcular (Hatalı alarm).

  • False Negative (FN): Aslında taşınan ancak modelin taşınmadı dediği yolcular (Kaçırılan durum).

4.1. Lojistik Regresyon

library(tidymodels)

log_spec <- logistic_reg(mode = "classification") %>%
  set_engine("glm")

log_wf <- workflow() %>%
  add_recipe(rec) %>%
  add_model(log_spec)

log_fit <- fit(log_wf, data = deneme_train)

log_preds <- predict(log_fit, new_data = deneme_test, type = "class") %>%
  bind_cols(deneme_test %>% select(Transported))

conf_mat(log_preds, truth = Transported, estimate = .pred_class)
metrics(log_preds, truth = Transported, estimate = .pred_class)
metric_set(precision, recall, f_meas)(
  log_preds, truth = Transported, estimate = .pred_class
)

Lojistik regresyon, ikili sınıflandırma problemleri için temel bir referans model olarak kullanılmıştır.

Tahmin / Gerçek False (TN/FP) True (FN/TP)
False 843 (TN) 197 (FN)
True 236 (FP) 898 (TP)

Lojistik regresyon %80.1 accuracy ile temel bir başarı sergilemiştir.

4.2. Random Forest

rf_spec <- rand_forest(
  mode = "classification",
  trees = 500
) %>%
  set_engine("ranger")
rf_wf <- workflow() %>%
  add_recipe(rec) %>%
  add_model(rf_spec)
rf_fit <- fit(rf_wf, data = deneme_train)
rf_preds <- predict(rf_fit, new_data = deneme_test, type = "class") %>%
  bind_cols(deneme_test %>% select(Transported))
conf_mat(rf_preds, truth = Transported, estimate = .pred_class)
metrics(rf_preds, truth = Transported, estimate = .pred_class)
metric_set(precision, recall, f_meas)(
  rf_preds, truth = Transported, estimate = .pred_class
)
Tahmin / Gerçek False (TN/FP) True (FN/TP)
False 874 (TN) 196 (FN)
True 205 (FP) 899 (TP)

Random Forest, hem TN (874) hem de TP (899) değerlerinde en dengeli sonuçları vererek %81.6 doğruluğa ulaşmıştır.

4.3. XGBoost

xgb_spec <- boost_tree( mode = "classification", trees = 500, tree_depth = 6, learn_rate = 0.05, sample_size = 0.8 ) %>% set_engine("xgboost")

xgb_wf <- workflow() %>% add_recipe(rec) %>% add_model(xgb_spec)

xgb_fit <- fit(xgb_wf, data = deneme_train)

xgb_preds <- predict(xgb_fit, new_data = deneme_test, type = "class") %>% bind_cols(deneme_test %>% select(Transported))

conf_mat(xgb_preds, truth = Transported, estimate = .pred_class) metrics(xgb_preds, truth = Transported, estimate = .pred_class) metric_set(precision, recall, f_meas)( xgb_preds, truth = Transported, estimate = .pred_class )
Tahmin / Gerçek False (TN/FP) True (FN/TP)
False 890 (TN) 215 (FN)
True 189 (FP) 880 (TP)

XGBoost, gerçek “False” olanları yakalamada (890 TN) çok başarılı olsa da, gerçek “True” olanların bir kısmını (215 FN) kaçırmıştır.

4.4.Model Performanslarının Karşılaştırılması


``` r
library(ggplot2)

compare_long <- compare_f1 %>%
  pivot_longer(
    cols = c(precision, recall, f_meas),
    names_to = "metric",
    values_to = "value"
  )

ggplot(compare_long, aes(x = model, y = value, fill = metric)) +
  geom_col(position = "dodge") +
  labs(
    title = "Modellerin Performans Ölçütlerine Göre Karşılaştırılması",
    x = "Model",
    y = "Değer"
  ) +
  theme_minimal()
compare_f1 <- bind_rows(
  metric_set(precision, recall, f_meas)(
    log_preds, truth = Transported, estimate = .pred_class
  ) %>% mutate(model = "Logistic"),
  metric_set(precision, recall, f_meas)(
    rf_preds, truth = Transported, estimate = .pred_class
  ) %>% mutate(model = "RandomForest"),
  metric_set(precision, recall, f_meas)(
    xgb_preds, truth = Transported, estimate = .pred_class
  ) %>% mutate(model = "XGBoost")
) %>%
  select(model, .metric, .estimate) %>%
  pivot_wider(names_from = .metric, values_from = .estimate)

compare_f1
Model Accuracy (Doğruluk) F1-Score Kappa
Logistic Regression 0.801 0.796 0.602
Random Forest 0.816 0.813 0.631
XGBoost 0.814 0.815 0.628

Modellerden elde edilen sayısal sonuçlar şu şekilde yorumlanmalıdır:

  • Accuracy (Doğruluk): Genel başarıyı gösterir. %81.6’lık bir doğruluk, modelin 100 yolcudan yaklaşık 82’sini doğru sınıflandırdığını ifade eder.

  • Precision (Kesinlik): Modelin “Taşındı” dediği yolcuların ne kadarının gerçekte taşındığını gösterir. Yüksek olması, gereksiz yere “anomali” alarmı verilmediğini kanıtlar.

  • Recall (Duyarlılık): Gerçekten taşınan yolcuların ne kadarının model tarafından yakalanabildiğini gösterir. Bu projede kritik olan, taşınan yolcuları gözden kaçırmamaktır (düşük FN oranı).

  • F1-Score: Precision ve Recall arasındaki dengeyi temsil eder. Random Forest’ın 0.813’lük F1 skoru, modelin hem yanlış alarm vermediğini hem de taşınanları yüksek oranda yakaladığını teyit eder.

  • Kappa Skoru: Modelin şans eseri doğru tahmin yapma ihtimalini dışarıda bırakarak başarısını ölçer. 0.631’lik Kappa değeri, modelin sınıflar arasında “iyi derecede” bir uyum ve ayırt edicilik sağladığını gösterir.

5.Final Model ve Kaggle Submission

final_rf_fit <- fit(rf_wf, data = train)

test_pred <- predict(final_rf_fit, new_data = test, type = "class")

submission <- sub0 %>%
  select(PassengerId) %>%
  bind_cols(test_pred) %>%
  rename(Transported = .pred_class)

write.csv(submission, "submission.csv", row.names = FALSE)

Tüm eğitim verisi kullanılarak Random Forest modeli yeniden eğitilmiş ve Kaggle formatına uygun tahmin dosyası oluşturulmuştur.

5.1.Kaggle Submission Sonucu

Aşağıda, geliştirilen modelin Kaggle test verisi üzerindeki doğrulama sonucu yer almaktadır.

Bu çalışma sonucunda, uygun bir öznitelik mühendisliği ve disiplinli bir ön işleme (preprocessing) süreciyle %82 civarında bir doğruluğa ulaşılabileceği görülmüştür. Kaggle üzerindeki public score sonucu 0.79939 olarak kaydedilmiştir.