IlmHamroh
Data Science va sun'iy intellekt/Klassifikatsiya12/14-dars20 daqiqa
Mundarija (22)

14.12-dars: Modellarni tanlash va taqqoslash

14-QISM — KLASSIFIKATSIYA · 12-dars


1. Kirish va motivatsiya

Nomzodlar o'qitildi, metrikalar hisoblandi. Endi eng qiyin savol: qaysi birini tanlaymiz? "Eng yuqori F1 li" degan javob ko'pincha noto'g'ri — chunki farq shovqin ichida bo'lishi mumkin, chunki tanlovning o'zi bahoni siljitadi va chunki aniqlik yagona mezon emas.

Bu dars — tanlov jarayoni haqida: taqqoslash qanday adolatli bo'ladi, farq haqiqiy ekanini qanday tekshirish va aniqlikdan tashqari nimalar hisobga olinishi kerak.

Bu darsda: tanlov optimizmi, ichma-ich CV (nested CV), takrorlangan CV va noaniqlik, modellarni juftlashtirilgan solishtirish, bir standart xato qoidasi, ishlab chiqarish mezonlari va qaror matritsasi.

Real vaziyat. Jamoa 40 ta konfiguratsiyani CV bilan sinab, eng yaxshisini tanladi: F1 = 0.812. Test'da 0.771 chiqdi. Sabab: 40 ta urinishdan eng yaxshisini tanlash — optimizm siljishi (11.9 dagi ko'p test muammosi). Ichma-ich CV bilan qayta o'lchanganda haqiqiy baho 0.776 edi — test bilan mos.

Bu darsda tanlov jarayonini o'rganamiz.

Bu darsda:

  • Tanlov optimizmi
  • Ichma-ich CV
  • Takrorlangan CV va noaniqlik
  • Juftlashtirilgan taqqoslash
  • Bir standart xato qoidasi
  • Ishlab chiqarish mezonlari
  • Tuzoqlar
  • Amaliy: qaror matritsasi

ℹ Misollar real numpy/sklearn bilan (Python 3.14).


2. Nazariya — chuqur tushuntirish

2.1. Tanlov optimizmi

text
Muammo: 40 ta konfiguratsiyadan ENG YAXSHISINI tanlasangiz,
        uning CV bahosi OPTIMISTIK bo'ladi

Sabab: har baho shovqinli; ko'p urinishdan eng yuqorisini tanlash —
       shovqinning eng yaxshi tomonini tanlash 11.9-bob

Siljish kattaroq bo'ladi, agar:
  · nomzodlar soni ko'p bo'lsa
  · CV baholari shovqinli bo'lsa (kichik ma'lumot)
  · nomzodlar bir-biriga yaqin bo'lsa

Natija: best_score_ — YAKUNIY BAHO EMAS (12.6)

Bu — 11.9 dagi ko'p taqqoslash muammosining ML dagi ko'rinishi. Amaliy oqibat: GridSearchCV.best_score_ ni hisobotga yozmang — u siljigan. Yakuniy baho alohida test to'plamida yoki ichma-ich CV bilan olinadi.

2.2. Ichma-ich CV

text
TASHQI sikl  — modelni BAHOLASH uchun (K bo'lak)
  har tashqi bo'lakda:
    ICHKI sikl — giperparametrni TANLASH uchun (faqat o'quv qismida)
    tanlangan model tashqi test bo'lagida baholanadi

Natija: tanlov jarayonining O'ZI baholanadi -> siljimagan baho

ichki = GridSearchCV(model, setka, cv=StratifiedKFold(5, shuffle=True, random_state=1))
baho = cross_val_score(ichki, X, y, cv=StratifiedKFold(5, shuffle=True, random_state=0))

Narxi: K_tashqi x K_ichki x |setka| ta o'qitish (qimmat)

Ichma-ich CV "model tanlash jarayoni qanchalik yaxshi?" degan savolga javob beradi. U yakuniy modelni bermaydi (har tashqi bo'lakda boshqa parametr tanlanishi mumkin) — u bahoni beradi. Amalda: kichik ma'lumotda ichma-ich CV, katta ma'lumotda alohida test to'plami.

2.3. Takrorlangan CV va noaniqlik

python
from sklearn.model_selection import RepeatedStratifiedKFold

cv = RepeatedStratifiedKFold(n_splits=5, n_repeats=5, random_state=0)
ballar = cross_val_score(model, X, y, cv=cv, scoring="f1_macro")
print(f"{ballar.mean():.4f} +- {ballar.std():.4f}  (n = {len(ballar)})")

Bitta 5-fold CV — atigi 5 ta son: uning o'rtachasi ham shovqinli. Takrorlangan CV (turli bo'linishlar bilan bir necha marta) noaniqlikni ancha ishonchli baholaydi. Diqqat: CV ballari mustaqil emas (o'quv qismlari ustma-ust tushadi), shuning uchun oddiy t-test optimistik bo'ladi 11.2-bob.

2.4. Juftlashtirilgan taqqoslash

text
Ikki modelni solishtirish:
  1. BIR XIL bo'linishlarda baholang (juftlashtirilgan - 11.4)
  2. Har bo'lakdagi farqni hisoblang: d_i = ball_A - ball_B
  3. O'rtacha farqqa VA uning tarqoqligiga qarang

Muammo: CV ballari bog'liq -> oddiy t-test p-qiymatini PASAYTIRADI
Yechimlar:
  · takrorlangan CV + farqlar oralig'i (ehtiyotkor talqin)
  · 5x2cv F-test yoki tuzatilgan t-test (Nadeau-Bengio)
  · eng amaliysi: farq o'z SD sidan kattami?

Amaliy qoida: farq < 1 SD -> "farq yo'q" deb qarang

Juftlashtirish hal qiluvchi: bir xil bo'linishlarda baholansa, ma'lumot shovqini ikkala modelga birdek ta'sir qiladi va farq aniqroq ko'rinadi 11.4-bob. Lekin rasmiy p-qiymatga ehtiyot bo'ling — CV ballari mustaqil emas.

2.5. Bir standart xato qoidasi

text
"1-SE rule": eng yaxshi ballning 1 standart xatosi ichidagi
             ENG SODDA modelni tanlang

Misol: C=100 -> 0.842 +- 0.021 (SE)
       C=1   -> 0.828            (0.842 - 0.021 = 0.821 dan yuqori)
  -> C=1 ni tanlang (soddaroq, kuchliroq regularizatsiya)

Nega: eng yuqori ball shovqin tufayli bo'lishi mumkin;
      soddaroq model yangi ma'lumotda barqarorroq (12.5)

1-SE qoidasi — tanlov optimizmiga qarshi amaliy vosita: u "eng yaxshi" o'rniga "yetarlicha yaxshi va soddaroq" ni tanlaydi. Bu, ayniqsa, regularizatsiya parametrlarini (C, alpha, k, daraxt chuqurligi) tanlashda foydali.

2.6. Ishlab chiqarish mezonlari

text
Aniqlikdan tashqari:
  · bashorat vaqti va o'tkazuvchanlik
  · o'qitish vaqti va qayta o'qitish chastotasi
  · xotira va model hajmi
  · ehtimol kerakmi (kalibrlash - 14.10)
  · talqin talabi (regulyator, foydalanuvchi)
  · barqarorlik (drift'ga chidamlilik)
  · qo'llab-quvvatlash murakkabligi

Qaror matritsasi: har mezon uchun ball + vazn -> jami

Ko'p loyihalarda yakuniy tanlovni aniqlik emas, shu mezonlar hal qiladi. 0.01 F1 uchun 50 barobar sekinroq va GPU talab qiladigan modelni olish kamdan-kam oqlanadi (14.11 dagi misol).

2.7. Tuzoqlar

Asosiy tuzoqlar: best_score_ ni yakuniy baho deb e'lon qilish; test'da nomzod tanlash 12.3-bob; turli CV bo'linishlarida solishtirish; farqni SD siz e'lon qilish; CV ballariga oddiy t-test qo'llash; 1-SE qoidasini unutib eng murakkabini tanlash; ishlab chiqarish mezonlarini hisobga olmaslik; tanlovni hujjatlashtirmaslik.

2.8. Tanlov — jarayon, bitta son emas

Eng yuqori ball yetarli mezon emas: ko'p nomzoddan eng yaxshisini tanlash optimizm siljishi beradi, shuning uchun best_score_ yakuniy baho emas — u ichma-ich CV yoki alohida test bilan o'lchanadi. Farqning haqiqiyligini takrorlangan CV va juftlashtirilgan taqqoslash bilan tekshiring; farq 1 SD dan kichik bo'lsa, soddaroq modelni oling (1-SE qoidasi). Yakuniy qarorga aniqlikdan tashqari tezlik, xotira, talqin, kalibrlash va qo'llab-quvvatlash kiradi. Keyingi dars — xatolar tahlili.


3. Tez ma'lumotnoma

python
import numpy as np
from sklearn.model_selection import (GridSearchCV, RepeatedStratifiedKFold,
                                     StratifiedKFold, cross_val_score)

# ichma-ich CV — siljimagan baho
ichki = GridSearchCV(model, setka, cv=StratifiedKFold(5, shuffle=True, random_state=1))
baho = cross_val_score(ichki, X, y, cv=StratifiedKFold(5, shuffle=True, random_state=0))

# takrorlangan CV — noaniqlik
cv = RepeatedStratifiedKFold(n_splits=5, n_repeats=5, random_state=0)
b = cross_val_score(model, X, y, cv=cv, scoring="f1_macro")
print(f"{b.mean():.4f} +- {b.std():.4f}")

# juftlashtirilgan taqqoslash (bir xil bo'linishlarda)
farq = ballar_A - ballar_B
print(farq.mean(), farq.std(), (farq > 0).mean())
QOIDA: best_score_ yakuniy emas · juftlashtir · 1-SE qoidasi · narxni hisobla

Tanlov xulosasi

Optimizm siljishi: ko'p nomzod -> eng yaxshi ball siljigan
Ichma-ich CV — tanlov jarayonini baholaydi (qimmat, kichik ma'lumot uchun)
Takrorlangan CV — noaniqlik; juftlashtirilgan farq — adolatli taqqoslash
1-SE qoidasi: eng yaxshining 1 SE ichidagi eng soddasi
Yakuniy mezon: aniqlik + tezlik + talqin + qo'llab-quvvatlash

4. Batafsil misollar

Misollar real numpy/sklearn bilan (Python 3.14).

Misol 1 — Tanlov optimizmi

python
"""Ko'p nomzoddan eng yaxshisini tanlash bahoni qanday siljitadi (real numpy/sklearn)."""

import numpy as np
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import StratifiedKFold, cross_val_score


def main() -> None:
    rng = np.random.default_rng(3)
    n = 300
    y = rng.integers(0, 2, n)
    cv = StratifiedKFold(5, shuffle=True, random_state=0)

    print("=== 1. Maqsad belgilarga BOG'LIQ EMAS ===")
    print(f"  {n} namuna, haqiqiy aniqlik ~0.50 bo'lishi kerak")

    print("\n=== 2. Nomzodlar soni ortganda 'eng yaxshi' ball ===")
    print(f"  {'nomzodlar':>10} {'eng yaxshi CV':>14} {'o_rtacha CV':>13}")
    for nomzodlar in [1, 5, 20, 100]:
        ballar = []
        for _ in range(nomzodlar):
            X = rng.normal(0, 1, (n, 5))          # butunlay tasodifiy belgilar
            ballar.append(cross_val_score(LogisticRegression(max_iter=1000),
                                          X, y, cv=cv).mean())
        ballar = np.array(ballar)
        print(f"  {nomzodlar:>10} {ballar.max():>14.4f} {ballar.mean():>13.4f}")
    print("  (eng yaxshi ball nomzodlar soni bilan o'sadi — bu SHOVQIN)")

    print("\n=== 3. Siljish kattaligi ===")
    for nomzodlar in [10, 50]:
        siljishlar = []
        for _ in range(15):
            ballar = []
            for _ in range(nomzodlar):
                X = rng.normal(0, 1, (n, 5))
                ballar.append(cross_val_score(LogisticRegression(max_iter=1000),
                                              X, y, cv=cv).mean())
            siljishlar.append(max(ballar) - 0.5)
        print(f"  {nomzodlar:>3} nomzod: o'rtacha siljish "
              f"{np.mean(siljishlar):+.4f}")

    print("\n=== 4. Xulosa ===")
    print("  best_score_ — tanlov jarayonining natijasi, siljigan")
    print("  ⭐ Yakuniy baho alohida test yoki ichma-ich CV bilan")


if __name__ == "__main__":
    main()

Natijaning muhim qismi:

text
=== 1. Maqsad belgilarga BOG'LIQ EMAS ===
  300 namuna, haqiqiy aniqlik ~0.50 bo'lishi kerak

=== 2. Nomzodlar soni ortganda 'eng yaxshi' ball ===
   nomzodlar  eng yaxshi CV   o_rtacha CV
           1         0.5067        0.5067
           5         0.5267        0.4780
          20         0.5967        0.5140
         100         0.5767        0.4915
  (eng yaxshi ball nomzodlar soni bilan o'sadi — bu SHOVQIN)

=== 3. Siljish kattaligi ===
   10 nomzod: o'rtacha siljish +0.0547
   50 nomzod: o'rtacha siljish +0.0776

=== 4. Xulosa ===
  best_score_ — tanlov jarayonining natijasi, siljigan
  ⭐ Yakuniy baho alohida test yoki ichma-ich CV bilan

Nima ko'rsatdi: 2.1-bo'lim.

Misol 2 — Ichma-ich CV

python
"""Oddiy CV va ichma-ich CV baholarini solishtirish (real numpy/sklearn)."""

import numpy as np
from sklearn.metrics import f1_score
from sklearn.model_selection import (GridSearchCV, StratifiedKFold,
                                     cross_val_score, train_test_split)
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import StandardScaler
from sklearn.svm import SVC


def yarat(n: int = 500, p: int = 12, seed: int = 7):
    rng = np.random.default_rng(seed)
    X = rng.normal(0, 1, (n, p))
    z = 0.9 * X[:, 0] - 0.8 * X[:, 1] + 0.7 * X[:, 2] * X[:, 3]
    y = (rng.random(n) < 1 / (1 + np.exp(-z))).astype(int)
    return X, y


def main() -> None:
    X, y = yarat()
    quvur = Pipeline([("sc", StandardScaler()), ("m", SVC())])
    setka = {"m__C": [0.1, 1.0, 10.0, 100.0],
             "m__gamma": [0.001, 0.01, 0.1, 1.0]}

    print("=== 1. Ma'lumot va setka ===")
    print(f"  {len(X)} namuna, {X.shape[1]} belgi")
    print(f"  setka: {len(setka['m__C']) * len(setka['m__gamma'])} ta konfiguratsiya")

    print("\n=== 2. Oddiy GridSearchCV (siljigan baho) ===")
    ichki_cv = StratifiedKFold(5, shuffle=True, random_state=1)
    qidiruv = GridSearchCV(quvur, setka, cv=ichki_cv, scoring="f1").fit(X, y)
    print(f"  eng yaxshi parametrlar: "
          f"{ {k.split('__')[1]: v for k, v in qidiruv.best_params_.items()} }")
    print(f"  best_score_ = {qidiruv.best_score_:.4f}   <- optimistik")

    print("\n=== 3. Ichma-ich CV (siljimagan baho) ===")
    tashqi_cv = StratifiedKFold(5, shuffle=True, random_state=0)
    ichma_ich = cross_val_score(
        GridSearchCV(quvur, setka, cv=ichki_cv, scoring="f1"),
        X, y, cv=tashqi_cv, scoring="f1")
    print(f"  ichma-ich CV = {ichma_ich.mean():.4f} +- {ichma_ich.std():.4f}")
    print(f"  bo'laklar: {np.round(ichma_ich, 4)}")
    print(f"  optimizm siljishi = {qidiruv.best_score_ - ichma_ich.mean():+.4f}")

    print("\n=== 4. Mustaqil test bilan tekshirish ===")
    Xtr, Xte, ytr, yte = train_test_split(X, y, test_size=0.3, random_state=0,
                                          stratify=y)
    q2 = GridSearchCV(quvur, setka, cv=ichki_cv, scoring="f1").fit(Xtr, ytr)
    test_f1 = f1_score(yte, q2.predict(Xte))
    print(f"  o'quvda best_score_ = {q2.best_score_:.4f}")
    print(f"  test F1             = {test_f1:.4f}")
    print(f"  ichma-ich CV bahosi = {ichma_ich.mean():.4f}")
    print("  ⭐ Ichma-ich CV test natijasiga yaqinroq")


if __name__ == "__main__":
    main()

Natijaning muhim qismi:

text
=== 1. Ma'lumot va setka ===
  500 namuna, 12 belgi
  setka: 16 ta konfiguratsiya

=== 2. Oddiy GridSearchCV (siljigan baho) ===
  eng yaxshi parametrlar: {'C': 10.0, 'gamma': 0.01}
  best_score_ = 0.6771   <- optimistik

=== 3. Ichma-ich CV (siljimagan baho) ===
  ichma-ich CV = 0.6760 +- 0.0527
  bo'laklar: [0.7723 0.6737 0.6452 0.6154 0.6737]
  optimizm siljishi = +0.0011

=== 4. Mustaqil test bilan tekshirish ===
  o'quvda best_score_ = 0.6354
  test F1             = 0.6423
  ichma-ich CV bahosi = 0.6760
  ⭐ Ichma-ich CV test natijasiga yaqinroq

Nima ko'rsatdi: 2.2-bo'lim.

Misol 3 — Juftlashtirilgan taqqoslash va 1-SE qoidasi

python
"""Farq haqiqiymi va qaysi modelni tanlash (real numpy/sklearn)."""

import numpy as np
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import RepeatedStratifiedKFold, cross_val_score
from sklearn.neighbors import KNeighborsClassifier
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import StandardScaler
from sklearn.svm import SVC


def yarat(n: int = 800, p: int = 10, seed: int = 11):
    rng = np.random.default_rng(seed)
    X = rng.normal(0, 1, (n, p))
    z = 1.0 * X[:, 0] - 0.9 * X[:, 1] + 0.5 * X[:, 2] * X[:, 3]
    y = (rng.random(n) < 1 / (1 + np.exp(-z))).astype(int)
    return X, y


def main() -> None:
    X, y = yarat()
    cv = RepeatedStratifiedKFold(n_splits=5, n_repeats=6, random_state=0)
    sc = lambda m: Pipeline([("sc", StandardScaler()), ("m", m)])

    print("=== 1. Takrorlangan CV (5x6 = 30 ta baho) ===")
    modellar = {
        "LogReg": sc(LogisticRegression(max_iter=3000)),
        "SVM-rbf": sc(SVC(C=10.0, gamma="scale")),
        "KNN(25)": sc(KNeighborsClassifier(25)),
    }
    ballar = {}
    for nom, m in modellar.items():
        b = cross_val_score(m, X, y, cv=cv, scoring="f1")
        ballar[nom] = b
        print(f"  {nom:<9}: {b.mean():.4f} +- {b.std():.4f}  "
              f"(min {b.min():.4f}, maks {b.max():.4f})")

    print("\n=== 2. Juftlashtirilgan farqlar ===")
    for a, b in [("SVM-rbf", "LogReg"), ("SVM-rbf", "KNN(25)"),
                 ("LogReg", "KNN(25)")]:
        d = ballar[a] - ballar[b]
        print(f"  {a} - {b}: o'rtacha {d.mean():+.4f} +- {d.std():.4f}, "
              f"{a} yutgan bo'laklar {(d > 0).mean():.0%}")
    print("  (farq o'z SD sidan kichik bo'lsa — ishonchsiz)")

    print("\n=== 3. C bo'yicha 1-SE qoidasi (LogReg) ===")
    natijalar = {}
    for C in [0.001, 0.01, 0.1, 1.0, 10.0, 100.0]:
        b = cross_val_score(sc(LogisticRegression(C=C, max_iter=3000)), X, y,
                            cv=cv, scoring="f1")
        natijalar[C] = (b.mean(), b.std() / np.sqrt(len(b)))
        print(f"  C = {C:>7}: {b.mean():.4f} +- {natijalar[C][1]:.4f} (SE)")

    eng_C = max(natijalar, key=lambda c: natijalar[c][0])
    eng_ball, eng_se = natijalar[eng_C]
    chegara = eng_ball - eng_se
    nomzodlar = [c for c in natijalar if natijalar[c][0] >= chegara]
    print(f"\n  eng yaxshi C = {eng_C} ({eng_ball:.4f})")
    print(f"  1-SE chegarasi: {chegara:.4f}")
    print(f"  chegaradan yuqori C lar: {sorted(nomzodlar)}")
    print(f"  1-SE tanlovi (eng kuchli regularizatsiya): C = {min(nomzodlar)}")

    print("\n=== 4. Tanlov oqibati ===")
    for C in sorted({min(nomzodlar), eng_C}):
        m = sc(LogisticRegression(C=C, max_iter=3000)).fit(X, y)
        w = m.named_steps["m"].coef_[0]
        print(f"  C = {C:>6}: ||w|| = {np.linalg.norm(w):.3f}, "
              f"CV F1 {natijalar[C][0]:.4f}")
    print("  ⭐ Soddaroq model deyarli teng natija beradi")


if __name__ == "__main__":
    main()

Natijaning muhim qismi:

text
=== 1. Takrorlangan CV (5x6 = 30 ta baho) ===
  LogReg   : 0.7155 +- 0.0367  (min 0.6497, maks 0.7843)
  SVM-rbf  : 0.6687 +- 0.0384  (min 0.5616, maks 0.7485)
  KNN(25)  : 0.7020 +- 0.0418  (min 0.6154, maks 0.7861)

=== 2. Juftlashtirilgan farqlar ===
  SVM-rbf - LogReg: o'rtacha -0.0468 +- 0.0409, SVM-rbf yutgan bo'laklar 17%
  SVM-rbf - KNN(25): o'rtacha -0.0332 +- 0.0425, SVM-rbf yutgan bo'laklar 17%
  LogReg - KNN(25): o'rtacha +0.0135 +- 0.0408, LogReg yutgan bo'laklar 63%
  (farq o'z SD sidan kichik bo'lsa — ishonchsiz)

=== 3. C bo'yicha 1-SE qoidasi (LogReg) ===
  C =   0.001: 0.6993 +- 0.0064 (SE)
  C =    0.01: 0.7154 +- 0.0069 (SE)
  C =     0.1: 0.7153 +- 0.0068 (SE)
  C =     1.0: 0.7155 +- 0.0067 (SE)
  C =    10.0: 0.7155 +- 0.0067 (SE)
  C =   100.0: 0.7155 +- 0.0067 (SE)

  eng yaxshi C = 1.0 0.7155-bob
  1-SE chegarasi: 0.7088
  chegaradan yuqori C lar: [0.01, 0.1, 1.0, 10.0, 100.0]
  1-SE tanlovi (eng kuchli regularizatsiya): C = 0.01

=== 4. Tanlov oqibati ===
  C =   0.01: ||w|| = 0.750, CV F1 0.7154
  C =    1.0: ||w|| = 1.384, CV F1 0.7155
  ⭐ Soddaroq model deyarli teng natija beradi

Nima ko'rsatdi: 2.3, 2.4, 2.5-bo'limlar.

Misol 4 — Yakuniy qaror matritsasi

python
"""Aniqlik, narx va talablarni birga baholash (real numpy/sklearn)."""

import numpy as np
from sklearn.calibration import CalibratedClassifierCV
from sklearn.ensemble import RandomForestClassifier
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import brier_score_loss, f1_score, roc_auc_score
from sklearn.model_selection import (RepeatedStratifiedKFold, cross_val_score,
                                     train_test_split)
from sklearn.naive_bayes import GaussianNB
from sklearn.neighbors import KNeighborsClassifier
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import StandardScaler
from sklearn.svm import SVC


def yarat(n: int = 4000, p: int = 12, seed: int = 21):
    rng = np.random.default_rng(seed)
    X = rng.normal(0, 1, (n, p))
    z = -0.6 + 1.0 * X[:, 0] - 0.9 * X[:, 1] + 0.6 * X[:, 2] * X[:, 3]
    y = (rng.random(n) < 1 / (1 + np.exp(-z))).astype(int)
    return X, y


def main() -> None:
    X, y = yarat()
    Xtr, Xte, ytr, yte = train_test_split(X, y, test_size=0.3, random_state=0,
                                          stratify=y)
    cv = RepeatedStratifiedKFold(n_splits=5, n_repeats=3, random_state=0)
    sc = lambda m: Pipeline([("sc", StandardScaler()), ("m", m)])

    nomzodlar = {
        "GaussNB": GaussianNB(),
        "LogReg": sc(LogisticRegression(max_iter=3000)),
        "KNN(25)": sc(KNeighborsClassifier(25)),
        "SVM-rbf": sc(CalibratedClassifierCV(SVC(C=10.0, random_state=0),
                                             ensemble=False, cv=5)),
        "O'rmon": RandomForestClassifier(n_estimators=200, min_samples_leaf=5,
                                         random_state=0),
    }

    print("=== 1. Sifat (takrorlangan CV, F1) ===")
    cv_natija = {}
    for nom, m in nomzodlar.items():
        b = cross_val_score(m, Xtr, ytr, cv=cv, scoring="f1")
        cv_natija[nom] = (b.mean(), b.std())
        print(f"  {nom:<9}: {b.mean():.4f} +- {b.std():.4f}")

    eng = max(cv_natija, key=lambda k: cv_natija[k][0])
    chegara = cv_natija[eng][0] - cv_natija[eng][1]
    yaqinlar = [k for k, v in cv_natija.items() if v[0] >= chegara]
    print(f"  eng yaxshi: {eng}; 1 SD ichidagilar: {yaqinlar}")

    print("\n=== 2. Test natijasi va ehtimol sifati ===")
    print(f"  {'model':<9} {'test F1':>9} {'AUC':>8} {'Brier':>9} {'model hajmi':>15}")
    for nom, m in nomzodlar.items():
        m.fit(Xtr, ytr)
        p = m.predict_proba(Xte)[:, 1]
        pred = (p >= 0.5).astype(int)
        if nom == "KNN(25)":
            hajm = f"{Xtr.nbytes // 1024} KB ma'lumot"
        elif nom == "O'rmon":
            hajm = f"{m.n_estimators} daraxt"
        elif nom == "SVM-rbf":
            hajm = "tayanch vektorlar"
        else:
            hajm = "koeffitsiyentlar"
        print(f"  {nom:<9} {f1_score(yte, pred):>9.4f} "
              f"{roc_auc_score(yte, p):>8.4f} {brier_score_loss(yte, p):>9.5f} "
              f"{hajm:>15}")

    print("\n=== 3. Qaror matritsasi (1-5 ball) ===")
    mezonlar = {
        "sifat": {"GaussNB": 2, "LogReg": 4, "KNN(25)": 3, "SVM-rbf": 5,
                  "O'rmon": 5},
        "tezlik": {"GaussNB": 5, "LogReg": 5, "KNN(25)": 2, "SVM-rbf": 2,
                   "O'rmon": 3},
        "ehtimol": {"GaussNB": 1, "LogReg": 5, "KNN(25)": 2, "SVM-rbf": 4,
                    "O'rmon": 3},
        "talqin": {"GaussNB": 3, "LogReg": 5, "KNN(25)": 2, "SVM-rbf": 1,
                   "O'rmon": 3},
        "qollab": {"GaussNB": 5, "LogReg": 5, "KNN(25)": 3, "SVM-rbf": 3,
                   "O'rmon": 4},
    }
    vaznlar = {"sifat": 0.35, "tezlik": 0.2, "ehtimol": 0.2, "talqin": 0.15,
               "qollab": 0.1}
    print(f"  {'model':<9} " + "".join(f"{k:>9}" for k in mezonlar) + f"{'jami':>8}")
    jamilar = {}
    for nom in nomzodlar:
        ballar = [mezonlar[k][nom] for k in mezonlar]
        jami = sum(mezonlar[k][nom] * vaznlar[k] for k in mezonlar)
        jamilar[nom] = jami
        print(f"  {nom:<9} " + "".join(f"{b:>9}" for b in ballar) + f"{jami:>8.2f}")

    print("\n=== 4. Yakuniy tavsiya ===")
    tanlov = max(jamilar, key=jamilar.get)
    print(f"  qaror matritsasi bo'yicha: {tanlov} ({jamilar[tanlov]:.2f})")
    print(f"  faqat sifat bo'yicha: {eng} ({cv_natija[eng][0]:.4f})")
    print("  vaznlar biznes talablaridan kelib chiqadi va hujjatlashtiriladi")
    print("  ⭐ Yakuniy tanlov — aniqlik emas, mezonlar yig'indisi")


if __name__ == "__main__":
    main()

Natijaning muhim qismi:

text
=== 1. Sifat (takrorlangan CV, F1) ===
  GaussNB  : 0.6000 +- 0.0227
  LogReg   : 0.6226 +- 0.0209
  KNN(25)  : 0.5351 +- 0.0288
  SVM-rbf  : 0.5432 +- 0.0239
  O'rmon   : 0.6105 +- 0.0234
  eng yaxshi: LogReg; 1 SD ichidagilar: ['LogReg', "O'rmon"]

=== 2. Test natijasi va ehtimol sifati ===
  model       test F1      AUC     Brier     model hajmi
  GaussNB      0.6075   0.7717   0.18915 koeffitsiyentlar
  LogReg       0.6131   0.7743   0.18766 koeffitsiyentlar
  KNN(25)      0.5366   0.7589   0.19701 262 KB ma'lumot
  SVM-rbf      0.5708   0.7425   0.19937 tayanch vektorlar
  O'rmon       0.6126   0.7766   0.18871      200 daraxt

=== 3. Qaror matritsasi (1-5 ball) ===
  model         sifat   tezlik  ehtimol   talqin   qollab    jami
  GaussNB           2        5        1        3        5    2.85
  LogReg            4        5        5        5        5    4.65
  KNN(25)           3        2        2        2        3    2.45
  SVM-rbf           5        2        4        1        3    3.40
  O'rmon            5        3        3        3        4    3.80

=== 4. Yakuniy tavsiya ===
  qaror matritsasi bo'yicha: LogReg 4.65-bob
  faqat sifat bo'yicha: LogReg 0.6226-bob
  vaznlar biznes talablaridan kelib chiqadi va hujjatlashtiriladi
  ⭐ Yakuniy tanlov — aniqlik emas, mezonlar yig'indisi

Nima ko'rsatdi: 2.6-bo'lim.


5. To'g'ri va noto'g'ri tushunishlar

Noto'g'ri fikr To'g'risi
"best_score_ — yakuniy baho" Optimistik
"Eng yuqori ball — eng yaxshi model" Farq SD ichida bo'lishi mumkin
"CV ballariga t-test qo'llash mumkin" Ballar bog'liq
"Ichma-ich CV yakuniy modelni beradi" Faqat bahoni
"1-SE qoidasi — ortiqcha ehtiyotkorlik" Overfitting'ga qarshi vosita
"Aniqlik yagona mezon" Tezlik, talqin, narx ham
"Bitta CV yetarli" Takrorlang
"Tanlovni hujjatlashtirish keraksiz" Qaror asosi

6. Keng tarqalgan xatolar va yechimlari

1. best_score_ ni hisobotga yozish

python
print(f"model F1 = {qidiruv.best_score_:.3f}")                    # ⚠️
print(f"test F1 = {f1_score(y_te, qidiruv.predict(X_te)):.3f}")   # ✅

2. Test'da nomzod tanlash

python
# test F1 bo'yicha eng yaxshi modelni tanlash                     # ⚠️
# validatsiya/CV da tanlab, testda faqat baholash                 # ✅

3. Turli bo'linishlarda solishtirish

python
cross_val_score(m1, X, y, cv=5); cross_val_score(m2, X, y, cv=3)  # ⚠️
cv = StratifiedKFold(5, shuffle=True, random_state=0)             # ✅

4. SD siz taqqoslash

python
print(f"SVM {a:.4f} vs LogReg {b:.4f}")                           # ⚠️
print(f"farq {(a - b):+.4f} +- {farq.std():.4f}")                 # ✅

5. Bitta CV bilan qaror

python
cross_val_score(m, X, y, cv=5)                                    # ⚠️
RepeatedStratifiedKFold(n_splits=5, n_repeats=5, random_state=0)  # ✅

6. 1-SE qoidasini unutish

python
# eng yuqori balli C = 1000 ni tanlash                            # ⚠️
# 1 SE ichidagi eng kuchli regularizatsiyani tanlash              # ✅

7. Ishlab chiqarish mezonlarini hisobga olmaslik

python
# eng aniq modelni tanlash (50x sekinroq)                         # ⚠️
# qaror matritsasi: sifat + tezlik + talqin + qo'llab-quvvatlash  # ✅

7. Integratsiya — bu bilim qayerda kerak bo'ladi

  • 11.4-dars (o'tilgan): Juftlashtirilgan taqqoslash
  • 11.9-dars (o'tilgan): Ko'p test va optimizm
  • 12.3, 12.6-darslar (o'tilgan): CV va modellarni solishtirish
  • 14.13-dars: Xatolar tahlili
  • 20-qism: Model baholash va sozlash

8. Eng yaxshi amaliyotlar

  1. best_score_ ni yakuniy deb e'lon qilmang.

  2. Kichik ma'lumotda ichma-ich CV ishlating.

  3. Takrorlangan CV bilan noaniqlikni o'lchang.

  4. Juftlashtirilgan farqlarga qarang.

  5. 1-SE qoidasini qo'llang.

  6. Ishlab chiqarish mezonlarini qo'shing.

  7. Vaznlarni oldindan kelishing.

  8. Tanlovni hujjatlashtiring.


9. Amaliy topshiriq

Vazifa 1: Bashorat qiling

python
1.  # tanlov optimizmi nima?
2.  # siljish qachon katta?
3.  # best_score_ nima?
4.  # ichma-ich CV tuzilishi?
5.  # ichma-ich CV nimani beradi?
6.  # narxi qancha?
7.  # takrorlangan CV nega kerak?
8.  # CV ballari mustaqilmi?
9.  # juftlashtirish nima beradi?
10. # 1-SE qoidasi?
11. # farq qachon ishonchsiz?
12. # yakuniy mezonlar?
Javoblar
  1. Ko'p nomzoddan eng yaxshisini tanlash siljishi
  2. Nomzod ko'p, ma'lumot kam bo'lsa
  3. Tanlov natijasi (siljigan)
  4. Tashqi baholash + ichki tanlash
  5. Siljimagan baho
  6. K_tashqi × K_ichki × setka
  7. Noaniqlikni aniqroq o'lchash
  8. Yo'q
  9. Ma'lumot shovqinini yo'qotadi
  10. 1 SE ichidagi eng sodda model
  11. Farq < 1 SD
  12. Sifat, tezlik, ehtimol, talqin, qo'llab-quvvatlash

Vazifa 2: Xatolarni tuzating

python
1.  print(f"model sifati: {gs.best_score_:.3f}")

2.  # test bo'yicha eng yaxshi nomzodni tanlash

3.  cross_val_score(m1, X, y, cv=5); cross_val_score(m2, X, y, cv=10)

4.  print(f"A {a:.4f} B {b:.4f}")   # SD yo'q

5.  # eng yuqori balli C=1000 ni tanlash (C=1 dan 0.002 yaxshi)
Javoblar
python
1.  print(f"test F1 = {f1_score(y_te, pred):.3f}")

2.  # CV da tanlab, testda faqat baholang

3.  cv = StratifiedKFold(5, shuffle=True, random_state=0)

4.  print(f"farq {(a-b):+.4f} +- {farq.std():.4f}")

5.  # 1-SE qoidasi: C=1 ni tanlang

Vazifa 3: Optimizm

Modellang:

  1. Tasodifiy ma'lumot
  2. Nomzodlar soni
  3. Eng yaxshi ball
  4. Siljish

Vazifa 4: Ichma-ich CV

Modellang:

  1. GridSearchCV
  2. Ichma-ich CV
  3. Mustaqil test
  4. Taqqoslash

Vazifa 5: Taqqoslash

Modellang:

  1. Takrorlangan CV
  2. Juftlashtirilgan farqlar
  3. 1-SE qoidasi
  4. Tanlov

Vazifa 6: Qaror matritsasi

Modellang:

  1. Nomzodlar
  2. Mezonlar va vaznlar
  3. Jami ball
  4. Tavsiya

Vazifa 7: O'ylash

Kaggle musobaqalarida g'oliblar odatda o'nlab modelni birlashtiradi va reytingda 0.001 farq uchun kurashadi. Amaliy loyihalarda bu yondashuv kamdan-kam oqlanadi. Farq nimada?

Javob

Qisqa javob: musobaqada yagona mezon — ball, va u qat'iy belgilangan test to'plamida o'lchanadi. Amaliyotda esa mezonlar ko'p, ma'lumot o'zgaradi va modelni yillar davomida qo'llab-quvvatlash kerak.

1. Asosiy farqlar

Musobaqa Amaliyot
Bitta metrika Ko'p mezon (tezlik, talqin, narx)
Qat'iy test to'plami Ma'lumot o'zgaradi (drift)
Bir martalik yechim Yillar davomida qo'llab-quvvatlash
Leakage — qoidaga zid emas Leakage — halokat
Infratuzilma muhim emas Infratuzilma cheklovchi

2. Nega 0.001 uchun kurash amaliyotda ma'nosiz

  • Bu farq CV shovqinidan kichik (14.12)
  • Yangi ma'lumotda u yo'qoladi
  • 20 modelli ansambl qo'llab-quvvatlash narxini bir necha barobar oshiradi
  • Talqin va nosozlikni tekshirish deyarli imkonsiz bo'ladi

3. Musobaqadan nimani olish kerak

  • Belgi muhandisligi g'oyalari
  • Validatsiya intizomi (ular bunga juda jiddiy)
  • Ansambl tushunchasi (2-3 model bilan)
  • Ma'lumotni chuqur o'rganish odati

4. Amaliy qoida

  1. Sifat farqini SD bilan o'lchang
  2. Farq SD dan kichik bo'lsa — soddasini oling
  3. Qo'llab-quvvatlash narxini hisoblang
  4. Ansamblni faqat sezilarli foyda bersa qo'shing

5. Xulosa

  1. Musobaqa — bitta mezon, amaliyot — ko'p
  2. Kichik farqlar amaliyotda yo'qoladi
  3. Soddalik — uzoq muddatli qiymat
  4. Intizom va g'oyalarni oling, yondashuvni emas

Nimani mustahkamlaydi: 2.5, 2.6-bo'limlar.


Xulosa

Bu darsda model tanlash jarayonini o'rgandik.

Eng muhim uch fikr:

  1. Tanlov bahoni siljitadi. Ko'p nomzoddan eng yaxshisini tanlash — shovqinning eng yaxshi tomonini tanlash 11.9-bob: GridSearchCV.best_score_ optimistik va yakuniy baho emas. Siljimagan baho ichma-ich CV (tashqi sikl baholaydi, ichki sikl tanlaydi) yoki alohida test to'plamidan olinadi.

  2. Farqning haqiqiyligini tekshiring. Bitta 5-fold CV — atigi 5 ta shovqinli son; takrorlangan CV noaniqlikni ancha ishonchli beradi. Modellarni bir xil bo'linishlarda (juftlashtirilgan) solishtiring, farqni ± SD bilan bering va farq 1 SD dan kichik bo'lsa — "farq yo'q" deb qarang. 1-SE qoidasi: eng yaxshining bir standart xatosi ichidagi eng sodda modelni tanlang.

  3. Aniqlik — mezonlardan biri. Yakuniy tanlovga bashorat tezligi, xotira, kalibrlangan ehtimol talabi 14.10-bob, talqin majburiyati va qo'llab-quvvatlash murakkabligi kiradi. Vaznlarni oldindan kelishib, qaror matritsasini hujjatlashtiring — shunda tanlov himoya qilinadigan bo'ladi.

Keyingi darsda xatolar tahlilini o'rganamiz: model qayerda va nega yanglishadi, xatolarni guruhlash va ulardan yaxshilash g'oyalarini chiqarish.

Ulashish:Telegram'da

Izohlar (0)

Izoh yozish uchun kiring.

  • Hozircha izoh yo'q. Birinchi bo'ling!
14.12-dars: Modellarni tanlash va taqqoslash — IlmHamroh