Sınıflandırma Problemlerinde En İyi Algoritmayı Seçmek: KNN, SVM, Decision Tree ve Random Forest ile Karşılaştırmalı Pipeline
Ş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.
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:
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.
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üphane | Görevi bu defterde |
|---|---|
numpy | Sayısal dizi işlemleri; IQR hesabı, sınıf sayımları (np.bincount) için. |
pandas | Sonuç tablosunu (results_df) okunaklı bir DataFrame olarak tutmak için. |
matplotlib / seaborn | Confusion matrix ısı haritaları ve özellik önemi çubuk grafikleri. |
time | Her modelin aramaya ne kadar süre harcadığını ölçmek — gerçek projelerde hız da bir karardır. |
joblib | Eğitilmiş modeli diske kaydetmek/yüklemek (Python'ın pickle'ından NumPy dizileri için daha verimlidir). |
scikit-learn | Veri seti, ön işleme, modeller, model seçimi ve metrikler — defterin omurgası. |
scipy.stats | randint, 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). |
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.
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
%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.
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ı.
| Parametre | Anlamı | Etkisi |
|---|---|---|
test_size=0.2 | Verinin %20'si teste ayrılır | 142/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=y | Sınıf oranlarını korur | Test 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=42 | Rastgeleliği sabitler | Aynı kodu tekrar çalıştırdığınızda aynı bölünmeyi elde edersiniz — tekrar üretilebilirlik (reproducibility) için kritiktir. |
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.
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.
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.
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ı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):kdeğeri. Küçükk→ gürültüye duyarlı (overfit); büyükk→ sınır bulanıklaşır (underfit).weights:uniform(her komşu eşit oy) vsdistance(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) vsmanhattan(ızgara mesafesi).p: Minkowski mesafe formülünün üssü;p=1manhattan,p=2euclidean ile örtüşür.
Destek Vektör Makinesi
SVC · sınır tabanlı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üçükC→ geniş marj, bazı hatalara izin (az overfit); büyükC→ 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ükgamma→ her nokta sadece çok yakınını etkiler (karmaşık, kıvrımlı sınır); küçükgamma→ daha düz sınır.kernel:rbf(radyal, eğrisel sınırlar) vspoly(polinom dereceli sınırlar).
Karar Ağacı
DecisionTreeClassifier · kural tabanlı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) vsentropy(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)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,log2veya 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.
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.
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.
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.
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ütun | Soru |
|---|---|
CV F1-Macro | Model, 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-Macro | Model, 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 Accuracy | Referans 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. |
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.
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.
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?
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.
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.
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
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).
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.
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.
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.
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