IlmHamroh
Data Science va sun'iy intellekt/Neyron tarmoqlar12/12-dars25 daqiqa
Mundarija (21)

20.12-dars: Amaliyot — to'liq loyiha

20-QISM — NEYRON TARMOQLAR · 12-dars


1. Kirish va motivatsiya

20-qismda neyronning ichidan boshlab, to'liq o'rgatish siklgacha bordik. Endi hammasini bitta loyihada ishlatamiz.

Bu amaliyotning maqsadi — tarmoq qurish emas. Maqsad — tarmoq kerakmi yoki yo'qmi degan savolga javob berish va javobni raqam bilan asoslash. Tabular ma'lumotda bu savol jiddiy: gradient boosting ko'pincha neyron tarmoqdan yaxshiroq ishlaydi, tezroq o'rgatiladi va sozlashga kamroq vaqt oladi.

Shuning uchun loyihani 18-qismdagi tartib bilan olib boramiz: validatsiya dizayni, bazaviy modellar, keyin tarmoq, keyin taqqoslash va faqat oxirida yopiq testni ochish.

Va yana bir narsa: tarmoqning tashxisi. Loss egri chizig'i, qatlamlar statistikasi, o'lgan neyronlar, gradient normalari — bularning hammasini bir joyda ko'rsatamiz, chunki amalda ular alohida emas, birga o'qiladi.

Real vaziyat. Jamoa tabular vazifada ikki hafta tarmoq sozladi va AUC 0.842 ga chiqdi. Keyin HistGradientBoosting ni sukut parametrlari bilan ishga tushirdi — 0.851, 12 soniyada. Tarmoq loyihadan chiqarildi.

Bu darsda to'liq neyron tarmoq loyihasini quramiz.

Bu darsda:

  • Tarmoq kerakmi degan savol
  • To'liq loyiha tartibi
  • Tashxis
  • Taqqoslash
  • Saqlash va topshirish
  • Tuzoqlar
  • Amaliy: yakuniy loyiha

ℹ Misollar real torch/sklearn bilan (Python 3.14, torch 2.14 CPU).


2. Nazariya — chuqur tushuntirish

2.1. Tarmoq kerakmi

text
TARMOQ USTUN BO'LADIGAN HOLATLAR:
  rasm, audio, matn (tuzilmali signal)
  juda katta ma'lumot (> 1M namuna)
  murakkab nochiziqli o'zaro ta'sirlar
  tayyor modellardan transfer learning
  ko'p chiqishli / ko'p vazifali o'rgatish
  embedding kerak bo'lgan vazifalar

GRADIENT BOOSTING USTUN BO'LADIGAN HOLATLAR:
  TABULAR ma'lumot (jadval)
  aralash turlar (son + kategoriya)
  o'rta hajm (1k - 1M)
  yo'qolgan qiymatlar ko'p
  sozlashga vaqt kam

BU TAJRIBA EMAS, ADABIYOTDA O'LCHANGAN:
  Grinsztajn va b. (2022): tabular ma'lumotda daraxtlar ustun
  Shwartz-Ziv & Armon (2022): xuddi shunday xulosa

⭐ HAR DOIM bazaviy sifatida boosting ni qo'ying

Tabular ma'lumotda tarmoq avtomatik tanlov emas — uni isbotlash kerak.

2.2. Loyiha tartibi

text
1. MA'LUMOT: o'qish, nuqsonlar, guruh/vaqt tuzilmasi
2. DIZAYN: CV strategiyasi, test to'plamini QULFLASH
3. METRIKA: vazifadan kelib chiqqan bitta asosiy metrika
4. BAZAVIY: chiziqli model + gradient boosting
5. TARMOQ: kichikdan boshlab, qadamma-qadam
6. TASHXIS: loss egri, qatlam statistikasi, gradient
7. REGULARIZATSIYA: erta to'xtash -> wd -> dropout
8. TAQQOSLASH: juftlashgan, SE bilan
9. TEST: BIR MARTA
10. PAKET: model + masshtablovchi + metama'lumot

HAR QADAMDA: natijani yozing, bitta o'zgarish qiling

Tartib 18-qismdagi bilan bir xil — faqat modellar ro'yxatiga tarmoq qo'shildi.

2.3. Tarmoq uchun tayyorlash

text
MAJBURIY:
  masshtablash (StandardScaler) - tarmoqlar buni TALAB qiladi
  kategoriyalarni kodlash (one-hot yoki embedding)
  yo'qolgan qiymatlarni to'ldirish (NaN tarmoqni buzadi)

TAVSIYA:
  maqsadni ham masshtablash (regressiyada)
  chetdagi qiymatlarni qisish (clip)
  kuchli qiyshiq taqsimotda log

DIQQAT: HAMMASI QUVUR ICHIDA (19-qism)
  masshtablovchi o'QUV to'plamida fit qilinadi
  test to'plamida faqat transform

KATEGORIYA KO'P DARAJALI BO'LSA:
  one-hot -> o'lcham portlashi
  embedding -> tarmoq uchun tabiiy yechim (26-qism)

Masshtablash tarmoq uchun ixtiyoriy emas — daraxtlardan asosiy farqlaridan biri.

2.4. Tashxis ro'yxati

text
O'RGATISHDAN OLDIN:
  [ ] boshlang'ich loss ~ log(K) mi
  [ ] har qatlam std ~1 mi
  [ ] X.std() ~1 mi
  [ ] yorliqlar 0..K-1 mi

O'RGATISH DAVOMIDA:
  [ ] train va val loss ikkalasi ham tushyaptimi
  [ ] NaN yo'qmi
  [ ] gradient normasi barqarormi
  [ ] o'lgan neyron ulushi < 50% mi

O'RGATISHDAN KEYIN:
  [ ] val loss eng past nuqtadan keyin o'sdimi (yodlash)
  [ ] bazaviydan 2*SE dan ortiq yaxshimi
  [ ] bir necha seed da barqarormi
  [ ] kalibratsiya yaxshimi

Tashxisni o'rgatishdan oldin boshlang — bu soatlarni tejaydi.

2.5. Taqqoslash

text
NOTO'G'RI: tarmoq 0.85, boosting 0.84 -> tarmoq g'olib

TO'G'RI:
  bir xil CV bo'linishida (juftlashgan)
  farqning SE sini hisoblab
  bir necha seed bilan
  o'rgatish VAQTINI ham hisobga olib

QAROR MEZONI (18-qism):
  farq > 2*SE bo'lsa - sezilarli
  aks holda - SODDAROQ modelni tanlang

⭐ Teng natijada boosting tanlanadi:
   tezroq, sozlashga oson, tushuntirish qulay

Teng natijada soddaroq model g'olib — bu qoida tarmoqlarga ham tegishli.

2.6. Saqlash va topshirish

text
PAKETGA KIRADIGAN NARSALAR:
  model.state_dict()        <- og'irliklar (butun model EMAS)
  arxitektura ta'rifi       <- qayta qurish uchun
  masshtablovchi            <- MAJBURIY
  belgilar va tartibi
  metrikalar (cv, test)
  versiyalar (torch, sklearn, python)
  seed, davrlar, eng yaxshi davr
  nazorat namunasi + bashoratlar

NIMA UCHUN state_dict:
  torch.save(model) pickle ga tayanadi - sinf yo'li kerak
  state_dict faqat tensorlar - xavfsizroq va ko'chma

YUKLASHDA:
  model = Arxitektura(...)              # qayta quriladi
  model.load_state_dict(holat)
  model.eval()                          # UNUTMANG

state_dict saqlang, butun modelni emas — ko'chma va xavfsizroq.

2.7. Tuzoqlar

Asosiy tuzoqlar: boosting bilan taqqoslamaslik; masshtablovchini saqlamaslik; model.eval() ni yuklashdan keyin unutish; test to'plamini bir necha marta ochish; bitta seed natijasiga tayanish; o'rgatish vaqtini hisobga olmaslik; tashxisni faqat muammo chiqqanda qilish.


3. Tez ma'lumotnoma

python
# 1. dizayn
tr, te = GroupShuffleSplit(1, test_size=0.25, random_state=SEED) ...

# 2. bazaviy
HistGradientBoostingClassifier(random_state=SEED)
LogisticRegression(max_iter=2000)

# 3. tarmoq
sc = StandardScaler().fit(X_tr)
model = torch.nn.Sequential(...)
opt = torch.optim.AdamW(model.parameters(), lr=1e-2, weight_decay=1e-4)

# 4. tashxis
assert abs(boshlangich_loss - np.log(K)) < 0.1
print([A.std().item() for A in qatlam_chiqishlari])

# 5. paket
torch.save({"holat": model.state_dict(),
            "arxitektura": olchamlar,
            "masshtablovchi": sc,
            "metrika": {...},
            "versiyalar": {...}}, "model.pt")

Amaliyot xulosasi

bazaviy: boosting MAJBURIY
tayyorlash: masshtablash MAJBURIY
tashxis: oldin, davomida, keyin
taqqoslash: juftlashgan + SE
paket: state_dict + masshtablovchi + metama'lumot

4. Batafsil misollar

Misollar real torch/sklearn bilan (Python 3.14, torch 2.14 CPU).

Misol 1 — Ma'lumot, dizayn va bazaviy modellar

python
"""1-qadam: vazifani qo'yish va bazaviyni o'rnatish."""

import numpy as np
import pandas as pd
from sklearn.ensemble import HistGradientBoostingClassifier
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import roc_auc_score
from sklearn.model_selection import (GroupKFold, GroupShuffleSplit,
                                     cross_val_score)
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import StandardScaler

SEED = 42
MAQSAD = "javob"
GURUH = "mijoz"


def yarat(seed: int = 7, mijozlar: int = 800) -> pd.DataFrame:
    """Marketing javobi: guruh tuzilmasi va nochiziqli bog'liqlik."""
    rng = np.random.default_rng(seed)
    qatorlar = []
    for m in range(mijozlar):
        imzo = rng.normal(0, 1)
        segment = rng.integers(0, 4)
        for _ in range(int(rng.integers(3, 12))):
            yosh = float(rng.normal(38, 12))
            daromad = float(rng.lognormal(13.2, 0.55))
            aloqa = int(rng.integers(0, 15))
            kun = int(rng.integers(0, 365))
            fasl = np.sin(2 * np.pi * kun / 365)
            kuch = (-1.4
                    + 0.9 * np.tanh((yosh - 40) / 12)
                    + 0.7 * np.tanh((np.log(daromad) - 13.2) / 0.5)
                    + 1.2 * np.tanh((yosh - 40) / 12)
                    * np.tanh((np.log(daromad) - 13.2) / 0.5)
                    + 0.5 * fasl
                    - 0.12 * aloqa
                    + 0.35 * segment
                    + 1.3 * imzo)
            qatorlar.append([m, segment, yosh, daromad, aloqa, kun,
                             int(rng.random() < 1 / (1 + np.exp(-kuch)))])
    return pd.DataFrame(qatorlar,
                        columns=[GURUH, "segment", "yosh", "daromad",
                                 "aloqa", "kun", MAQSAD])


def main() -> None:
    df = yarat()
    belgilar = ["segment", "yosh", "daromad", "aloqa", "kun"]
    X = df[belgilar].to_numpy(dtype=float)
    y = df[MAQSAD].to_numpy()
    guruh = df[GURUH].to_numpy()

    print("=== 1. Ma'lumot ===")
    print(f"  {len(df)} qator, {df[GURUH].nunique()} mijoz, "
          f"{len(belgilar)} belgi")
    print(f"  javob ulushi: {y.mean():.2%}")
    print(f"  {'belgi':<10} {'o_rtacha':>12} {'std':>12} {'noyob':>8}")
    for i, b in enumerate(belgilar):
        print(f"  {b:<10} {X[:, i].mean():>12.2f} {X[:, i].std():>12.2f} "
              f"{len(np.unique(X[:, i])):>8}")

    print("\n=== 2. Dizayn: testni QULFLASH ===")
    tr, te = next(GroupShuffleSplit(1, test_size=0.25, random_state=SEED)
                  .split(X, y, groups=guruh))
    Xtr, ytr, gtr = X[tr], y[tr], guruh[tr]
    Xte, yte = X[te], y[te]
    print(f"  ish to'plami: {len(tr)} qator, "
          f"{len(np.unique(gtr))} mijoz")
    print(f"  TEST (yopiq): {len(te)} qator, "
          f"{len(np.unique(guruh[te]))} mijoz")
    print(f"  mijoz kesishishi: {len(np.intersect1d(gtr, guruh[te]))}")
    print(f"  metrika: roc_auc (nomutanosiblik "
          f"{ytr.mean():.1%} / {1 - ytr.mean():.1%})")

    print("\n=== 3. Bazaviy modellar ===")
    cv = GroupKFold(5)
    modellar = {
        "LogisticRegression": make_pipeline(
            StandardScaler(), LogisticRegression(max_iter=2000)),
        "HistGradientBoosting": HistGradientBoostingClassifier(
            max_iter=250, early_stopping=False, random_state=SEED),
    }
    print(f"  {'model':<24} {'CV AUC':>9} {'std':>8} {'SE':>8}")
    natijalar = {}
    for nom, m in modellar.items():
        b = cross_val_score(m, Xtr, ytr, cv=cv, groups=gtr,
                            scoring="roc_auc")
        natijalar[nom] = b
        se = float(b.std(ddof=1) / np.sqrt(len(b)))
        print(f"  {nom:<24} {b.mean():>9.4f} {b.std():>8.4f} "
              f"{se:>8.4f}")

    print("\n=== 4. Qaror chegarasi ===")
    eng_nom = max(natijalar, key=lambda k: natijalar[k].mean())
    eng = natijalar[eng_nom]
    se = float(eng.std(ddof=1) / np.sqrt(len(eng)))
    print(f"  eng yaxshi bazaviy: {eng_nom} ({eng.mean():.4f})")
    print(f"  SE: {se:.4f}, chegara (2*SE): {2 * se:.4f}")
    print(f"  tarmoq {eng.mean() + 2 * se:.4f} dan yuqori bo'lishi kerak")

    print("\n=== 5. Nochiziqlilik bormi ===")
    lr_b = natijalar["LogisticRegression"].mean()
    gb_b = natijalar["HistGradientBoosting"].mean()
    print(f"  chiziqli:  {lr_b:.4f}")
    print(f"  daraxtlar: {gb_b:.4f}")
    print(f"  farq: {gb_b - lr_b:+.4f}")
    xulosa = ("bor - tarmoq sinashga arziydi" if gb_b - lr_b > 2 * se
              else "yo'q - tarmoq foyda bermasligi mumkin")
    print(f"  nochiziqlilik: {xulosa}")
    print("  ⭐ Tarmoqni sinashdan OLDIN bazaviyni o'rnating")


if __name__ == "__main__":
    main()

Natijaning muhim qismi:

text
=== 1. Ma'lumot ===
  5644 qator, 800 mijoz, 5 belgi
  javob ulushi: 22.54%
  belgi          o_rtacha          std    noyob
  segment            1.55         1.10        4
  yosh              37.84        12.00     5644
  daromad       626020.96    372111.58     5644
  aloqa              7.00         4.31       15
  kun              181.46       106.25      365

=== 2. Dizayn: testni QULFLASH ===
  ish to'plami: 4234 qator, 600 mijoz
  TEST (yopiq): 1410 qator, 200 mijoz
  mijoz kesishishi: 0
  metrika: roc_auc (nomutanosiblik 23.1% / 76.9%)

=== 3. Bazaviy modellar ===
  model                       CV AUC      std       SE
  LogisticRegression          0.6805   0.0406   0.0203
  HistGradientBoosting        0.6491   0.0216   0.0108

=== 4. Qaror chegarasi ===
  eng yaxshi bazaviy: LogisticRegression 0.6805-bob
  SE: 0.0203, chegara (2*SE): 0.0406
  tarmoq 0.7210 dan yuqori bo'lishi kerak

=== 5. Nochiziqlilik bormi ===
  chiziqli:  0.6805
  daraxtlar: 0.6491
  farq: -0.0314
  nochiziqlilik: yo'q - tarmoq foyda bermasligi mumkin
  ⭐ Tarmoqni sinashdan OLDIN bazaviyni o'rnating

Nima ko'rsatdi: 2.1, 2.2-bo'limlar.

Misol 2 — Tarmoqni qurish va tashxis

python
"""2-qadam: tarmoq + o'rgatishdan oldin va davomida tashxis."""

import numpy as np
import pandas as pd
import torch
from sklearn.model_selection import GroupShuffleSplit
from sklearn.preprocessing import StandardScaler

SEED = 42
MAQSAD = "javob"
GURUH = "mijoz"


def yarat(seed: int = 7, mijozlar: int = 800) -> pd.DataFrame:
    rng = np.random.default_rng(seed)
    qatorlar = []
    for m in range(mijozlar):
        imzo = rng.normal(0, 1)
        segment = rng.integers(0, 4)
        for _ in range(int(rng.integers(3, 12))):
            yosh = float(rng.normal(38, 12))
            daromad = float(rng.lognormal(13.2, 0.55))
            aloqa = int(rng.integers(0, 15))
            kun = int(rng.integers(0, 365))
            fasl = np.sin(2 * np.pi * kun / 365)
            kuch = (-1.4 + 0.9 * np.tanh((yosh - 40) / 12)
                    + 0.7 * np.tanh((np.log(daromad) - 13.2) / 0.5)
                    + 1.2 * np.tanh((yosh - 40) / 12)
                    * np.tanh((np.log(daromad) - 13.2) / 0.5)
                    + 0.5 * fasl - 0.12 * aloqa + 0.35 * segment
                    + 1.3 * imzo)
            qatorlar.append([m, segment, yosh, daromad, aloqa, kun,
                             int(rng.random() < 1 / (1 + np.exp(-kuch)))])
    return pd.DataFrame(qatorlar,
                        columns=[GURUH, "segment", "yosh", "daromad",
                                 "aloqa", "kun", MAQSAD])


def tarmoq(kirish, kenglik=64, dropout=0.2, seed=SEED):
    torch.manual_seed(seed)
    return torch.nn.Sequential(
        torch.nn.Linear(kirish, kenglik), torch.nn.ReLU(),
        torch.nn.Dropout(dropout),
        torch.nn.Linear(kenglik, kenglik // 2), torch.nn.ReLU(),
        torch.nn.Linear(kenglik // 2, 2))


def main() -> None:
    df = yarat()
    belgilar = ["segment", "yosh", "daromad", "aloqa", "kun"]
    X = df[belgilar].to_numpy(dtype=float)
    y = df[MAQSAD].to_numpy()
    guruh = df[GURUH].to_numpy()
    tr, te = next(GroupShuffleSplit(1, test_size=0.25, random_state=SEED)
                  .split(X, y, groups=guruh))

    print("=== 1. Masshtablash - tarmoq uchun MAJBURIY ===")
    print(f"  {'belgi':<10} {'xom std':>12} {'masshtablangan':>16}")
    sc = StandardScaler().fit(X[tr])
    Xs = sc.transform(X[tr])
    for i, b in enumerate(belgilar):
        print(f"  {b:<10} {X[tr][:, i].std():>12.2f} "
              f"{Xs[:, i].std():>16.4f}")
    print("  daromad xom holda 10^5 tartibida - tarmoqni buzadi")

    Xtr_t = torch.tensor(Xs, dtype=torch.float32)
    Xte_t = torch.tensor(sc.transform(X[te]), dtype=torch.float32)
    ytr_t = torch.tensor(y[tr], dtype=torch.int64)
    yte_t = torch.tensor(y[te], dtype=torch.int64)

    print("\n=== 2. O'rgatishdan OLDINGI tashxis ===")
    model = tarmoq(len(belgilar))
    kriteriy = torch.nn.CrossEntropyLoss()
    model.eval()
    with torch.no_grad():
        boshlangich = kriteriy(model(Xtr_t), ytr_t).item()
    kutilgan = float(np.log(2))
    print(f"  {'tekshiruv':<28} {'qiymat':>12} {'holat':>8}")
    print(f"  {'boshlang_ich loss':<28} {boshlangich:>12.4f} "
          f"{'OK' if abs(boshlangich - kutilgan) < 0.15 else 'XATO':>8}")
    print(f"  {'kutilgan log(2)':<28} {kutilgan:>12.4f}")
    print(f"  {'X.std()':<28} {Xtr_t.std().item():>12.4f} "
          f"{'OK' if 0.5 < Xtr_t.std() < 2 else 'XATO':>8}")
    print(f"  {'yorliqlar':<28} "
          f"{str(sorted(set(y.tolist()))):>12} "
          f"{'OK' if set(y.tolist()) == {0, 1} else 'XATO':>8}")
    print(f"  {'parametrlar':<28} "
          f"{sum(p.numel() for p in model.parameters()):>12}")

    print("\n=== 3. Qatlamlar bo'ylab signal ===")
    with torch.no_grad():
        A = Xtr_t
        print(f"  {'bosqich':<14} {'shakl':>12} {'std':>10} {'nol %':>8}")
        print(f"  {'kirish':<14} {str(tuple(A.shape)):>12} "
              f"{A.std().item():>10.4f} "
              f"{(A == 0).float().mean().item():>8.1%}")
        for i, qatlam in enumerate(model):
            A = qatlam(A)
            if isinstance(qatlam, (torch.nn.Linear, torch.nn.ReLU)):
                print(f"  {f'{type(qatlam).__name__} {i}':<14} "
                      f"{str(tuple(A.shape)):>12} {A.std().item():>10.4f} "
                      f"{(A == 0).float().mean().item():>8.1%}")

    print("\n=== 4. O'rgatish va kuzatish ===")
    opt = torch.optim.AdamW(model.parameters(), lr=0.01,
                            weight_decay=1e-4)
    g = torch.Generator().manual_seed(SEED)
    print(f"  {'davr':>6} {'o_quv loss':>12} {'grad norma':>12} "
          f"{'o_lgan %':>10} {'test AUC':>10}")
    from sklearn.metrics import roc_auc_score
    for davr in range(1, 61):
        model.train()
        tartib = torch.randperm(len(ytr_t), generator=g)
        jami, n, normalar = 0.0, 0, []
        for boshi in range(0, len(ytr_t), 128):
            idx = tartib[boshi:boshi + 128]
            opt.zero_grad()
            loss = kriteriy(model(Xtr_t[idx]), ytr_t[idx])
            loss.backward()
            normalar.append(float(torch.nn.utils.clip_grad_norm_(
                model.parameters(), 1e9)))
            opt.step()
            jami += loss.item() * len(idx)
            n += len(idx)
        if davr in (1, 5, 15, 30, 60):
            model.eval()
            with torch.no_grad():
                birinchi = torch.relu(model[0](Xtr_t))
                olgan = (birinchi.max(dim=0).values == 0).float().mean()
                p = torch.softmax(model(Xte_t), dim=1)[:, 1].numpy()
            print(f"  {davr:>6} {jami / n:>12.4f} "
                  f"{np.mean(normalar):>12.4f} {olgan.item():>10.1%} "
                  f"{roc_auc_score(yte_t.numpy(), p):>10.4f}")

    print("\n=== 5. O'rgatishdan KEYINGI tashxis ===")
    model.eval()
    with torch.no_grad():
        chiqish = model(Xte_t)
        p = torch.softmax(chiqish, dim=1)[:, 1].numpy()
    print(f"  {'tekshiruv':<28} {'qiymat':>12}")
    print(f"  {'test AUC':<28} "
          f"{roc_auc_score(yte_t.numpy(), p):>12.4f}")
    print(f"  {'bashoratlar o_rtachasi':<28} {p.mean():>12.4f}")
    print(f"  {'haqiqiy ulush':<28} {yte_t.float().mean().item():>12.4f}")
    print(f"  {'p > 0.99 ulushi':<28} {(p > 0.99).mean():>12.4f}")
    print(f"  {'og_irliklar normasi':<28} "
          f"{sum(p_.norm().item() ** 2 for p_ in model.parameters()) ** 0.5:>12.4f}")
    print("  ⭐ Tashxisni oldin, davomida va keyin qiling")


if __name__ == "__main__":
    main()

Natijaning muhim qismi:

text
=== 1. Masshtablash - tarmoq uchun MAJBURIY ===
  belgi           xom std   masshtablangan
  segment            1.09           1.0000
  yosh              12.15           1.0000
  daromad       370727.01           1.0000
  aloqa              4.31           1.0000
  kun              106.25           1.0000
  daromad xom holda 10^5 tartibida - tarmoqni buzadi

=== 2. O'rgatishdan OLDINGI tashxis ===
  tekshiruv                          qiymat    holat
  boshlang_ich loss                  0.7034       OK
  kutilgan log(2)                    0.6931
  X.std()                            1.0000       OK
  yorliqlar                          [0, 1]       OK
  parametrlar                          2530

=== 3. Qatlamlar bo'ylab signal ===
  bosqich               shakl        std    nol %
  kirish            (4234, 5)     1.0000     0.0%
  Linear 0         (4234, 64)     0.6421     0.0%
  ReLU 1           (4234, 64)     0.3611    53.2%
  Linear 3         (4234, 32)     0.2393     0.0%
  ReLU 4           (4234, 32)     0.1462    48.6%
  Linear 5          (4234, 2)     0.0806     0.0%

=== 4. O'rgatish va kuzatish ===
    davr   o_quv loss   grad norma   o_lgan %   test AUC
       1       0.5299       0.1978       1.6%     0.7258
       5       0.4934       0.1516       1.6%     0.7237
      15       0.4876       0.1816       3.1%     0.7232
      30       0.4891       0.1912       3.1%     0.7141
      60       0.4673       0.2022       3.1%     0.6878

=== 5. O'rgatishdan KEYINGI tashxis ===
  tekshiruv                          qiymat
  test AUC                           0.6878
  bashoratlar o_rtachasi             0.2519
  haqiqiy ulush                      0.2085
  p > 0.99 ulushi                    0.0000
  og_irliklar normasi               16.4885
  ⭐ Tashxisni oldin, davomida va keyin qiling

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

Misol 3 — Juftlashgan taqqoslash

python
"""3-qadam: tarmoq bazaviydan yaxshimi - halol taqqoslash."""

import numpy as np
import pandas as pd
import torch
from sklearn.ensemble import HistGradientBoostingClassifier
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import roc_auc_score
from sklearn.model_selection import GroupKFold, GroupShuffleSplit
from sklearn.preprocessing import StandardScaler

SEED = 42
MAQSAD = "javob"
GURUH = "mijoz"


def yarat(seed: int = 7, mijozlar: int = 800) -> pd.DataFrame:
    rng = np.random.default_rng(seed)
    qatorlar = []
    for m in range(mijozlar):
        imzo = rng.normal(0, 1)
        segment = rng.integers(0, 4)
        for _ in range(int(rng.integers(3, 12))):
            yosh = float(rng.normal(38, 12))
            daromad = float(rng.lognormal(13.2, 0.55))
            aloqa = int(rng.integers(0, 15))
            kun = int(rng.integers(0, 365))
            fasl = np.sin(2 * np.pi * kun / 365)
            kuch = (-1.4 + 0.9 * np.tanh((yosh - 40) / 12)
                    + 0.7 * np.tanh((np.log(daromad) - 13.2) / 0.5)
                    + 1.2 * np.tanh((yosh - 40) / 12)
                    * np.tanh((np.log(daromad) - 13.2) / 0.5)
                    + 0.5 * fasl - 0.12 * aloqa + 0.35 * segment
                    + 1.3 * imzo)
            qatorlar.append([m, segment, yosh, daromad, aloqa, kun,
                             int(rng.random() < 1 / (1 + np.exp(-kuch)))])
    return pd.DataFrame(qatorlar,
                        columns=[GURUH, "segment", "yosh", "daromad",
                                 "aloqa", "kun", MAQSAD])


def tarmoq_orgat(Xtr, ytr, Xva, kenglik=64, dropout=0.2, davrlar=55,
                 seed=SEED):
    torch.manual_seed(seed)
    model = torch.nn.Sequential(
        torch.nn.Linear(Xtr.shape[1], kenglik), torch.nn.ReLU(),
        torch.nn.Dropout(dropout),
        torch.nn.Linear(kenglik, kenglik // 2), torch.nn.ReLU(),
        torch.nn.Linear(kenglik // 2, 2))
    opt = torch.optim.AdamW(model.parameters(), lr=0.01,
                            weight_decay=1e-3)
    kriteriy = torch.nn.CrossEntropyLoss()
    Xtr_t = torch.tensor(Xtr, dtype=torch.float32)
    ytr_t = torch.tensor(ytr, dtype=torch.int64)
    g = torch.Generator().manual_seed(seed)
    for _ in range(davrlar):
        model.train()
        tartib = torch.randperm(len(ytr_t), generator=g)
        for boshi in range(0, len(ytr_t), 128):
            idx = tartib[boshi:boshi + 128]
            opt.zero_grad()
            kriteriy(model(Xtr_t[idx]), ytr_t[idx]).backward()
            opt.step()
    model.eval()
    with torch.no_grad():
        return torch.softmax(
            model(torch.tensor(Xva, dtype=torch.float32)),
            dim=1)[:, 1].numpy()


def main() -> None:
    df = yarat()
    belgilar = ["segment", "yosh", "daromad", "aloqa", "kun"]
    X = df[belgilar].to_numpy(dtype=float)
    y = df[MAQSAD].to_numpy()
    guruh = df[GURUH].to_numpy()
    tr, te = next(GroupShuffleSplit(1, test_size=0.25, random_state=SEED)
                  .split(X, y, groups=guruh))
    Xtr, ytr, gtr = X[tr], y[tr], guruh[tr]

    print("=== 1. Juftlashgan CV (bir xil foldlar) ===")
    cv = GroupKFold(5)
    ballar = {"LogReg": [], "HistGB": [], "Tarmoq": []}
    for k, (i_tr, i_va) in enumerate(cv.split(Xtr, ytr, groups=gtr)):
        sc = StandardScaler().fit(Xtr[i_tr])
        A, B = sc.transform(Xtr[i_tr]), sc.transform(Xtr[i_va])
        lr = LogisticRegression(max_iter=2000).fit(A, ytr[i_tr])
        ballar["LogReg"].append(
            roc_auc_score(ytr[i_va], lr.predict_proba(B)[:, 1]))
        gb = HistGradientBoostingClassifier(
            max_iter=150, early_stopping=False,
            random_state=SEED).fit(Xtr[i_tr], ytr[i_tr])
        ballar["HistGB"].append(
            roc_auc_score(ytr[i_va], gb.predict_proba(Xtr[i_va])[:, 1]))
        p = tarmoq_orgat(A, ytr[i_tr], B)
        ballar["Tarmoq"].append(roc_auc_score(ytr[i_va], p))
    print(f"  {'fold':>5} {'LogReg':>9} {'HistGB':>9} {'Tarmoq':>9}")
    for k in range(5):
        print(f"  {k + 1:>5} {ballar['LogReg'][k]:>9.4f} "
              f"{ballar['HistGB'][k]:>9.4f} "
              f"{ballar['Tarmoq'][k]:>9.4f}")

    print("\n=== 2. O'rtachalar ===")
    print(f"  {'model':<10} {'CV AUC':>9} {'std':>8} {'SE':>8}")
    for nom, b in ballar.items():
        b = np.array(b)
        print(f"  {nom:<10} {b.mean():>9.4f} {b.std(ddof=1):>8.4f} "
              f"{b.std(ddof=1) / np.sqrt(5):>8.4f}")

    print("\n=== 3. Juftlashgan farqlar ===")
    print(f"  {'taqqoslash':<20} {'o_rt farq':>11} {'SE':>9} "
          f"{'sezilarlimi':>13}")
    juftlar = [("Tarmoq - HistGB", "Tarmoq", "HistGB"),
               ("Tarmoq - LogReg", "Tarmoq", "LogReg"),
               ("HistGB - LogReg", "HistGB", "LogReg")]
    for nom, a, b in juftlar:
        d = np.array(ballar[a]) - np.array(ballar[b])
        se = float(d.std(ddof=1) / np.sqrt(len(d)))
        print(f"  {nom:<20} {d.mean():>+11.4f} {se:>9.4f} "
              f"{str(abs(d.mean()) > 2 * se):>13}")
    print("  juftlashgan farq fold shovqinini YO'Q QILADI")

    print("\n=== 4. Tarmoqning barqarorligi ===")
    sc = StandardScaler().fit(Xtr)
    i_tr, i_va = next(iter(cv.split(Xtr, ytr, groups=gtr)))
    sc2 = StandardScaler().fit(Xtr[i_tr])
    A, B = sc2.transform(Xtr[i_tr]), sc2.transform(Xtr[i_va])
    seed_ballari = [roc_auc_score(ytr[i_va],
                                  tarmoq_orgat(A, ytr[i_tr], B, seed=s))
                    for s in range(4)]
    seed_ballari = np.array(seed_ballari)
    print(f"  4 ta seed: {seed_ballari.round(4).tolist()}")
    print(f"  o'rtacha {seed_ballari.mean():.4f}, "
          f"std {seed_ballari.std(ddof=1):.4f}")
    print(f"  seed tarqoqligi fold tarqoqligidan "
          f"{'KICHIK' if seed_ballari.std(ddof=1) < np.array(ballar['Tarmoq']).std(ddof=1) else 'KATTA'}")

    print("\n=== 5. Vaqt va murakkablikni ham hisobga olamiz ===")
    jadval = [
        ("LogReg", "5 ta koeffitsiyent", "juda tez", "to'liq"),
        ("HistGB", "~150 daraxt", "tez", "belgi muhimligi"),
        ("Tarmoq", "~4900 parametr", "sekinroq", "qiyin"),
    ]
    print(f"  {'model':<10} {'murakkablik':<20} {'o_rgatish':<11} "
          f"{'tushuntirish'}")
    for a, b, c, d in jadval:
        print(f"  {a:<10} {b:<20} {c:<11} {d}")
    farq = (np.array(ballar["Tarmoq"]) - np.array(ballar["HistGB"]))
    se = float(farq.std(ddof=1) / np.sqrt(5))
    qaror = ("Tarmoq" if farq.mean() > 2 * se else "HistGB")
    print(f"  QAROR: {qaror}")
    print("  ⭐ Teng natijada SODDAROQ model tanlanadi")


if __name__ == "__main__":
    main()

Natijaning muhim qismi:

text
=== 1. Juftlashgan CV (bir xil foldlar) ===
   fold    LogReg    HistGB    Tarmoq
      1    0.7099    0.6588    0.6840
      2    0.6582    0.6492    0.6777
      3    0.7354    0.6881    0.7187
      4    0.6184    0.6153    0.6458
      5    0.6804    0.6672    0.6797

=== 2. O'rtachalar ===
  model         CV AUC      std       SE
  LogReg        0.6805   0.0454   0.0203
  HistGB        0.6557   0.0268   0.0120
  Tarmoq        0.6812   0.0259   0.0116

=== 3. Juftlashgan farqlar ===
  taqqoslash             o_rt farq        SE   sezilarlimi
  Tarmoq - HistGB          +0.0255    0.0034          True
  Tarmoq - LogReg          +0.0007    0.0102         False
  HistGB - LogReg          -0.0248    0.0101          True
  juftlashgan farq fold shovqinini YO'Q QILADI

=== 4. Tarmoqning barqarorligi ===
  4 ta seed: [0.6872, 0.6951, 0.6854, 0.693]
  o'rtacha 0.6902, std 0.0046
  seed tarqoqligi fold tarqoqligidan KICHIK

=== 5. Vaqt va murakkablikni ham hisobga olamiz ===
  model      murakkablik          o_rgatish   tushuntirish
  LogReg     5 ta koeffitsiyent   juda tez    to'liq
  HistGB     ~150 daraxt          tez         belgi muhimligi
  Tarmoq     ~4900 parametr       sekinroq    qiyin
  QAROR: Tarmoq
  ⭐ Teng natijada SODDAROQ model tanlanadi

Nima ko'rsatdi: 2.5-bo'lim.

Misol 4 — Yopiq test, paket va hisobot

python
"""4-qadam: testni bir marta ochish va artefakt tayyorlash."""

import io
import shutil
import sys
import tempfile
from pathlib import Path

import numpy as np
import pandas as pd
import sklearn
import torch
from sklearn.ensemble import HistGradientBoostingClassifier
from sklearn.metrics import (average_precision_score, brier_score_loss,
                             roc_auc_score)
from sklearn.model_selection import GroupShuffleSplit
from sklearn.preprocessing import StandardScaler

SEED = 42
MAQSAD = "javob"
GURUH = "mijoz"
BELGILAR = ["segment", "yosh", "daromad", "aloqa", "kun"]


def yarat(seed: int = 7, mijozlar: int = 800) -> pd.DataFrame:
    rng = np.random.default_rng(seed)
    qatorlar = []
    for m in range(mijozlar):
        imzo = rng.normal(0, 1)
        segment = rng.integers(0, 4)
        for _ in range(int(rng.integers(3, 12))):
            yosh = float(rng.normal(38, 12))
            daromad = float(rng.lognormal(13.2, 0.55))
            aloqa = int(rng.integers(0, 15))
            kun = int(rng.integers(0, 365))
            fasl = np.sin(2 * np.pi * kun / 365)
            kuch = (-1.4 + 0.9 * np.tanh((yosh - 40) / 12)
                    + 0.7 * np.tanh((np.log(daromad) - 13.2) / 0.5)
                    + 1.2 * np.tanh((yosh - 40) / 12)
                    * np.tanh((np.log(daromad) - 13.2) / 0.5)
                    + 0.5 * fasl - 0.12 * aloqa + 0.35 * segment
                    + 1.3 * imzo)
            qatorlar.append([m, segment, yosh, daromad, aloqa, kun,
                             int(rng.random() < 1 / (1 + np.exp(-kuch)))])
    return pd.DataFrame(qatorlar,
                        columns=[GURUH] + BELGILAR + [MAQSAD])


ARXITEKTURA = [len(BELGILAR), 64, 32, 2]


def tarmoq_yasa(olchamlar=ARXITEKTURA, dropout=0.2, seed=SEED):
    torch.manual_seed(seed)
    qatlamlar = []
    for i in range(len(olchamlar) - 1):
        qatlamlar.append(torch.nn.Linear(olchamlar[i], olchamlar[i + 1]))
        if i < len(olchamlar) - 2:
            qatlamlar.append(torch.nn.ReLU())
            if i == 0 and dropout > 0:
                qatlamlar.append(torch.nn.Dropout(dropout))
    return torch.nn.Sequential(*qatlamlar)


def orgat(model, Xtr, ytr, davrlar=90, seed=SEED):
    opt = torch.optim.AdamW(model.parameters(), lr=0.01,
                            weight_decay=1e-3)
    kriteriy = torch.nn.CrossEntropyLoss()
    Xt = torch.tensor(Xtr, dtype=torch.float32)
    yt = torch.tensor(ytr, dtype=torch.int64)
    g = torch.Generator().manual_seed(seed)
    for _ in range(davrlar):
        model.train()
        tartib = torch.randperm(len(yt), generator=g)
        for boshi in range(0, len(yt), 128):
            idx = tartib[boshi:boshi + 128]
            opt.zero_grad()
            kriteriy(model(Xt[idx]), yt[idx]).backward()
            opt.step()
    return model


@torch.no_grad()
def ehtimollik(model, X):
    model.eval()
    return torch.softmax(model(torch.tensor(X, dtype=torch.float32)),
                         dim=1)[:, 1].numpy()


def main() -> None:
    df = yarat()
    X = df[BELGILAR].to_numpy(dtype=float)
    y = df[MAQSAD].to_numpy()
    guruh = df[GURUH].to_numpy()
    tr, te = next(GroupShuffleSplit(1, test_size=0.25, random_state=SEED)
                  .split(X, y, groups=guruh))
    sc = StandardScaler().fit(X[tr])

    print("=== 1. Yopiq testni BIR MARTA ochamiz ===")
    model = orgat(tarmoq_yasa(), sc.transform(X[tr]), y[tr])
    gb = HistGradientBoostingClassifier(
        max_iter=250, early_stopping=False,
        random_state=SEED).fit(X[tr], y[tr])
    p_nn = ehtimollik(model, sc.transform(X[te]))
    p_gb = gb.predict_proba(X[te])[:, 1]
    print(f"  {'model':<10} {'AUC':>9} {'AP':>9} {'Brier':>9}")
    for nom, p in [("Tarmoq", p_nn), ("HistGB", p_gb)]:
        print(f"  {nom:<10} {roc_auc_score(y[te], p):>9.4f} "
              f"{average_precision_score(y[te], p):>9.4f} "
              f"{brier_score_loss(y[te], p):>9.5f}")
    print(f"  ansambl (o'rtacha): "
          f"{roc_auc_score(y[te], (p_nn + p_gb) / 2):.4f}")

    print("\n=== 2. Kalibratsiya ===")
    print(f"  {'chorak':<18} {'o_rt bashorat':>14} {'haqiqiy ulush':>14}")
    chegaralar = np.quantile(p_nn, [0, 0.25, 0.5, 0.75, 1.0])
    for i in range(4):
        maska = (p_nn >= chegaralar[i]) & (p_nn <= chegaralar[i + 1])
        print(f"  {f'[{chegaralar[i]:.2f}, {chegaralar[i+1]:.2f}]':<18} "
              f"{p_nn[maska].mean():>14.4f} {y[te][maska].mean():>14.4f}")

    print("\n=== 3. Paket ===")
    papka = Path(tempfile.mkdtemp(prefix="nn_loyiha_"))
    try:
        nazorat_X = sc.transform(X[te][:40])
        paket = {
            "holat": model.state_dict(),
            "arxitektura": ARXITEKTURA,
            "dropout": 0.2,
            "masshtablovchi": sc,
            "belgilar": BELGILAR,
            "metrika": {"test_auc": round(
                float(roc_auc_score(y[te], p_nn)), 4)},
            "versiyalar": {"torch": torch.__version__,
                           "sklearn": sklearn.__version__,
                           "numpy": np.__version__,
                           "python": sys.version.split()[0]},
            "seed": SEED,
            "davrlar": 90,
            "sana": "2026-09-22",
            "nazorat": {"X": nazorat_X,
                        "p": ehtimollik(model, nazorat_X)},
        }
        yol = papka / "model.pt"
        torch.save(paket, yol)
        print(f"  kalitlar: {sorted(paket)}")
        print(f"  fayl hajmi: {yol.stat().st_size / 1024:.1f} KB")
        holat_hajmi = io.BytesIO()
        torch.save(model.state_dict(), holat_hajmi)
        print(f"  faqat state_dict: {holat_hajmi.tell() / 1024:.1f} KB")

        print("\n=== 4. Yuklash va uch tekshiruv ===")
        yuklangan = torch.load(yol, weights_only=False)
        qayta = tarmoq_yasa(yuklangan["arxitektura"],
                            yuklangan["dropout"])
        qayta.load_state_dict(yuklangan["holat"])
        qayta.eval()
        p_qayta = ehtimollik(qayta, yuklangan["nazorat"]["X"])
        tekshiruvlar = [
            ("belgilar va tartibi",
             yuklangan["belgilar"] == BELGILAR),
            ("torch versiyasi",
             yuklangan["versiyalar"]["torch"] == torch.__version__),
            ("nazorat bashoratlari",
             bool(np.allclose(p_qayta, yuklangan["nazorat"]["p"],
                              atol=1e-6))),
        ]
        print(f"  {'tekshiruv':<24} {'natija':>8}")
        for nom, holat in tekshiruvlar:
            print(f"  {nom:<24} {'OK' if holat else 'XATO':>8}")

        print("\n=== 5. eval() ni unutish narxi ===")
        qayta.train()
        p_xato = ehtimollik(qayta, yuklangan["nazorat"]["X"])
        qayta.eval()
        p_togri = ehtimollik(qayta, yuklangan["nazorat"]["X"])
        print(f"  ehtimollik() ichida eval() bor - shuning uchun")
        print(f"  ikkalasi ham to'g'ri: "
              f"{bool(np.allclose(p_xato, p_togri))}")
        qayta.train()
        with torch.no_grad():
            xom = torch.softmax(
                qayta(torch.tensor(yuklangan["nazorat"]["X"],
                                   dtype=torch.float32)),
                dim=1)[:, 1].numpy()
        print(f"  eval() SIZ (train rejimida) farq: "
              f"{np.abs(xom - p_togri).mean():.4f}")

        print("\n=== 6. Yakuniy hisobot ===")
        print("  VAZIFA: marketing javobini bashorat qilish")
        print("  METRIKA: roc_auc")
        print("  DIZAYN: GroupKFold(5) mijoz bo'yicha; "
              "test GroupShuffleSplit 25%")
        print(f"  NATIJA: tarmoq {roc_auc_score(y[te], p_nn):.4f}, "
              f"HistGB {roc_auc_score(y[te], p_gb):.4f}")
        print(f"  ARXITEKTURA: {' -> '.join(map(str, ARXITEKTURA))}, "
              f"{sum(p.numel() for p in model.parameters())} parametr")
        print(f"  REGULARIZATSIYA: dropout 0.2, weight_decay 1e-3")
        print(f"  JARAYON: seed {SEED}, 90 davr, AdamW lr=0.01")
        print("  CHEKLOVLAR: sun'iy ma'lumot; kategoriya kam darajali;")
        print("    drift tekshirilmagan; tarmoq HistGB dan sezilarli")
        print("    ustun bo'lmasa - HistGB tavsiya qilinadi")
        print("  ⭐ 20-qism yakunlandi: neyrondan artefaktgacha")
    finally:
        shutil.rmtree(papka, ignore_errors=True)


if __name__ == "__main__":
    main()

Natijaning muhim qismi:

text
=== 1. Yopiq testni BIR MARTA ochamiz ===
  model            AUC        AP     Brier
  Tarmoq        0.6962    0.4551   0.14637
  HistGB        0.6742    0.4061   0.16014
  ansambl (o'rtacha): 0.6933

=== 2. Kalibratsiya ===
  chorak              o_rt bashorat  haqiqiy ulush
  [0.00, 0.12]               0.0679         0.1105
  [0.12, 0.18]               0.1539         0.1364
  [0.18, 0.29]               0.2261         0.1960
  [0.29, 0.97]               0.4646         0.3909

=== 3. Paket ===
  kalitlar: ['arxitektura', 'belgilar', 'davrlar', 'dropout', 'holat', 'masshtablovchi', 'metrika', 'nazorat', 'sana', 'seed', 'versiyalar']
  fayl hajmi: 16.5 KB
  faqat state_dict: 12.5 KB

=== 4. Yuklash va uch tekshiruv ===
  tekshiruv                  natija
  belgilar va tartibi            OK
  torch versiyasi                OK
  nazorat bashoratlari           OK

=== 5. eval() ni unutish narxi ===
  ehtimollik() ichida eval() bor - shuning uchun
  ikkalasi ham to'g'ri: True
  eval() SIZ (train rejimida) farq: 0.0310

=== 6. Yakuniy hisobot ===
  VAZIFA: marketing javobini bashorat qilish
  METRIKA: roc_auc
  DIZAYN: GroupKFold(5) mijoz bo'yicha; test GroupShuffleSplit 25%
  NATIJA: tarmoq 0.6962, HistGB 0.6742
  ARXITEKTURA: 5 -> 64 -> 32 -> 2, 2530 parametr
  REGULARIZATSIYA: dropout 0.2, weight_decay 1e-3
  JARAYON: seed 42, 90 davr, AdamW lr=0.01
  CHEKLOVLAR: sun'iy ma'lumot; kategoriya kam darajali;
    drift tekshirilmagan; tarmoq HistGB dan sezilarli
    ustun bo'lmasa - HistGB tavsiya qilinadi
  ⭐ 20-qism yakunlandi: neyrondan artefaktgacha

Nima ko'rsatdi: 2.6-bo'lim.


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

Noto'g'ri fikr To'g'risi
"Tarmoq zamonaviyroq, demak yaxshiroq" Tabularda ko'pincha yo'q
"Masshtablash ixtiyoriy" Tarmoq uchun majburiy
"Tashxis muammo chiqqanda" Oldindan qiling
"0.85 > 0.84 demak g'olib" SE ni hisoblang
"Butun modelni saqlash qulay" state_dict ko'chma
"Masshtablovchini qayta hisoblasa bo'ladi" Yo'q — saqlang
"Bitta yurish yetarli" Bir necha seed
"Tezlik muhim emas" Qaror mezonining bir qismi

6. Keng tarqalgan xatolar va yechimlari

1. Boosting bilan taqqoslamaslik

python
# faqat tarmoq variantlarini sinash              # ⚠️
HistGradientBoostingClassifier(random_state=S)   # ✅ bazaviy

2. Masshtablovchini saqlamaslik

python
torch.save(model.state_dict(), "m.pt")           # ⚠️
torch.save({"holat": ..., "masshtablovchi": sc}) # ✅

3. Yuklashdan keyin eval() yo'q

python
model.load_state_dict(holat); model(X)           # ⚠️
model.load_state_dict(holat); model.eval()       # ✅

4. Testni bir necha marta ochish

python
# har variantni testda tekshirish                # ⚠️
# faqat yakuniy modelni                          # ✅

5. Bitta seed

python
ball = orgat(seed=0)                             # ⚠️
ballar = [orgat(seed=s) for s in range(5)]       # ✅

6. Tashxissiz o'rgatish

python
# to'g'ridan-to'g'ri 500 davr                    # ⚠️
assert abs(boshlangich - np.log(K)) < 0.15       # ✅

7. Juftlashmagan taqqoslash

python
# har modelga boshqa bo'linish                   # ⚠️
# bir xil foldlar, farqning SE si                # ✅

7. Integratsiya — bu bilim qayerda kerak bo'ladi

  • 20.1-20.11-darslar (o'tilgan): Butun qism
  • 18-qism (o'tilgan): Baholash dizayni
  • 19-qism (o'tilgan): Quvur va paket
  • 21-qism: PyTorch to'liq
  • 29-qism: MLOps va deploy

8. Eng yaxshi amaliyotlar

  1. Bazaviy sifatida boosting qo'ying.

  2. Masshtablashni quvurga kiriting.

  3. Tashxisni oldindan qiling.

  4. Juftlashgan taqqoslang.

  5. Testni bir marta oching.

  6. state_dict saqlang.

  7. Masshtablovchini paketga qo'shing.

  8. Bir necha seed bilan tekshiring.


9. Amaliy topshiriq

Vazifa 1: Bashorat qiling

python
1.  # tabularda qaysi model ustun?
2.  # tarmoq qachon ustun?
3.  # bazaviy nima?
4.  # masshtablash majburiymi?
5.  # boshlang'ich loss nima bo'lishi kerak?
6.  # tashxis qachon?
7.  # taqqoslash qanday?
8.  # qaror mezoni?
9.  # nima saqlanadi?
10. # yuklashdan keyin nima?
11. # necha seed?
12. # test necha marta?
Javoblar
  1. Gradient boosting
  2. Rasm, matn, juda katta ma'lumot
  3. Chiziqli + boosting
  4. Ha
  5. log(K)
  6. Oldin, davomida, keyin
  7. Juftlashgan, bir xil foldlarda
  8. Farq > 2*SE
  9. state_dict + masshtablovchi + metama'lumot
  10. eval()
  11. Kamida 5
  12. Bir marta

Vazifa 2: Xatolarni tuzating

python
1.  # faqat tarmoq variantlarini sinash

2.  torch.save(model.state_dict(), "m.pt")

3.  model.load_state_dict(holat); model(X)

4.  ball = orgat(seed=0)

5.  # har modelga boshqa bo'linish
Javoblar
python
1.  HistGradientBoostingClassifier(random_state=SEED)

2.  torch.save({"holat": ..., "masshtablovchi": sc}, "m.pt")

3.  model.load_state_dict(holat); model.eval()

4.  ballar = [orgat(seed=s) for s in range(5)]

5.  # bir xil foldlar, farqning SE si

Vazifa 3: Dizayn

Modellang:

  1. Ma'lumot
  2. Qulflash
  3. Bazaviy
  4. Chegara

Vazifa 4: Tashxis

Modellang:

  1. Masshtab
  2. Oldindan
  3. Signal
  4. Keyin

Vazifa 5: Taqqoslash

Modellang:

  1. Juftlashgan
  2. O'rtachalar
  3. Farqlar
  4. Barqarorlik

Vazifa 6: Paket

Modellang:

  1. Test
  2. Kalibratsiya
  3. Saqlash
  4. Yuklash

Vazifa 7: O'ylash

Rahbaringiz so'radi: "Nega neyron tarmoq ishlatmadik? Hamma ishlatadi-ku." Qanday javob berasiz?

Javob

Qisqa javob: "Ishlatdik va o'lchadik. Tabular ma'lumotda u gradient boosting dan sezilarli ustun chiqmadi, shuning uchun soddaroq modelni tanladik."

Raqam bilan ko'rsating:

Model CV AUC SE O'rgatish vaqti Sozlangan parametr
LogisticRegression 0.78 0.006 0.1 s 0
HistGradientBoosting 0.85 0.007 12 s 3
Neyron tarmoq 0.851 0.009 180 s 7

Juftlashgan farq: +0.001 ± 0.008 — ya'ni shovqin ichida.

Nima uchun bu tasodif emas:

Bu natija adabiyotda takroran o'lchangan:

  • Grinsztajn va b. (2022) — 45 ta tabular ma'lumot to'plamida daraxtlar ustun chiqdi
  • Shwartz-Ziv & Armon (2022) — xuddi shunday xulosa

Sabab — tabular ma'lumotning tabiatida: belgilar bir jinsli emas (yosh va daromad butunlay boshqa narsa), o'zaro ta'sirlar siyrak, va ma'lumotda ko'p tekis bo'lmagan (piecewise) bog'liqlik bor. Daraxtlar aynan shunga mos.

Tarmoqlar esa bir jinsli, tuzilmali signalda ustun: piksellar, tovush namunalari, so'zlar ketma-ketligi.

Qachon fikrni o'zgartiramiz:

  1. Ma'lumot 10 barobar ko'paysa — tarmoqlar hajm bilan yaxshi masshtablanadi
  2. Yangi modallik qo'shilsa — matn izohlari, rasmlar, audio
  3. Embedding kerak bo'lsa — juda ko'p darajali kategoriyalar (masalan 100 000 mahsulot)
  4. Ko'p vazifali o'rgatish kerak bo'lsa — bitta model bir necha chiqish beradi
  5. Transfer learning imkoni paydo bo'lsa

Nima qilganimizni ayting:

"Tarmoqni jiddiy sinadik: arxitekturani qadamma-qadam kattalashtirdik, dropout, weight decay va erta to'xtashni sozladik, 5 ta seed bilan tekshirdik. Natija 0.851 ± 0.009 — HistGB ning 0.850 ± 0.007 idan farqi yo'q. Lekin tarmoq 15 barobar sekin o'rgatiladi, sozlashga ikki hafta ketdi va uni tushuntirish qiyinroq."

Muhim ohang: "tarmoq yomon" demang. "Bu vazifada foyda bermadi, mana o'lchov" deng. Va qachon qayta ko'rib chiqishni aniq ayting.

Qo'shimcha imkoniyat: ansambl. Ikkala modelning bashoratlarini o'rtachalash ko'pincha ikkalasidan ham yaxshiroq chiqadi, chunki ular turli xatolar qiladi. Buni ham o'lchab ko'rish arziydi.

Nimani mustahkamlaydi: 2.1, 2.5-bo'limlar.


Xulosa

Bu darsda to'liq neyron tarmoq loyihasini qurdik.

Eng muhim uch fikr:

  1. Tabular ma'lumotda tarmoq avtomatik tanlov emas. Bazaviy sifatida HistGradientBoosting ni qo'yish majburiy, chunki u ko'pincha tarmoqdan yaxshiroq, tezroq va sozlashga osonroq. Bu shaxsiy fikr emas — 2022-yilda o'nlab ma'lumot to'plamida o'lchangan natija. Tarmoqni tanlash uchun uning ustunligini 2*SE dan ortiq ko'rsatish kerak.

  2. Tashxis o'rgatishdan oldin boshlanadi. Boshlang'ich loss log(K) ga tengmi, har qatlam std si 1 atrofidami, kirish masshtablanganmi, yorliqlar 0..K-1 mi — bu to'rt tekshiruv bir necha soniya oladi va soatlab qidiruvni oldini oladi. O'rgatish davomida esa gradient normasi, o'lgan neyron ulushi va ikkala loss kuzatiladi.

  3. Natija — model emas, artefakt. state_dict, arxitektura ta'rifi, masshtablovchi, belgilar tartibi, metrikalar, versiyalar va nazorat namunasi — hammasi bitta paketda. Yuklashdan keyin model.eval() chaqirish shart, aks holda dropout faol qoladi va bashoratlar tasodifiy bo'ladi.

Bu bilan 20-qism — Neyron tarmoqlar yakunlandi. Biz neyronning ichidan boshlab, to'liq loyihagacha bordik: aktivatsiya, arxitektura, oldinga va orqaga tarqalish, loss, optimizatorlar, boshlanish, normalizatsiya, tensorlar, regularizatsiya va o'rgatish sikli.

Keyingi qismda PyTorchni to'liq o'rganamiz: nn.Module bilan o'z qatlamlaringizni qurish, Dataset va DataLoader, GPU, modelni saqlash va yuklash, va katta loyihalarni tashkil qilish.

Ulashish:Telegram'da

Izohlar (0)

Izoh yozish uchun kiring.

  • Hozircha izoh yo'q. Birinchi bo'ling!
20.12-dars: Amaliyot — to'liq loyiha — IlmHamroh