Veri sızıntısı (leakage): en pahalı hata
Önkoşul:Öznitelik mühendisliği, ölçekleme ve kodlama
Kanca
Aynı veri, aynı model, aynı test kümesi. Tek fark: kategori kodlamasını train_test_split’ten ÖNCE mi yoksa SONRA mı yaptığın. Bu tek sıralama farkı, test R²’sini 0.739’dan 0.677’ye düşürüyor. Hangisi gerçek, ve neden bu kadar fark var?
Sezgi
- derste kategorik verileri sayıya çevirmeyi (kodlama) öğrendik. Ama bir soruyu atladık: bu kodlamayı NE ZAMAN yapmalısın — veriyi eğitim/test diye bölmeden önce mi, sonra mı?
Veri sızıntısı, test verisi hakkındaki bilginin, farkında olmadan eğitim sürecine karışmasıdır. En sinsi biçimi tam olarak bu: kategori ortalaması gibi bir öznitelik, TÜM veriden (test dahil) hesaplanırsa, her test satırının KENDİ hedef değeri, o satırı tahmin etmek için kullanılan sayıya karışmış olur. Sonuç: model gerçekte olduğundan çok daha “başarılı” görünür — ama bu başarı sahte, üretime çıkınca kaybolur.
❌ Sızıntılı — kategori ortalaması TÜM veriden
Test R² = 0.773
Test satırlarının kendi y değeri, hesaplanan kategori ortalamasına sızmış — skor gerçekte olduğundan iyi görünüyor.
✓ Doğru — kategori ortalaması SADECE eğitimden
Test R² = 0.710
Test verisi, ortalama hesabına hiç karışmadı — bu skor modelin gerçek dünyada vereceği performansı yansıtıyor.
144 eğitim / 56 test satırı. Kategori sayısını artır (grup başına düşen satır azalır) — sızıntının etkisi büyüsün.
Kategori sayısını artır (grup başına düşen satır azalsın): sızıntılı ve doğru skorlar arasındaki fark büyüyor. Küçük gruplarda, her satırın kendi değeri grup ortalamasını çok daha fazla etkiliyor — sızıntı da o kadar şiddetleniyor.
Mekanizma
Aynı veriyle iki farklı sırayı deneyelim. Önce YANLIŞ sıra: kodlamayı bölmeden önce yap.
Veri: 200 satır, 25 kategori
# 200 satır, 25 kategori (grup başına ortalama 8 satır — küçük gruplar, sızıntıya müsait).
n, kategori_sayisi = 200, 25
gercek_ortalamalar = rng.uniform(0, 100, size=kategori_sayisi)
kategoriler = rng.integers(0, kategori_sayisi, size=n)
y = gercek_ortalamalar[kategoriler] + rng.normal(0, 15, size=n)
veri = pd.DataFrame({"kategori": kategoriler, "y": y})
egitim, test = train_test_split(veri, test_size=0.3, random_state=42)
print(f"Eğitim: {len(egitim)} satır, Test: {len(test)} satır, {kategori_sayisi} kategori")Eğitim: 140 satır, Test: 60 satır, 25 kategoriYanlış: TÜM veriden kodlama
# YANLIŞ: kategori ortalamasını TÜM veriden (eğitim + test) hesaplıyoruz —
# yani test satırlarının KENDİ y değeri, kullandığımız ortalamaya karışıyor.
tum_ortalamalar = veri.groupby("kategori")["y"].mean()
test_sizintili = test.copy()
test_sizintili["tahmin"] = test_sizintili["kategori"].map(tum_ortalamalar)
r2_sizintili = r2_score(test_sizintili["y"], test_sizintili["tahmin"])
print(f"\nSızıntılı R² (test): {r2_sizintili:.3f}")
print("Bu skor gerçek olamayacak kadar iyi — çünkü test satırının kendi y'si, tahminine karıştı.")
Sızıntılı R² (test): 0.739
Bu skor gerçek olamayacak kadar iyi — çünkü test satırının kendi y'si, tahminine karıştı.R²=0.739 — kulağa iyi geliyor. Şimdi DOĞRU sırayı deneyelim: önce böl, kodlamayı SADECE eğitim verisinden öğren, sonra test verisine uygula.
Doğru: sadece eğitimden kodlama
# DOĞRU: kategori ortalamasını SADECE eğitim verisinden hesaplıyoruz, test'e SONRADAN uyguluyoruz.
egitim_ortalamalari = egitim.groupby("kategori")["y"].mean()
genel_ortalama = egitim["y"].mean()
test_dogru = test.copy()
test_dogru["tahmin"] = test_dogru["kategori"].map(egitim_ortalamalari).fillna(genel_ortalama)
r2_dogru = r2_score(test_dogru["y"], test_dogru["tahmin"])
print(f"\nDoğru R² (test): {r2_dogru:.3f}")
print("Bu, modelin YENİ, hiç görmediği veride vereceği gerçekçi performans.")
Doğru R² (test): 0.677
Bu, modelin YENİ, hiç görmediği veride vereceği gerçekçi performans.R²=0.677 — daha düşük ama gerçek. Çoğu kişi “veri aynı, sonuç da yakın olmalı, ikisi de kabul edilebilir” der. Değil, çünkü aradaki fark rastgele bir dalgalanma değil, sistematik bir yanılgı: sızıntılı versiyonda her test satırının kendi hedef değeri, kendisini “tahmin etmek” için kullanılan sayıya doğrudan katkı veriyor — bu, sınavdan önce cevap anahtarına göz atmak gibi.
Matematik
Sızıntının aritmetiği
| Değişken | Anlamı |
|---|---|
| k kategorisindeki toplam satır sayısı (sızıntılı hesapta test dahil) | |
| k kategorisindeki SADECE eğitim satırı sayısı |
küçüldükçe (az satırlı kategoriler), bir test satırının kendi değeri, grup ortalamasının daha büyük bir kısmını oluşturur — payı büyür. Bu yüzden yüksek kardinaliteli (çok kategorili, az örnekli) değişkenlerde sızıntı çok daha tehlikelidir.
Kod
Kategori sayısı arttıkça (grup boyutu küçüldükçe) sızıntının nasıl şiddetlendiğini doğrudan ölçelim:
Kategori sayısının sızıntıya etkisi
# Sızıntı, grup boyutu küçüldükçe (kategori sayısı arttıkça) daha da şiddetleniyor.
for k in [5, 10, 25, 50]:
gercek_ort_k = rng.uniform(0, 100, size=k)
kat_k = rng.integers(0, k, size=n)
y_k = gercek_ort_k[kat_k] + rng.normal(0, 15, size=n)
veri_k = pd.DataFrame({"kategori": kat_k, "y": y_k})
egitim_k, test_k = train_test_split(veri_k, test_size=0.3, random_state=42)
tum_ort_k = veri_k.groupby("kategori")["y"].mean()
r2_siz_k = r2_score(test_k["y"], test_k["kategori"].map(tum_ort_k))
egt_ort_k = egitim_k.groupby("kategori")["y"].mean()
tahmin_dogru_k = test_k["kategori"].map(egt_ort_k).fillna(egitim_k["y"].mean())
r2_dog_k = r2_score(test_k["y"], tahmin_dogru_k)
print(f"kategori={k:>3} (grup başına ~{n // k} satır) sızıntılı R²={r2_siz_k:.3f} doğru R²={r2_dog_k:.3f} fark={r2_siz_k - r2_dog_k:.3f}")kategori= 5 (grup başına ~40 satır) sızıntılı R²=0.829 doğru R²=0.810 fark=0.019
kategori= 10 (grup başına ~20 satır) sızıntılı R²=0.817 doğru R²=0.767 fark=0.050
kategori= 25 (grup başına ~8 satır) sızıntılı R²=0.770 doğru R²=0.669 fark=0.101
kategori= 50 (grup başına ~4 satır) sızıntılı R²=0.831 doğru R²=0.731 fark=0.101Grup başına ~40 satırda fark sadece 0.019 — küçük. Grup başına ~4-8 satıra düşünce fark 0.101’e çıkıyor — beş kat daha büyük bir yanılgı. Az örnekli kategoriler, sızıntının en tehlikeli olduğu yerdir.
Nerede işe yarar
Veri sızıntısı, makine öğrenmesi projelerinin en sinsi ve en pahalı hatasıdır:
- Hedef kodlama (target encoding). Bu derste gördüğümüz tam senaryo — kategori başına hedef ortalaması, her zaman sadece eğitim verisinden öğrenilmeli.
- Ölçekleme ve normalizasyon. 15. derste gördüğümüz
StandardScaler,fit()işlemini SADECE eğitim verisiyle yapmalı, test verisine sadecetransform()uygulanmalı. - Zaman serisi verisi. Geleceğe ait bilginin (örn. “bu ayın toplam satışı”) geçmişi tahmin etmek için kullanılan bir özniteliğe karışması, klasik bir zaman sızıntısıdır.
Bu 3 hatayı yaparsın:
- Herhangi bir istatistiği (ortalama, std, kodlama)
train_test_split’ten ÖNCE hesaplamak. - Yüksek performanslı bir test skorunu sorgulamadan kabul etmek — özellikle “gerçek olamayacak kadar iyi” görünüyorsa.
- Çapraz doğrulama (cross-validation) kullanırken her kat (fold) için kodlamayı yeniden hesaplamayı unutmak — aynı sızıntı orada da olabilir.
Kendini test et
1. Veri sızıntısı tam olarak nedir?
- Veri setinde eksik değer olması
- Test verisi hakkındaki bilginin, farkında olmadan eğitim sürecine karışması (doğru cevap)
- Modelin çok yavaş çalışması
- Veri setinin çok büyük olması
Neden: Veri sızıntısı, normalde modelin bilmemesi gereken test/gelecek bilgisinin, eğitim veya öznitelik hesaplama sürecine sızmasıdır — bu da yapay olarak şişirilmiş bir performans görüntüsü yaratır.
2. Notebook örneğinde sızıntılı R² (0.739) neden doğru R'den (0.677) daha yüksek çıktı?
- Sızıntılı hesaplama daha doğru bir yöntemdir
- Her test satırının kendi y değeri, o satırı 'tahmin etmek' için kullanılan kategori ortalamasına katkıda bulunuyordu (doğru cevap)
- Rastgele bir sapma, önemi yok
- Test seti çok küçüktü
Neden: Kategori ortalaması TÜM veriden (test dahil) hesaplanınca, her test satırının kendi hedef değeri kendi tahminine karışıyor — bu suni bir avantaj yaratıyor.
3. Kategori sayısı arttıkça (grup boyutu küçüldükçe) sızıntının etkisi neden büyüyor?
- Büyümüyor, sabit kalıyor
- Küçük gruplarda bir satırın kendi değeri, grup ortalamasının daha büyük bir payını oluşturur (doğru cevap)
- Küçük gruplarda hesaplama hatası artar
- Kategori sayısının sızıntıyla ilgisi yoktur
Neden: Bir grupta n_k satır varsa, tek bir satırın ortalamaya katkısı 1/n_k'dır. n_k küçüldükçe (az örnekli kategoriler) bu katkı büyür, sızıntı şiddetlenir.
Özet
Özet
- Veri sızıntısı, test verisi hakkındaki bilginin eğitim sürecine (öznitelik hesaplama dahil) karışmasıdır.
- Kategori kodlama, ölçekleme gibi her istatistik, SADECE eğitim verisinden öğrenilmeli, sonra test verisine uygulanmalıdır.
- Sızıntı, test skorunu yapay olarak şişirir — üretimde bu performans kaybolur.
- Az örnekli (yüksek kardinaliteli) kategorik değişkenlerde sızıntının etkisi çok daha şiddetlidir.
- Bir test skoru "gerçek olamayacak kadar iyi" görünüyorsa, ilk şüphelenilmesi gereken şey veri sızıntısıdır.