Bir önceki dersimizde (Karar Ağaçları), tek bir ağacın veri setindeki küçük değişimlere veya boşluklara karşı ne kadar hassas olduğunu görmüştük.
Hatırlayalım:
GPA 3.3 olan bir öğrenci için sezgisel olarak “Yüksek Burs” beklerken,
Algoritma matematiksel optimum nokta olarak 3.35 eşiğini seçtiği için bu öğrenciyi “Düşük Burs” grubuna atmış ve 15.50 Bin TL tahmin etmişti.
Bu durum, tek bir ağacın yüksek varyansa sahip olduğunu gösterir. Yani model kararsızdır ve veriye aşırı uyum sağlayabilir. Bu sorunu çözmek için tek bir “uzman” yerine, yüzlerce uzmandan oluşan bir “konsey” kuracağız. İşte bu yapıya Random Forest (Rastgele Orman) diyoruz.
Random Forest, Bagging (Bootstrap Aggregating) yönteminin geliştirilmiş halidir. İki temel mekanizma üzerine kuruludur:
Her ağaç, orijinal veri setinin tamamını görmez.
Veri setinden rastgele, iadeli (replacement) seçim yapılarak yeni alt kümeler oluşturulur.
Örneğin: 7 satırlık veri setinden rastgele 7 seçim yapılır ama bazı satırlar 2-3 kez seçilirken, bazıları hiç seçilmez.
Sonuç: Her ağaç verinin farklı bir perspektifini öğrenir. Kimi ağaç 3.35’ten bölerken, kimi ağaç (o veriyi görmediği için) 3.1’den bölebilir.
Her düğümde (split), eldeki tüm değişkenlere bakılmaz. Değişkenlerin rastgele bir alt kümesi seçilir.
Amaç: Ağaçların birbirine benzemesini (korelasyonu) engellemektir. Eğer çok güçlü bir değişken varsa (örn: GPA), tek ağaçta herkes onu kullanır. Ama rastgelelik sayesinde bazı ağaçlar GPA’yı göremez ve başka değişkenlere odaklanır.
Analoji: Bir hastaya teşhis koymak için tek bir doktora gitmek yerine 100 doktora gidiyoruz. Ancak her doktora hastanın tahlillerinin sadece bir kısmını gösteriyoruz. Sonunda 100 doktorun verdiği kararın ortalamasını alıyoruz.
Şimdi aynı veri seti üzerinde hem Tek Karar Ağacı (Decision Tree) hem de Random Forest modellerini çalıştırıp sonuçları kıyaslayalım.
# Veri setini tekrar oluşturalım
df_burs <- data.frame(
ID = 1:7,
GPA = c(2.5, 2.7, 3.0, 3.2, 3.5, 3.7, 4.0),
Burs = c(10, 12, 18, 22, 30, 35, 40)
)
# Test verisi (Problem yaratan öğrenci)
test_ogrenci <- data.frame(GPA = 3.3)
İki modeli de kuralım. Random Forest için randomForest
paketini kullanacağız.
(Not: Tek Karar Ağacı’nın zayıflığını net göstermek için derinliğini 1 ile sınırlandırıyoruz.)
library(tidyverse)
library(rpart)
library(randomForest)
library(kableExtra)
set.seed(123) # Tekrarlanabilirlik için
# 1. Tek Karar Ağacı (CART)
# maxdepth = 1 ekleyerek ağacın erkenden karar vermesini ve hata yapmasını sağlıyoruz
model_tree <- rpart(Burs ~ GPA, data = df_burs, method = "anova",
control = rpart.control(minsplit = 2, cp = 0.001, maxdepth = 1))
# 2. Random Forest (500 Ağaçlı Orman)
# nodesize = 1 ve mtry = 1 ile küçük veri setine uyarlıyoruz
model_rf <- randomForest(Burs ~ GPA, data = df_burs, ntree = 500, nodesize = 1, mtry = 1)
# Tahminlerin Yapılması
pred_tree <- predict(model_tree, test_ogrenci)
pred_rf <- predict(model_rf, test_ogrenci)
İşte kritik an! Bakalım modeller GPA 3.3 için ne diyor?
library(tidyverse)
library(rpart)
library(randomForest)
library(kableExtra)
results <- data.frame(
Model = c("Tek Karar Ağacı (rpart)", "Random Forest (500 Ağaç)"),
Tahmin_Burs = c(pred_tree, pred_rf),
Aciklama = c("Keskin bir sınır (3.35) belirlediği için sol dala (düşük) attı.",
"500 farklı ağacın ortalamasını aldığı için ara bir değer buldu.")
)
# Tabloyu yazdır
results %>%
kbl(caption = "Tablo 3: Model Tahminlerinin Karşılaştırılması") %>%
kable_styling(bootstrap_options = c("striped", "hover"), full_width = F) %>%
column_spec(2, bold = T, color = "black")
| Model | Tahmin_Burs | Aciklama |
|---|---|---|
| Tek Karar Ağacı (rpart) | 15.500 | Keskin bir sınır (3.35) belirlediği için sol dala (düşük) attı. |
| Random Forest (500 Ağaç) | 24.196 | 500 farklı ağacın ortalamasını aldığı için ara bir değer buldu. |
Yorum:
-Tek Ağaç (15.50): Verideki boşluktan (gap) dolayı 3.35 noktasından keskin bir bölünme yaptı ve 3.3 notunu “başarısız” gruba (sol dal) dahil etti. Sonuç çok düşük.
-Random Forest (~24.2): 500 ağacın kimisi verinin farklı yerlerinden bölmeler yaptı. Hepsinin ortalaması alındığında, 15.50 ile 31.75 arasında, gerçeğe daha yakın ve dengeli bir tahmin üretildi.
Random Forest’ın neden daha iyi olduğunu anlamak için, modellerin GPA ekseni boyunca nasıl davrandığını çizdirelim.
library(tidyverse)
library(rpart)
library(randomForest)
library(kableExtra)
# Simülasyon verisi
sim_data <- data.frame(GPA = seq(2.5, 4.0, 0.01))
# Tahminler
sim_data$Tree_Pred <- predict(model_tree, sim_data)
sim_data$RF_Pred <- predict(model_rf, sim_data)
# Görselleştirme
ggplot(df_burs, aes(x = GPA, y = Burs)) +
# Gerçek Veri Noktaları
geom_point(size = 4, color = "black") +
# Tek Ağaç Çizgisi (Kırmızı)
geom_line(data = sim_data, aes(x = GPA, y = Tree_Pred, color = "Tek Karar Ağacı"), size = 1.2, linetype="dashed") +
# Random Forest Çizgisi (Mavi)
geom_line(data = sim_data, aes(x = GPA, y = RF_Pred, color = "Random Forest"), size = 1.2) +
# Test Noktamız (3.3)
geom_vline(xintercept = 3.3, linetype = "dotted", color = "gray40") +
annotate("text", x = 3.3, y = 10, label = "Test: 3.3",
angle = 90, vjust = -0.5, color="gray40", size = 2) + # size büyütüldü
labs(title = "Model Davranışlarının Karşılaştırılması",
subtitle = "Tek ağaç basamaklı (keskin) hareket ederken, Random Forest daha yumuşak geçişler yapar.",
y = "Tahmini Burs Miktarı (Bin TL)",
color = "Model") +
scale_color_manual(values = c("Tek Karar Ağacı" = "#E74C3C", "Random Forest" = "#3498DB")) +
theme_minimal() +
theme(legend.position = "bottom")
Şekil 5: Tek Ağaç vs Random Forest Tahmin Çizgileri
Grafik Yorumu ve Kritik Değerlendirme
-Kırmızı Çizgi (Underfitting/Eksik Öğrenme): Tek karar ağacını bilerek sınırladığımız için (derinlik=1), model veriyi aşırı basitleştirmiştir. 3.34 notu ile 3.36 notu arasında devasa bir burs farkı oluşturur. Bu durum gerçek hayatta mantıksızdır.
-Mavi Çizgi (Hassas Geçiş): Random Forest, tek bir büyük basamak yerine çok sayıda küçük basamak oluşturmuştur.
İlk bakışta noktalara çok yapıştığı için karmaşık (overfit) gibi dursa da, aslında yaptığı şey “Merdiven Etkisi”dir.
Tek ağaçtaki “Uçurum” riskini ortadan kaldırmış, GPA arttıkça bursun kademeli olarak arttığı daha gerçekçi bir senaryo çizmiştir.
Veri seti çok küçük (7 satır) olduğu için çizgi kırık görünmektedir; veri sayısı arttıkça bu mavi çizgi daha pürüzsüz bir hal alacaktır.
-Yüksek Doğruluk: Genellikle tekil ağaçlardan çok daha iyi performans gösterir.
-Overfitting Direnci: Çok sayıda ağacın ortalaması alındığı için ezberleme riski düşüktür.
-Özellik Önemi (Feature Importance): Hangi değişkenin (örneğin GPA mı, Mülakat mı?) daha önemli olduğunu sayısal olarak verebilir.
-Yavaşlık: Tek bir ağaç yerine 500 ağaç eğitmek ve tahmin almak daha maliyetlidir (Gerçek zamanlı sistemlerde sorun olabilir).
-Kara Kutu (Black Box) Etkisi: Tek bir ağacı çizip “Bakın buradan böldü” diyebiliyorduk. Ancak 500 ağacı aynı anda görselleştirmek ve yorumlamak imkansızdır. Şeffaflık azalır.
Bugün, modelleri birbirinden bağımsız eğitip ortalamasını alarak (Bagging) varyansı düşürdük ve daha kararlı bir sonuç elde ettik.
Ancak dikkat ederseniz, Random Forest hala “demokratik” bir yöntemdir. İyi ağaçla kötü ağacın oyu eşittir. Peki ya şöyle yapsaydık?
-Ağaçları sırayla eğitsek?
-İkinci ağaç, birinci ağacın yaptığı hatalara odaklansa?
-Üçüncü ağaç, ikincinin hatalarını düzeltse?
Bu “Hata Odaklı” yaklaşıma Boosting (Güçlendirme) diyoruz. Bir sonraki dersimizde, Kaggle yarışmalarının vazgeçilmezleri olan Gradient Boosting, XGBoost ve LightGBM algoritmalarını inceleyerek serimizi tamamlayacağız.
Önceki derste Random Forest ile ağaçları paralel (bağımsız) eğitip ortalamasını almıştık. Boosting yöntemlerinde ise ağaçlar seri (sıralı) eğitilir.
Temel Felsefe: “Bir önceki ağacın yaptığı hatayı, bir sonraki ağaçla düzelt.”
Bu derste Boosting ailesinin 3 büyük üyesini aynı veri seti üzerinde yarıştıracağız:
-GBM: Temel algoritma.
-XGBoost: Hız ve performans optimizasyonu.
-LightGBM: Büyük veride hız ve “Yaprak-Odaklı” büyüme.
Analizlerimizde kullandığımız 7 satırlık “Burs Tahmin” veri setini hazırlayalım.
library(tidyverse)
library(gbm) # Klasik Gradient Boosting
library(xgboost) # eXtreme Gradient Boosting
library(lightgbm) # Light Gradient Boosting Machine
library(kableExtra)
set.seed(123)
df_burs <- data.frame(
GPA = c(2.5, 2.7, 3.0, 3.2, 3.5, 3.7, 4.0),
Burs = c(10, 12, 18, 22, 30, 35, 40)
)
# Test noktamız
test_data <- data.frame(GPA = 3.3)
GBM, bu işin atasıdır. Hata fonksiyonunun (Loss Function) türevini (gradyanını) alarak ilerler.
Nasıl Çalışır? İlk ağaç veriyi tahmin eder. Hatalar (Residuals) hesaplanır. İkinci ağaç Burs miktarını değil, bu Hataları tahmin etmeye çalışır.
Dezavantajı: Yavaştır ve parametre ayarı zordur.
(Not: Aşağıdaki kodda bag.fraction = 1 ve n.minobsinnode = 1 ayarları yapılmıştır. Bunun sebebi veri setimizin çok küçük (7 satır) olmasıdır. Standart ayarlarda GBM veriyi bölmekte zorlanır.)
library(tidyverse)
library(gbm) # Klasik Gradient Boosting
library(xgboost) # eXtreme Gradient Boosting
library(lightgbm) # Light Gradient Boosting Machine
library(kableExtra)
set.seed(123)
# GBM Modeli
model_gbm <- gbm(Burs ~ GPA,
data = df_burs,
distribution = "gaussian", # Regresyon için
n.trees = 100, # Ağaç sayısı
interaction.depth = 3, # Ağaç derinliği
shrinkage = 0.1, # Learning Rate (Adım büyüklüğü)
# --- KÜÇÜK VERİ AYARI ---
n.minobsinnode = 1, # Yaprakta en az 1 kişi olsun (Normalde daha yüksek istenir)
bag.fraction = 1 # Verinin tamamını kullan (Rastgele seçme)
# -----------------------
)
# Tahmin
pred_gbm <- predict(model_gbm, test_data, n.trees = 100)
Kaggle yarışmalarının kralı olarak bilinen XGBoost, GBM’in optimize edilmiş ve hızlandırılmış halidir.
Farkı Nedir?
Hız: İşlemci çekirdeklerini (CPU Cores) paralel kullanarak GBM’den çok daha hızlı çalışır.
Düzenlileştirme (Regularization): L1 ve L2 cezalandırma yöntemleri ile aşırı öğrenmeyi (Overfitting) engellemeye çalışır.
Eksik Veri: Verideki NA değerlerini
otomatik olarak yönetir, doldurmaya gerek bırakmaz.
Veri Yapısı: XGBoost, veriyi xgb.DMatrix adı verilen, performans için optimize edilmiş özel bir matris formatında ister.
library(tidyverse)
library(gbm) # Klasik Gradient Boosting
library(xgboost) # eXtreme Gradient Boosting
library(lightgbm) # Light Gradient Boosting Machine
library(kableExtra)
set.seed(123)
# 1. Veriyi Hazırlama (DMatrix Dönüşümü)
# XGBoost sadece sayısal matris kabul eder, dataframe kabul etmez.
dtrain_xgb <- xgb.DMatrix(data = as.matrix(df_burs$GPA), label = df_burs$Burs)
# 2. Parametrelerin Belirlenmesi
params_xgb <- list(
objective = "reg:squarederror", # Regresyon problemi için hata fonksiyonu
eta = 0.3, # Learning Rate (Adım büyüklüğü)
max_depth = 2, # Ağaç derinliği (Küçük veri olduğu için sığ tuttuk)
lambda = 1 # L2 Regularization (Overfitting engellemek için)
)
# 3. Modelin Eğitilmesi
model_xgb <- xgb.train(
params = params_xgb,
data = dtrain_xgb,
nrounds = 50, # 50 tur boyunca hatayı düzeltmeye çalış
verbose = 0 # Eğitim çıktılarını gizle
)
# 4. Tahmin
# Test verisini de matris formatına çevirmeliyiz
test_matrix <- xgb.DMatrix(data = as.matrix(test_data$GPA))
pred_xgb <- predict(model_xgb, test_matrix)
Microsoft tarafından geliştirilen LightGBM, özellikle devasa veri setlerinde hız ve bellek performansı için tasarlanmış, XGBoost’un en büyük rakibidir.
Devrimsel Farkı: Yaprak Odaklı Büyüme (Leaf-wise Growth)
-Diğerleri (XGBoost, GBM): Ağacı katman katman (Level-wise) büyütür. Dengeli gider.
-LightGBM: Hatayı en çok düşüren yaprağı bulur ve sadece oradan derinleşir (Leaf-wise).
-Sonuç: Çok daha hızlıdır ve hatayı hızla düşürür. Ancak küçük verilerde (bizim örneğimizdeki gibi) aşırı öğrenme (overfitting) riski yüksektir.
library(tidyverse)
library(gbm) # Klasik Gradient Boosting
library(xgboost) # eXtreme Gradient Boosting
library(lightgbm) # Light Gradient Boosting Machine
library(kableExtra)
set.seed(123)
# 1. Veri Hazırlığı
# LightGBM kendi özel veri yapısını kullanır
dtrain_lgb <- lgb.Dataset(data = as.matrix(df_burs$GPA), label = df_burs$Burs)
# 2. Parametre Ayarları
# NOT: Veri setimiz çok küçük (7 satır) olduğu için LightGBM'in
# standart güvenlik kısıtlamalarını (min_data) düşürmek zorundayız.
params_lgb <- list(
objective = "regression",
metric = "l2",
learning_rate = 0.1,
num_leaves = 4, # Derinlik yerine yaprak sayısı kontrol edilir
min_data_in_leaf = 1 # Normalde bu değer 20+'dir. Veri az diye 1 yaptık.
)
# 3. Model Eğitimi
model_lgb <- lgb.train(
params = params_lgb,
data = dtrain_lgb,
nrounds = 50,
verbose = -1 # Gereksiz uyarıları gizle
)
# 4. Tahmin
# LightGBM matris formatında tahmin ister
pred_lgb <- predict(model_lgb, as.matrix(test_data$GPA))
Şimdi GPA = 3.3 olan test öğrencimiz için 3 dev algoritmanın tahminlerine bakalım.
| Algoritma | Büyüme_Stratejisi | Tahmin_3.3 |
|---|---|---|
| GBM (Ata) | Level-wise (Katmanlı) | 22.00001 |
| XGBoost (Kral) | Level-wise (Katmanlı) | 21.97489 |
| LightGBM (Hızcı) | Leaf-wise (Yaprak Odaklı) | 28.97349 |
Sonuçların Yorumu:
Tabloyu incelediğimizde algoritmaların karakterlerini net bir şekilde görüyoruz:
-GBM ve XGBoost: Genellikle birbirine yakın sonuçlar üretirler. “Katmanlı Büyüme” (Level-wise) stratejisi sayesinde, küçük veri setlerinde daha dengeli ve muhafazakar tahminler yaparlar.
-LightGBM: Muhtemelen diğerlerinden daha farklı (genellikle daha yüksek) bir tahmin üretmiştir.
Bunun sebebi “Yaprak Odaklı” (Leaf-wise) büyümesidir.
Veri setimiz çok küçük olduğu için (7 satır), LightGBM verideki küçük boşluklara aşırı duyarlı davranabilir. Bu, “Büyük Veri” algoritmasını “Küçük Veri”de kullanmanın bir yan etkisidir.
Algoritmaların karakterini anlamanın en iyi yolu, tahmin çizgilerini üst üste çizmektir.
library(tidyverse)
library(gbm)
library(xgboost)
library(lightgbm)
library(kableExtra)
# Daha önce eğitilmiş modellerin hafızada olduğundan emin olalım
# (model_gbm, model_xgb, model_lgb)
# Modellerin hafızada olduğundan emin olalım
set.seed(123)
# Simülasyon verisi
sim_x <- seq(2.5, 4.0, 0.01)
sim_data <- data.frame(GPA = sim_x)
# 1. GBM Tahmini
sim_data$GBM <- predict(model_gbm, sim_data, n.trees = 100)
# 2. XGBoost Tahmini
dsim_xgb <- xgb.DMatrix(data = as.matrix(sim_x))
sim_data$XGB <- predict(model_xgb, dsim_xgb)
# 3. LightGBM Tahmini
sim_data$LGB <- predict(model_lgb, as.matrix(sim_x))
# Görselleştirme
ggplot(df_burs, aes(x = GPA, y = Burs)) +
# Gerçek Veriler
geom_point(size = 5, color = "black", alpha = 0.7) +
# --- XGBoost (Kırmızı - Altta Kalsın) ---
geom_line(data = sim_data, aes(x = GPA, y = XGB, color = "XGBoost"), size = 2, alpha = 0.4) +
# --- LightGBM (Mor - Ortada) ---
geom_line(data = sim_data, aes(x = GPA, y = LGB, color = "LightGBM"), size = 1.2, linetype = "dotted") +
# --- GBM (Yeşil - Üste Çıkaralım ve Hafif Kaydıralım) ---
# KRİTİK REVİZE: Görünürlük için +0.2 ofset ekledik
geom_line(data = sim_data, aes(x = GPA, y = GBM + 0.2, color = "GBM"), size = 1, linetype = "dashed") +
# Açıklamalar
labs(title = "Boosting Algoritmalarının Karşılaştırması",
subtitle = "GBM ve XGBoost neredeyse aynı sonucu vermiştir (Yeşil çizgi görünürlük için hafifçe kaydırılmıştır).",
y = "Burs Miktarı (Bin TL)",
x = "Not Ortalaması (GPA)",
color = "Algoritma") +
scale_color_manual(values = c("GBM" = "#27AE60", "XGBoost" = "#E74C3C", "LightGBM" = "#8E44AD")) +
theme_minimal() +
theme(legend.position = "bottom", text = element_text(size = 12))
Şekil 7: Boosting Ailesinin Davranış Analizi
Sol: Random Forest (Dengeli), Sağ: Boosting Ailesi (Agresif)
Yukarıdaki iki grafiği (Şekil 5 ve Şekil 7) yan yana koyduğumuzda, algoritmaların “Düşünce Yapıları” arasındaki farkı net bir şekilde görebiliyoruz.
Random Forest grafiğine baktığınızda, mavi çizginin veri noktalarının (siyah noktalar) tam üzerinden geçmek yerine, onların arasından süzüldüğünü görürsünüz.
Yorum:
Model, her bir öğrencinin notunu ezberlemeye çalışmamış, genel trendi (GPA arttıkça Burs artar) yakalamıştır. Avantajı: Yeni bir öğrenci geldiğinde (Test verisi), modelin aşırı tepki verme riski düşüktür. Varyansı düşürmüştür. En güvenli limandır.
İkinci grafikte XGBoost ve GBM’in (neredeyse üst üste binen çizgiler) siyah noktalara yapıştığını görüyoruz.
Yorum:
Bu algoritmalar “Hata Odaklı” (Boosting) çalıştığı için, eğitim setindeki hatayı sıfıra indirmek adına çok agresif davranmışlardır. Her basamağı tam verinin olduğu yere kurmuşlardır.
Risk: Bu durum eğitim verisi için mükemmeldir (Yüksek Doğruluk). Ancak bu kadar sıkı uyum, Overfitting (Ezberleme) riskini gösterir. Eğer veri setinde gürültü (hatalı veri) olsaydı, onu da doğru sanıp öğreneceklerdi.
Mor noktalı çizgiye dikkat edin. Diğerleri gibi her veri noktasında basamak oluşturmamış, bazı aralıkları (örneğin 2.5 - 3.0 arası) düz geçmiştir.
Yorum:
LightGBM “Yaprak Odaklı” (Leaf-wise) büyüdüğü ve min_data_in_leaf kısıtlaması olduğu için, bu kadar küçük veri setinde detaylarda boğulmamayı seçmiştir.
Ders: LightGBM, az veriyle (Small Data) çalışmak için tasarlanmamıştır. Onun gücü milyonlarca satırlık verilerde ortaya çıkar; burada ise biraz “kaba” kalmıştır.
Ara Özet: Peki Hangisi Kazandı?
Bu yarışın tek bir kazananı yoktur, görev dağılımı vardır:
| Algoritma | Karakteri | Ne Zaman Seçilmeli? |
|---|---|---|
| Random Forest | Diplomat | Veriniz az veya orta boyuttaysa, ayar yapmakla uğraşmadan ‘iyi ve güvenli’ sonuç istiyorsanız. |
| XGBoost / GBM | Keskin Nişancı | Yarışmalardaysanız, veriniz temizse ve %0.1’lik performans artışı bile sizin için önemliyse. |
| LightGBM | Sprinter (Koşucu) | Veriniz RAM’e sığmayacak kadar büyükse ve hız sizin için her şeyse. |
Buraya kadar incelediğimiz algoritmaların (RF, XGBoost, LightGBM) ortak bir zayıf noktası vardır: Matematiksel olmayan (Kategorik) verileri sevmezler.
Veri setinizde “İstanbul, Ankara, İzmir” yazıyorsa, bu algoritmalar hata verir.
Sizin bu verileri “1, 2, 3” gibi sayılara çevirmeniz (Encoding) gerekir.
Ancak bu çeviri işlemi bilgi kaybına yol açar. İşte tam bu noktada, Rus teknoloji devi Yandex’in geliştirdiği CatBoost (Categorical Boosting) devreye girer. CatBoost, “Ben sayıya çevirmeden de anlarım” diyen tek algoritmadır.
Şimdi CatBoost’un bu yeteneğini ve “Simetrik Ağaç” yapısını inceleyelim.
Yandex tarafından geliştirilen CatBoost, özellikle “Kategorik Değişkenler” (Categorical Features) ile başa çıkma konusundaki ustalığıyla bilinir.
-Doğal Kategori Desteği: XGBoost veya LightGBM kullanırken, “Şehir” gibi bir sütunu önce sayılara (One-Hot Encoding) çevirmeniz gerekir. CatBoost ise bunu kendi içinde otomatik ve çok daha akıllıca (Target Statistics yöntemiyle) yapar.
-Simetrik Ağaçlar (Symmetric Trees): Diğer algoritmalar ağacı dengesiz büyütebilirken, CatBoost her seviyede dengeli (simetrik) bir yapı kurmaya zorlar. Bu, tahmin hızını inanılmaz artırır ve overfitting riskini azaltır.
Tutarlılık sağlamak için aynı veri setini (GPA -> Burs) kullanıyoruz.
(Not: Verimizde kategorik değişken olmasa da, CatBoost’un simetrik ağaç yapısının sayısal verideki davranışını gözlemleyeceğiz.)
# Veri Seti
df_burs <- data.frame(
GPA = c(2.5, 2.7, 3.0, 3.2, 3.5, 3.7, 4.0),
Burs = c(10, 12, 18, 22, 30, 35, 40)
)
# Test Verisi (GPA = 3.3)
test_data <- data.frame(GPA = 3.3)
CatBoost, veriyi load_pool adı verilen özel bir havuza dönüştürmemizi ister.
set.seed(123)
# Veri setlerini emin olmak için tekrar tanımlayalım
df_burs <- data.frame(
GPA = c(2.5, 2.7, 3.0, 3.2, 3.5, 3.7, 4.0),
Burs = c(10, 12, 18, 22, 30, 35, 40)
)
test_data <- data.frame(GPA = 3.3)
# --- CatBoost İşlemleri ---
# CatBoost Havuzunun (Pool) Oluşturulması
# DÜZELTME: drop = FALSE ekleyerek verinin "Data Frame" olarak kalmasını sağlıyoruz.
train_pool <- catboost.load_pool(data = df_burs[, -2, drop = FALSE],
label = df_burs[, 2])
test_pool <- catboost.load_pool(data = test_data)
# Parametreler
params_cat <- list(
loss_function = 'RMSE',
iterations = 100,
depth = 3,
learning_rate = 0.1,
logging_level = 'Silent'
)
# Modelin Eğitilmesi
set.seed(123)
model_cat <- catboost.train(train_pool, params = params_cat)
# Tahmin
pred_cat <- catboost.predict(model_cat, test_pool)
# Sonucu Görelim
print(paste("CatBoost Tahmini:", round(pred_cat, 2)))
## [1] "CatBoost Tahmini: 21.96"
Şimdi elimizdeki 4 büyük algoritmayı aynı tablo ve grafik üzerinde karşılaştıralım.
(Not: Önceki modelleri de burada hızlıca tekrar çalıştırıyoruz ki hepsi bellekte olsun)
# --- 1. GBM ---
model_gbm <- gbm(Burs ~ GPA, data = df_burs, distribution = "gaussian", n.trees = 100, interaction.depth = 3, shrinkage = 0.1, n.minobsinnode = 1)
pred_gbm <- predict(model_gbm, test_data, n.trees = 100)
# --- 2. XGBoost ---
dtrain_xgb <- xgb.DMatrix(data = as.matrix(df_burs$GPA), label = df_burs$Burs)
model_xgb <- xgb.train(params = list(objective = "reg:squarederror", eta = 0.3, max_depth = 2), data = dtrain_xgb, nrounds = 50, verbose = 0)
pred_xgb <- predict(model_xgb, xgb.DMatrix(data = as.matrix(test_data$GPA)))
# --- 3. LightGBM ---
dtrain_lgb <- lgb.Dataset(data = as.matrix(df_burs$GPA), label = df_burs$Burs)
model_lgb <- lgb.train(params = list(objective = "regression", learning_rate = 0.1, num_leaves = 4, min_data_in_leaf = 1), data = dtrain_lgb, nrounds = 50, verbose = 0)
## [LightGBM] [Warning] No further splits with positive gain, best gain: -inf
## [LightGBM] [Warning] No further splits with positive gain, best gain: -inf
## [LightGBM] [Warning] No further splits with positive gain, best gain: -inf
## [LightGBM] [Warning] No further splits with positive gain, best gain: -inf
## [LightGBM] [Warning] No further splits with positive gain, best gain: -inf
## [LightGBM] [Warning] No further splits with positive gain, best gain: -inf
## [LightGBM] [Warning] No further splits with positive gain, best gain: -inf
## [LightGBM] [Warning] No further splits with positive gain, best gain: -inf
## [LightGBM] [Warning] No further splits with positive gain, best gain: -inf
## [LightGBM] [Warning] No further splits with positive gain, best gain: -inf
## [LightGBM] [Warning] No further splits with positive gain, best gain: -inf
## [LightGBM] [Warning] No further splits with positive gain, best gain: -inf
## [LightGBM] [Warning] No further splits with positive gain, best gain: -inf
## [LightGBM] [Warning] No further splits with positive gain, best gain: -inf
## [LightGBM] [Warning] No further splits with positive gain, best gain: -inf
## [LightGBM] [Warning] No further splits with positive gain, best gain: -inf
## [LightGBM] [Warning] No further splits with positive gain, best gain: -inf
## [LightGBM] [Warning] No further splits with positive gain, best gain: -inf
## [LightGBM] [Warning] No further splits with positive gain, best gain: -inf
## [LightGBM] [Warning] No further splits with positive gain, best gain: -inf
## [LightGBM] [Warning] No further splits with positive gain, best gain: -inf
## [LightGBM] [Warning] No further splits with positive gain, best gain: -inf
## [LightGBM] [Warning] No further splits with positive gain, best gain: -inf
## [LightGBM] [Warning] No further splits with positive gain, best gain: -inf
## [LightGBM] [Warning] No further splits with positive gain, best gain: -inf
## [LightGBM] [Warning] No further splits with positive gain, best gain: -inf
## [LightGBM] [Warning] No further splits with positive gain, best gain: -inf
## [LightGBM] [Warning] No further splits with positive gain, best gain: -inf
## [LightGBM] [Warning] No further splits with positive gain, best gain: -inf
## [LightGBM] [Warning] No further splits with positive gain, best gain: -inf
## [LightGBM] [Warning] No further splits with positive gain, best gain: -inf
## [LightGBM] [Warning] No further splits with positive gain, best gain: -inf
## [LightGBM] [Warning] No further splits with positive gain, best gain: -inf
## [LightGBM] [Warning] No further splits with positive gain, best gain: -inf
## [LightGBM] [Warning] No further splits with positive gain, best gain: -inf
## [LightGBM] [Warning] No further splits with positive gain, best gain: -inf
## [LightGBM] [Warning] No further splits with positive gain, best gain: -inf
## [LightGBM] [Warning] No further splits with positive gain, best gain: -inf
## [LightGBM] [Warning] No further splits with positive gain, best gain: -inf
## [LightGBM] [Warning] No further splits with positive gain, best gain: -inf
## [LightGBM] [Warning] No further splits with positive gain, best gain: -inf
## [LightGBM] [Warning] No further splits with positive gain, best gain: -inf
## [LightGBM] [Warning] No further splits with positive gain, best gain: -inf
## [LightGBM] [Warning] No further splits with positive gain, best gain: -inf
## [LightGBM] [Warning] No further splits with positive gain, best gain: -inf
## [LightGBM] [Warning] No further splits with positive gain, best gain: -inf
## [LightGBM] [Warning] No further splits with positive gain, best gain: -inf
## [LightGBM] [Warning] No further splits with positive gain, best gain: -inf
## [LightGBM] [Warning] No further splits with positive gain, best gain: -inf
## [LightGBM] [Warning] No further splits with positive gain, best gain: -inf
pred_lgb <- predict(model_lgb, as.matrix(test_data$GPA))
# CatBoost sonucu (Eğer yüklü değilse NA döner)
cat_res <- if(catboost_available) pred_cat else NA
results <- data.frame(
Algoritma = c("GBM", "XGBoost", "LightGBM", "CatBoost"),
Özellik = c("Temel Yöntem", "Optimize & Regülerize", "Hız & Yaprak Odaklı", "Kategorik Uzman & Simetrik"),
Tahmin_3.3 = c(pred_gbm, pred_xgb, pred_lgb, cat_res)
)
results %>%
kbl(caption = "Tablo 6: Mahşerin 4 Atlısı - Tahmin Karşılaştırması") %>%
kable_styling(bootstrap_options = c("striped", "hover"), full_width = F) %>%
column_spec(3, bold = T, color = "darkred")
| Algoritma | Özellik | Tahmin_3.3 |
|---|---|---|
| GBM | Temel Yöntem | 22.17363 |
| XGBoost | Optimize & Regülerize | 21.97489 |
| LightGBM | Hız & Yaprak Odaklı | 28.97349 |
| CatBoost | Kategorik Uzman & Simetrik | 21.96414 |
Tüm modellerin davranışlarını tek grafikte görelim. Bu grafik, algoritmaların karakterini anlamak için en değerli çıktıdır.
# Simülasyon verisi
sim_x <- seq(2.5, 4.0, 0.01)
sim_data <- data.frame(GPA = sim_x)
# Tahminleri Üret
sim_data$GBM <- predict(model_gbm, sim_data, n.trees = 100)
sim_data$XGB <- predict(model_xgb, xgb.DMatrix(data = as.matrix(sim_x)))
sim_data$LGB <- predict(model_lgb, as.matrix(sim_x))
if(catboost_available){
pool_sim <- catboost.load_pool(data = as.matrix(sim_x))
sim_data$CAT <- catboost.predict(model_cat, pool_sim)
} else {
sim_data$CAT <- NA
}
# Görselleştirme
p <- ggplot(df_burs, aes(x = GPA, y = Burs)) +
geom_point(size = 5, color = "black", alpha = 0.5) +
# GBM (Yeşil)
geom_line(data = sim_data, aes(x = GPA, y = GBM, color = "GBM"), size = 1, linetype = "dashed") +
# XGBoost (Kırmızı)
geom_line(data = sim_data, aes(x = GPA, y = XGB, color = "XGBoost"), size = 1.1) +
# LightGBM (Mor)
geom_line(data = sim_data, aes(x = GPA, y = LGB, color = "LightGBM"), size = 1.1, linetype = "dotdash")
# CatBoost varsa çiz (Turuncu)
if(catboost_available){
p <- p + geom_line(data = sim_data, aes(x = GPA, y = CAT, color = "CatBoost"), size = 1.3, alpha = 0.8)
}
p + labs(title = "Büyük Final: Boosting Algoritmalarının Savaşı",
subtitle = "CatBoost (Turuncu) simetrik yapısı sayesinde genellikle daha dengeli ve kararlı basamaklar oluşturur.",
y = "Burs Miktarı", x = "GPA", color = "Algoritma") +
scale_color_manual(values = c("GBM"="#27AE60", "XGBoost"="#E74C3C", "LightGBM"="#8E44AD", "CatBoost"="#F39C12")) +
theme_minimal() +
theme(legend.position = "bottom", text = element_text(size=12))
Şekil 8: Tüm Boosting Algoritmalarının Karşılaştırması
Bu çalışmada, basit bir Karar Ağacı ile başladığımız yolculuğu, modern yapay zekanın 4 büyük “Boosting” algoritmasıyla tamamladık. Elde ettiğimiz Tablo 6 ve Şekil 8, algoritmaların karakteristiği hakkında bize çok net mesajlar veriyor.
Tabloya baktığımızda GBM, XGBoost ve CatBoost modellerinin ~22.00 civarında (birbirine çok yakın) tahminler ürettiğini görüyoruz. Ancak LightGBM şaşırtıcı bir şekilde 28.97 tahmininde bulunmuştur.
Bu bir hata değil, algoritmanın doğasıdır:
Sebep: LightGBM, “Yaprak Odaklı” (Leaf-wise) büyür ve genelleme yapmak için büyük veri kitlelerine ihtiyaç duyar.
Sonuç: Elimizdeki veri seti çok küçük (7 satır) olduğu için LightGBM, verinin alt yapısındaki ince detayları (3.2 ile 3.5 arasındaki kırılımı) yakalayamamış, 3.3 not ortalamasını üst grupla (3.5 not alan ve 30 Burs alan grup) bir tutma eğilimine girmiştir. Bu durum, “Küçük veride LightGBM kullanmanın riski”nin somut kanıtıdır.
Şekil 8’deki grafik savaşını kare kare inceleyelim:
-XGBoost (Kırmızı) ve CatBoost (Turuncu):
Bu iki algoritma grafikte neredeyse üst üste binmiştir.
Siyah veri noktalarına (gerçek değerlere) mükemmel bir hassasiyetle uyum sağlamışlardır.
Yorum: Küçük ve orta ölçekli verilerde, iyi ayarlanmış bir XGBoost ile CatBoost genellikle benzer ve yüksek performans gösterir. CatBoost’un simetrik yapısı burada XGBoost’un optimize yapısıyla başa baş gitmiştir.
-GBM (Yeşil - Kesikli):
Ataları olarak, diğer ikisini (Kırmızı ve Turuncu) yakından takip etmiş ancak biraz daha temkinli davranmıştır.
-LightGBM (Mor - Noktalı):
Grafiğe dikkat ederseniz, mor çizgi 2.5 ile 3.2 arasında neredeyse dümdüz gitmektedir. Verinin o bölgesindeki basamakları (artışları) “görmezden gelmiştir”.
Bu durum Underfitting (Eksik Öğrenme) belirtisidir. Algoritma “Bu kadar az veriyle ben burayı bölmem” diyerek kaba bir tahmin yapmıştır.
Analizlerimiz sonucunda veri bilimcinin karar matrisi şöyle olmalıdır:
| Algoritma | En İyi Olduğu Senaryo | Zayıf Yönü |
|---|---|---|
| Random Forest | Veri az/orta ise ve “Ayarla uğraşmayayım, güvenli sonuç alayım” diyorsanız. | Aşırı karmaşık ilişkileri yakalamada Boosting kadar keskin değildir. |
| XGBoost | Performans (Accuracy) tek kriterse ve parametre (tuning) yapacak vaktiniz varsa. | Parametreleri yanlış ayarlanırsa (bizim örneğimizdeki gibi) veriyi ezberleyebilir. |
| LightGBM | Veriniz Milyonlarca satırsa (Big Data). | Küçük verilerde (Bkz: Tablo Sonucu) başarısızdır. |
| CatBoost | Verinizde Kategorik (Metin) değişkenler varsa veya parametre ayarı yapmadan hızlı/iyi sonuç istiyorsanız. | Çok büyük sayısal verilerde bazen LightGBM’den yavaş kalabilir. |
Makine öğrenmesi yolculuğumuzun bu uzun soluklu durağında, “Ağaç Tabanlı Modeller” (Tree-Based Models) dünyasını en temelinden en zirvesine kadar keşfettik. Basit bir If-Else mantığından yola çıkarak, günümüzün en karmaşık problemlerini çözen yapay zeka sistemlerine nasıl ulaşıldığını adım adım deneyimledik.
Gelin, bu yolculukta heybemizde neler biriktiğine ve ufukta bizi nelerin beklediğine son bir kez bakalım.
Bu seride üç ana evrimsel süreci inceledik:
Karar Ağaçları (Bireysel Akıl):
Veriyi “böl ve yönet” stratejisiyle parçalara ayırdık. Entropy ve Gini ile saflığı aradık, Varyans Azaltımı ile sayısal tahminler yaptık. Gördük ki; tek bir ağaç şeffaftır, anlaşılırdır ama aynı zamanda kırılgandır. Verideki en ufak bir gürültü, tüm yapıyı değiştirebilir (Yüksek Varyans).
Rastgele Ormanlar (Kolektif Akıl - Bagging):
Tek bir ağacın kararsızlığını yenmek için “Kalabalıkların Bilgeliği”ne başvurduk. Yüzlerce bağımsız ağacı eğitip ortalamalarını alarak (Bagging), varyansı düşürdük ve “Güvenli Liman”a ulaştık.
Boosting Algoritmaları (Hata Odaklı Evrim - Boosting):
Bağımsızlık yerine işbirliğini seçtik. Her model, bir öncekinin hatasını düzelterek ilerledi. GBM ile temeli attık, XGBoost ile hızı ve performansı optimize ettik, LightGBM ile büyük verinin hızına yetiştik ve CatBoost ile kategorik verilerin dilinden anladık.
Sonuçta gördük ki; doğru ayarlandığında bu algoritmalar, tablosal (tabular) verilerde insanüstü bir performans sergileyebilir.
Gelin, bu yolculuğu nasıl taçlandıracağımıza ve ufukta bizi nelerin beklediğine bakalım.
Teorik olarak Karar Ağaçları, Random Forest ve Boosting (XGBoost, LightGBM, CatBoost) algoritmalarının nasıl çalıştığını öğrendik. Ancak bir veri bilimci, algoritmaları sadece teorik olarak bilmekle yetinemez; onları savaş alanında test etmelidir.
Bu yüzden bu seriyi bitirmeden önce, bir sonraki bölümde dev bir Uygulama Projesi gerçekleştireceğiz: Titanic Hayatta Kalma Tahmini.
Bu projede sadece ağaç tabanlı modelleri değil, geçmişte öğrendiğimiz klasikleri de sahaya süreceğiz:
Klasikler: Lojistik Regresyon ve k-NN.
Modernler: Karar Ağaçları, Random Forest, XGBoost, LightGBM ve CatBoost.
Veri temizlemeden (EDA) öznitelik mühendisliğine (Feature Engineering), model kurulumundan hiperparametre optimizasyonuna kadar uçtan uca (End-to-End) bir çalışma yaparak “Hangi algoritma ne zaman kazanır?” sorusunun cevabını verilerle vereceğiz.
Titanic projesiyle bu defteri kapattığımızda, geriye dönüp baktığımızda önemli bir gerçeği fark edeceğiz. İncelediğimiz tüm bu ağaç tabanlı modellerin ortak bir çalışma prensibi vardır: Veriyi dik açılı kutulara bölmek.
\[ \text{Eğer } X < 5 \text{ ve } Y > 10 \text{ ise...} \] Ancak doğa her zaman dik açılı değildir. Gerçek hayat problemleri her zaman “Kutu” mantığına sığmaz.
- Ya verilerimiz iç içe geçmiş halkalar şeklindeyse?
- Ya sınıfları ayıran sınır düz bir çizgi değil de, karmaşık bir eğri veya dalga ise?
İşte bu noktada ağaçların yeteneği sınırlanır ve sahneye Geometrinin Efendisi çıkar.
Titanic projesiyle ağaç tabanlı modellere veda ettikten sonra, bir sonraki ders serimizde , makine öğrenmesine tamamen farklı bir felsefeyle yaklaşan Destek Vektör Makineleri (Support Vector Machines - SVM) algoritmasını inceleyeceğiz.
SVM, veriyi kutulara hapsetmeye çalışmaz. Aksine:
1. Sınıflar arasından geçebilecek “En Geniş Otoyolu” (Maximum Margin) inşa etmeye çalışır.
2. Veriler karmaşıksa, onları “Kernel Trick” (Çekirdek Hilesi) adı verilen matematiksel bir sihirle üst boyutlara taşır ve orada çözüm arar.
Ağaçların merdiven kurduğu yerde, SVM köprüler inşa eder.
Önce Titanic projesinde, ardından veri biliminin bu büyüleyici geometrik dünyasında görüşmek üzere.
Veriyle kalın. Lütfen takipte kalın dostlar…