Kural yazmak yerine kuralı öğrenmek
Ilgaz Bank'ın kredi biriminde yıllardır şöyle kurallar yazılır: "Geliri 30 bin TL'nin altındaysa ve son bir yılda iki gecikmesi varsa reddet." Kurallar çoğaldıkça birbirine çarpar, yeni bir müşteri profili geldiğinde kimse hangi kuralı güncelleyeceğini bilemez. Makine öğrenmesi (machine learning, ML) bu işi tersine çevirir: kuralı sen yazmazsın, geçmiş örneklerden (geçmiş başvurular ve sonuçları) bir algoritma kuralı kendisi öğrenir. Sen veriyi hazırlar, doğru soruyu sorar ve öğrenilen modelin gerçekten işe yarayıp yaramadığını ölçersin.
Bu derste ML'in temel sözlüğünü, scikit-learn kütüphanesinin ortak arayüzünü, ilk üç modelini ve en önemlisi bir modeli dürüstçe değerlendirmeyi öğreneceksin. numpy dizilerini (numpy dersi) ve pandas DataFrame'lerini (pandas-giris) bildiğini varsayıyoruz; ortalama, varyans gibi kavramlar istatistik dersinden tanıdık gelecek.
Öğrenme türleri
| Tür | Veride ne var? | Soru | Örnek |
|---|---|---|---|
| Denetimli (supervised) — regresyon | Özellikler + sayısal hedef | "Ne kadar?" | Sepet tutarı, teslim süresi, ev fiyatı |
| Denetimli — sınıflandırma | Özellikler + kategorik hedef | "Hangisi?" | Kredi temerrüde düşer mi, e-posta spam mı |
| Denetimsiz (unsupervised) | Yalnızca özellikler, hedef yok | "Nasıl gruplanıyor?" | Müşteri segmentleri, anormal işlemler |
| Pekiştirmeli (reinforcement) | Ortam + ödül sinyali | "Hangi hamle?" | Depo robotu rotası, oyun oynayan ajan |
Bu dersten itibaren üç ders boyunca denetimli öğrenmeye odaklanacağız; kümeleme gibi denetimsiz yöntemleri ml-kumeleme dersinde göreceksin.
Özellik, hedef ve X/y
Bir tablo düşün: her satır bir örnek (sample; bir müşteri, bir sipariş), her sütun bir özellik (feature; yaş, gelir, sepet adedi). Tahmin etmek istediğin sütun ise hedef (target, label). scikit-learn geleneği: özellik matrisine büyük X, hedef vektörüne küçük y denir. X her zaman iki boyutludur: şekli (örnek_sayısı, özellik_sayısı). Tek özelliğin olsa bile (n, 1) olmalı.
import numpy as np from sklearn.datasets import load_iris, make_classification iris = load_iris() # internet gerektirmez, kütüphaneyle gelir X, y = iris.data, iris.target print(X.shape, y.shape) # 150 çiçek, 4 özellik print(iris.feature_names) print(list(iris.target_names), np.bincount(y)) # Sentetik veri: kontrollü deneyler için Xs, ys = make_classification(n_samples=200, n_features=5, n_informative=3, random_state=42) print(Xs.shape, np.bincount(ys)) metrekare = np.array([65, 90, 120, 150]) print(metrekare.shape, metrekare.reshape(-1, 1).shape) # tek özellik: (n, 1)
load_iris, load_diabetes, load_wine, load_breast_cancer scikit-learn ile birlikte diske gelir; ağ istemez. make_classification ve make_regression ise istediğin boyutta sentetik veri üretir. random_state verdiğinde her çalıştırmada aynı veri çıkar; deneylerin tekrarlanabilir olur.Eğitim ve test: modelin sınav kâğıdı
Bir öğrenciyi, çalıştığı soruların aynısıyla sınava sokarsan ezberlediği için tam not alır ama konuyu anlayıp anlamadığını öğrenemezsin. Modeller de böyledir. Bu yüzden veriyi en baştan ikiye ayırırız: eğitim seti (training set) ile model öğrenir, test seti (test set) ile hiç görmediği veride nasıl davrandığını ölçeriz. Test setine değerlendirme anına kadar dokunulmaz.
import numpy as np
from sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split
X, y = load_iris(return_X_y=True)
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=0.2, random_state=42, stratify=y
)
print(X_train.shape, X_test.shape)
print("eğitim sınıf dağılımı:", np.bincount(y_train))
print("test sınıf dağılımı: ", np.bincount(y_test))
test_size=0.2: verinin %20'si teste ayrılır (yaygın aralık %20-30).random_state=42: karıştırma her seferinde aynı olur. Vermezsen her çalıştırmada farklı bölme ve farklı skor görürsün; hata ayıklamak imkânsızlaşır.stratify=y: sınıf oranları eğitim ve testte korunur. Sınıflandırmada, özellikle dengesiz sınıflarda (temerrüt oranı %5 gibi) neredeyse her zaman ver.
scikit-learn'ün ortak dili: estimator API
scikit-learn'ün en güzel yanı tutarlılığıdır: yüzlerce farklı algoritma aynı metotlarla kullanılır. Bir modeli öğrendiğinde hepsini öğrenmiş olursun.
| Metot | Ne yapar | Kimde var |
|---|---|---|
fit(X, y) | Veriden öğrenir, nesnenin kendisini döndürür | Hepsi (denetimsizde yalnız fit(X)) |
predict(X) | Yeni örnekler için tahmin üretir | Modeller (regresör, sınıflandırıcı) |
score(X, y) | Varsayılan metrik: regresyonda R², sınıflandırmada doğruluk | Modeller |
predict_proba(X) | Sınıf olasılıkları | Çoğu sınıflandırıcı |
transform(X) / fit_transform | Veriyi dönüştürür (ölçekleme, kodlama) | Dönüştürücüler (transformer) |
Öğrenilen her şey alt çizgiyle biten özniteliklerde saklanır: coef_, intercept_, classes_, feature_importances_. Alt çizgi "bu değer fit sırasında hesaplandı" demektir; fit etmeden erişirsen AttributeError ya da NotFittedError alırsın. Hiperparametreler (kullanıcının seçtiği ayarlar: n_neighbors, max_depth) ise kurucuda verilir.
İlk üç model
Doğrusal regresyon (LinearRegression)
Hedefi özelliklerin ağırlıklı toplamı olarak modeller: ŷ = b + w1·x1 + w2·x2 + .... Hızlı, yorumlanabilir, her regresyon projesinde ilk denenecek temel çizgidir (baseline).
import numpy as np
from sklearn.datasets import load_diabetes
from sklearn.linear_model import LinearRegression
from sklearn.model_selection import train_test_split
from sklearn.metrics import mean_absolute_error, mean_squared_error, r2_score
X, y = load_diabetes(return_X_y=True) # 442 hasta, 10 özellik, hedef: 1 yıl sonraki hastalık ilerlemesi
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)
model = LinearRegression()
model.fit(X_train, y_train)
tahmin = model.predict(X_test)
print("MAE :", round(mean_absolute_error(y_test, tahmin), 1))
print("RMSE:", round(float(np.sqrt(mean_squared_error(y_test, tahmin))), 1))
print("R² :", round(r2_score(y_test, tahmin), 3))
print("score() ile aynı mı:", round(model.score(X_test, y_test), 3))
taban = np.full_like(y_test, y_train.mean(), dtype=float) # hep ortalamayı söyleyen model
print("Taban çizgi MAE:", round(mean_absolute_error(y_test, taban), 1))
k-en yakın komşu (KNeighborsClassifier) ve karar ağacı (DecisionTreeClassifier)
KNN yeni örneğe en yakın k eğitim örneğine bakar ve çoğunluğun sınıfını söyler; "öğrenme" aslında veriyi ezberlemektir. Karar ağacı ise veriyi "petal uzunluğu 2.45'ten küçük mü?" gibi evet/hayır sorularıyla dallara böler; kuralları insanın okuyabileceği biçimdedir.
from sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split
from sklearn.neighbors import KNeighborsClassifier
from sklearn.tree import DecisionTreeClassifier, export_text
X, y = load_iris(return_X_y=True)
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=42, stratify=y)
knn = KNeighborsClassifier(n_neighbors=5).fit(X_train, y_train)
agac = DecisionTreeClassifier(max_depth=2, random_state=0).fit(X_train, y_train)
print("KNN doğruluk :", round(knn.score(X_test, y_test), 3))
print("Ağaç doğruluk :", round(agac.score(X_test, y_test), 3))
print("İlk 5 tahmin :", agac.predict(X_test[:5]), "gerçek:", y_test[:5])
print(export_text(agac, feature_names=["sepal_l", "sepal_w", "petal_l", "petal_w"]))
Metrikler: neyi ölçüyoruz?
| Metrik | Problem | Yorum | Ne zaman |
|---|---|---|---|
| MAE (ortalama mutlak hata) | Regresyon | Ortalama kaç birim yanılıyorum; hedefle aynı birim (TL, gün) | İş birimine anlatmak kolay |
| RMSE (kök ortalama kare hata) | Regresyon | Büyük hataları daha çok cezalandırır | Büyük sapmalar pahalıysa |
| R² (belirleme katsayısı) | Regresyon | 1 mükemmel, 0 "hep ortalamayı söylemek" kadar, negatif daha kötü | Modeller arası karşılaştırma |
| Accuracy (doğruluk) | Sınıflandırma | Doğru tahmin oranı | Sınıflar dengeliyse |
Bir metrik tek başına anlamsızdır; her zaman bir taban çizgiyle (baseline) karşılaştır. Regresyonda "hep eğitim ortalamasını söyle", sınıflandırmada "hep en sık sınıfı söyle" modelleri (DummyRegressor, DummyClassifier) en ucuz rakiptir. Temerrüt oranı %5 olan bir portföyde "kimse temerrüde düşmez" diyen model %95 doğruluk alır ve tamamen işe yaramaz. Dengesiz sınıflar için precision, recall ve ROC-AUC'yi bir sonraki derste göreceksin.
Aşırı ve eksik öğrenme
Eksik öğrenme (underfitting): model veriyi yakalayamayacak kadar basittir; eğitimde de testte de kötüdür. Aşırı öğrenme (overfitting): model eğitim verisindeki gürültüyü bile ezberler; eğitimde parlak, testte sönüktür. Teşhis aracın, eğitim ve test skorlarını yan yana görmektir.
from sklearn.datasets import make_classification
from sklearn.model_selection import train_test_split
from sklearn.tree import DecisionTreeClassifier
# flip_y=0.15: etiketlerin %15'i rastgele bozuk (gerçek hayattaki gürültü)
X, y = make_classification(n_samples=600, n_features=8, n_informative=4, flip_y=0.15, random_state=1)
X_tr, X_te, y_tr, y_te = train_test_split(X, y, test_size=0.3, random_state=0, stratify=y)
print("derinlik eğitim test")
for derinlik in [1, 3, 5, 10, None]:
m = DecisionTreeClassifier(max_depth=derinlik, random_state=0).fit(X_tr, y_tr)
print(f"{str(derinlik):>8} {m.score(X_tr, y_tr):.3f} {m.score(X_te, y_te):.3f}")
Derinlik 1'de iki skor da düşük: eksik öğrenme. Derinlik sınırsızken eğitim 1.000'e çıkar ama test düşer: model gürültüyü ezberledi. Tatlı nokta ikisinin arasındadır. Bu ayarı test setine bakarak seçersen test seti artık "görülmemiş" değildir; doğru yöntem (doğrulama seti ve çapraz doğrulama (cross-validation)) ml-pipeline-degerlendirme dersinde.
Veri sızıntısı: en pahalı hata
Veri sızıntısı (data leakage), modelin eğitim sırasında tahmin anında elinde olmayacak bir bilgiyi görmesidir. Sonuç: testte muhteşem, canlıda felaket. Tipik biçimleri:
- Hedeften türetilmiş özellik: kredi temerrüdü tahmininde "tahsilat departmanına devredildi mi" sütunu. Bu bilgi ancak temerrütten sonra oluşur.
- Test verisinin ön işlemeye karışması: ölçekleyiciyi ya da ortalamayla eksik doldurmayı bölmeden önce tüm veriye fit etmek.
- Zaman sızıntısı: gelecekteki satırlarla eğitip geçmişi test etmek.
- Tekrarlanan kayıtlar: aynı müşterinin satırları hem eğitimde hem testte.
ML proje akışı: CRISP-DM
Endüstride en yaygın süreç modeli CRISP-DM'dir (Cross-Industry Standard Process for Data Mining). Doğrusal değil, döngüseldir; değerlendirmede çıkan sonuç seni sık sık başa döndürür.
- İş anlayışı: Hangi karar iyileşecek? Başarı nasıl ölçülecek (ör. "temerrüt kayıplarını %10 azalt")?
- Veri anlayışı: Hangi veri var, kalitesi ne, hedef nasıl tanımlanıyor (90 gün gecikme = temerrüt)?
- Veri hazırlama: Temizleme, birleştirme, özellik üretme; zamanın çoğu buraya gider.
- Modelleme: Taban çizgi, sonra birkaç aday model.
- Değerlendirme: Test metrikleri ve iş metriği; sızıntı kontrolü, adalet ve açıklanabilirlik.
- Yayına alma (deployment): Modeli servis et, izle, performans düşerse yeniden eğit.
random_state, aynı veri sürümüyle aynı sayılar çıkmalıdır. Bu yüzden ilk günden bölme tohumlarını sabitlemek, taban çizgiyi raporlamak ve test setini en sona saklamak profesyonel alışkanlıktır.Yanlılık-varyans dengesi
Bir modelin hatası kabaca üç parçaya ayrılır: yanlılık (bias; modelin varsayımları gerçeği yakalayamıyor, eksik öğrenme), varyans (variance; model eğitim verisindeki küçük değişikliklere aşırı duyarlı, aşırı öğrenme) ve indirgenemez gürültü. Model karmaşıklığını artırmak yanlılığı düşürür ama varyansı artırır. KNN'de küçük k, ağaçta büyük max_depth varyansı artırır. Daha fazla veri toplamak varyansı azaltmanın en güvenilir yoludur.
fit sonrası nesne ve zincirleme
fit nesnenin kendisini döndürdüğü için KNeighborsClassifier(3).fit(X, y).predict(X_yeni) yazılabilir. Aynı nesneyi farklı veriyle yeniden fit etmek önceki öğrenmeyi tamamen siler; iki ayrı model istiyorsan iki nesne oluştur ya da sklearn.base.clone kullan.
import numpy as np from sklearn.base import clone from sklearn.neighbors import KNeighborsClassifier X = np.array([[1], [2], [3], [10], [11], [12]]) y = np.array([0, 0, 0, 1, 1, 1]) sablon = KNeighborsClassifier(n_neighbors=3) m1 = clone(sablon).fit(X, y) m2 = clone(sablon).fit(X[:4], y[:4]) print(m1.predict([[9]]), m2.predict([[9]])) print(sablon.get_params()["n_neighbors"], hasattr(sablon, "classes_"))
Tekrarlanabilirlik kontrol listesi
Bölmede, sentetik veride ve rastgelelik içeren her modelde (DecisionTreeClassifier, RandomForestClassifier) random_state ver; kütüphane sürümlerini requirements.txt ile sabitle; veri kesim tarihini kaydet. Aynı sayıyı yarın yeniden üretemiyorsan modeli savunamazsın.