Mundarija (21)
- 1. Kirish va motivatsiya
- 2. Nazariya — chuqur tushuntirish
- 2.1. Loyiha xaritasi
- 2.2. Ma'lumot quvuri
- 2.3. O'rgatish
- 2.4. Taqqoslash va qaror
- 2.5. Topshirish paketi
- 2.6. Hisobot
- 2.7. Tuzoqlar
- 3. Tez ma'lumotnoma
- 4. Batafsil misollar
- Misol 1 — Konfig, ma'lumot va tekshiruvlar
- Misol 2 — Trainer, callback va seedlar
- Misol 3 — HistGradientBoosting bilan juftlashgan taqqoslash
- Misol 4 — Test, topshirish paketi va hisobot
- 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
21.12-dars: Amaliyot — to'liq PyTorch loyihasi
21-QISM — PYTORCH · 12-dars
1. Kirish va motivatsiya
21-qismda PyTorch ning barcha asosiy qurilish bloklarini ko'rdik: nn.Module, bloklar, Dataset va DataLoader, optimizator va jadvallar, Trainer va callback lar, qurilmalar, checkpoint, regularizatsiya, diagnostika, embedding va loyiha tuzilishi. Endi ularni bitta ishlaydigan loyihaga yig'amiz.
Vazifa — telekom mijozlarining ketishini (churn) bashorat qilish. Ma'lumotda son belgilar (oylik to'lov, xizmat muddati, qo'ng'iroqlar soni) va kategoriyali belgilar (tarif, shahar, qurilma) bor. Shahar — 40 darajali, ya'ni embedding uchun tabiiy nomzod.
Loyiha 18- va 20-qismdagi tartibni saqlaydi: dizayn va testni qulflash, tez tekshiruvlar, o'rgatish, bazaviy bilan juftlashgan taqqoslash, testni bir marta ochish va topshirish paketi. Farq shundaki, endi har bosqich 21-qismda yozilgan qayta ishlatiladigan komponent bilan bajariladi.
Muhim natija oldindan emas, oxirida ma'lum bo'ladi: tarmoq HistGradientBoosting dan yaxshimi yoki yo'qmi — buni juftlashgan taqqoslash hal qiladi. Loyihaning qiymati tarmoqning g'alabasida emas, javobning ishonchliligida.
Real vaziyat. Jamoa birinchi haftada ishlaydigan modelni yozdi. Ikkinchi haftada uni hech kim qayta ola olmadi — konfig yo'q edi, eng yaxshi holat havola bilan saqlangan, masshtablovchi paketga kirmagan. Ikkinchi urinishda xuddi shu model ushbu darsdagi tuzilma bilan qayta yozildi va o'sha kuni ishlab chiqarishga topshirildi.
Bu darsda to'liq PyTorch loyihasini quramiz.
Bu darsda:
- Konfigdan boshlanadigan quvur
- Trainer, callback va eng yaxshi holat
- Tez tekshiruvlar va bitta batch testi
- Bazaviy bilan juftlashgan taqqoslash
- Topshirish paketi va hisobot
- Tuzoqlar
- Amaliy: yakuniy loyiha
ℹ Misollar real torch/sklearn bilan (Python 3.14, torch 2.14 CPU).
2. Nazariya — chuqur tushuntirish
2.1. Loyiha xaritasi
Konfig (frozen dataclass)
|
v
Ma'lumot: yarat -> bo'lish (test QULFLANADI) -> lug'at + masshtab (o'quvda)
|
v
Dataset (son + kat) -> DataLoader (generator bilan)
|
v
Model: Embedding lar + son -> blok -> bosh
|
v
Tez tekshiruvlar + bitta batch testi 21.9-bob
|
v
Trainer + EngYaxshisi (deepcopy) + ErtaToxtash 21.5-bob
|
v
Juftlashgan CV: Tarmoq vs HistGB (18, 20.12)
|
v
Test BIR MARTA -> paket (weights_only) -> Bashoratchi -> jurnalHar bosqich — alohida, sinaladigan komponent; kirish nuqtasi faqat ularni yig'adi.
2.2. Ma'lumot quvuri
1. BO'LISH: o'quv / validatsiya / test (test QULFLANADI)
2. O'QUVDA QURILADI:
lug'atlar (kategoriya -> 1..n, 0 noma'lum)
masshtab (mean, scale)
3. HAMMA TO'PLAMGA QO'LLANADI:
kodla(qiymatlar, lug'at), (x - mean) / scale
4. Dataset: {"son": ..., "kat": ..., "y": ...}
TEKSHIRUV:
val/test da noma'lum kategoriya ulushi
masshtablangan son belgilar ~ N(0, 1)Lug'at va masshtab faqat o'quvda — ular paketga ham aynan shu ko'rinishda kiradi.
2.3. O'rgatish
Trainer:
forward_batch(batch) -> model(batch["son"], batch["kat"]), batch["y"]
metrika: AUC - butun davr bashoratlarida 21.5-bob
callback: EngYaxshisi(monitor="val_auc", rejim="max", sabr=8)
OPTIMIZATOR:
AdamW, bias/norm decay siz 21.4-bob
CosineAnnealingLR, davr birligida
BIR NECHA SEED:
3-5 seed -> o'rtacha +- std Eng yaxshi holat deepcopy bilan va oxirida tiklanadi — aks holda hisobot oxirgi davrniki bo'ladi.
2.4. Taqqoslash va qaror
BIR XIL FOLDLAR:
LogisticRegression (one-hot) - eng sodda bazaviy
HistGradientBoosting(categorical_features=...)
TabularTarmoq
JUFTLASHGAN FARQ:
d_k = AUC_tarmoq,k - AUC_hgb,k
qaror: |mean(d)| > 2 * SE(d) bo'lsa - sezilarli
QOIDA: eng yaxshisidan SEZILARLI yomon bo'lmagan ENG SODDA model
soddalik tartibi: LogReg < HistGB < TarmoqQarorni juftlashgan farq va uning SE si belgilaydi — bitta AUC emas.
2.5. Topshirish paketi
paket = {
"format": 1,
"konfig": asdict(konfig),
"holat": model.state_dict(),
"kirish": {"son": [...], "kat": [...],
"mean": [...], "scale": [...],
"lugatlar": {"tarif": {...}, ...}},
"metrika": {"val_auc": ..., "test_auc": ...},
"versiyalar": {"torch": str(torch.__version__), ...},
"nazorat": {"son": tensor, "kat": tensor, "p": tensor},
}
torch.save(paket, yol)
torch.load(yol, weights_only=True) # HAMMASI xavfsiz turlarPaketda pickle qilinadigan obyekt yo'q — lug'at, masshtab, versiya — hammasi son va satr.
2.6. Hisobot
VAZIFA, METRIKA, DIZAYN (bo'lish, CV)
NATIJA: tarmoq o'rtacha +- std (seedlar), HistGB, juftlashgan farq +- SE
QAROR va uning asosi
TEST: bir marta ochilgan natija
PAKET: format, hajm, nazorat tekshiruvi
CHEKLOVLAR: sun'iy ma'lumot, vaqt drift i tekshirilmagan,
yangi kategoriyalar 0 ga tushadiHisobotda qaror va uning dalili — faqat eng yaxshi raqam emas.
2.7. Tuzoqlar
Asosiy tuzoqlar: testni tarmoq tanlashda ishlatish; lug'at va masshtabni butun ma'lumotda qurish; eng yaxshi holatni havola bilan saqlash; bitta seed natijasini hisobot qilish; bazaviy bilan boshqa foldlarda taqqoslash; paketga sklearn obyekti yoki TorchVersion qo'yish; yuklashdan keyin eval() ni unutish; tekshiruvlarsiz uzoq o'rgatish.
3. Tez ma'lumotnoma
k = Konfig()
seed_everything(k.seed)
tayyor = Tayyorlovchi(SON, KAT).fit(df.iloc[tr])
dl_tr = DataLoader(tayyor.dataset(df.iloc[tr], y[tr]), k.batch,
shuffle=True, generator=torch.Generator().manual_seed(k.seed))
model = TabularTarmoq(tayyor.hajmlar(), k)
tekshir(model, dl_tr) # 21.9
tr_ = Trainer(model, opt, kriteriy, {"auc": auc},
callbacklar=[EngYaxshisi("val_auc", "max", k.sabr)])
tarix = tr_.fit(dl_tr, dl_va, k.davrlar)
paket = paket_yasa(model, k, tayyor, metrika, nazorat)
torch.save(paket, yol); Bashoratchi(yol)Amaliyot xulosasi
konfig -> ma'lumot (o'quvda lug'at/masshtab) -> Dataset
model: embedding + son -> blok -> bosh
tekshiruv -> Trainer + EngYaxshisi -> seedlar
juftlashgan CV vs HistGB -> qaror
test bir marta -> paket (weights_only) -> hisobot4. Batafsil misollar
Misollar real torch/sklearn bilan (Python 3.14, torch 2.14 CPU).
Misol 1 — Konfig, ma'lumot va tekshiruvlar
"""1-qadam: quvurni qurish va o'rgatishdan oldin tekshirish."""
import math
import random
from dataclasses import dataclass
import numpy as np
import pandas as pd
import torch
import torch.nn as nn
from sklearn.model_selection import train_test_split
from torch.utils.data import DataLoader, Dataset
SON = ["tolov", "muddat", "qongiroq", "internet_gb", "shikoyat"]
KAT = ["tarif", "shahar", "qurilma"]
@dataclass(frozen=True)
class Konfig:
seed: int = 42
lr: float = 3e-3
batch: int = 128
davrlar: int = 40
yashirin: int = 64
dropout: float = 0.1
weight_decay: float = 1e-4
sabr: int = 8
def seed_everything(seed):
random.seed(seed)
np.random.seed(seed)
torch.manual_seed(seed)
def yarat(n=7000, seed=7):
rng = np.random.default_rng(seed)
tarif = rng.integers(0, 5, n)
shahar = rng.integers(0, 40, n)
qurilma = rng.integers(0, 8, n)
shahar_t = rng.normal(0, 0.7, 40)
tarif_t = np.array([0.6, 0.2, 0.0, -0.4, -0.8])
tolov = rng.lognormal(4.0, 0.4, n)
muddat = rng.gamma(2.0, 12.0, n)
qongiroq = rng.poisson(30, n).astype(float)
internet = rng.lognormal(2.5, 0.8, n)
shikoyat = rng.poisson(0.6, n).astype(float)
z = (np.log(tolov) - 4.0) / 0.4
kuch = (-1.4 + tarif_t[tarif] + shahar_t[shahar]
+ 0.6 * z ** 2 # U-shakl: juda arzon va
- 1.4 * (muddat > 24) # juda qimmat tarif
+ 1.0 * (muddat < 3) # yangi mijoz
+ 0.9 * shikoyat * (tarif >= 3) # o'zaro ta'sir
+ 0.8 * (internet > 30) * (qurilma < 2))
y = (kuch + rng.logistic(0, 1, n) > 0).astype(np.int64)
df = pd.DataFrame({"tolov": tolov, "muddat": muddat,
"qongiroq": qongiroq, "internet_gb": internet,
"shikoyat": shikoyat, "tarif": tarif,
"shahar": shahar, "qurilma": qurilma})
return df, y
class Tayyorlovchi:
def fit(self, df):
self.mean = df[SON].mean().to_numpy()
self.scale = df[SON].std(ddof=0).to_numpy()
self.lugatlar = {c: {int(k): i + 1 for i, k in
enumerate(np.unique(df[c]))} for c in KAT}
return self
def hajmlar(self):
return [len(self.lugatlar[c]) + 1 for c in KAT]
def dataset(self, df, y):
son = (df[SON].to_numpy() - self.mean) / self.scale
kat = np.column_stack([[self.lugatlar[c].get(int(v), 0)
for v in df[c]] for c in KAT])
return JadvalDataset(son, kat, y)
class JadvalDataset(Dataset):
def __init__(self, son, kat, y):
self.son = torch.as_tensor(son, dtype=torch.float32)
self.kat = torch.as_tensor(kat, dtype=torch.int64)
self.y = torch.as_tensor(y, dtype=torch.int64)
def __len__(self):
return len(self.y)
def __getitem__(self, i):
return {"son": self.son[i], "kat": self.kat[i], "y": self.y[i]}
class TabularTarmoq(nn.Module):
def __init__(self, hajmlar, k):
super().__init__()
d = [min(16, (n + 1) // 2) for n in hajmlar]
self.embler = nn.ModuleList(
[nn.Embedding(n, di) for n, di in zip(hajmlar, d)])
self.tarmoq = nn.Sequential(
nn.Linear(len(SON) + sum(d), k.yashirin), nn.ReLU(),
nn.Dropout(k.dropout), nn.Linear(k.yashirin, k.yashirin),
nn.ReLU(), nn.Linear(k.yashirin, 2))
def forward(self, son, kat):
e = [emb(kat[:, i]) for i, emb in enumerate(self.embler)]
return self.tarmoq(torch.cat([son] + e, dim=1))
def main() -> None:
k = Konfig()
seed_everything(k.seed)
df, y = yarat()
print("=== 1. Ma'lumot va dizayn ===")
idx = np.arange(len(df))
ish, te = train_test_split(idx, test_size=0.2, random_state=k.seed,
stratify=y)
tr, va = train_test_split(ish, test_size=0.25, random_state=k.seed,
stratify=y[ish])
print(f" jami {len(df)}, ketish ulushi {y.mean():.3f}")
print(f" o'quv {len(tr)}, validatsiya {len(va)}, TEST (qulf) {len(te)}")
print(f" kategoriyalar: "
f"{ {c: int(df[c].nunique()) for c in KAT} }")
print("\n=== 2. O'quvda qurilgan tayyorlash ===")
tayyor = Tayyorlovchi().fit(df.iloc[tr])
ds_tr = tayyor.dataset(df.iloc[tr], y[tr])
ds_va = tayyor.dataset(df.iloc[va], y[va])
print(f" lug'at hajmlari (+0 noma'lum): {tayyor.hajmlar()}")
print(f" o'quv son belgilar: o'rtacha "
f"{ds_tr.son.mean(0).abs().max().item():.4f} (maks |.|), "
f"std {ds_tr.son.std(0).mean().item():.4f}")
nomalum = (ds_va.kat == 0).float().mean(0)
print(f" val da noma'lum kategoriya ulushi: "
f"{[round(v, 4) for v in nomalum.tolist()]}")
print("\n=== 3. Tez tekshiruvlar 21.9-bob ===")
g = torch.Generator().manual_seed(k.seed)
dl = DataLoader(ds_tr, batch_size=k.batch, shuffle=True, generator=g)
model = TabularTarmoq(tayyor.hajmlar(), k)
b = next(iter(dl))
model.eval()
with torch.no_grad():
chiqish = model(b["son"], b["kat"])
boshl = nn.functional.cross_entropy(chiqish, b["y"]).item()
param = sum(p.numel() for p in model.parameters())
tekshiruvlar = [
("chiqish shakli", tuple(chiqish.shape),
tuple(chiqish.shape) == (k.batch, 2)),
("yorliqlar", sorted(set(b["y"].tolist())),
set(b["y"].tolist()) <= {0, 1}),
("boshlang'ich loss", round(boshl, 3),
abs(boshl - math.log(2)) < 0.3),
("parametrlar", param, param > 0),
]
print(f" {'tekshiruv':<20} {'qiymat':>16} {'holat':>7}")
for nom, q, ok in tekshiruvlar:
print(f" {nom:<20} {str(q):>16} {'OK' if ok else 'XATO':>7}")
print("\n=== 4. Bitta batch testi ===")
model.train()
for m in model.modules():
if isinstance(m, nn.Dropout):
m.p = 0.0
opt = torch.optim.Adam(model.parameters(), lr=1e-2)
kichik = {kk: v[:32] for kk, v in b.items()}
for _ in range(300):
opt.zero_grad()
loss = nn.functional.cross_entropy(
model(kichik["son"], kichik["kat"]), kichik["y"])
loss.backward()
opt.step()
print(f" 32 namuna, 300 qadam: loss {loss.item():.5f}")
print(f" natija: {'OTDI' if loss.item() < 0.01 else 'OTMADI'}")
print(" ⭐ Quvur tayyor - o'rgatishga o'tish mumkin")
if __name__ == "__main__":
main()Natijaning muhim qismi:
=== 1. Ma'lumot va dizayn ===
jami 7000, ketish ulushi 0.279
o'quv 4200, validatsiya 1400, TEST (qulf) 1400
kategoriyalar: {'tarif': 5, 'shahar': 40, 'qurilma': 8}
=== 2. O'quvda qurilgan tayyorlash ===
lug'at hajmlari (+0 noma'lum): [6, 41, 9]
o'quv son belgilar: o'rtacha 0.0000 (maks |.|), std 1.0001
val da noma'lum kategoriya ulushi: [0.0, 0.0, 0.0]
=== 3. Tez tekshiruvlar 21.9-bob ===
tekshiruv qiymat holat
chiqish shakli (128, 2) OK
yorliqlar [0, 1] OK
boshlang'ich loss 0.743 OK
parametrlar 6929 OK
=== 4. Bitta batch testi ===
32 namuna, 300 qadam: loss 0.00000
natija: OTDI
⭐ Quvur tayyor - o'rgatishga o'tish mumkinNima ko'rsatdi: 2.1, 2.2-bo'limlar.
Misol 2 — Trainer, callback va seedlar
"""2-qadam: o'rgatish, eng yaxshi holat va bir necha seed."""
import copy
import random
from dataclasses import dataclass, replace
import numpy as np
import pandas as pd
import torch
import torch.nn as nn
from sklearn.metrics import roc_auc_score
from sklearn.model_selection import train_test_split
from torch.utils.data import DataLoader, Dataset
SON = ["tolov", "muddat", "qongiroq", "internet_gb", "shikoyat"]
KAT = ["tarif", "shahar", "qurilma"]
@dataclass(frozen=True)
class Konfig:
seed: int = 42
lr: float = 3e-3
batch: int = 128
davrlar: int = 40
yashirin: int = 64
dropout: float = 0.1
weight_decay: float = 1e-4
sabr: int = 8
def seed_everything(seed):
random.seed(seed)
np.random.seed(seed)
torch.manual_seed(seed)
def yarat(n=7000, seed=7):
rng = np.random.default_rng(seed)
tarif = rng.integers(0, 5, n)
shahar = rng.integers(0, 40, n)
qurilma = rng.integers(0, 8, n)
shahar_t = rng.normal(0, 0.7, 40)
tarif_t = np.array([0.6, 0.2, 0.0, -0.4, -0.8])
tolov = rng.lognormal(4.0, 0.4, n)
muddat = rng.gamma(2.0, 12.0, n)
qongiroq = rng.poisson(30, n).astype(float)
internet = rng.lognormal(2.5, 0.8, n)
shikoyat = rng.poisson(0.6, n).astype(float)
z = (np.log(tolov) - 4.0) / 0.4
kuch = (-1.4 + tarif_t[tarif] + shahar_t[shahar]
+ 0.6 * z ** 2 # U-shakl: juda arzon va
- 1.4 * (muddat > 24) # juda qimmat tarif
+ 1.0 * (muddat < 3) # yangi mijoz
+ 0.9 * shikoyat * (tarif >= 3) # o'zaro ta'sir
+ 0.8 * (internet > 30) * (qurilma < 2))
y = (kuch + rng.logistic(0, 1, n) > 0).astype(np.int64)
df = pd.DataFrame({"tolov": tolov, "muddat": muddat,
"qongiroq": qongiroq, "internet_gb": internet,
"shikoyat": shikoyat, "tarif": tarif,
"shahar": shahar, "qurilma": qurilma})
return df, y
class Tayyorlovchi:
def fit(self, df):
self.mean = df[SON].mean().to_numpy()
self.scale = df[SON].std(ddof=0).to_numpy()
self.lugatlar = {c: {int(k): i + 1 for i, k in
enumerate(np.unique(df[c]))} for c in KAT}
return self
def hajmlar(self):
return [len(self.lugatlar[c]) + 1 for c in KAT]
def dataset(self, df, y):
son = (df[SON].to_numpy() - self.mean) / self.scale
kat = np.column_stack([[self.lugatlar[c].get(int(v), 0)
for v in df[c]] for c in KAT])
return JadvalDataset(son, kat, y)
class JadvalDataset(Dataset):
def __init__(self, son, kat, y):
self.son = torch.as_tensor(son, dtype=torch.float32)
self.kat = torch.as_tensor(kat, dtype=torch.int64)
self.y = torch.as_tensor(y, dtype=torch.int64)
def __len__(self):
return len(self.y)
def __getitem__(self, i):
return {"son": self.son[i], "kat": self.kat[i], "y": self.y[i]}
class TabularTarmoq(nn.Module):
def __init__(self, hajmlar, k):
super().__init__()
d = [min(16, (n + 1) // 2) for n in hajmlar]
self.embler = nn.ModuleList(
[nn.Embedding(n, di) for n, di in zip(hajmlar, d)])
self.tarmoq = nn.Sequential(
nn.Linear(len(SON) + sum(d), k.yashirin), nn.ReLU(),
nn.Dropout(k.dropout), nn.Linear(k.yashirin, k.yashirin),
nn.ReLU(), nn.Linear(k.yashirin, 2))
def forward(self, son, kat):
e = [emb(kat[:, i]) for i, emb in enumerate(self.embler)]
return self.tarmoq(torch.cat([son] + e, dim=1))
class EngYaxshisi:
def __init__(self, monitor, sabr):
self.monitor, self.sabr = monitor, sabr
def fit_boshi(self, tr):
self.eng, self.holat, self.davr, self.hisob = -np.inf, None, 0, 0
def davr_oxiri(self, tr, davr, log):
if log[self.monitor] > self.eng:
self.eng, self.davr, self.hisob = log[self.monitor], davr, 0
self.holat = copy.deepcopy(tr.model.state_dict())
else:
self.hisob += 1
tr.toxtash = self.hisob >= self.sabr
def fit_oxiri(self, tr):
tr.model.load_state_dict(self.holat)
class Trainer:
def __init__(self, model, opt, sched, callbacklar):
self.model, self.opt, self.sched = model, opt, sched
self.callbacklar, self.tarix, self.toxtash = callbacklar, [], False
def _davr(self, dl, orgatish):
self.model.train(orgatish)
jami, n, plar, ylar = 0.0, 0, [], []
with torch.set_grad_enabled(orgatish):
for b in dl:
if orgatish:
self.opt.zero_grad()
ch = self.model(b["son"], b["kat"])
loss = nn.functional.cross_entropy(ch, b["y"])
if orgatish:
loss.backward()
self.opt.step()
jami += loss.item() * len(b["y"])
n += len(b["y"])
plar.append(torch.softmax(ch.detach(), 1)[:, 1])
ylar.append(b["y"])
return {"loss": jami / n,
"auc": roc_auc_score(torch.cat(ylar).numpy(),
torch.cat(plar).numpy())}
def fit(self, dl_tr, dl_va, davrlar):
for cb in self.callbacklar:
cb.fit_boshi(self)
for davr in range(1, davrlar + 1):
t, v = self._davr(dl_tr, True), self._davr(dl_va, False)
log = {"train_loss": t["loss"], "val_loss": v["loss"],
"val_auc": v["auc"]}
self.sched.step()
self.tarix.append({"davr": davr, **log})
for cb in self.callbacklar:
cb.davr_oxiri(self, davr, log)
if self.toxtash:
break
for cb in self.callbacklar:
cb.fit_oxiri(self)
return pd.DataFrame(self.tarix)
def guruhlar(model, wd):
decay = [p for p in model.parameters() if p.ndim >= 2]
siz = [p for p in model.parameters() if p.ndim < 2]
return [{"params": decay, "weight_decay": wd},
{"params": siz, "weight_decay": 0.0}]
def bitta_yurish(k, tayyor, ds_tr, ds_va):
seed_everything(k.seed)
model = TabularTarmoq(tayyor.hajmlar(), k)
opt = torch.optim.AdamW(guruhlar(model, k.weight_decay), lr=k.lr)
sched = torch.optim.lr_scheduler.CosineAnnealingLR(opt, k.davrlar)
g = torch.Generator().manual_seed(k.seed)
dl_tr = DataLoader(ds_tr, batch_size=k.batch, shuffle=True,
generator=g)
dl_va = DataLoader(ds_va, batch_size=1024)
eyx = EngYaxshisi("val_auc", k.sabr)
tarix = Trainer(model, opt, sched, [eyx]).fit(dl_tr, dl_va, k.davrlar)
return model, tarix, eyx
def main() -> None:
k = Konfig()
df, y = yarat()
idx = np.arange(len(df))
ish, te = train_test_split(idx, test_size=0.2, random_state=k.seed,
stratify=y)
tr, va = train_test_split(ish, test_size=0.25, random_state=k.seed,
stratify=y[ish])
tayyor = Tayyorlovchi().fit(df.iloc[tr])
ds_tr = tayyor.dataset(df.iloc[tr], y[tr])
ds_va = tayyor.dataset(df.iloc[va], y[va])
print("=== 1. Bitta yurish ===")
model, tarix, eyx = bitta_yurish(k, tayyor, ds_tr, ds_va)
print(f" {len(tarix)} davr, eng yaxshi val_auc {eyx.eng:.4f} "
f"({eyx.davr}-davr)")
korsat = tarix[tarix["davr"].isin([1, 5, 10, eyx.davr,
len(tarix)])]
print(korsat.round(4).to_string(index=False))
print("\n=== 2. Tiklangan holat tekshiruvi ===")
model.eval()
with torch.no_grad():
p = torch.softmax(model(ds_va.son, ds_va.kat), 1)[:, 1].numpy()
tiklangan = roc_auc_score(y[va], p)
print(f" tiklangan model val_auc: {tiklangan:.4f}")
print(f" callback eslagan: {eyx.eng:.4f}")
print(f" mos: {abs(tiklangan - eyx.eng) < 1e-9}")
print("\n=== 3. Bir necha seed ===")
ballar = []
for s in range(4):
_, _, e = bitta_yurish(replace(k, seed=s), tayyor, ds_tr, ds_va)
ballar.append(e.eng)
ballar = np.array(ballar)
print(f" 4 seed val_auc: {ballar.round(4).tolist()}")
print(f" o'rtacha {ballar.mean():.4f} +- {ballar.std(ddof=1):.4f}")
print(" ⭐ Hisobotga o'rtacha va std yoziladi")
if __name__ == "__main__":
main()Natijaning muhim qismi:
=== 1. Bitta yurish ===
17 davr, eng yaxshi val_auc 0.7765 (9-davr)
davr train_loss val_loss val_auc
1 0.6052 0.5670 0.6571
5 0.4939 0.4973 0.7615
9 0.4496 0.4856 0.7765
10 0.4423 0.4934 0.7724
17 0.4076 0.5146 0.7603
=== 2. Tiklangan holat tekshiruvi ===
tiklangan model val_auc: 0.7765
callback eslagan: 0.7765
mos: True
=== 3. Bir necha seed ===
4 seed val_auc: [0.7814, 0.7707, 0.7668, 0.7717]
o'rtacha 0.7727 +- 0.0062
⭐ Hisobotga o'rtacha va std yoziladiNima ko'rsatdi: 2.3-bo'lim.
Misol 3 — HistGradientBoosting bilan juftlashgan taqqoslash
"""3-qadam: bir xil foldlarda tarmoq va boosting (real torch/sklearn)."""
import random
from dataclasses import dataclass
import numpy as np
import pandas as pd
import torch
import torch.nn as nn
from sklearn.ensemble import HistGradientBoostingClassifier
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import roc_auc_score
from sklearn.model_selection import StratifiedKFold, train_test_split
from sklearn.compose import ColumnTransformer
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import OneHotEncoder, StandardScaler
from torch.utils.data import DataLoader, Dataset
SON = ["tolov", "muddat", "qongiroq", "internet_gb", "shikoyat"]
KAT = ["tarif", "shahar", "qurilma"]
@dataclass(frozen=True)
class Konfig:
seed: int = 42
lr: float = 3e-3
batch: int = 128
davrlar: int = 25
yashirin: int = 64
dropout: float = 0.1
weight_decay: float = 1e-4
def seed_everything(seed):
random.seed(seed)
np.random.seed(seed)
torch.manual_seed(seed)
def yarat(n=7000, seed=7):
rng = np.random.default_rng(seed)
tarif = rng.integers(0, 5, n)
shahar = rng.integers(0, 40, n)
qurilma = rng.integers(0, 8, n)
shahar_t = rng.normal(0, 0.7, 40)
tarif_t = np.array([0.6, 0.2, 0.0, -0.4, -0.8])
tolov = rng.lognormal(4.0, 0.4, n)
muddat = rng.gamma(2.0, 12.0, n)
qongiroq = rng.poisson(30, n).astype(float)
internet = rng.lognormal(2.5, 0.8, n)
shikoyat = rng.poisson(0.6, n).astype(float)
z = (np.log(tolov) - 4.0) / 0.4
kuch = (-1.4 + tarif_t[tarif] + shahar_t[shahar]
+ 0.6 * z ** 2 # U-shakl: juda arzon va
- 1.4 * (muddat > 24) # juda qimmat tarif
+ 1.0 * (muddat < 3) # yangi mijoz
+ 0.9 * shikoyat * (tarif >= 3) # o'zaro ta'sir
+ 0.8 * (internet > 30) * (qurilma < 2))
y = (kuch + rng.logistic(0, 1, n) > 0).astype(np.int64)
df = pd.DataFrame({"tolov": tolov, "muddat": muddat,
"qongiroq": qongiroq, "internet_gb": internet,
"shikoyat": shikoyat, "tarif": tarif,
"shahar": shahar, "qurilma": qurilma})
return df, y
class JadvalDataset(Dataset):
def __init__(self, son, kat, y):
self.son = torch.as_tensor(son, dtype=torch.float32)
self.kat = torch.as_tensor(kat, dtype=torch.int64)
self.y = torch.as_tensor(y, dtype=torch.int64)
def __len__(self):
return len(self.y)
def __getitem__(self, i):
return {"son": self.son[i], "kat": self.kat[i], "y": self.y[i]}
class TabularTarmoq(nn.Module):
def __init__(self, hajmlar, k):
super().__init__()
d = [min(16, (n + 1) // 2) for n in hajmlar]
self.embler = nn.ModuleList(
[nn.Embedding(n, di) for n, di in zip(hajmlar, d)])
self.tarmoq = nn.Sequential(
nn.Linear(len(SON) + sum(d), k.yashirin), nn.ReLU(),
nn.Dropout(k.dropout), nn.Linear(k.yashirin, k.yashirin),
nn.ReLU(), nn.Linear(k.yashirin, 2))
def forward(self, son, kat):
e = [emb(kat[:, i]) for i, emb in enumerate(self.embler)]
return self.tarmoq(torch.cat([son] + e, dim=1))
def tarmoq_auc(df, y, tr, va, k):
mean = df.iloc[tr][SON].mean().to_numpy()
scale = df.iloc[tr][SON].std(ddof=0).to_numpy()
lug = {c: {int(v): i + 1 for i, v in enumerate(np.unique(df.iloc[tr][c]))}
for c in KAT}
def ds(idx):
son = (df.iloc[idx][SON].to_numpy() - mean) / scale
kat = np.column_stack([[lug[c].get(int(v), 0)
for v in df.iloc[idx][c]] for c in KAT])
return JadvalDataset(son, kat, y[idx])
d_tr, d_va = ds(tr), ds(va)
seed_everything(k.seed)
model = TabularTarmoq([len(lug[c]) + 1 for c in KAT], k)
opt = torch.optim.AdamW(model.parameters(), lr=k.lr,
weight_decay=k.weight_decay)
sched = torch.optim.lr_scheduler.CosineAnnealingLR(opt, k.davrlar)
g = torch.Generator().manual_seed(k.seed)
dl = DataLoader(d_tr, batch_size=k.batch, shuffle=True, generator=g)
for _ in range(k.davrlar):
model.train()
for b in dl:
opt.zero_grad()
nn.functional.cross_entropy(model(b["son"], b["kat"]),
b["y"]).backward()
opt.step()
sched.step()
model.eval()
with torch.no_grad():
p = torch.softmax(model(d_va.son, d_va.kat), 1)[:, 1].numpy()
return roc_auc_score(y[va], p)
def main() -> None:
k = Konfig()
df, y = yarat()
idx = np.arange(len(df))
ish, _ = train_test_split(idx, test_size=0.2, random_state=k.seed,
stratify=y)
df_ish, y_ish = df.iloc[ish].reset_index(drop=True), y[ish]
cv = StratifiedKFold(4, shuffle=True, random_state=0)
print("=== 1. Uch model, bir xil foldlar ===")
natija = {"LogReg": [], "HistGB": [], "Tarmoq": []}
for f, (tr, va) in enumerate(cv.split(df_ish, y_ish)):
lr = make_pipeline(
ColumnTransformer([("son", StandardScaler(), SON),
("kat", OneHotEncoder(
handle_unknown="ignore"), KAT)]),
LogisticRegression(max_iter=2000))
lr.fit(df_ish.iloc[tr], y_ish[tr])
natija["LogReg"].append(roc_auc_score(
y_ish[va], lr.predict_proba(df_ish.iloc[va])[:, 1]))
gb = HistGradientBoostingClassifier(
max_iter=200, learning_rate=0.05, categorical_features=KAT,
early_stopping=False, random_state=0)
gb.fit(df_ish.iloc[tr], y_ish[tr])
natija["HistGB"].append(roc_auc_score(
y_ish[va], gb.predict_proba(df_ish.iloc[va])[:, 1]))
natija["Tarmoq"].append(tarmoq_auc(df_ish, y_ish, tr, va, k))
print(f" {'fold':>5} {'LogReg':>9} {'HistGB':>9} {'Tarmoq':>9}")
for f in range(4):
print(f" {f + 1:>5} {natija['LogReg'][f]:>9.4f} "
f"{natija['HistGB'][f]:>9.4f} {natija['Tarmoq'][f]:>9.4f}")
print("\n=== 2. O'rtachalar ===")
for nom, b in natija.items():
b = np.array(b)
print(f" {nom:<8} {b.mean():.4f} +- {b.std(ddof=1):.4f}")
print("\n=== 3. Juftlashgan farqlar ===")
print(f" {'taqqoslash':<20} {'o_rt farq':>10} {'SE':>8} "
f"{'sezilarli':>10}")
for a, b_ in [("Tarmoq", "HistGB"), ("Tarmoq", "LogReg"),
("HistGB", "LogReg")]:
d = np.array(natija[a]) - np.array(natija[b_])
se = d.std(ddof=1) / np.sqrt(len(d))
print(f" {a + ' - ' + b_:<20} {d.mean():>+10.4f} {se:>8.4f} "
f"{str(abs(d.mean()) > 2 * se):>10}")
print("\n=== 4. Qaror: eng yaxshisidan sezilarli yomon bo'lmagan")
print(" ENG SODDA model ===")
oddiydan = ["LogReg", "HistGB", "Tarmoq"] # soddalik tartibi
eng = max(natija, key=lambda m: np.mean(natija[m]))
print(f" o'rtacha bo'yicha eng yaxshi: {eng}")
for m in oddiydan:
if m == eng:
tanlov, sabab = m, "eng yaxshisining o'zi"
break
d = np.array(natija[eng]) - np.array(natija[m])
se = d.std(ddof=1) / np.sqrt(len(d))
print(f" {m:<7}: {eng} dan {d.mean():+.4f} past, "
f"2*SE = {2 * se:.4f}")
if d.mean() <= 2 * se:
tanlov, sabab = m, f"{eng} dan sezilarli yomon emas"
break
print(f" TANLOV: {tanlov} ({sabab})")
print(" ⭐ Eng sodda bazaviyni ham taqqoslashga qo'shing")
if __name__ == "__main__":
main()Natijaning muhim qismi:
=== 1. Uch model, bir xil foldlar ===
fold LogReg HistGB Tarmoq
1 0.7091 0.7806 0.7486
2 0.7084 0.7679 0.7484
3 0.6688 0.7335 0.7290
4 0.7076 0.7710 0.7352
=== 2. O'rtachalar ===
LogReg 0.6984 +- 0.0198
HistGB 0.7633 +- 0.0205
Tarmoq 0.7403 +- 0.0098
=== 3. Juftlashgan farqlar ===
taqqoslash o_rt farq SE sezilarli
Tarmoq - HistGB -0.0229 0.0071 True
Tarmoq - LogReg +0.0419 0.0068 True
HistGB - LogReg +0.0648 0.0025 True
=== 4. Qaror: eng yaxshisidan sezilarli yomon bo'lmagan
ENG SODDA model ===
o'rtacha bo'yicha eng yaxshi: HistGB
LogReg : HistGB dan +0.0648 past, 2*SE = 0.0050
TANLOV: HistGB (eng yaxshisining o'zi)
⭐ Eng sodda bazaviyni ham taqqoslashga qo'shingNima ko'rsatdi: 2.4-bo'lim.
Misol 4 — Test, topshirish paketi va hisobot
"""4-qadam: testni bir marta ochish, paket, Bashoratchi, hisobot."""
import copy
import json
import random
import shutil
import sys
import tempfile
from dataclasses import asdict, dataclass
from pathlib import Path
import numpy as np
import pandas as pd
import torch
import torch.nn as nn
from sklearn.compose import ColumnTransformer
from sklearn.ensemble import HistGradientBoostingClassifier
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 OneHotEncoder, StandardScaler
SON = ["tolov", "muddat", "qongiroq", "internet_gb", "shikoyat"]
KAT = ["tarif", "shahar", "qurilma"]
FORMAT = 1
@dataclass(frozen=True)
class Konfig:
seed: int = 42
lr: float = 3e-3
batch: int = 128
davrlar: int = 40
yashirin: int = 64
dropout: float = 0.1
weight_decay: float = 1e-4
sabr: int = 8
def seed_everything(seed):
random.seed(seed)
np.random.seed(seed)
torch.manual_seed(seed)
def yarat(n=7000, seed=7):
rng = np.random.default_rng(seed)
tarif = rng.integers(0, 5, n)
shahar = rng.integers(0, 40, n)
qurilma = rng.integers(0, 8, n)
shahar_t = rng.normal(0, 0.7, 40)
tarif_t = np.array([0.6, 0.2, 0.0, -0.4, -0.8])
tolov = rng.lognormal(4.0, 0.4, n)
muddat = rng.gamma(2.0, 12.0, n)
qongiroq = rng.poisson(30, n).astype(float)
internet = rng.lognormal(2.5, 0.8, n)
shikoyat = rng.poisson(0.6, n).astype(float)
z = (np.log(tolov) - 4.0) / 0.4
kuch = (-1.4 + tarif_t[tarif] + shahar_t[shahar]
+ 0.6 * z ** 2 # U-shakl: juda arzon va
- 1.4 * (muddat > 24) # juda qimmat tarif
+ 1.0 * (muddat < 3) # yangi mijoz
+ 0.9 * shikoyat * (tarif >= 3) # o'zaro ta'sir
+ 0.8 * (internet > 30) * (qurilma < 2))
y = (kuch + rng.logistic(0, 1, n) > 0).astype(np.int64)
df = pd.DataFrame({"tolov": tolov, "muddat": muddat,
"qongiroq": qongiroq, "internet_gb": internet,
"shikoyat": shikoyat, "tarif": tarif,
"shahar": shahar, "qurilma": qurilma})
return df, y
class TabularTarmoq(nn.Module):
def __init__(self, hajmlar, yashirin, dropout):
super().__init__()
d = [min(16, (n + 1) // 2) for n in hajmlar]
self.embler = nn.ModuleList(
[nn.Embedding(n, di) for n, di in zip(hajmlar, d)])
self.tarmoq = nn.Sequential(
nn.Linear(len(SON) + sum(d), yashirin), nn.ReLU(),
nn.Dropout(dropout), nn.Linear(yashirin, yashirin),
nn.ReLU(), nn.Linear(yashirin, 2))
def forward(self, son, kat):
e = [emb(kat[:, i]) for i, emb in enumerate(self.embler)]
return self.tarmoq(torch.cat([son] + e, dim=1))
class Kirish:
"""Lug'at va masshtab - faqat son va lug'atlar (weights_only)."""
def __init__(self, mean, scale, lugatlar):
self.mean = np.asarray(mean)
self.scale = np.asarray(scale)
self.lugatlar = lugatlar
@classmethod
def fit(cls, df):
return cls(df[SON].mean().to_numpy(),
df[SON].std(ddof=0).to_numpy(),
{c: {str(int(k)): i + 1 for i, k in
enumerate(np.unique(df[c]))} for c in KAT})
def hajmlar(self):
return [len(self.lugatlar[c]) + 1 for c in KAT]
def tensorlar(self, df):
son = (df[SON].to_numpy(dtype=float) - self.mean) / self.scale
kat = np.column_stack([[self.lugatlar[c].get(str(int(v)), 0)
for v in df[c]] for c in KAT])
return (torch.tensor(son, dtype=torch.float32),
torch.tensor(kat, dtype=torch.int64))
def lugat(self):
return {"son": SON, "kat": KAT, "mean": self.mean.tolist(),
"scale": self.scale.tolist(), "lugatlar": self.lugatlar}
def orgat(k, kirish, df_tr, y_tr, df_va, y_va):
seed_everything(k.seed)
model = TabularTarmoq(kirish.hajmlar(), k.yashirin, k.dropout)
opt = torch.optim.AdamW(model.parameters(), lr=k.lr,
weight_decay=k.weight_decay)
sched = torch.optim.lr_scheduler.CosineAnnealingLR(opt, k.davrlar)
s_tr, c_tr = kirish.tensorlar(df_tr)
s_va, c_va = kirish.tensorlar(df_va)
yt = torch.tensor(y_tr)
g = torch.Generator().manual_seed(k.seed)
eng, holat, hisob = -1.0, None, 0
for _ in range(k.davrlar):
model.train()
tartib = torch.randperm(len(yt), generator=g)
for i in range(0, len(yt), k.batch):
idx = tartib[i:i + k.batch]
opt.zero_grad()
nn.functional.cross_entropy(model(s_tr[idx], c_tr[idx]),
yt[idx]).backward()
opt.step()
sched.step()
model.eval()
with torch.no_grad():
p = torch.softmax(model(s_va, c_va), 1)[:, 1].numpy()
auc = roc_auc_score(y_va, p)
if auc > eng:
eng, hisob = auc, 0
holat = copy.deepcopy(model.state_dict())
else:
hisob += 1
if hisob >= k.sabr:
break
model.load_state_dict(holat)
return model, eng
class Bashoratchi:
def __init__(self, yol):
p = torch.load(yol, weights_only=True)
if p["format"] != FORMAT:
raise ValueError("format mos emas")
k = p["konfig"]
ki = p["kirish"]
self.kirish = Kirish(ki["mean"], ki["scale"], ki["lugatlar"])
self.model = TabularTarmoq(self.kirish.hajmlar(), k["yashirin"],
k["dropout"])
self.model.load_state_dict(p["holat"])
self.model.eval()
with torch.no_grad():
q = torch.softmax(self.model(p["nazorat"]["son"],
p["nazorat"]["kat"]), 1)[:, 1]
self.nazorat_ok = bool(torch.allclose(q, p["nazorat"]["p"],
atol=1e-6))
def ehtimollik(self, df):
s, c = self.kirish.tensorlar(df)
with torch.no_grad():
return torch.softmax(self.model(s, c), 1)[:, 1].numpy()
def main() -> None:
k = Konfig()
df, y = yarat()
idx = np.arange(len(df))
ish, te = train_test_split(idx, test_size=0.2, random_state=k.seed,
stratify=y)
tr, va = train_test_split(ish, test_size=0.25, random_state=k.seed,
stratify=y[ish])
kirish = Kirish.fit(df.iloc[tr])
print("=== 1. Yakuniy modellar ===")
model, val_auc = orgat(k, kirish, df.iloc[tr], y[tr], df.iloc[va],
y[va])
gb = HistGradientBoostingClassifier(
max_iter=200, learning_rate=0.05, categorical_features=KAT,
early_stopping=False, random_state=0).fit(df.iloc[ish], y[ish])
lr_ = make_pipeline(
ColumnTransformer([("son", StandardScaler(), SON),
("kat", OneHotEncoder(handle_unknown="ignore"),
KAT)]),
LogisticRegression(max_iter=2000)).fit(df.iloc[ish], y[ish])
print(f" tarmoq val_auc (eng yaxshi davr): {val_auc:.4f}")
print("\n=== 2. Testni BIR MARTA ochamiz ===")
s_te, c_te = kirish.tensorlar(df.iloc[te])
model.eval()
with torch.no_grad():
p_nn = torch.softmax(model(s_te, c_te), 1)[:, 1].numpy()
p_gb = gb.predict_proba(df.iloc[te])[:, 1]
p_lr = lr_.predict_proba(df.iloc[te])[:, 1]
test_nn = roc_auc_score(y[te], p_nn)
test_gb = roc_auc_score(y[te], p_gb)
test_lr = roc_auc_score(y[te], p_lr)
print(f" {'model':<10} {'test AUC':>9}")
print(f" {'LogReg':<10} {test_lr:>9.4f}")
print(f" {'HistGB':<10} {test_gb:>9.4f}")
print(f" {'Tarmoq':<10} {test_nn:>9.4f}")
print(" test QARORNI o'zgartirmaydi - u faqat tasdiqlaydi")
papka = Path(tempfile.mkdtemp(prefix="churn_"))
try:
print("\n=== 3. Topshirish paketi ===")
nazorat_df = df.iloc[te[:30]]
ns, nk = kirish.tensorlar(nazorat_df)
with torch.no_grad():
np_ = torch.softmax(model(ns, nk), 1)[:, 1]
paket = {
"format": FORMAT,
"konfig": asdict(k),
"holat": model.state_dict(),
"kirish": kirish.lugat(),
"metrika": {"val_auc": round(float(val_auc), 4),
"test_auc": round(float(test_nn), 4)},
"versiyalar": {"torch": str(torch.__version__),
"python": sys.version.split()[0]},
"nazorat": {"son": ns, "kat": nk, "p": np_},
}
yol = papka / "churn_model.pt"
torch.save(paket, yol)
print(f" kalitlar: {sorted(paket)}")
print(f" hajm: {yol.stat().st_size / 1024:.1f} KB")
print("\n=== 4. Bashoratchi (weights_only=True) ===")
b = Bashoratchi(yol)
print(f" nazorat tekshiruvi: {'OK' if b.nazorat_ok else 'XATO'}")
yangi = df.iloc[te[:3]].copy()
yangi.loc[yangi.index[0], "shahar"] = 999 # noma'lum
p = b.ehtimollik(yangi)
for (_, q), pp in zip(yangi.iterrows(), p):
print(f" tarif={int(q.tarif)}, shahar={int(q.shahar):>3}, "
f"muddat={q.muddat:5.1f} -> ketish {pp:.3f}")
print(" shahar=999 o'quvda yo'q -> 0 (noma'lum) indeksiga tushdi")
print("\n=== 5. Jurnal yozuvi ===")
jurnal = papka / "jurnal.jsonl"
with open(jurnal, "a", encoding="utf-8") as f:
f.write(json.dumps({"konfig": asdict(k),
"metrika": paket["metrika"]}) + "\n")
print(f" {jurnal.read_text(encoding='utf-8').strip()[:76]}...")
print("\n=== 6. Yakuniy hisobot ===")
print(" VAZIFA: telekom mijozining ketishini bashorat qilish")
print(" METRIKA: ROC AUC")
print(" DIZAYN: stratifikatsiyalangan 60/20/20; test bir marta")
print(f" TEST: LogReg {test_lr:.4f}, HistGB {test_gb:.4f}, "
f"tarmoq {test_nn:.4f}")
print(" QAROR: 3-qadamdagi juftlashgan CV asosida (test emas)")
print(" ESLATMA: paketlash bu yerda tarmoq uchun ko'rsatildi;")
print(" qaror bo'yicha ishlab chiqarishga tanlangan model")
print(" shu tartibda (konfig + kirish + nazorat) paketlanadi")
print(f" PAKET: format {FORMAT}, weights_only=True, nazorat "
f"{'OK' if b.nazorat_ok else 'XATO'}")
print(" CHEKLOVLAR: sun'iy ma'lumot; vaqt drift i tekshirilmagan;")
print(" yangi kategoriyalar umumiy 0-vektorga tushadi")
print(" ⭐ 21-qism yakunlandi: bo'laklardan ishlaydigan loyihagacha")
finally:
shutil.rmtree(papka, ignore_errors=True)
if __name__ == "__main__":
main()Natijaning muhim qismi:
=== 1. Yakuniy modellar ===
tarmoq val_auc (eng yaxshi davr): 0.7737
=== 2. Testni BIR MARTA ochamiz ===
model test AUC
LogReg 0.7101
HistGB 0.7692
Tarmoq 0.7538
test QARORNI o'zgartirmaydi - u faqat tasdiqlaydi
=== 3. Topshirish paketi ===
kalitlar: ['format', 'holat', 'kirish', 'konfig', 'metrika', 'nazorat', 'versiyalar']
hajm: 34.4 KB
=== 4. Bashoratchi (weights_only=True) ===
nazorat tekshiruvi: OK
tarif=0, shahar=999, muddat= 16.0 -> ketish 0.651
tarif=0, shahar= 38, muddat= 4.9 -> ketish 0.238
tarif=1, shahar= 28, muddat= 61.8 -> ketish 0.047
shahar=999 o'quvda yo'q -> 0 (noma'lum) indeksiga tushdi
=== 5. Jurnal yozuvi ===
{"konfig": {"seed": 42, "lr": 0.003, "batch": 128, "davrlar": 40, "yashirin"...
=== 6. Yakuniy hisobot ===
VAZIFA: telekom mijozining ketishini bashorat qilish
METRIKA: ROC AUC
DIZAYN: stratifikatsiyalangan 60/20/20; test bir marta
TEST: LogReg 0.7101, HistGB 0.7692, tarmoq 0.7538
QAROR: 3-qadamdagi juftlashgan CV asosida (test emas)
ESLATMA: paketlash bu yerda tarmoq uchun ko'rsatildi;
qaror bo'yicha ishlab chiqarishga tanlangan model
shu tartibda (konfig + kirish + nazorat) paketlanadi
PAKET: format 1, weights_only=True, nazorat OK
CHEKLOVLAR: sun'iy ma'lumot; vaqt drift i tekshirilmagan;
yangi kategoriyalar umumiy 0-vektorga tushadi
⭐ 21-qism yakunlandi: bo'laklardan ishlaydigan loyihagachaNima ko'rsatdi: 2.5, 2.6-bo'limlar.
5. To'g'ri va noto'g'ri tushunishlar
| Noto'g'ri fikr | To'g'risi |
|---|---|
| "Loyiha = model" | Konfig, quvur, tekshiruv, paket, hisobot |
| "Tekshiruvlar uzoq" | Bir necha soniya |
| "Eng yaxshi holat avtomatik saqlanadi" | deepcopy va tiklash kerak |
| "Tarmoq zamonaviy — tanlaymiz" | Juftlashgan farq hal qiladi |
| "Test bilan model tanlash mumkin" | Test bir marta, oxirida |
| "Paketga sklearn obyektini qo'yish qulay" | weights_only rad etadi |
| "Noma'lum kategoriya kam uchraydi" | Ishlab chiqarishda albatta |
| "Bitta seed yetarli" | O'rtacha va std |
6. Keng tarqalgan xatolar va yechimlari
1. Test bilan tanlash
for k in konfiglar: print(test_auc(k)) # ⚠️
for k in konfiglar: print(val_auc(k)) # ✅ test oxirida2. Butun ma'lumotda tayyorlash
Kirish.fit(df) # ⚠️
Kirish.fit(df.iloc[tr]) # ✅3. Havola bilan saqlash
holat = model.state_dict() # ⚠️
holat = copy.deepcopy(model.state_dict()) # ✅4. Boshqa foldlarda taqqoslash
cross_val_score(gb, ...); cross_val_score(net, ...) # ⚠️ har xil bo'linish
for tr, va in cv.split(X, y): ... # ✅ bir xil foldlar5. Paketda obyekt
{"masshtablovchi": StandardScaler()} # ⚠️
{"mean": [...], "scale": [...]} # ✅6. Lug'at kalitlari turi
{np.int64(3): 1} # ⚠️ weights_only muammo
{"3": 1} # ✅ satr kalit7. Yuklashdan keyin eval() yo'q
model.load_state_dict(p["holat"]); model(x) # ⚠️
model.load_state_dict(p["holat"]); model.eval() # ✅7. Integratsiya — bu bilim qayerda kerak bo'ladi
- 21.1-21.11-darslar (o'tilgan): Butun qism
- 20.12-dars (o'tilgan): Tarmoq va boosting
- 18-qism (o'tilgan): Baholash dizayni
- 22-qism: Kompyuter ko'rish — xuddi shu skelet, rasm bilan
- 29-qism: Deploy va monitoring
8. Eng yaxshi amaliyotlar
Konfigdan boshlang.
Testni birinchi qadamda qulflang.
Tayyorlashni o'quvda quring.
Tekshiruv va bitta batch testi.
Eng yaxshi holatni
deepcopy.Bir xil foldlarda taqqoslang.
Paketni xavfsiz turlardan tuzing.
Hisobotda qaror va dalil.
9. Amaliy topshiriq
Vazifa 1: Bashorat qiling
1. # loyiha qaysi komponentdan boshlanadi?
2. # test qachon qulflanadi?
3. # lug'at qayerda quriladi?
4. # bitta batch testi nimani ko'rsatadi?
5. # eng yaxshi holat qanday saqlanadi?
6. # tarmoq va boosting qanday taqqoslanadi?
7. # qaror mezoni?
8. # teng natijada qaysi model?
9. # paketda sklearn obyekti bo'ladimi?
10. # lug'at kalitlari qanday turda?
11. # noma'lum kategoriya qayerga tushadi?
12. # hisobotda nima bo'lishi shart?Javoblar
- Konfig
- Birinchi qadamda
- O'quv to'plamida
- Model/loss/optimizator ishlashini
copy.deepcopy(state_dict())- Bir xil foldlarda, juftlashgan
|farq| > 2*SE- Soddaroq (HistGB)
- Yo'q
- Satr
- 0-indeks
- Qaror, dalil, cheklovlar
Vazifa 2: Xatolarni tuzating
1. Kirish.fit(df)
2. holat = model.state_dict()
3. {"masshtablovchi": StandardScaler()}
4. {np.int64(3): 1}
5. for k in konfiglar: print(test_auc(k))Javoblar
1. Kirish.fit(df.iloc[tr])
2. holat = copy.deepcopy(model.state_dict())
3. {"mean": [...], "scale": [...]}
4. {"3": 1}
5. for k in konfiglar: print(val_auc(k)) # test faqat oxiridaVazifa 3: Quvur
Modellang:
- Dizayn
- Tayyorlash
- Tekshiruvlar
- Bitta batch
Vazifa 4: O'rgatish
Modellang:
- Bitta yurish
- Tiklash
- Seedlar
- Jurnal
Vazifa 5: Taqqoslash
Modellang:
- Foldlar
- O'rtachalar
- Farqlar
- Qaror
Vazifa 6: Topshirish
Modellang:
- Test
- Paket
- Bashoratchi
- Hisobot
Vazifa 7: O'ylash
Juftlashgan CV da tarmoq HistGB dan ustun chiqmadi, lekin testda tarmoq 0.01 yuqori ball oldi. Menejer "demak tarmoqni tanlaymiz" dedi. Nima deysiz?
Javob
Qisqa javob: yo'q — qaror CV da qabul qilingan, test uni o'zgartirmaydi.
Nima uchun:
1. Test — bitta namuna. Test to'plami ~1400 qator. Bunday hajmda AUC ning standart xatosi taxminan 0.01 atrofida. Ya'ni 0.01 farq — tasodif chegarasida. CV esa 4 ta fold bo'yicha juftlashgan farqni va uning SE sini berdi — bu ancha ishonchliroq dalil.
2. Testni qaror uchun ishlatish — uni validatsiyaga aylantirish. Agar test natijasiga qarab model tanlasak, test endi "ko'rilmagan ma'lumot" emas. Keyingi hisobotdagi test balli optimistik bo'ladi (18-qism, "g'olib la'nati").
3. Qaror mezoni oldindan belgilangan edi: |farq| > 2*SE. CV da bu shart bajarilmadi → soddaroq model. Mezonni natijani ko'rgandan keyin o'zgartirish — p-hacking ning bir ko'rinishi.
Menejerga javob:
"Test farqi 0.01 — bu test to'plami hajmida tasodifiy tebranish bilan bir xil kattalikda. Biz qarorni oldindan belgilangan mezon bo'yicha, 4 ta foldda juftlashgan taqqoslash bilan qabul qildik — u yerda farq sezilarli emas edi. Shuning uchun HistGB ni tanlaymiz: u tezroq, sozlash va tushuntirish osonroq. Agar tarmoqning ustunligini tekshirmoqchi bo'lsak — ko'proq fold yoki ko'proq ma'lumot bilan yangi taqqoslash o'tkazamiz, testga qarab emas."
Qachon fikrni o'zgartirish mumkin:
- Yangi, mustaqil ma'lumot to'plamida tarmoq izchil ustun chiqsa
- CV foldlari soni oshirilganda farq
2*SEdan oshsa - Tarmoqning boshqa foydasi bo'lsa: masalan, shahar embeddinglari boshqa vazifada kerak bo'lsa (21.10)
Muhim nuans: testda tarmoq yomonroq chiqqanda ham xuddi shu mantiq ishlaydi — biz qarorni testga qarab o'zgartirmaymiz. Bu bir tomonlama qoida emas.
Nimani mustahkamlaydi: 2.4-bo'lim.
Xulosa
Bu darsda to'liq PyTorch loyihasini qurdik.
Eng muhim uch fikr:
Loyiha — komponentlar zanjiri, har biri alohida sinaladi. Konfig → ma'lumot (lug'at va masshtab faqat o'quvda) →
Dataset→ embedding li model → tez tekshiruvlar va bitta batch testi →Trainerva eng yaxshi holat → juftlashgan taqqoslash → test → paket. 1-misolda to'rtta tez tekshiruv va bitta batch testi o'rgatishdan oldin quvurni tasdiqladi.Qaror juftlashgan farq bilan, test esa faqat tasdiqlash uchun. Logistik regressiya,
HistGradientBoostingva tarmoq bir xil foldlarda baholandi. Ma'lumotda nochiziqliliklar bo'lgani uchun logistik regressiya ikkalasidan sezilarli ortda qoldi,HistGradientBoostingesa tarmoqdan ham2*SEdan ortiq yaxshi chiqdi — qoida bo'yicha u tanlandi. Test oxirida bir marta ochildi va xuddi shu tartibni tasdiqladi; uning vazifasi qarorni o'zgartirish emas.Paket — xavfsiz turlardan, o'zini tekshiradigan. Lug'atlar satr kalitli, masshtab ro'yxat, versiya
str— shuning uchun paketweights_only=Truebilan to'liq yuklandi.Bashoratchiyuklanganda nazorat namunasini qayta hisoblab o'zini tekshirdi, o'quvda bo'lmagan shahar esa xatosiz 0-indeksga tushdi. Paketlash mexanizmi bu darsda tarmoq uchun ko'rsatildi; ishlab chiqarishga esa 3-qadam qarori bo'yichaHistGradientBoostingtopshiriladi — va bu ham halol natija.
Bu bilan 21-qism — PyTorch yakunlandi. Endi bizda har qanday chuqur o'rganish loyihasi uchun tayyor skelet bor. Keyingi qismda uni kompyuter ko'rishga qo'llaymiz: rasm ma'lumoti, konvolyutsiya, CNN arxitekturalari, augmentatsiya va transfer learning.
Izohlar (0)
Izoh yozish uchun kiring.
- Hozircha izoh yo'q. Birinchi bo'ling!