Mashq 3: yashirin leakage
Nega bu mashq
0-bobda agent_komissiyasi ustunini ko'rsatdim va "bu leakage" dedim. Oson edi — nomi ham, ma'nosi ham ochiq.
Bu mashqda men hech narsa aytmayman.
Faylda leakage bor. U ID ustuniga o'xshaydi — va siz "ID ni tashla" qoidasini bilasiz, demak uni tashlaysiz. Lekin savol boshqa: nega u leakage, va shu turdagi tuzoqni keyingi safar qanday topasiz?
Bundan tashqari faylda ikkinchi shubhali ustun bor. U leakage emas — u haqiqiy signal. Ikkisini ajratish — bu mashqning asosiy ishi.
Ma'lumot bilan tanishuv
import pandas as pd td = pd.read_csv(DATA / "talaba_bitirdi.csv", keep_default_na=False, na_values=[""]) print("shakli:", td.shape) print() print(td.dtypes.to_string()) print() show(td.head(8))
shakli: (900, 9) talaba_id object kirish_bali int64 davomat_foiz int64 uy_vazifa_bali int64 ishlaydi int64 masofa_km float64 oxirgi_tolov_oyi int64 diplom_seriya object bitirdi int64
| talaba_id | kirish_bali | davomat_foiz | uy_vazifa_bali | ishlaydi | masofa_km | oxirgi_tolov_oyi | diplom_seriya | bitirdi |
|---|---|---|---|---|---|---|---|---|
| T-50000 | 62 | 93 | 82 | 0 | 3.6 | 10 | D-200000 | 1 |
| T-50001 | 66 | 100 | 69 | 0 | 7.2 | 10 | D-200001 | 1 |
| T-50002 | 58 | 100 | 42 | 1 | 2.4 | 9 | D-200002 | 1 |
| T-50003 | 50 | 75 | 100 | 1 | 16 | 10 | D-200003 | 1 |
| T-50004 | 56 | 88 | 55 | 0 | 5.7 | 8 | D-200004 | 1 |
| T-50005 | 48 | 80 | 74 | 0 | 7.5 | 12 | D-200005 | 1 |
| T-50006 | 63 | 79 | 74 | 0 | 25.7 | 8 | D-200006 | 1 |
| T-50007 | 81 | 98 | 95 | 1 | 6.6 | 14 | D-200007 | 1 |
print("Target:") print(td.bitirdi.value_counts(normalize=True).sort_index().round(4).to_string()) print() print(f"BAZA (hammaga '1' desa): {td.bitirdi.mean():.4f}") print() yoq = td.isna().sum() print("Bo'sh qiymatlar:") print(yoq[yoq > 0].to_string() if (yoq > 0).any() else " yo'q")
Target: bitirdi 0 0.1144 1 0.8856 BAZA (hammaga '1' desa): 0.8856 Bo'sh qiymatlar: diplom_seriya 103
Topshiriq
0-bobdagi 12 qadamni o'zingiz yuring. Hech kim sizga yo'l ko'rsatmaydi. Daftaringizga quyidagilarni yozib boring:
import pandas as pd show(pd.DataFrame([ {"#": 1, "Yozib qo'yiladigan javob": "Nechta qator, nechta ustun"}, {"#": 2, "Yozib qo'yiladigan javob": "\"Men ___ ni ___ dan bashorat qilaman\" — bitta jumla"}, {"#": 3, "Yozib qo'yiladigan javob": "Vazifa turi: regression / binary / multiclass"}, {"#": 4, "Yozib qo'yiladigan javob": "Target nisbati va BAZA raqami"}, {"#": 5, "Yozib qo'yiladigan javob": "Split qildingizmi? stratify kerakmi?"}, {"#": 6, "Yozib qo'yiladigan javob": "Dummy natijasi"}, {"#": 7, "Yozib qo'yiladigan javob": "Qaysi ustunlarni tashladingiz va NEGA"}, {"#": 8, "Yozib qo'yiladigan javob": "Missing va turlar bilan nima qildingiz"}, {"#": 9, "Yozib qo'yiladigan javob": "Birinchi halol natijangiz"}, {"#": 10, "Yozib qo'yiladigan javob": "Qaysi metrikani tanladingiz va NEGA"}, {"#": 11, "Yozib qo'yiladigan javob": "Xatolar qaysi guruhda to'plangan"}, {"#": 12, "Yozib qo'yiladigan javob": "Qaysi BITTA o'zgarishni qildingiz va natija"}, ]))
| # | Yozib qo'yiladigan javob |
|---|---|
| 1 | Nechta qator, nechta ustun |
| 2 | "Men ___ ni ___ dan bashorat qilaman" — bitta jumla |
| 3 | Vazifa turi: regression / binary / multiclass |
| 4 | Target nisbati va BAZA raqami |
| 5 | Split qildingizmi? stratify kerakmi? |
| 6 | Dummy natijasi |
| 7 | Qaysi ustunlarni tashladingiz va NEGA |
| 8 | Missing va turlar bilan nima qildingiz |
| 9 | Birinchi halol natijangiz |
| 10 | Qaysi metrikani tanladingiz va NEGA |
| 11 | Xatolar qaysi guruhda to'plangan |
| 12 | Qaysi BITTA o'zgarishni qildingiz va natija |
Va eng muhim, 13-savol:
Qaysi gipotezangiz RAD ETILDI?
Agar bitta ham rad etilmagan bo'lsa — siz hech narsani sinab ko'rmadingiz. Qaytib boring.
Yagona ko'rsatma
Boshqa yordam bermayman. Faqat bitta narsa:
Har bir ustun uchun shu savolni bering
"Bashorat qilish PAYTIDA bu ustun menda bo'ladimi?"
Yangi talaba hozir o'qishga kirdi. Uning haqida qaysi ustunlar hoziroq ma'lum? Qaysi biri faqat keyinroq paydo bo'ladi?
Bu savol leakage ni ustun nomiga qaramasdan topadi.
Baza 0.8852 — accuracy sizni aldaydi
Target 88.6% "bitirdi". Demak "hammaga bitirdi" deydigan model ~0.885 oladi.
Agar sizning modelingiz 0.89 chiqarsa — bu deyarli hech narsa degani. Boshqa metrika kerak. Qaysi biri? 6 va 8-bobni qayta o'qing.
O'zingizni tekshirish
Avval o'zingiz ishlang. Rostdan ham. Keyin pastga qarang.
from sklearn.model_selection import train_test_split from sklearn.linear_model import LogisticRegression from sklearn.dummy import DummyClassifier from sklearn.metrics import accuracy_score, f1_score, recall_score, precision_score halol = ["kirish_bali", "davomat_foiz", "uy_vazifa_bali", "ishlaydi", "masofa_km"] y = td["bitirdi"] tr, te, ytr, yte = train_test_split(td, y, test_size=0.3, random_state=42, stratify=y) baza = accuracy_score(yte, DummyClassifier(strategy="most_frequent").fit(tr[halol], ytr).predict(te[halol])) m1 = LogisticRegression(max_iter=2000).fit(tr[halol], ytr) b1 = m1.predict(te[halol]) print(f"BAZA : {baza:.4f}") print(f"Halol 5 ustun — accuracy: {accuracy_score(yte, b1):.4f} (farq {accuracy_score(yte, b1)-baza:+.4f})") print(f"Halol 5 ustun — F1 : {f1_score(yte, b1):.4f}") print(f"Halol 5 ustun — recall : {recall_score(yte, b1):.4f}")
BAZA : 0.8852 Halol 5 ustun — accuracy: 0.8963 (farq +0.0111) Halol 5 ustun — F1 : 0.9447 Halol 5 ustun — recall : 1.0000
Mana birinchi dars: model bazadan atigi bir oz yuqori. Accuracy bu yerda deyarli foydasiz.
Endi tuzoqni ochamiz.
td2 = td.copy() td2["diplom_bor"] = td2.diplom_seriya.notna().astype(int) tr2, te2 = td2.loc[tr.index], td2.loc[te.index] m2 = LogisticRegression(max_iter=2000).fit(tr2[halol + ["diplom_bor"]], ytr) b2 = m2.predict(te2[halol + ["diplom_bor"]]) print(f"+ diplom_seriya (bor/yo'q): accuracy {accuracy_score(yte, b2):.4f} <-- MUKAMMAL") print() print("Nega? Tekshiramiz:") print(pd.crosstab(td2.diplom_bor, td2.bitirdi).to_string())
+ diplom_seriya (bor/yo'q): accuracy 1.0000 <-- MUKAMMAL Nega? Tekshiramiz: bitirdi 0 1 diplom_bor 0 103 0 1 0 797
Mana. diplom_seriya faqat bitirganlarda bor.
Diqqat qiling: muammo ustun ID bo'lgani uchun emas. Muammo shundaki, uning bo'sh yoki bo'lmasligi target ni aytib turadi. Agar siz uni OneHotEncoder ga bersangiz ham, notna() qilsangiz ham — natija bir xil yolg'on bo'ladi.
Va mantiqan ham ochiq: diplom seriyasi bitirgandan KEYIN beriladi. Bashorat qilish paytida u yo'q.
Endi ikkinchi shubhali ustun.
m3 = LogisticRegression(max_iter=2000).fit(tr[halol + ["oxirgi_tolov_oyi"]], ytr) b3 = m3.predict(te[halol + ["oxirgi_tolov_oyi"]]) print(f"+ oxirgi_tolov_oyi: accuracy {accuracy_score(yte, b3):.4f} F1 {f1_score(yte, b3):.4f}") print() print("Guruhlar bo'yicha o'rtacha:") print(td.groupby("bitirdi").oxirgi_tolov_oyi.agg(["mean", "min", "max"]).round(2).to_string())
+ oxirgi_tolov_oyi: accuracy 0.9037 F1 0.9465
Guruhlar bo'yicha o'rtacha:
mean min max
bitirdi
0 7.71 2 14
1 11.05 6 14
Bu ustun ham target bilan bog'liq — bitirganlar ko'proq oy to'lagan. Lekin u leakage emas. Farqni ko'ring:
show(pd.DataFrame([ {"Ustun": "diplom_seriya", "Target bilan bog'liq": "To'liq (100%)", "Bashorat paytida bormi": "YO'Q — bitirgandan keyin beriladi", "Hukmi": "LEAKAGE — tashlanadi"}, {"Ustun": "oxirgi_tolov_oyi", "Target bilan bog'liq": "Qisman", "Bashorat paytida bormi": "HA — har oy ma'lum bo'ladi", "Hukmi": "Haqiqiy signal — ishlatiladi (lekin ehtiyot bilan)"}, ]))
| Ustun | Target bilan bog'liq | Bashorat paytida bormi | Hukmi |
|---|---|---|---|
| diplom_seriya | To'liq (100%) | YO'Q — bitirgandan keyin beriladi | LEAKAGE — tashlanadi |
| oxirgi_tolov_oyi | Qisman | HA — har oy ma'lum bo'ladi | Haqiqiy signal — ishlatiladi (lekin ehtiyot bilan) |
"Target bilan bog'liq" va "leakage" bir xil narsa EMAS
Agar siz "target bilan kuchli bog'liq hamma ustunni tashlaymiz" qoidasini olsangiz — siz eng foydali xususiyatlarni tashlab yuborasiz. Model qurishdan maqsad aynan shunday ustunlarni topish.
Leakage ni ajratadigan savol bog'liqlik kuchi emas. Savol — vaqt: bu ma'lumot bashorat qilish paytida mavjudmi?
oxirgi_tolov_oyi uchun ehtiyot nuqtasi bor: agar siz semestr oxirida bashorat qilsangiz, u halol. Agar talaba birinchi kuni bashorat qilmoqchi bo'lsangiz — u ham leakage bo'ladi, chunki hali to'lov tarixi yo'q.
Demak leakage savolga bog'liq, ustunga emas.
Uch mashqdan keyin
show(pd.DataFrame([ {"Mashq": "42. penguins", "Nimani sinadi": "Naqsh buzilishi — binary dan multiclass ga", "Asosiy dars": "average='macro' va 3x3 matritsa"}, {"Mashq": "43. iflos savdo", "Nimani sinadi": "Tozalash ishning 80 foizi", "Asosiy dars": "Kirill/lotin, matn son, dublikat; va 'model qurib bo'lmaydi' halol xulosasi"}, {"Mashq": "44. yashirin leakage", "Nimani sinadi": "Tuzoqni o'zingiz topish", "Asosiy dars": "Leakage ni VAQT savoli ajratadi, bog'liqlik kuchi emas"}, ]))
| Mashq | Nimani sinadi | Asosiy dars |
|---|---|---|
| 42. penguins | Naqsh buzilishi — binary dan multiclass ga | average='macro' va 3x3 matritsa |
| 43. iflos savdo | Tozalash ishning 80 foizi | Kirill/lotin, matn son, dublikat; va 'model qurib bo'lmaydi' halol xulosasi |
| 44. yashirin leakage | Tuzoqni o'zingiz topish | Leakage ni VAQT savoli ajratadi, bog'liqlik kuchi emas |
Agar uch mashqni ham o'zingiz yurib chiqqan bo'lsangiz — siz endi yangi dataset oldida to'xtamaydigan odam bo'ldingiz.
Protokolni esda tuting. Bazani birinchi o'lchang. Mukammal natijadan shubhalaning. Xatolarni o'qing. Va har bir rad etilgan gipotezani yozib qo'ying — ular sizning haqiqiy bilimingiz.
Bu bobni bepul o'qidingiz.
Kitob o'rgatadi, lekin sizning kodingiz nega ishlamaganini ayta olmaydi. Kursda uy vazifangiz tekshiriladi.