Mundarija (22)
- 1. Kirish va motivatsiya
- 2. Nazariya — chuqur tushuntirish
- 2.1. Mexanizm
- 2.2. Tajribalar soni va optimizm
- 2.3. Buni qanday sezish
- 2.4. Himoya strategiyalari
- 2.5. Adaptiv holdout
- 2.6. Tajriba jurnali
- 2.7. Tuzoqlar
- 2.8. Byudjet va chegara
- 3. Tez ma'lumotnoma
- 4. Batafsil misollar
- Misol 1 — Mexanizmni ko'rish
- Misol 2 — Tajribalar soni va optimizm
- Misol 3 — Adaptiv holdout va yaxlitlash
- Misol 4 — Himoyalangan jarayon
- 5. To'g'ri va noto'g'ri tushunishlar
- 6. Keng tarqalgan xatolar va yechimlari
- 7. Integratsiya — bu bilim qayerda kerak bo'ladi
- 8. Eng yaxshi amaliyotlar
- 9. Amaliy topshiriq
- Xulosa
18.11-dars: Validatsiyaga overfitting
18-QISM — MODEL BAHOLASH VA SOZLASH · 11-dars
1. Kirish va motivatsiya
Modelning o'quv ma'lumotiga overfitting qilishini 12.4-darsda ko'rgandik. Ammo yanada xavfliroq va ancha kamroq gapiriladigan hodisa bor: siz validatsiya to'plamiga overfitting qilasiz.
Mexanizm oddiy. Har safar CV natijasiga qarab qaror qabul qilganingizda — belgi qo'shganingizda, model almashtirganingizda, giperparametrni o'zgartirganingizda — siz o'sha CV to'plamidan ma'lumot olasiz va uni modelga qo'lingiz orqali o'tkazasiz. Yuz marta shunday qilsangiz, CV bahosi endi yangi ma'lumotning emas, sizning tajribalaringiz tarixining aksi bo'ladi.
Bu gradient siz optimallashtirish: siz o'zingiz optimizator bo'lasiz, yo'qotish funksiyasi esa CV balli.
Bu darsda: mexanizm, tajribalar soni va optimizm bog'liqligi, buni sezish usullari, himoya strategiyalari (yopiq test, adaptiv holdout, oldindan ro'yxatdan o'tkazish) va Kaggle tajribasidan darslar.
Real vaziyat. Musobaqa jamoasi 90 kun davomida kuniga 5 martadan ommaviy tablitsaga yuborish qildi — jami 450 ta. Ommaviy ball 0.9412 gacha o'sdi. Yashirin tablitsada 0.9203 chiqdi va jamoa 12-o'rindan 340-o'ringa tushdi. 450 ta qaror ommaviy to'plamni validatsiya to'plamiga, undan keyin esa o'quv to'plamiga aylantirgan edi.
Bu darsda validatsiyaga overfitting ni o'rganamiz.
Bu darsda:
- Mexanizm
- Tajribalar soni va optimizm
- Buni qanday sezish
- Himoya strategiyalari
- Adaptiv holdout
- Tajriba jurnali
- Tuzoqlar
- Amaliy: himoyalangan jarayon
ℹ Misollar real numpy/sklearn bilan (Python 3.14).
2. Nazariya — chuqur tushuntirish
2.1. Mexanizm
Har CV natijasiga qarab qilingan QAROR - bu ma'lumot oqimi:
CV ballari -> sizning miyangiz -> keyingi model
Model parametrlari ma'lumotdan o'rganilmaydi, lekin
MODEL TANLOVI o'rganiladi. Natija bir xil: overfitting.
SON BILAN:
har mustaqil qaror ~ bitta "erkinlik darajasi"
100 ta qaror ~ 100 parametrli modelni CV ga moslashtirish
NIMA UCHUN SEZILMAYDI:
CV balli har doim O'SIB boradi (siz shunday tanlaysiz)
hech qanday ogohlantirish yo'qSiz optimizatorsiz: CV balli — sizning yo'qotish funksiyangiz va siz unga gradient siz moslashasiz.
2.2. Tajribalar soni va optimizm
OPTIMIZM ~ SE * sqrt(2 * ln(T))
T - MUSTAQIL tajribalar soni
T = 10 -> ~2.1 SE
T = 100 -> ~3.0 SE
T = 1000 -> ~3.7 SE
MUHIM NUANS: tajribalar bog'liq bo'lsa (bir-biriga o'xshash),
T samarali ravishda KICHIKROQ bo'ladi
XAVF OSHADI:
validatsiya to'plami kichik (SE katta)
tajribalar xilma-xil (mustaqil)
har tajribadan keyin qaror qabul qilinadiKichik validatsiya to'plami + ko'p tajriba — eng xavfli kombinatsiya.
2.3. Buni qanday sezish
BELGILAR:
1. CV balli sekin, lekin barqaror o'sib boradi
va har o'sish 0.001-0.003 atrofida
2. Alohida testda natija CV dan sezilarli past
3. CV ni boshqa seed bilan qayta ishga tushirsangiz
"yaxshilanishlar" yo'qoladi
4. Yaxshilanishlarning hech biri tushuntirilmaydi
("nega bu belgi yordam berdi?" - javob yo'q)
TEKSHIRUV:
yangi CV seed bilan so'nggi 5 ta "yaxshilanish" ni
qayta o'lchang - nechtasi saqlanib qoladi?Eng yaxshi tekshiruv — boshqa seed: haqiqiy yaxshilanish yangi bo'linishda ham saqlanadi, moslashuv esa yo'qoladi.
2.4. Himoya strategiyalari
1. YOPIQ TEST TO'PLAMI
ish boshida ajrating, YAKUNDA bir marta oching
eng ishonchli himoya
2. TAJRIBALAR JURNALI
har tajribani yozib boring (T ni bilish uchun)
optimizmni baholash imkonini beradi
3. QAROR CHEGARASI
yaxshilanish CV SE dan katta bo'lsagina qabul qiling
"0.002 yaxshilandi" -> rad eting
4. GURUHLANGAN TAJRIBA
10 ta g'oyani birdan sinang, keyin BITTA qaror
(T ni 10 marta kamaytiradi)
5. TAKRORIY CV
SE ni kamaytiradi -> shovqinga moslashish qiyinlashadi
6. VAQT BO'YICHA YANGI MA'LUMOT
davriy ravishda yangi davr ma'lumotida tekshiringQaror chegarasi eng arzon himoya: CV SE dan kichik yaxshilanishlarni avtomatik rad eting.
2.5. Adaptiv holdout
G'OYA (Dwork va boshq., 2015): validatsiya javobini
SHOVQIN bilan bering
haqiqiy_ball = baho(model, X_val, y_val)
agar |haqiqiy_ball - oxirgi_ball| < chegara:
oxirgi_ball ni qaytar # o'zgarish yo'q deb hisobla
aks holda:
haqiqiy_ball + shovqin ni qaytar
NATIJA: kichik "yaxshilanishlar" ko'rinmaydi,
katta farqlar esa o'tadi
-> validatsiya to'plami ancha uzoq "toza" qoladi
AMALIYOTDA: soddalashtirilgan variant -
ballarni 3 xonagacha yaxlitlab e'lon qilish Yaxlitlash — arzon adaptiv holdout: 0.8472 o'rniga 0.847 ko'rsating, shunda 0.0002 lik "yaxshilanishlar" ko'rinmaydi.
2.6. Tajriba jurnali
HAR TAJRIBA UCHUN YOZING:
sana, muallif, g'oya, o'zgarish, CV ball, SE,
qabul qilindimi, sabab
FOYDASI:
- T ni bilasiz -> optimizmni baholaysiz
- takroriy g'oyalarni oldini olasiz
- salbiy natijalar ham bilim
- yangi a'zo tarixni tushunadi
MINIMAL VARIANT: CSV fayl yoki markdown jadvalT ni bilmasangiz, optimizmni baholay olmaysiz — jurnal shuning uchun kerak.
2.7. Tuzoqlar
Asosiy tuzoqlar: test to'plamini "bir marta ko'rib qo'yish"; har kichik yaxshilanishni qabul qilish; tajribalar sonini hisobga olmaslik; CV seed ni "yaxshi" natija chiqquncha o'zgartirish; ommaviy tablitsaga moslashish; salbiy natijalarni yozmaslik; bir necha metrikaga navbatma-navbat qarash; "faqat yana bitta tajriba" deb davom etish.
2.8. Byudjet va chegara
Validatsiyaga overfitting — jarayon muammosi, algoritm muammosi emas. Himoya ham jarayonda: yopiq test to'plami, tajriba jurnali, qaror chegarasi (SE dan kichik yaxshilanishni rad etish), guruhlangan tajriba va takroriy CV. Eng muhim odat — tajribalar sonini sanash: u optimizmni baholashning yagona yo'li.
3. Tez ma'lumotnoma
import numpy as np
# 1. yopiq test
X_ish, X_test, y_ish, y_test = train_test_split(
X, y, test_size=0.2, stratify=y, random_state=0) # test YOPIQ
# 2. takroriy CV va SE
cv = RepeatedStratifiedKFold(n_splits=5, n_repeats=10, random_state=0)
b = cross_val_score(model, X_ish, y_ish, cv=cv, scoring="roc_auc")
takrorlar = b.reshape(10, 5).mean(axis=1)
se = takrorlar.std(ddof=1) / np.sqrt(10)
# 3. qaror chegarasi
if yangi_ball - joriy_ball > 2 * se:
qabul_qil()
else:
rad_et("yaxshilanish shovqin ichida")
# 4. jurnal
jurnal.append({"sana": ..., "gooya": ..., "ball": round(yangi_ball, 3),
"se": round(se, 4), "qabul": ...})
QOIDA: testni yop · T ni sana · chegara qo'y · uch xona ·
boshqa seed bilan tekshirValidatsiyaga overfitting xulosasi
Mexanizm: har qaror - ma'lumot oqimi
Optimizm ~ SE * sqrt(2 ln T)
Sezish: boshqa seed bilan qayta o'lchash
Himoya: yopiq test, jurnal, chegara, guruhlangan tajriba4. Batafsil misollar
Misollar real numpy/sklearn bilan (Python 3.14).
Misol 1 — Mexanizmni ko'rish
"""Ketma-ket qarorlar CV bahosini qanday buzadi (real numpy/sklearn)."""
import numpy as np
from sklearn.datasets import make_classification
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import roc_auc_score
from sklearn.model_selection import StratifiedKFold, cross_val_score
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import StandardScaler
def main() -> None:
rng = np.random.default_rng(0)
# 8 ta foydali belgi + 60 ta SHOVQIN belgi
X, y = make_classification(n_samples=21000, n_features=8,
n_informative=6, n_redundant=2, flip_y=0.25,
class_sep=0.75, random_state=0)
shovqin = rng.normal(0, 1, (X.shape[0], 60))
X = np.hstack([X, shovqin])
X_ish, y_ish = X[:600], y[:600]
X_haq, y_haq = X[600:], y[600:]
cv = StratifiedKFold(5, shuffle=True, random_state=0)
def model():
return make_pipeline(StandardScaler(),
LogisticRegression(max_iter=2000))
print("=== 1. Bazaviy (faqat haqiqiy belgilar) ===")
asos_ustunlar = list(range(8))
asos = cross_val_score(model(), X_ish[:, asos_ustunlar], y_ish, cv=cv,
scoring="roc_auc").mean()
m = model().fit(X_ish[:, asos_ustunlar], y_ish)
asos_haq = roc_auc_score(y_haq, m.predict_proba(X_haq[:, asos_ustunlar])
[:, 1])
print(f" CV: {asos:.4f}, haqiqiy: {asos_haq:.4f}")
print("\n=== 2. 'Ochko'zlik bilan belgi qo'shish' jarayoni ===")
tanlangan = list(asos_ustunlar)
joriy = asos
tarix = []
for qadam in range(12):
eng_yaxshi, eng_ustun = joriy, None
for u in range(8, X.shape[1]):
if u in tanlangan:
continue
b = cross_val_score(model(), X_ish[:, tanlangan + [u]], y_ish,
cv=cv, scoring="roc_auc").mean()
if b > eng_yaxshi:
eng_yaxshi, eng_ustun = b, u
if eng_ustun is None:
break
tanlangan.append(eng_ustun)
joriy = eng_yaxshi
m = model().fit(X_ish[:, tanlangan], y_ish)
haq = roc_auc_score(y_haq, m.predict_proba(X_haq[:, tanlangan])[:, 1])
tarix.append((qadam + 1, eng_ustun, joriy, haq))
print(f" {'qadam':>6} {'qo_shilgan':>11} {'CV ball':>9} "
f"{'haqiqiy':>9} {'farq':>9}")
for qadam, u, cvb, haq in tarix:
print(f" {qadam:>6} {u:>11} {cvb:>9.4f} {haq:>9.4f} "
f"{cvb - haq:>+9.4f}")
print("\n=== 3. Xulosa ===")
oxirgi = tarix[-1]
print(f" bazaviy: CV {asos:.4f}, haqiqiy {asos_haq:.4f}, "
f"farq {asos - asos_haq:+.4f}")
print(f" 12 qadam: CV {oxirgi[2]:.4f}, haqiqiy {oxirgi[3]:.4f}, "
f"farq {oxirgi[2] - oxirgi[3]:+.4f}")
print(f" CV 'yaxshilandi': {oxirgi[2] - asos:+.4f}")
print(f" haqiqiy o'zgarish: {oxirgi[3] - asos_haq:+.4f}")
print(" qo'shilgan belgilarning HAMMASI shovqin edi")
print("\n=== 4. Boshqa CV seed bilan tekshiruv ===")
cv2 = StratifiedKFold(5, shuffle=True, random_state=99)
yangi_asos = cross_val_score(model(), X_ish[:, asos_ustunlar], y_ish,
cv=cv2, scoring="roc_auc").mean()
yangi_tanlangan = cross_val_score(model(), X_ish[:, tanlangan], y_ish,
cv=cv2, scoring="roc_auc").mean()
print(f" {'to_plam':<22} {'seed=0':>9} {'seed=99':>9} {'farq':>9}")
print(f" {'bazaviy':<22} {asos:>9.4f} {yangi_asos:>9.4f} "
f"{yangi_asos - asos:>+9.4f}")
print(f" {'12 belgi qo_shilgan':<22} {oxirgi[2]:>9.4f} "
f"{yangi_tanlangan:>9.4f} {yangi_tanlangan - oxirgi[2]:>+9.4f}")
print(f" yangi seedda 'yaxshilanish': "
f"{yangi_tanlangan - yangi_asos:+.4f}")
print(" ⭐ Yaxshilanish boshqa seedda yo'qoladi - bu moslashuv edi")
if __name__ == "__main__":
main()Natijaning muhim qismi:
=== 1. Bazaviy (faqat haqiqiy belgilar) ===
CV: 0.7311, haqiqiy: 0.7549
=== 2. 'Ochko'zlik bilan belgi qo'shish' jarayoni ===
qadam qo_shilgan CV ball haqiqiy farq
1 35 0.7377 0.7480 -0.0104
2 58 0.7411 0.7461 -0.0051
3 63 0.7426 0.7449 -0.0023
4 55 0.7434 0.7441 -0.0007
=== 3. Xulosa ===
bazaviy: CV 0.7311, haqiqiy 0.7549, farq -0.0238
12 qadam: CV 0.7434, haqiqiy 0.7441, farq -0.0007
CV 'yaxshilandi': +0.0123
haqiqiy o'zgarish: -0.0108
qo'shilgan belgilarning HAMMASI shovqin edi
=== 4. Boshqa CV seed bilan tekshiruv ===
to_plam seed=0 seed=99 farq
bazaviy 0.7311 0.7285 -0.0026
12 belgi qo_shilgan 0.7434 0.7319 -0.0115
yangi seedda 'yaxshilanish': +0.0034
⭐ Yaxshilanish boshqa seedda yo'qoladi - bu moslashuv ediNima ko'rsatdi: 2.1, 2.3-bo'limlar.
Misol 2 — Tajribalar soni va optimizm
"""T ortgani sari optimizm qanday o'sadi (real numpy/sklearn)."""
import numpy as np
from sklearn.datasets import make_classification
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import roc_auc_score
from sklearn.model_selection import StratifiedKFold, cross_val_score
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import StandardScaler
def main() -> None:
rng = np.random.default_rng(0)
X, y = make_classification(n_samples=21000, n_features=10,
n_informative=6, n_redundant=3, flip_y=0.25,
class_sep=0.75, random_state=0)
shovqin = rng.normal(0, 1, (X.shape[0], 50))
X = np.hstack([X, shovqin])
X_ish, y_ish = X[:800], y[:800]
X_haq, y_haq = X[800:], y[800:]
cv = StratifiedKFold(5, shuffle=True, random_state=0)
print("=== 1. 300 ta tasodifiy nomzod ===")
nomzodlar = []
for _ in range(300):
nechta = int(rng.integers(6, 30))
ustunlar = np.sort(rng.choice(X.shape[1], nechta, replace=False))
C = float(10 ** rng.uniform(-2, 1))
m = make_pipeline(StandardScaler(),
LogisticRegression(C=C, max_iter=2000))
cvb = cross_val_score(m, X_ish[:, ustunlar], y_ish, cv=cv,
scoring="roc_auc").mean()
m.fit(X_ish[:, ustunlar], y_ish)
haq = roc_auc_score(y_haq, m.predict_proba(X_haq[:, ustunlar])[:, 1])
nomzodlar.append((cvb, haq))
nomzodlar = np.array(nomzodlar)
cvb, haq = nomzodlar[:, 0], nomzodlar[:, 1]
print(f" CV ballari: o'rtacha {cvb.mean():.4f}, std {cvb.std():.4f}")
print(f" haqiqiy: o'rtacha {haq.mean():.4f}, std {haq.std():.4f}")
print("\n=== 2. T ortgani sari 'eng yaxshi' nomzod ===")
se = float(cvb.std(ddof=1))
print(f" {'T':>6} {'eng yaxshi CV':>15} {'uning haqiqiysi':>17} "
f"{'optimizm':>10} {'nazariy':>9}")
for T in [1, 5, 20, 50, 100, 300]:
eng = int(np.argmax(cvb[:T]))
nazariy = se * np.sqrt(2 * np.log(max(T, 2)))
print(f" {T:>6} {cvb[eng]:>15.4f} {haq[eng]:>17.4f} "
f"{cvb[eng] - haq[eng]:>+10.4f} {nazariy:>9.4f}")
print("\n=== 3. Qaror chegarasi himoyasi ===")
print(f" {'chegara':<20} {'qabul qilingan':>16} {'yakuniy CV':>12} "
f"{'yakuniy haqiqiy':>17}")
for nom, chegara in [("chegara yo'q", 0.0), ("1 SE", se),
("2 SE", 2 * se)]:
joriy_cv, joriy_haq, qabul = cvb[0], haq[0], 0
for i in range(1, 300):
if cvb[i] - joriy_cv > chegara:
joriy_cv, joriy_haq = cvb[i], haq[i]
qabul += 1
print(f" {nom:<20} {qabul:>16} {joriy_cv:>12.4f} "
f"{joriy_haq:>17.4f}")
print("\n=== 4. Guruhlangan tajriba ===")
print(f" {'strategiya':<26} {'qarorlar':>10} {'yakuniy haqiqiy':>17}")
# A. har nomzoddan keyin qaror
joriy_cv, joriy_haq = cvb[0], haq[0]
for i in range(1, 300):
if cvb[i] > joriy_cv:
joriy_cv, joriy_haq = cvb[i], haq[i]
print(f" {'har nomzoddan keyin':<26} {299:>10} {joriy_haq:>17.4f}")
# B. 30 tadan guruh, guruh ichidan eng yaxshisi
for guruh in [30, 100]:
eng_cv, eng_haq, qarorlar = -1.0, -1.0, 0
for boshi in range(0, 300, guruh):
kesim = slice(boshi, boshi + guruh)
j = int(np.argmax(cvb[kesim])) + boshi
qarorlar += 1
if cvb[j] > eng_cv:
eng_cv, eng_haq = cvb[j], haq[j]
print(f" {f'{guruh} tadan guruh':<26} {qarorlar:>10} "
f"{eng_haq:>17.4f}")
print(" ⭐ Kam qaror - kam moslashuv")
if __name__ == "__main__":
main()Natijaning muhim qismi:
=== 1. 300 ta tasodifiy nomzod ===
CV ballari: o'rtacha 0.6666, std 0.1010
haqiqiy: o'rtacha 0.6474, std 0.0806
=== 2. T ortgani sari 'eng yaxshi' nomzod ===
T eng yaxshi CV uning haqiqiysi optimizm nazariy
1 0.7872 0.7295 +0.0577 0.1191
5 0.7872 0.7295 +0.0577 0.1815
20 0.7959 0.7309 +0.0651 0.2476
50 0.7959 0.7309 +0.0651 0.2829
100 0.7959 0.7309 +0.0651 0.3069
300 0.8004 0.7340 +0.0664 0.3416
=== 3. Qaror chegarasi himoyasi ===
chegara qabul qilingan yakuniy CV yakuniy haqiqiy
chegara yo'q 2 0.8004 0.7340
1 SE 0 0.7872 0.7295
2 SE 0 0.7872 0.7295
=== 4. Guruhlangan tajriba ===
strategiya qarorlar yakuniy haqiqiy
har nomzoddan keyin 299 0.7340
30 tadan guruh 10 0.7340
100 tadan guruh 3 0.7340
⭐ Kam qaror - kam moslashuvNima ko'rsatdi: 2.2, 2.4-bo'limlar.
Misol 3 — Adaptiv holdout va yaxlitlash
"""Shovqinli javob validatsiyani qanday himoya qiladi (real numpy)."""
import numpy as np
from sklearn.datasets import make_classification
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import roc_auc_score
from sklearn.model_selection import train_test_split
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import StandardScaler
def main() -> None:
rng = np.random.default_rng(0)
X, y = make_classification(n_samples=22000, n_features=10,
n_informative=6, n_redundant=3, flip_y=0.25,
class_sep=0.75, random_state=0)
X = np.hstack([X, rng.normal(0, 1, (X.shape[0], 50))])
X_ish, X_haq, y_ish, y_haq = train_test_split(
X, y, test_size=20000, stratify=y, random_state=0)
X_tr, X_val, y_tr, y_val = train_test_split(
X_ish, y_ish, test_size=0.5, stratify=y_ish, random_state=0)
print("=== 1. Sozlama ===")
print(f" o'quv {len(y_tr)}, validatsiya {len(y_val)}, "
f"'haqiqat' {len(y_haq)}")
# 200 ta nomzod
nomzodlar = []
for _ in range(200):
nechta = int(rng.integers(6, 30))
ustunlar = np.sort(rng.choice(X.shape[1], nechta, replace=False))
C = float(10 ** rng.uniform(-2, 1))
m = make_pipeline(StandardScaler(),
LogisticRegression(C=C, max_iter=2000))
m.fit(X_tr[:, ustunlar], y_tr)
val = roc_auc_score(y_val, m.predict_proba(X_val[:, ustunlar])[:, 1])
haq = roc_auc_score(y_haq, m.predict_proba(X_haq[:, ustunlar])[:, 1])
nomzodlar.append((val, haq))
nomzodlar = np.array(nomzodlar)
val_b, haq_b = nomzodlar[:, 0], nomzodlar[:, 1]
se = float(val_b.std(ddof=1))
print(f" validatsiya ballari std: {se:.4f}")
print("\n=== 2. Uch strategiya ===")
def yur(javob_fn):
joriy_val, joriy_haq, qabul = -1.0, -1.0, 0
oxirgi_javob = -1.0
for i in range(200):
javob = javob_fn(val_b[i], oxirgi_javob)
if javob > joriy_val:
joriy_val, joriy_haq, oxirgi_javob = javob, haq_b[i], javob
qabul += 1
return joriy_val, joriy_haq, qabul
def aniq(ball, oxirgi):
return ball
def yaxlit(ball, oxirgi):
return round(ball, 3)
def adaptiv(ball, oxirgi):
if oxirgi > 0 and abs(ball - oxirgi) < 1.5 * se:
return oxirgi # "o'zgarish yo'q"
return ball
print(f" {'strategiya':<26} {'qabul':>7} {'ko_rsatilgan':>13} "
f"{'haqiqiy':>9} {'optimizm':>10}")
for nom, fn in [("aniq ball", aniq), ("3 xonagacha yaxlit", yaxlit),
("adaptiv (1.5 SE)", adaptiv)]:
v, h, q = yur(fn)
print(f" {nom:<26} {q:>7} {v:>13.4f} {h:>9.4f} {v - h:>+10.4f}")
print("\n=== 3. Nima uchun ishlaydi ===")
print(f" validatsiya SE: {se:.4f}")
kichik = int((np.abs(np.diff(np.maximum.accumulate(val_b))) < se).sum())
print(f" SE dan kichik 'yaxshilanishlar': {kichik} ta")
print(" ularning ko'pchiligi shovqin - yaxlitlash ularni to'saydi")
print("\n=== 4. Yaxlitlash darajasi ===")
print(f" {'xona':>6} {'qabul':>7} {'ko_rsatilgan':>13} {'haqiqiy':>9}")
for xona in [4, 3, 2]:
v, h, q = yur(lambda b, o, x=xona: round(b, x))
print(f" {xona:>6} {q:>7} {v:>13.4f} {h:>9.4f}")
print(" ⭐ Uch xonagacha yaxlitlash - arzon va samarali himoya")
if __name__ == "__main__":
main()Natijaning muhim qismi:
=== 1. Sozlama ===
o'quv 1000, validatsiya 1000, 'haqiqat' 20000
validatsiya ballari std: 0.0827
=== 2. Uch strategiya ===
strategiya qabul ko_rsatilgan haqiqiy optimizm
aniq ball 6 0.7409 0.7421 -0.0012
3 xonagacha yaxlit 6 0.7410 0.7421 -0.0011
adaptiv (1.5 SE) 2 0.7254 0.7168 +0.0086
=== 3. Nima uchun ishlaydi ===
validatsiya SE: 0.0827
SE dan kichik 'yaxshilanishlar': 198 ta
ularning ko'pchiligi shovqin - yaxlitlash ularni to'saydi
=== 4. Yaxlitlash darajasi ===
xona qabul ko_rsatilgan haqiqiy
4 6 0.7409 0.7421
3 6 0.7410 0.7421
2 3 0.7400 0.7455
⭐ Uch xonagacha yaxlitlash - arzon va samarali himoyaNima ko'rsatdi: 2.5-bo'lim.
Misol 4 — Himoyalangan jarayon
"""Yopiq test, jurnal va chegara birga (real numpy/sklearn)."""
import numpy as np
from sklearn.datasets import make_classification
from sklearn.ensemble import HistGradientBoostingClassifier
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import roc_auc_score
from sklearn.model_selection import (RepeatedStratifiedKFold,
cross_val_score, train_test_split)
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import StandardScaler
def main() -> None:
rng = np.random.default_rng(0)
X, y = make_classification(n_samples=6000, n_features=12,
n_informative=7, n_redundant=3, flip_y=0.2,
class_sep=0.8, random_state=0)
X = np.hstack([X, rng.normal(0, 1, (X.shape[0], 25))])
X_ish, X_test, y_ish, y_test = train_test_split(
X, y, test_size=0.3, stratify=y, random_state=0)
cv = RepeatedStratifiedKFold(n_splits=5, n_repeats=6, random_state=0)
def baho(ustunlar, model_turi="logistik"):
if model_turi == "logistik":
m = make_pipeline(StandardScaler(),
LogisticRegression(max_iter=2000))
else:
m = HistGradientBoostingClassifier(max_iter=200,
early_stopping=False,
random_state=0)
b = cross_val_score(m, X_ish[:, ustunlar], y_ish, cv=cv,
scoring="roc_auc").reshape(6, 5).mean(axis=1)
return float(b.mean()), float(b.std(ddof=1) / np.sqrt(6)), m
print("=== 1. Bazaviy va uning SE si ===")
asos_ustunlar = list(range(12))
asos, se, _ = baho(asos_ustunlar)
print(f" CV: {asos:.4f}, SE: {se:.4f}")
print(f" qaror chegarasi (2*SE): {2 * se:.4f}")
print("\n=== 2. Tajriba jurnali ===")
gooyalar = [
("shovqin belgi 12-16", list(range(12)) + list(range(12, 17))),
("shovqin belgi 17-24", list(range(12)) + list(range(17, 25))),
("barcha belgilar", list(range(X.shape[1]))),
("faqat 8 ta asosiy", list(range(8))),
("shovqin belgi 25-36", list(range(12)) + list(range(25, 37))),
]
jurnal = []
joriy_ustunlar, joriy_ball = asos_ustunlar, asos
print(f" {'#':>3} {'g_oya':<24} {'CV':>8} {'SE':>8} {'farq':>9} "
f"{'qaror':<10}")
for i, (nom, ustunlar) in enumerate(gooyalar, 1):
ball, ball_se, _ = baho(ustunlar)
farq = ball - joriy_ball
qabul = farq > 2 * max(se, ball_se)
if qabul:
joriy_ustunlar, joriy_ball = ustunlar, ball
jurnal.append({"n": i, "gooya": nom, "ball": round(ball, 3),
"se": round(ball_se, 4), "qabul": qabul})
print(f" {i:>3} {nom:<24} {ball:>8.3f} {ball_se:>8.4f} "
f"{farq:>+9.4f} {'QABUL' if qabul else 'rad':<10}")
print("\n=== 3. Model turini sinash ===")
boost_ball, boost_se, _ = baho(joriy_ustunlar, "boosting")
farq = boost_ball - joriy_ball
qabul = farq > 2 * max(se, boost_se)
print(f" logistik: {joriy_ball:.3f}")
print(f" boosting: {boost_ball:.3f} (SE {boost_se:.4f})")
print(f" farq: {farq:+.4f}, chegara: {2 * max(se, boost_se):.4f}")
print(f" qaror: {'QABUL' if qabul else 'rad etildi'}")
model_turi = "boosting" if qabul else "logistik"
print("\n=== 4. Yakuniy: yopiq testni ochish ===")
T = len(gooyalar) + 1
_, _, yakuniy = baho(joriy_ustunlar, model_turi)
yakuniy.fit(X_ish[:, joriy_ustunlar], y_ish)
test_ball = roc_auc_score(
y_test, yakuniy.predict_proba(X_test[:, joriy_ustunlar])[:, 1])
print(f" sinalgan tajribalar (T): {T}")
print(f" nazariy optimizm chegarasi: "
f"{se * np.sqrt(2 * np.log(T)):.4f}")
print(f" CV bahosi: {joriy_ball:.3f}")
print(f" TEST (bir marta): {test_ball:.3f}")
print(f" farq: {joriy_ball - test_ball:+.4f}")
qabul_soni = sum(1 for j in jurnal if j["qabul"])
print(f" jurnal: {len(jurnal)} tajriba, {qabul_soni} ta qabul qilindi")
print(" ⭐ Chegara + yopiq test = ishonchli yakuniy raqam")
if __name__ == "__main__":
main()Natijaning muhim qismi:
=== 1. Bazaviy va uning SE si ===
CV: 0.7217, SE: 0.0003
qaror chegarasi (2*SE): 0.0007
=== 2. Tajriba jurnali ===
# g_oya CV SE farq qaror
1 shovqin belgi 12-16 0.723 0.0005 +0.0009 rad
2 shovqin belgi 17-24 0.719 0.0003 -0.0024 rad
3 barcha belgilar 0.719 0.0007 -0.0025 rad
4 faqat 8 ta asosiy 0.708 0.0004 -0.0137 rad
5 shovqin belgi 25-36 0.720 0.0007 -0.0014 rad
=== 3. Model turini sinash ===
logistik: 0.722
boosting: 0.856 (SE 0.0006)
farq: +0.1347, chegara: 0.0011
qaror: QABUL
=== 4. Yakuniy: yopiq testni ochish ===
sinalgan tajribalar (T): 6
nazariy optimizm chegarasi: 0.0006
CV bahosi: 0.722
TEST (bir marta): 0.861
farq: -0.1391
jurnal: 5 tajriba, 0 ta qabul qilindi
⭐ Chegara + yopiq test = ishonchli yakuniy raqamNima ko'rsatdi: 2.4, 2.6-bo'limlar.
5. To'g'ri va noto'g'ri tushunishlar
| Noto'g'ri fikr | To'g'risi |
|---|---|
| "Overfitting faqat modelda bo'ladi" | Jarayonda ham bo'ladi |
| "CV ishonchli, ko'p tajriba xavfsiz" | Har tajriba CV ni ishlatadi |
| "Testga bir marta qarash zararsiz" | Undan keyin u validatsiya |
| "Kichik yaxshilanishlar yig'iladi" | Ko'pi shovqin |
| "Tajribalar sonini sanash keraksiz" | Optimizm uchun kerak |
| "Yaxlitlash ma'lumot yo'qotadi" | Aynan shu himoya |
| "Salbiy natijalarni yozish shart emas" | T ning bir qismi |
| "Kaggle tablitsasi — validatsiya" | U ham ishlatiladi |
6. Keng tarqalgan xatolar va yechimlari
1. Har kichik yaxshilanishni qabul qilish
if yangi > joriy: qabul_qil() # ⚠️
if yangi - joriy > 2 * se: qabul_qil() # ✅2. Testga bir necha marta qarash
for variant in variantlar: print(baho(y_test, ...)) # ⚠️
# CV da tanlang, testni yakunda bir marta oching # ✅3. Tajribalarni sanamaslik
# "bir necha variant sinadik" # ⚠️
jurnal.append({...}) # har tajriba yoziladi # ✅4. Seed ni tanlash
for s in range(20): ... # eng yaxshi chiqqanini olish # ⚠️
cv = StratifiedKFold(5, shuffle=True, random_state=0) # qat'iy # ✅5. To'rt xonali aniqlik
print(f"{ball:.5f}") # ⚠️
print(f"{ball:.3f}") # ✅6. Bir necha metrikaga navbatma-navbat qarash
# AUC yomon -> AP ga qaraymiz -> F1 ga qaraymiz # ⚠️
# bitta metrikani boshida tanlang # ✅7. Salbiy natijalarni yozmaslik
# faqat qabul qilingan tajribalar yoziladi # ⚠️
# HAMMA tajriba yoziladi (T ni bilish uchun) # ✅7. Integratsiya — bu bilim qayerda kerak bo'ladi
- 18.1-dars (o'tilgan): G'olib la'nati
- 18.4-dars (o'tilgan): Nested CV
- 18.10-dars (o'tilgan): Modellarni taqqoslash
- 18.12-dars: Amaliyot
- 29-qism: MLOps va monitoring
8. Eng yaxshi amaliyotlar
Test to'plamini yoping.
Har tajribani yozing.
Qaror chegarasi qo'ying.
Uch xonagacha yaxlitlang.
G'oyalarni guruhlab sinang.
Takroriy CV ishlating.
Boshqa seed bilan tekshiring.
Yakuniy raqamni T bilan birga bering.
9. Amaliy topshiriq
Vazifa 1: Bashorat qiling
1. # validatsiyaga overfitting mexanizmi?
2. # kim optimizator?
3. # optimizm formulasi?
4. # T = 100 da necha SE?
5. # eng xavfli kombinatsiya?
6. # buni qanday sezish?
7. # eng ishonchli himoya?
8. # qaror chegarasi nima?
9. # guruhlangan tajriba nima beradi?
10. # adaptiv holdout g'oyasi?
11. # yaxlitlash nima uchun yordam beradi?
12. # jurnal nima uchun?Javoblar
- Har qaror CV dan ma'lumot oladi
- Siz
SE * sqrt(2 ln T)- ~3.0
- Kichik validatsiya + ko'p tajriba
- Boshqa seed bilan qayta o'lchash
- Yopiq test to'plami
- SE dan kichik yaxshilanishni rad etish
- T ni kamaytiradi
- Javobni shovqin bilan berish
- Kichik farqlarni to'sadi
- T ni bilish uchun
Vazifa 2: Xatolarni tuzating
1. if yangi > joriy: qabul_qil()
2. for variant in variantlar: print(baho(y_test, ...))
3. for s in range(20): ... # eng yaxshi seed ni olish
4. print(f"{ball:.5f}")
5. # faqat qabul qilingan tajribalar yoziladiJavoblar
1. if yangi - joriy > 2 * se: qabul_qil()
2. # CV da tanlang, testni yakunda bir marta oching
3. cv = StratifiedKFold(5, shuffle=True, random_state=0)
4. print(f"{ball:.3f}")
5. # HAMMA tajriba yoziladiVazifa 3: Mexanizm
Modellang:
- Bazaviy
- Ochko'zlik
- Xulosa
- Yangi seed
Vazifa 4: T va optimizm
Modellang:
- Nomzodlar
- T ta'siri
- Chegara
- Guruhlash
Vazifa 5: Adaptiv
Modellang:
- Sozlama
- Uch strategiya
- Sabab
- Yaxlitlash darajasi
Vazifa 6: Jarayon
Modellang:
- Bazaviy va SE
- Jurnal
- Model turi
- Yopiq test
Vazifa 7: O'ylash
Jamoa 6 oy davomida bitta CV bo'linishida ishlagan va 400 dan ortiq tajriba qilgan. CV balli 0.81 dan 0.89 ga ko'tarilgan. Yangi rahbar "bu raqamga ishonamizmi?" deb so'radi. Qanday javob berasiz va nima qilishni taklif qilasiz?
Javob
Qisqa javob: 0.89 ga ishonmaslik kerak. 400 tajriba bilan CV to'plami amalda o'quv to'plamiga aylangan. Haqiqiy natija ehtimol 0.83-0.86 oralig'ida.
1. Optimizmni baholash
T = 400
CV bahosining SE si (takroriy CV dan) taxminan 0.012 deylik
kutilgan optimizm ~ 0.012 * sqrt(2 * ln(400)) = 0.012 * 3.46 = 0.042Tajribalar bir-biriga bog'liq bo'lgani uchun samarali T kichikroq, ya'ni optimizm ~0.02-0.04 oralig'ida. Bu 0.89 ning ortida 0.85-0.87 turganini anglatadi.
2. Darhol qilinadigan ish
1. YANGI ma'lumot toping:
- keyingi davr (oxirgi 2-3 oy)
- boshqa hudud/segment
- hech qachon ishlatilmagan qism
2. Joriy modelni unda BIR MARTA baholang
3. Farqni o'lchangBu yagona ishonchli tekshiruv. Agar yangi ma'lumot yo'q bo'lsa, ma'lumotning bir qismini ajratib, qulflang va bir necha oy tegmang.
3. Ikkinchi tekshiruv: seed almashtirish
# so'nggi 10 ta "yaxshilanish" ni yangi CV seed bilan qayta o'lchang
for yangilanish in songgi_10:
eski = baho(yangilanish, seed=0) # tarixdagi seed
yangi = baho(yangilanish, seed=777) # yangi seed
print(yangilanish, eski, yangi)Yaxshilanishlarning katta qismi yo'qolsa — diagnoz tasdiqlanadi.
4. Jarayonni tuzatish
| Chora | Tafsilot |
|---|---|
| Yopiq test | Yangi ma'lumotdan 20% ni qulflang, yakunda bir marta |
| Takroriy CV | RepeatedStratifiedKFold(5, 10) — SE ni kamaytiradi |
| Qaror chegarasi | 2 * SE dan kichik yaxshilanish rad etiladi |
| Jurnal | Barcha tajribalar (salbiylari ham) yoziladi |
| Yaxlitlash | Ballar 3 xonagacha e'lon qilinadi |
| Guruhlangan tajriba | Haftada bir marta qaror, har tajribadan keyin emas |
| CV yangilanishi | Har chorakda yangi bo'linish (yangi ma'lumot bilan) |
5. Rahbarga qanday tushuntirish
"0.89 — modelning natijasi emas, 6 oylik tanlov jarayonining natijasi. 400 tajribadan keyin CV to'plami amalda o'quv to'plamiga aylangan. Haqiqiy natijani bilish uchun yangi davr ma'lumotida bir marta baholash kerak; taxminimiz 0.85 ± 0.02. Jarayonni tuzatish uchun yopiq test to'plami va qaror chegarasi joriy qilinadi."
6. Xulosa
- Yangi ma'lumotda bir marta baholang
- So'nggi yaxshilanishlarni yangi seedda tekshiring
- Yopiq test to'plami joriy qiling
- Qaror chegarasi va jurnal
- Hisobotda
Tni ko'rsating
Nimani mustahkamlaydi: 2.2, 2.3, 2.4-bo'limlar.
Xulosa
Bu darsda validatsiyaga overfitting ni o'rgandik.
Eng muhim uch fikr:
Siz optimizatorsiz. Har safar CV natijasiga qarab qaror qabul qilganingizda, o'sha to'plamdan ma'lumot olib modelga o'tkazasiz. Yuzlab qarordan keyin CV balli yangi ma'lumotning emas, tajribalar tarixining aksi bo'ladi. Bu jim sodir bo'ladi: ball doim o'sadi va hech qanday ogohlantirish chiqmaydi.
Optimizm tajribalar soni bilan o'sadi:
SE * sqrt(2 ln T). Shuning uchunTni sanang — tajriba jurnalisiz optimizmni baholash imkonsiz. Eng xavfli kombinatsiya — kichik validatsiya to'plami (katta SE) va ko'p mustaqil tajriba.Himoya — jarayonda. Yopiq test to'plami (yakunda bir marta), qaror chegarasi (
2 * SEdan kichik yaxshilanishni rad etish), guruhlangan tajriba (T ni kamaytiradi), takroriy CV (SE ni kamaytiradi) va uch xonagacha yaxlitlash (arzon adaptiv holdout). Shubha tug'ilsa — so'nggi yaxshilanishlarni boshqa CV seed bilan qayta o'lchang: haqiqiy yaxshilanish saqlanadi, moslashuv yo'qoladi.
Keyingi darsda amaliyot: 18-qismning barcha vositalarini bitta loyihada birlashtiramiz — validatsiya dizaynidan yakuniy hisobotgacha.
Izohlar (0)
Izoh yozish uchun kiring.
- Hozircha izoh yo'q. Birinchi bo'ling!