Sınıflandırma Problemlerinde En İyi Algoritmayı Seçmek: KNN, SVM, Decision Tree ve Random Forest ile Karşılaştırmalı Pipeline

Şarap Defteri — KNN, SVM, Karar Ağacı ve Random Forest
MAKİNE ÖĞRENMESİ  ·  SINIFLANDIRMA  ·  SATIR SATIR REHBER

Şarabı Sınıflandırmak:
KNN, SVM, Karar Ağacı ve Random Forest

Elindeki not defteri, dört farklı algoritmayı aynı veri seti üzerinde adil şartlarda karşılaştıran gerçek bir veri bilimi hattı (pipeline). Bu sayfa o defterin her satırını açıyor: her terimin anlamını, her kararın gerekçesini ve bir adımın diğerini nasıl etkilediğini.

178örnek, 13 öznitelik
3şarap sınıfı (cultivar)
4algoritma karşılaştırması
scikit-learnüzerine kurulu
0Giriş

Bu defter ne yapıyor, neden bu sırayla?

Defter, scikit-learn'ün load_wine veri setindeki 178 şarap örneğini, kimyasal analiz sonuçlarına (alkol oranı, fenol miktarı, renk yoğunluğu gibi 13 öznitelik) bakarak 3 farklı üzüm çeşidine (cultivar) ayırmaya çalışıyor. Asıl mesele "hangi model en iyisi" sorusunun cevabı değil — asıl mesele bu cevaba güvenilir şekilde nasıl ulaşılacağı.

Bu yüzden defter, gerçek dünyadaki bir veri bilimi projesinin iskeletini izliyor. Her adım, kendisinden sonraki adımı kısıtlıyor veya mümkün kılıyor:

Veriyi tanı Sızıntısız böl Pipeline kur Hiperparametre ara Doğru metrikle ölç Hatayı yorumla Üretime hazırla

Bu sayfada her bölüm, defterdeki bir hücreye karşılık geliyor. Kod bloklarının üstünde "bu satır ne yapıyor", altında ise "bu seçim neyi etkiliyor" başlıklarını bulacaksınız.

1Kurulum

Kütüphaneler: hangisi hangi görevde?

Defterin ilk hücresi alışılmadık bir şey yapıyor: kütüphaneleri tek tek import etmek yerine bir sözlükte (dict) tutup exec() ile sırayla çalıştırıyor ve her birinin başarıyla yüklenip yüklenmediğini try/except ile raporluyor. Bu, büyük bir proje şablonunda eksik bir kütüphaneyi hemen fark etmek için kullanışlı bir savunma deseni — küçük projelerde genelde gereksizdir, ama "hangi bağımlılık eksik" sorusuna anında cevap verir.

KütüphaneGörevi bu defterde
numpySayısal dizi işlemleri; IQR hesabı, sınıf sayımları (np.bincount) için.
pandasSonuç tablosunu (results_df) okunaklı bir DataFrame olarak tutmak için.
matplotlib / seabornConfusion matrix ısı haritaları ve özellik önemi çubuk grafikleri.
timeHer modelin aramaya ne kadar süre harcadığını ölçmek — gerçek projelerde hız da bir karardır.
joblibEğitilmiş modeli diske kaydetmek/yüklemek (Python'ın pickle'ından NumPy dizileri için daha verimlidir).
scikit-learnVeri seti, ön işleme, modeller, model seçimi ve metrikler — defterin omurgası.
scipy.statsrandint, uniform, loguniform: rastgele arama için sürekli olasılık dağılımları (aşağıda Bölüm 6'da neden önemli olduğunu göreceksiniz).
Bağlantı

Burada seçilen scipy.stats dağılımları, Bölüm 6'daki RandomizedSearchCV'nin nasıl çalıştığını doğrudan belirliyor: randint(3,30) gibi bir dağılım, arama sırasında her denemede 3 ile 29 arasında rastgele bir tam sayı üretir; sabit bir liste değildir.

2Keşifsel Veri Analizi

Veriyi tanımadan model kurulmaz

load_wine(), scikit-learn'ün içine gömülü, gerçek bir kimyasal analiz veri setini döndürür: İtalya'nın aynı bölgesinde üç farklı üzüm çeşidinden üretilmiş şarapların 13 kimyasal ölçümü (alkol, malik asit, kül, flavonoidler, renk yoğunluğu, prolin vb.).

data = load_wine()
X = data.data            # (178, 13) boyutlu öznitelik matrisi
y = data.target          # 0, 1, 2 etiketleri (3 cultivar)
feature_names = data.feature_names

Çıktı şunu gösteriyor: 178 örnek, 13 öznitelik, sınıf dağılımı class_0: 59, class_1: 71, class_2: 48. Bu sayılar küçük farklarla dengeli — yani aşırı bir sınıf dengesizliği yok, ama tam eşit de değil (48 ile 71 arasında fark var). Defter, bu hafif dengesizliği görmezden gelmiyor; ileride class_weight='balanced' ve f1_macro metriğiyle bunu telafi ediyor.

Neden IQR ile aykırı değer (outlier) bakıyoruz?

IQR (Çeyrekler Arası Genişlik), bir özniteliğin "normal" değer aralığını tanımlayan dağılıma dayanıklı (robust) bir ölçüdür. Klasik kural: Q1 - 1.5×IQR altı veya Q3 + 1.5×IQR üstü kalan noktalar potansiyel aykırı değerdir.

Q1 = np.percentile(X, 25, axis=0)
Q3 = np.percentile(X, 75, axis=0)
IQR = Q3 - Q1
outlier_ratio = np.mean(((X < (Q1 - 1.5*IQR)) | (X > (Q3 + 1.5*IQR))).any(axis=1))
# Çıktı: Potansiyel aykırı değer oranı: %9.55
Bu sonuç neyi etkiliyor?

%9.55'lik bir aykırı değer oranı "endişe verici" değil ama "yok sayılabilir" de değil. Bu bilgi, Bölüm 6'da StandardScaler kullanma kararını destekliyor: aykırı değer oranı çok daha yüksek olsaydı, ortalama/standart sapıma dayanan StandardScaler yerine medyan/IQR tabanlı RobustScaler tercih edilebilirdi. Aykırı değerler özellikle KNN ve SVM'i etkiler, çünkü bu iki model mesafeye dayanır; ağaç tabanlı modeller (Decision Tree, Random Forest) eşik tabanlı bölme yaptıkları için aykırı değerlere karşı doğal olarak daha dayanıklıdır.

3Veri Bölme

Stratified train-test split: neden sıradan bölme yetmez?

X_train, X_test, y_train, y_test = train_test_split(
    X, y, test_size=0.2, stratify=y, random_state=42
)

Sıradan (rastgele) bir bölme, küçük veri setlerinde şans eseri test kümesine bir sınıftan çok az örnek bırakabilir. stratify=y parametresi, bölme işleminin her sınıfın oranını eğitim ve test kümesinde korumasını garanti eder.

Sonuç: 142 eğitim / 36 test örneği; eğitimde [47 57 38], testte [12 14 10] — oranlar orijinal [59 71 48] dağılımıyla tutarlı.

ParametreAnlamıEtkisi
test_size=0.2Verinin %20'si teste ayrılır142/36'lık bölünmeyi belirler; küçük veri setinde test kümesi çok büyütülürse model yeterli veriyle eğitilemez.
stratify=ySınıf oranlarını korurTest setindeki performans ölçümünün gerçek sınıf dağılımını yansıtmasını sağlar; metrik seçimiyle (f1_macro) birlikte çalışır.
random_state=42Rastgeleliği sabitlerAynı kodu tekrar çalıştırdığınızda aynı bölünmeyi elde edersiniz — tekrar üretilebilirlik (reproducibility) için kritiktir.
Kritik nokta

X_test ve y_test, bu noktadan sonra defterin sonuna kadar sadece nihai değerlendirme için kullanılmalı. Hiçbir ölçekleme, hiçbir hiperparametre seçimi X_test'i "görmemeli". Bir sonraki bölüm tam olarak bunu nasıl garanti ettiğimizi açıklıyor.

4Mimari Karar

Pipeline ve veri sızıntısı (data leakage)

Veri sızıntısı, bir makine öğrenmesi projesinde yapılabilecek en sessiz ve en tehlikeli hatadır — model gerçek dünyada başarısız olmadan önce sizi yanlış bir güvenle kandırır.

Yanlış (ve çok yaygın) yaklaşım şudur:

# YANLIŞ — yapmayın
scaler = StandardScaler()
X_train_scaled = scaler.fit_transform(X_train)   # sadece train'e fit ✓ ama...
# ...sonra cross-validation her fold'da AYNI scaler'ı kullanırsa
# validation fold'unun istatistikleri zaten train istatistiklerine karışmıştır
# değil; ASIL tehlike scaler'ı TÜM X_train üzerinde fit edip CV yapmaktır.

Cross-validation (çapraz doğrulama) sırasında veri, K parçaya (fold) bölünür; her turda bir parça doğrulama, kalanı eğitim olur. Eğer StandardScaler, CV döngüsünden önce tüm X_train'e fit edilirse, her fold'un "doğrulama" parçasının ortalama/standart sapım bilgisi, scaler'ın parametrelerine zaten karışmış olur. Bu, doğrulama fold'unun teknik olarak hâlâ "görülmemiş" sayılmasını bozar ve CV skorlarını gerçekte olduğundan daha iyimser gösterir.

Defterdeki doğru çözüm, ölçekleyiciyi modelin kendisiyle birlikte bir Pipeline içine koymaktır:

Pipeline([
    ('scaler', StandardScaler()),
    ('clf', KNeighborsClassifier())
])

Pipeline, scikit-learn'de birden çok adımı (dönüştürücüler + son tahminci) tek bir nesne gibi davranacak şekilde zincirler. RandomizedSearchCV bu pipeline'ı her CV fold'unda yeniden fit ettiğinde, StandardScaler da sadece o foldun eğitim parçasına yeniden uyarlanır (refit edilir). Doğrulama parçası, ölçekleme istatistiklerini asla görmez.

Resmi kaynak ne diyor?

scikit-learn'ün kendi dokümantasyonu bunu net biçimde vurgular: pipeline kullanımı tahmincileri tek bir nesnede birleştirip, dönüştürücüler ve tahmincilerin parametrelerini "__" ayraçlı isimlerle birlikte ayarlamayı mümkün kılar, böylece her adım çapraz doğrulama döngüsünün içinde kalır, dışında değil. Resmi örnekte de pipeline'ın amacı açıkça test setinin eğitim setine sızmasını önlemek olarak belirtilir.

Neden bazı modellerde scaler yok?

Defterde Decision Tree ve Random Forest pipeline'larında StandardScaler bilerek yok:

'Decision Tree': {
    'pipeline': Pipeline([
        ('clf', DecisionTreeClassifier(random_state=42, class_weight='balanced'))
    ]),
    ...
}

Ağaç tabanlı modeller, bir özniteliği "alkol > 12.5 mi?" gibi eşik sorularıyla böler. Bu bölme mantığı, özniteliğin ölçeğinden (santimetre mi metre mi, 0-1 mi 0-100 mi) etkilenmez — sadece sıralamasından etkilenir. KNN ve SVM ise mesafeye/iç çarpıma dayandığı için, ölçeği büyük bir öznitelik (örn. proline, yüzlerce birim) ölçeği küçük bir özniteliği (örn. fenol oranı, 0-5 birim) tamamen ezer. Bu yüzden KNN ve SVM'de ölçekleme zorunlu, ağaçlarda gereksizdir.

5Dört Aday

Modeller ve hiperparametre uzayları

Dört modelin hepsinde dikkat edilen ortak bir nokta: class_weight='balanced'. Bu parametre, az temsil edilen sınıfların (örn. 48 örnekli class_2) eğitim sırasındaki hata payını otomatik olarak büyütür, böylece model çoğunluk sınıfını ezbere öğrenip azınlık sınıfını göz ardı edemez. (KNN'de bu parametre yoktur — KNN'in dengesizlikle başa çıkma yolu farklıdır, aşağıda anlatılıyor.)

K-En Yakın Komşu

KNeighborsClassifier · mesafe tabanlı
Ölçekleme gereksinimi
Yorumlanabilirlik
Eğitim hızı

Bir noktayı sınıflandırmak için, eğitim verisindeki en yakın k komşusuna bakar ve çoğunluk oyu (veya mesafe ağırlıklı oy) ile karar verir. "Eğitim" aşaması yoktur — tüm iş tahmin anında yapılır (lazy learner).

  • n_neighbors (3–30): k değeri. Küçük k → gürültüye duyarlı (overfit); büyük k → sınır bulanıklaşır (underfit).
  • weights: uniform (her komşu eşit oy) vs distance (yakın komşu daha ağır oy). Bölüm 11'de bu ikisi karşılaştırılıyor.
  • metric: euclidean (düz çizgi mesafesi) vs manhattan (ızgara mesafesi).
  • p: Minkowski mesafe formülünün üssü; p=1 manhattan, p=2 euclidean ile örtüşür.

Destek Vektör Makinesi

SVC · sınır tabanlı
Ölçekleme gereksinimi
Yorumlanabilirlik
Eğitim hızı

Sınıfları ayıran, marjini (sınıflar arası boşluğu) en geniş tutan bir hiper-düzlem arar. Doğrusal olarak ayrılamayan veriler için bir kernel (çekirdek fonksiyon) veriyi daha yüksek boyutlu bir uzaya taşır.

  • C (0.01–100, log-uniform): Hata toleransı. Küçük C → geniş marj, bazı hatalara izin (az overfit); büyük C → her noktayı doğru sınıflamaya çalışır (overfit riski).
  • gamma (0.001–1, log-uniform): RBF çekirdeğinde bir noktanın etki alanı. Büyük gamma → her nokta sadece çok yakınını etkiler (karmaşık, kıvrımlı sınır); küçük gamma → daha düz sınır.
  • kernel: rbf (radyal, eğrisel sınırlar) vs poly (polinom dereceli sınırlar).

Karar Ağacı

DecisionTreeClassifier · kural tabanlı
Ölçekleme gereksinimi
Yorumlanabilirlik
Eğitim hızı

Veriyi, her adımda "bir öznitelik </> bir eşik" sorularıyla art arda ikiye böler; her bölünme bir sınıfı diğerlerinden en iyi ayıran soruyu seçer. Sonuç, insanın okuyup takip edebileceği bir kurallar ağacıdır.

  • max_depth (3–20): Ağacın azami derinliği. Sınırsız derinlik → ezber (overfit).
  • min_samples_split / min_samples_leaf: Bir düğümün bölünebilmesi / bir yaprağın var olabilmesi için gereken asgari örnek sayısı — küçük yaprakların gürültüyü ezberlemesini önler.
  • criterion: gini (Gini saflık endeksi) vs entropy (bilgi kazancı) — bölünme kalitesini ölçen iki farklı matematiksel formül; pratikte sonuçları genelde birbirine yakındır.
  • ccp_alpha: Cost-Complexity Pruning (maliyet-karmaşıklık budama) gücü. Büyük değer → ağaç eğitim sonrası daha agresif budanır, daha basit/genellenebilir hale gelir.

Random Forest

RandomForestClassifier · topluluk (ensemble)
Ölçekleme gereksinimi
Yorumlanabilirlik
Eğitim hızı

Birbirinden biraz farklı şekilde eğitilmiş çok sayıda karar ağacının ortak oyuna (oylama) dayanır. Her ağaç, verinin rastgele bir alt örneğiyle (bootstrap) ve her bölünmede rastgele bir öznitelik alt kümesiyle eğitilir — bu rastgelelik, ağaçların birbirinden farklı hatalar yapmasını ve toplandığında hataların birbirini götürmesini sağlar.

  • n_estimators (50–300): Ormandaki ağaç sayısı. Daha fazla ağaç genelde daha kararlı ama daha yavaş sonuç verir.
  • max_depth, min_samples_split: Tek bir Karar Ağacı'ndaki gibi, her ağacın karmaşıklığını sınırlar.
  • max_features: Her bölünmede kaç öznitelik denenir (sqrt, log2 veya tümü). Bu, ağaçlar arası çeşitliliği kontrol eder — düşük değer daha çeşitli (daha az korele) ağaçlar üretir.
  • ccp_alpha: Tek ağaçtaki gibi budama; ormanda genelde daha küçük tutulur çünkü asıl genelleme gücü zaten ağaçların çoğunluğundan gelir.
Bağlantı: hiperparametre uzayı neden bu kadar geniş?

Her modelin param_dist sözlüğü, Bölüm 6'da RandomizedSearchCV'nin örnekleyeceği arama uzayını tanımlar. randint/uniform/loguniform kullanılması (sabit bir liste değil), aramanın sürekli bir aralıkta rastgele noktalar denemesine izin verir — bu da Bölüm 1'de neden scipy.stats'in içe aktarıldığını açıklar.

6Otomatik Ayarlama

RandomizedSearchCV, StratifiedKFold ve f1_macro

Bu üç parça birlikte çalışıyor; birini diğerinden ayırmak hatayı görünmez kılar.

Neden Grid Search değil, Randomized Search?

GridSearchCV, tanımladığınız her parametre kombinasyonunu tek tek dener — parametre sayısı arttıkça kombinasyon sayısı katlanarak büyür (kombinasyonsal patlama). RandomizedSearchCV ise sabit bir sayıda (burada n_iter=30) rastgele kombinasyon dener; resmi dokümantasyona göre bu yaklaşım büyük/sürekli parametre uzaylarında genellikle çok daha az denemeyle GridSearch'e yakın sonuçlar verir, çünkü pratikte sonucu belirleyen genelde sadece bir-iki parametre boyutudur ve rastgele örnekleme bu boyutları "ızgaraya" hapsolmadan tarar.

random_search = RandomizedSearchCV(
    estimator=config['pipeline'],
    param_distributions=config['param_dist'],
    n_iter=30,                 # 30 rastgele kombinasyon dene
    cv=cv_strategy,            # StratifiedKFold(5)
    scoring='f1_macro',        # hangi metriğe göre "en iyi"yi seç
    n_jobs=-1,                 # tüm CPU çekirdeklerini kullan
    random_state=42
)
random_search.fit(X_train, y_train)

StratifiedKFold neden burada da var?

Bölüm 3'te stratify=y ile train/test bölmesini dengelemiştik. Aynı mantık, eğitim verisi içindeki 5 katlı çapraz doğrulamaya da uygulanmalı — yoksa bir fold'da bir sınıftan çok az örnek kalabilir ve o fold'daki skor güvenilmez olur.

cv_strategy = StratifiedKFold(n_splits=5, shuffle=True, random_state=42)

shuffle=True, veriyi katlara ayırmadan önce karıştırır (veri setinin orijinal sırası anlamlıysa bu önemlidir); random_state=42 bu karıştırmayı da tekrar üretilebilir kılar.

Neden accuracy değil, f1_macro?

Accuracy (doğruluk), dengesiz sınıflarda yanıltıcıdır: 48 örneklik class_2'yi hep yanlış tahmin eden bir model bile, diğer iki sınıfı doğru bilerek yüksek bir doğruluk skoru gösterebilir.

F1 skoru, kesinlik (precision: "pozitif dediklerimin kaçı gerçekten pozitif") ile duyarlılığı (recall: "gerçek pozitiflerin kaçını yakaladım") birleştirir. average='macro' ise her sınıf için F1'i ayrı hesaplayıp basit ortalamasını alır — büyük sınıfa fazladan ağırlık vermez, her sınıfa eşit önem verir. Bu yüzden defterde 48 örneklik azınlık sınıfının performansı, 71 örneklik çoğunluk sınıfıyla eşit ağırlıkta sonuca yansır.

Üçü birden nasıl bağlanıyor?

class_weight='balanced' (Bölüm 5) modelin eğitim sırasında azınlık sınıfını göz ardı etmesini önlüyor; StratifiedKFold (burada) değerlendirme sırasında her katın sınıf oranını koruyor; f1_macro (burada) ise başarı ölçütünün kendisinde her sınıfa eşit söz hakkı veriyor. Üçü aynı sorunu — sınıf dengesizliği — üç farklı aşamada (eğitim, doğrulama, ölçüm) ele alıyor. Birini çıkarsanız, dengesizlik o aşamadan sızar.

7Karşılaştırma

Sonuç tablosunu okumak

results_df[['Model', 'Test F1-Macro', 'Test Accuracy', 'CV F1-Macro', 'Training Time']]

Bu tabloda dört sütun, dört farklı soruyu cevaplıyor:

SütunSoru
CV F1-MacroModel, hiperparametre aramasında görmediği katlarda ortalama olarak ne kadar iyi performans gösterdi? (±std, bu skorun katlar arasında ne kadar değiştiğini gösterir — büyük std = kararsız model.)
Test F1-MacroModel, hiç görmediği X_test üzerinde nasıl performans gösterdi? Bu, gerçek dünyadaki beklenen performansın en güvenilir tahminidir.
Test AccuracyReferans amaçlı; tek başına karar vermek için kullanılmamalı (Bölüm 6'daki sebep).
Training TimeÜretimde modeli periyodik olarak yeniden eğitecekseniz, bu maliyet önemlidir.
CV skoru ile test skoru çok farklıysa?

Eğer Test F1-Macro, CV F1-Macro'dan belirgin biçimde düşükse, bu genelde modelin hiperparametre aramasına (dolaylı olarak) aşırı uyduğunun bir işaretidir. Eğer test skoru CV skorundan belirgin biçimde yüksekse, bu küçük test kümesinde (36 örnek) şans faktörünü düşündürür — burada wine veri setinin küçüklüğü nedeniyle bazı modeller test setinde 1.0000 (mükemmel) F1 alabiliyor; bu, gerçek dünyada her zaman tekrarlanacağı garantisi vermez.

8Hata Analizi

Confusion Matrix: model nereyi karıştırıyor?

cm = confusion_matrix(y_test, y_pred)
sns.heatmap(cm, annot=True, fmt='d', cmap='Blues', ...)

Confusion matrix (karışıklık matrisi), tek bir sayıya (F1 skoru gibi) indirgenmeyen detayı verir: hangi sınıf hangi sınıfla karıştırılıyor? Köşegen üzerindeki sayılar doğru tahminleri, köşegen dışındakiler hataları gösterir. Örneğin model sürekli class_1'i class_2 ile karıştırıyorsa, bu iki üzüm çeşidinin kimyasal profillerinin birbirine yakın olduğunu — belki ek özniteliklere veya daha ayırt edici bir modele ihtiyaç olduğunu — gösterir.

Bağlantı

Burada görülen hata deseni, Bölüm 9'daki özellik önemine bakmak için bir motivasyon oluşturur: hangi öznitelikler iki karışan sınıfı ayırt etmede zayıf kalıyor olabilir?

9Açıklanabilirlik

Permutation Importance: model-agnostik özellik önemi

Karar Ağacı ve Random Forest'ın kendi içsel özellik önemi (feature_importances_) ölçütü vardır (bir öznitelik bölünmelerde ne kadar sık ve ne kadar etkili kullanıldı). Ama bu, KNN ve SVM gibi modellerde yoktur — bu modeller "hangi öznitelik daha önemli" sorusuna içeriden cevap veremez.

Permutation importance (izin permütasyonu önemi), bu sorunu modelin içine bakmadan, sadece dışarıdan gözlemleyerek çözer:

perm_importance = permutation_importance(
    model, X_test, y_test,
    n_repeats=10,
    scoring='f1_macro'
)

Mantık şu: bir özniteliğin değerlerini rastgele karıştırırsanız (örnekler arasında yer değiştirirsiniz), o öznitelikteki bilgi yok edilir ama veri setinin şekli (boyutu, diğer öznitelikler) aynı kalır. Eğer model performansı bu karıştırmadan sonra çok düşerse, o öznitelik modelin kararı için kritikti. Eğer performans hiç değişmezse, model o özniteliği zaten görmezden geliyordu. Bu işlem n_repeats=10 kez tekrarlanır çünkü karıştırma rastgele olduğu için tek bir denemenin sonucu şans eseri sapabilir; 10 tekrarın ortalaması ve standart sapması daha güvenilir bir tahmin verir.

Neden test setinde, eğitim setinde değil?

Permütasyon önemini eğitim verisi üzerinde hesaplamak, modelin ezberlediği ilişkileri "önemli" gösterebilir. Test verisi üzerinde hesaplamak, özniteliğin modelin genelleme gücüne ne kadar katkıda bulunduğunu ölçer — bu, iş paydaşlarına ("alkol oranı neden önemli?") açıklama yaparken daha doğru bir hikaye anlatır.

10Üretime Geçiş

En iyi modeli kaydetmek

best_model_name = results_df.iloc[results_df['Test F1-Macro'].astype(float).idxmax()]['Model']
best_model = best_pipelines[best_model_name]
joblib.dump(best_model, 'best_model.pkl')

Burada kaydedilen şey sadece bir sınıflandırıcı değil — tüm pipeline (varsa scaler dahil). Bu çok önemli: eğer sadece clf kaydedilseydi, üretimde modeli kullanırken gelen yeni veriyi aynı ölçekleme parametreleriyle (eğitimde öğrenilen ortalama/standart sapım) elle ölçeklemeniz gerekirdi — bu, ölçekleme adımının atlanması veya hatalı tekrarlanması riskini taşır. Pipeline'ı bütün olarak kaydetmek, "ham veri içeri, tahmin dışarı" garantisi verir:

# Üretimde tek satır:
model = joblib.load('best_model.pkl')
prediction = model.predict(new_raw_data)   # scaler dahil otomatik çalışır
Üretim için bilinmesi gereken sınır

joblib ile kaydedilen dosyalar, kaydedildiği aynı (veya uyumlu) scikit-learn sürümüyle yüklenmelidir; sürümler arası uyumluluk garanti değildir. Gerçek üretim sistemlerinde bu yüzden model sürümü, kütüphane sürümleriyle birlikte kayıt altına alınır (model kart / sürüm takibi).

11Bonus Deney

KNN'de "uniform" vs "distance"

Bu bölüm, arama sırasında bulunan en iyi diğer hiperparametreleri (k, metric, p) sabit tutup, sadece weights parametresini değiştirerek küçük, kontrollü bir deney yapıyor — bilimsel yöntemdeki "tek değişkeni değiştir" prensibinin küçük bir uygulaması.

knn_uniform = KNeighborsClassifier(**{**best_knn_params, 'weights':'uniform'})
knn_distance = KNeighborsClassifier(**{**best_knn_params, 'weights':'distance'})

cv_uniform = cross_val_score(knn_uniform, X_train_scaled, y_train, cv=5, scoring='f1_macro')
cv_distance = cross_val_score(knn_distance, X_train_scaled, y_train, cv=5, scoring='f1_macro')

weights='uniform', k komşunun her birine eşit oy verir. weights='distance' ise yakın komşulara daha ağır oy verir (genelde 1/mesafe ile ağırlıklandırılır) — sezgisel olarak daha mantıklı görünür ("bana en yakın komşum, 10. en yakın komşumdan daha güvenilir bir ipucudur"), ama her veri setinde doğru olmaz. Bu defterde sonuç distance'ın (0.9796) uniform'dan (0.9736) hafifçe daha iyi olduğunu gösteriyor — ama standart sapmalar (±0.025, ±0.027) örtüştüğü için bu fark istatistiksel olarak güçlü bir iddia değil, sadece bu veri setine özgü hafif bir eğilim.

12Defterde Dikkat Edilecek Nokta

Son hücredeki hata, neden orada?

Defterin son hücresi çalıştırıldığında bir SyntaxError veriyor: invalid character '🚀' (U+1F680). Bunun nedeni model hatası değil, bir defter düzenleme hatası: bu hücrenin içeriği Markdown formatında yazılmış (## Proje Tamamlandı! gibi başlıklar ve madde işaretleri içeriyor) ama hücre türü yanlışlıkla "kod" (code) olarak ayarlı kalmış. Python yorumlayıcısı, Markdown metnini geçerli Python kodu olarak okumaya çalışınca ## ve emoji karakterinde patlıyor.

Pratik ders

Jupyter/Colab defterlerinde hücre türünü (kod / Markdown) değiştirmek tek bir tuşla (genelde Esc sonra M veya Y) yapılır ve sık unutulan bir adımdır. Bu hatayı görmek, bir makine öğrenmesi defterinin her hatasının algoritmik olmadığını, çoğu zaman sadece dikkatsizlikten kaynaklandığını hatırlatması açısından öğretici.

13Referans

Sözlük: Defterde geçen her terim

EDA
Exploratory Data Analysis — modellemeden önce veriyi sayısal/görsel olarak tanıma süreci.
Veri sızıntısı (data leakage)
Test/doğrulama verisinden gelen bilginin, kasıtsız biçimde eğitim sürecine karışması; performansı yapay olarak şişirir.
Stratified split
Veriyi bölerken her sınıfın oranını koruyan bölme yöntemi.
Pipeline
Ön işleme adımlarını ve modeli tek bir nesnede zincirleyen scikit-learn yapısı; CV sızıntısını önler.
StandardScaler
Her özniteliği ortalama 0, standart sapma 1 olacak şekilde dönüştürür (z-skoru).
class_weight='balanced'
Azınlık sınıflara eğitim hatasında otomatik olarak daha fazla ağırlık veren ayar.
RandomizedSearchCV
Hiperparametre uzayından sabit sayıda rastgele kombinasyon deneyen arama yöntemi.
StratifiedKFold
Çapraz doğrulama katlarını oluştururken sınıf oranlarını koruyan bölme stratejisi.
f1_macro
Her sınıfın F1 skorunu eşit ağırlıkla ortalayan metrik; sınıf dengesizliğine karşı dayanıklı.
Confusion matrix
Gerçek ve tahmin edilen sınıfları karşılaştıran NxN tablo; hata desenlerini gösterir.
Permutation importance
Bir özniteliği rastgele karıştırıp performans düşüşünü ölçen, modelden bağımsız önem ölçütü.
IQR / aykırı değer
Çeyrekler arası genişlik; bu aralığın dışında kalan noktalar potansiyel aykırı değer sayılır.
random_state
Rastgelelik içeren işlemleri tekrar üretilebilir kılan sabit "tohum" (seed) değeri.
n_jobs=-1
İşlemi mevcut tüm CPU çekirdeklerinde paralel çalıştırma ayarı.
ccp_alpha
Karar ağaçlarında eğitim sonrası budama (pruning) gücünü kontrol eden parametre.
joblib
NumPy dizileri içeren Python nesnelerini (örn. eğitilmiş modelleri) etkin biçimde diske kaydedip yükleyen kütüphane.

Comments

Popular posts from this blog

CIFAR-10 Dataset Classification Using Convolutional Neural Networks (CNNs) With PyTorch

Radial Basis Function Networks with PyTorch

Deep Q-Network (DQN) CartPole With PyTorch