Mundarija (22)
- 1. Kirish va motivatsiya
- 2. Nazariya — chuqur tushuntirish
- 2.1. To'liq oqim
- 2.2. Modellarni halol taqqoslash
- 2.3. Sozlash
- 2.4. Xatolar tahlili
- 2.5. Belgi muhimligi
- 2.6. Chiqarish tayyorgarligi
- 2.7. Tuzoqlar
- 2.8. Tartib — asosiy ko'nikma
- 3. Tez ma'lumotnoma
- 4. Batafsil misollar
- Misol 1 — Ma'lumot va bazaviy modellar
- Misol 2 — Ansambllarni taqqoslash va sozlash
- Misol 3 — Xatolar tahlili va muhimlik
- Misol 4 — Chegara, taqqoslash va chiqarish
- 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
15.14-dars: Amaliyot — to'liq ansambl loyihasi
15-QISM — DARAXTLAR VA ANSAMBLLAR · 14-dars
1. Kirish va motivatsiya
Bu qismda qaror daraxtidan boshlab gradient boosting va stacking gacha bo'lgan yo'lni bosib o'tdik. Endi hammasini bitta loyihada birlashtiramiz: ma'lumotni ko'rishdan boshlab, ishlab chiqarishga tayyor modelgacha.
Amaliyot alohida usullarni takrorlash emas — qaror qabul qilish tartibini mashq qilish: qaysi modelni birinchi sinash, qachon sozlashni to'xtatish, qaysi metrikaga qarash, natijani qanday tekshirish va nima hujjatlashtirish.
Bu darsda: to'liq oqim (bazaviy model → ansambl → sozlash → tahlil → chiqarish), modellarni halol taqqoslash, xatolar tahlili, belgi muhimligi va yakuniy hisobot.
Real vaziyat. Ko'p loyiha "model 0.87 AUC berdi" bilan tugaydi va hech qachon ishlatilmaydi — chunki chegara tanlanmagan, kechikish o'lchanmagan, drift kuzatuvi yo'q va hech kim modelning nima qilishini tushuntira olmaydi. Bu darsdagi tartib shu holatning oldini oladi.
Bu darsda to'liq ansambl loyihasini quramiz.
Bu darsda:
- To'liq oqim
- Modellarni halol taqqoslash
- Sozlash
- Xatolar tahlili
- Belgi muhimligi
- Chiqarish tayyorgarligi
- Tuzoqlar
- Amaliy: yakuniy loyiha
ℹ Misollar real numpy/pandas/sklearn bilan (Python 3.14).
2. Nazariya — chuqur tushuntirish
2.1. To'liq oqim
1. MA'LUMOT: hajm, yo'qolgan qiymatlar, sinf nisbati, leakage tekshiruvi
2. AJRATISH: o'quv / validatsiya / test (yoki CV + test)
3. BAZAVIY: DummyClassifier va logistik regressiya 12.6-bob
4. ANSAMBL: RandomForest sozlanmasdan -> haqiqiy boshlang'ich
5. BOOSTING: HistGB / LightGBM, erta to'xtatish bilan
6. SOZLASH: tartib bilan 15.9-bob, tasodifiy qidiruv
7. TAHLIL: xatolar, muhimlik, kalibrlash, segmentlar
8. QAROR: chegara, narx-foyda
9. CHIQARISH: Pipeline saqlash, kechikish, drift rejasi 3 va 4-qadamni o'tkazib yubormang: DummyClassifier va sozlanmagan Random Forest sizga realistik kutish beradi. Ularsiz 0.87 AUC yaxshimi yoki yomonmi — bilib bo'lmaydi.
2.2. Modellarni halol taqqoslash
BIR XIL shartlarda:
- bir xil CV bo'linishi (random_state qotirilgan)
- bir xil metrika
- bir xil tayyorlash (Pipeline ichida)
- har model uchun O'ZIGA MOS sozlash byudjeti
NOTO'G'RI: RF ni sozlab, logistik regressiyani standart holda qoldirish
NOTO'G'RI: turli CV bo'linishlarida solishtirish
NOTO'G'RI: test to'plamida ko'p marta baholash
Farq MUHIMmi? CV foldlar bo'yicha std ni ko'ring
farq < 1 std bo'lsa - ehtimol shovqinCV standart og'ishi taqqoslashning ajralmas qismi: 0.842 va 0.847 farqi, agar std 0.008 bo'lsa, ahamiyatsiz. Bunday holda soddaroq yoki tezroq modelni tanlang.
2.3. Sozlash
Byudjetni oqilona taqsimlang:
RandomForest: 5-10 konfiguratsiya (max_features)
Boosting: 40-80 konfiguratsiya (tasodifiy qidiruv)
Stacking: faqat vaqt qolsa
Qachon to'xtash:
- so'nggi 20 sinov yaxshilanish bermasa
- yutuq CV std dan kichik bo'lsa
- narx-foyda salbiy bo'lsaTo'xtash qoidasini oldindan belgilang, aks holda sozlash cheksiz davom etadi va siz validatsiyaga overfitting qilasiz.
2.4. Xatolar tahlili
Har doim quyidagilarni ko'ring 14.13-bob:
1. Eng ishonchli XATO bashoratlar (p > 0.9 lekin y = 0)
2. Segmentlar bo'yicha metrika (hudud, kanal, davr)
3. Kalibrlash diagrammasi 14.10-bob
4. Chalkashlik matritsasi va chegara ta'siri
Ko'pincha topiladi:
- bitta segmentda model umuman ishlamaydi
- yorliq xatolari
- leakage belgisi (juda yaxshi natija)Segmentlar bo'yicha bo'lish — xatolar tahlilining eng samarali usuli: umumiy 0.85 AUC ostida bitta muhim segmentdagi 0.58 yashiringan bo'lishi mumkin.
2.5. Belgi muhimligi
Yakuniy hisobot uchun 15.11-bob:
- permutation importance (TEST to'plamida, n_repeats >= 10)
- korrelyatsiyali guruhlar bo'yicha
- partial dependence bilan yo'nalish
- MDI ni faqat ichki ko'rikda
Va albatta yozing: "bu bashorat foydaliligi, sababiy ta'sir emas"Hisobotdagi muhimlik diagrammasiga ogohlantirish yozuvi qo'shing — aks holda uni albatta sababiy talqin qilishadi.
2.6. Chiqarish tayyorgarligi
Yakunda tayyor bo'lishi kerak:
[ ] Pipeline (tayyorlash + model) joblib da
[ ] Versiyalar, belgilar ro'yxati, metrikalar
[ ] Tanlangan chegara va uning asosi
[ ] Kechikish va hajm o'lchovlari
[ ] Xatolar tahlili hisoboti
[ ] Drift kuzatuvi rejasi
[ ] Qayta o'qitish siyosati
[ ] Rollback rejasiBu ro'yxat model kartasi (model card) ning asosi: u modelni topshirishda va keyinchalik uni qo'llab-quvvatlashda kerak bo'ladi.
2.7. Tuzoqlar
Asosiy tuzoqlar: bazaviy modelsiz boshlash; test to'plamida ko'p marta baholash; modellarni turli shartlarda taqqoslash; CV std ni hisobga olmaslik; xatolar tahlilini o'tkazib yuborish; chegarani 0.5 da qoldirish; muhimlikni sababiy talqin qilish; kechikish va hajmni oxirida tekshirish.
2.8. Tartib — asosiy ko'nikma
Ansambl loyihasi tartib bilan olib boriladi: bazaviy model → sozlanmagan RF → boosting + erta to'xtatish → tartibli sozlash → xatolar tahlili → chegara → chiqarish. Modellarni faqat bir xil shartlarda taqqoslang va CV std ni hisobga oling. Yutuq std dan kichik bo'lsa — soddaroq modelni tanlang. Keyingi qism — nazoratsiz o'rganish.
3. Tez ma'lumotnoma
from sklearn.dummy import DummyClassifier
from sklearn.model_selection import StratifiedKFold, cross_validate
cv = StratifiedKFold(5, shuffle=True, random_state=0)
for nom, m in modellar.items():
n = cross_validate(m, Xtr, ytr, cv=cv, scoring=["roc_auc", "average_precision"])
print(nom, n["test_roc_auc"].mean(), n["test_roc_auc"].std())
# chegarani validatsiyada tanlash
pr, rc, ch = precision_recall_curve(yval, pval)
f1 = 2 * pr[:-1] * rc[:-1] / np.maximum(pr[:-1] + rc[:-1], 1e-12)
chegara = ch[np.argmax(f1)]
QOIDA: bazaviy model bilan boshla · bir xil CV · std ni ko'r ·
testda bir martaAmaliyot xulosasi
1 ma'lumot -> 2 ajratish -> 3 bazaviy -> 4 RF -> 5 boosting
-> 6 sozlash -> 7 tahlil -> 8 chegara -> 9 chiqarish
Bir xil shartlarda taqqosla; CV std ni hisobga ol
Testda faqat bir marta; chegarani validatsiyada tanla4. Batafsil misollar
Misollar real numpy/pandas/sklearn bilan (Python 3.14).
Misol 1 — Ma'lumot va bazaviy modellar
"""1-4 qadamlar: ma'lumot, ajratish, bazaviy, sozlanmagan RF (real pandas/sklearn)."""
import numpy as np
import pandas as pd
from sklearn.compose import ColumnTransformer
from sklearn.dummy import DummyClassifier
from sklearn.ensemble import RandomForestClassifier
from sklearn.impute import SimpleImputer
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import StratifiedKFold, cross_validate, train_test_split
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import OneHotEncoder, StandardScaler
def yarat(seed: int = 42, n: int = 12000) -> pd.DataFrame:
"""Yetkazib berish kechikishi: aralash belgilar, o'zaro ta'sirlar."""
rng = np.random.default_rng(seed)
hudud = rng.choice(["toshkent", "samarqand", "buxoro", "fargona",
"namangan"], n, p=[0.35, 0.2, 0.15, 0.2, 0.1])
tur = rng.choice(["oddiy", "tezkor", "yirik"], n, p=[0.6, 0.3, 0.1])
kanal = rng.choice(["ilova", "sayt", "qongiroq"], n, p=[0.5, 0.35, 0.15])
masofa = rng.gamma(3, 60, n)
ogirlik = rng.gamma(2, 4, n)
soat = rng.integers(0, 24, n).astype(float)
hafta_kuni = rng.integers(0, 7, n).astype(float)
kuryer_tajribasi = rng.gamma(2, 8, n)
hq = pd.Series(hudud).map({"toshkent": 0.0, "samarqand": 0.5,
"buxoro": 0.9, "fargona": 0.4,
"namangan": 0.6}).to_numpy()
tt = pd.Series(tur).map({"oddiy": 0.0, "tezkor": -0.9,
"yirik": 1.1}).to_numpy()
tig = ((soat >= 7) & (soat <= 10)) | ((soat >= 17) & (soat <= 19))
kuch = (-3.2 + 0.005 * masofa + 0.04 * ogirlik + hq + tt
+ 1.0 * tig # tig'iz soatlar
+ 0.8 * (tig & (hq > 0.4)) # o'zaro ta'sir
- 0.04 * kuryer_tajribasi
+ 0.4 * (hafta_kuni >= 5))
y = (rng.random(n) < 1 / (1 + np.exp(-kuch))).astype(int)
df = pd.DataFrame({"hudud": hudud, "tur": tur, "kanal": kanal,
"masofa": masofa, "ogirlik": ogirlik, "soat": soat,
"hafta_kuni": hafta_kuni,
"kuryer_tajribasi": kuryer_tajribasi, "kechikdi": y})
df.loc[rng.random(n) < 0.05, "kuryer_tajribasi"] = np.nan
return df
def tayyorlagich(kategoriya, sonli, masshtab: bool = False):
sonli_quvur = ([("imp", SimpleImputer(strategy="median")),
("sc", StandardScaler())] if masshtab
else [("imp", SimpleImputer(strategy="median"))])
return ColumnTransformer([
("k", OneHotEncoder(handle_unknown="ignore"), kategoriya),
("s", Pipeline(sonli_quvur), sonli)])
def main() -> None:
df = yarat()
print("=== 1. Ma'lumot ===")
print(f" qatorlar: {len(df)}, belgilar: {df.shape[1] - 1}")
print(f" kechikish ulushi: {df['kechikdi'].mean():.2%}")
print(f" yo'qolgan qiymatlar: "
f"{int(df.isna().sum().sum())} ta "
f"({df.isna().sum().sum() / df.size:.2%})")
print(f" kategoriyali: hudud({df['hudud'].nunique()}), "
f"tur({df['tur'].nunique()}), kanal({df['kanal'].nunique()})")
X = df.drop(columns="kechikdi")
y = df["kechikdi"]
Xtr, Xte, ytr, yte = train_test_split(X, y, test_size=0.25, random_state=0,
stratify=y)
print("\n=== 2. Ajratish ===")
print(f" o'quv {len(Xtr)}, test {len(Xte)}")
print(f" o'quvda kechikish {ytr.mean():.2%}, "
f"testda {yte.mean():.2%}")
kategoriya = ["hudud", "tur", "kanal"]
sonli = ["masofa", "ogirlik", "soat", "hafta_kuni", "kuryer_tajribasi"]
cv = StratifiedKFold(5, shuffle=True, random_state=0)
print("\n=== 3. Bazaviy modellar ===")
modellar = {
"Dummy(prior)": Pipeline([("t", tayyorlagich(kategoriya, sonli)),
("m", DummyClassifier(strategy="prior"))]),
"LogReg": Pipeline([("t", tayyorlagich(kategoriya, sonli, True)),
("m", LogisticRegression(max_iter=2000))]),
}
natijalar = {}
for nom, m in modellar.items():
n = cross_validate(m, Xtr, ytr, cv=cv,
scoring=["roc_auc", "average_precision"], n_jobs=1)
natijalar[nom] = n
print(f" {nom:<14}: ROC AUC {n['test_roc_auc'].mean():.4f} "
f"(+-{n['test_roc_auc'].std():.4f}), PR AUC "
f"{n['test_average_precision'].mean():.4f}")
print("\n=== 4. Sozlanmagan Random Forest ===")
rf = Pipeline([("t", tayyorlagich(kategoriya, sonli)),
("m", RandomForestClassifier(n_estimators=300,
random_state=0, n_jobs=1))])
n = cross_validate(rf, Xtr, ytr, cv=cv,
scoring=["roc_auc", "average_precision"], n_jobs=1)
natijalar["RandomForest"] = n
print(f" RandomForest : ROC AUC {n['test_roc_auc'].mean():.4f} "
f"(+-{n['test_roc_auc'].std():.4f}), PR AUC "
f"{n['test_average_precision'].mean():.4f}")
lr = natijalar["LogReg"]["test_roc_auc"].mean()
print(f" LogReg dan farq: {n['test_roc_auc'].mean() - lr:+.4f} "
f"(CV std {n['test_roc_auc'].std():.4f})")
print(" ⭐ Bazaviy modellar realistik kutish beradi")
if __name__ == "__main__":
main()Natijaning muhim qismi:
=== 1. Ma'lumot ===
qatorlar: 12000, belgilar: 8
kechikish ulushi: 17.82%
yo'qolgan qiymatlar: 579 ta (0.54%)
kategoriyali: hudud(5), tur(3), kanal(3)
=== 2. Ajratish ===
o'quv 9000, test 3000
o'quvda kechikish 17.82%, testda 17.83%
=== 3. Bazaviy modellar ===
Dummy(prior) : ROC AUC 0.5000 (+-0.0000), PR AUC 0.1782
LogReg : ROC AUC 0.7338 (+-0.0118), PR AUC 0.3965
=== 4. Sozlanmagan Random Forest ===
RandomForest : ROC AUC 0.7316 (+-0.0154), PR AUC 0.3969
LogReg dan farq: -0.0022 (CV std 0.0154)
⭐ Bazaviy modellar realistik kutish beradiNima ko'rsatdi: 2.1, 2.2-bo'limlar.
Misol 2 — Ansambllarni taqqoslash va sozlash
"""5-6 qadamlar: boosting, erta to'xtatish, tasodifiy qidiruv (real sklearn)."""
import numpy as np
import pandas as pd
from scipy.stats import randint, uniform
from sklearn.compose import ColumnTransformer
from sklearn.ensemble import (ExtraTreesClassifier,
HistGradientBoostingClassifier,
RandomForestClassifier)
from sklearn.impute import SimpleImputer
from sklearn.model_selection import (RandomizedSearchCV, StratifiedKFold,
cross_validate, train_test_split)
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import OneHotEncoder
def yarat(seed: int = 42, n: int = 12000) -> pd.DataFrame:
rng = np.random.default_rng(seed)
hudud = rng.choice(["toshkent", "samarqand", "buxoro", "fargona",
"namangan"], n, p=[0.35, 0.2, 0.15, 0.2, 0.1])
tur = rng.choice(["oddiy", "tezkor", "yirik"], n, p=[0.6, 0.3, 0.1])
kanal = rng.choice(["ilova", "sayt", "qongiroq"], n, p=[0.5, 0.35, 0.15])
masofa = rng.gamma(3, 60, n)
ogirlik = rng.gamma(2, 4, n)
soat = rng.integers(0, 24, n).astype(float)
hafta_kuni = rng.integers(0, 7, n).astype(float)
kuryer_tajribasi = rng.gamma(2, 8, n)
hq = pd.Series(hudud).map({"toshkent": 0.0, "samarqand": 0.5,
"buxoro": 0.9, "fargona": 0.4,
"namangan": 0.6}).to_numpy()
tt = pd.Series(tur).map({"oddiy": 0.0, "tezkor": -0.9,
"yirik": 1.1}).to_numpy()
tig = ((soat >= 7) & (soat <= 10)) | ((soat >= 17) & (soat <= 19))
kuch = (-3.2 + 0.005 * masofa + 0.04 * ogirlik + hq + tt + 1.0 * tig
+ 0.8 * (tig & (hq > 0.4)) - 0.04 * kuryer_tajribasi
+ 0.4 * (hafta_kuni >= 5))
y = (rng.random(n) < 1 / (1 + np.exp(-kuch))).astype(int)
df = pd.DataFrame({"hudud": hudud, "tur": tur, "kanal": kanal,
"masofa": masofa, "ogirlik": ogirlik, "soat": soat,
"hafta_kuni": hafta_kuni,
"kuryer_tajribasi": kuryer_tajribasi, "kechikdi": y})
df.loc[rng.random(n) < 0.05, "kuryer_tajribasi"] = np.nan
return df
def tayyorlagich(kategoriya, sonli):
return ColumnTransformer([
("k", OneHotEncoder(handle_unknown="ignore"), kategoriya),
("s", SimpleImputer(strategy="median"), sonli)])
def main() -> None:
df = yarat()
X = df.drop(columns="kechikdi")
y = df["kechikdi"]
Xtr, Xte, ytr, yte = train_test_split(X, y, test_size=0.25, random_state=0,
stratify=y)
kategoriya = ["hudud", "tur", "kanal"]
sonli = ["masofa", "ogirlik", "soat", "hafta_kuni", "kuryer_tajribasi"]
cv = StratifiedKFold(5, shuffle=True, random_state=0)
def quvur(model):
return Pipeline([("t", tayyorlagich(kategoriya, sonli)),
("m", model)])
print("=== 1. Uch ansambl (sozlanmagan) ===")
variantlar = {
"RandomForest": RandomForestClassifier(n_estimators=300, random_state=0,
n_jobs=1),
"ExtraTrees": ExtraTreesClassifier(n_estimators=300, random_state=0,
n_jobs=1),
"HistGB": HistGradientBoostingClassifier(learning_rate=0.1,
max_iter=500,
early_stopping=True,
validation_fraction=0.15,
n_iter_no_change=20,
random_state=0),
}
ballar = {}
print(f" {'model':<14} {'ROC AUC':>9} {'std':>8} {'PR AUC':>9}")
for nom, m in variantlar.items():
n = cross_validate(quvur(m), Xtr, ytr, cv=cv,
scoring=["roc_auc", "average_precision"], n_jobs=1)
ballar[nom] = (n["test_roc_auc"].mean(), n["test_roc_auc"].std())
print(f" {nom:<14} {n['test_roc_auc'].mean():>9.4f} "
f"{n['test_roc_auc'].std():>8.4f} "
f"{n['test_average_precision'].mean():>9.4f}")
print("\n=== 2. Random Forest: max_features ===")
print(f" {'max_features':>13} {'ROC AUC':>9} {'std':>8}")
for mf in ["sqrt", 0.3, 0.5, None]:
n = cross_validate(quvur(RandomForestClassifier(n_estimators=250,
max_features=mf,
random_state=0)),
Xtr, ytr, cv=cv, scoring="roc_auc", n_jobs=1)
nom = "None" if mf is None else str(mf)
print(f" {nom:>13} {n['test_score'].mean():>9.4f} "
f"{n['test_score'].std():>8.4f}")
print("\n=== 3. Boosting: tasodifiy qidiruv ===")
asos = quvur(HistGradientBoostingClassifier(learning_rate=0.1,
max_iter=600,
early_stopping=True,
validation_fraction=0.15,
n_iter_no_change=20,
random_state=0))
taqsimot = {"m__max_depth": randint(2, 9),
"m__min_samples_leaf": randint(10, 120),
"m__l2_regularization": uniform(0, 6),
"m__max_features": uniform(0.5, 0.5)}
q = RandomizedSearchCV(asos, taqsimot, n_iter=20, cv=cv, scoring="roc_auc",
random_state=0, n_jobs=1).fit(Xtr, ytr)
print(f" sinovlar: {len(q.cv_results_['params'])}")
qisqa = {k.replace("m__", ""): (round(float(v), 3)
if isinstance(v, (float, np.floating))
else int(v))
for k, v in q.best_params_.items()}
print(f" eng yaxshi: {qisqa}")
print(f" CV ROC AUC: {q.best_score_:.4f} "
f"(sozlanmagan {ballar['HistGB'][0]:.4f})")
print(f" yutuq: {q.best_score_ - ballar['HistGB'][0]:+.4f} "
f"(CV std {ballar['HistGB'][1]:.4f})")
print("\n=== 4. Yakuniy: eta ni kamaytirib qayta o'qitish ===")
eng = {k.replace("m__", ""): v for k, v in q.best_params_.items()}
print(f" {'eta':>6} {'CV ROC AUC':>12} {'std':>8}")
for eta in [0.1, 0.05]:
m = quvur(HistGradientBoostingClassifier(learning_rate=eta,
max_iter=2000,
early_stopping=True,
validation_fraction=0.15,
n_iter_no_change=30,
random_state=0, **eng))
n = cross_validate(m, Xtr, ytr, cv=cv, scoring="roc_auc", n_jobs=1)
print(f" {eta:>6.2f} {n['test_score'].mean():>12.4f} "
f"{n['test_score'].std():>8.4f}")
print(" ⭐ Yutuqni CV std bilan solishtiring")
if __name__ == "__main__":
main()Natijaning muhim qismi:
=== 1. Uch ansambl (sozlanmagan) ===
model ROC AUC std PR AUC
RandomForest 0.7316 0.0154 0.3969
ExtraTrees 0.6972 0.0147 0.3460
HistGB 0.7592 0.0112 0.4367
=== 2. Random Forest: max_features ===
max_features ROC AUC std
sqrt 0.7308 0.0146
0.3 0.7308 0.0146
0.5 0.7338 0.0134
None 0.7331 0.0090
=== 3. Boosting: tasodifiy qidiruv ===
sinovlar: 20
eng yaxshi: {'l2_regularization': 5.016, 'max_depth': 2, 'max_features': 0.544, 'min_samples_leaf': 90}
CV ROC AUC: 0.7738 (sozlanmagan 0.7592)
yutuq: +0.0146 (CV std 0.0112)
=== 4. Yakuniy: eta ni kamaytirib qayta o'qitish ===
eta CV ROC AUC std
0.10 0.7735 0.0095
0.05 0.7725 0.0097
⭐ Yutuqni CV std bilan solishtiringNima ko'rsatdi: 2.2, 2.3-bo'limlar.
Misol 3 — Xatolar tahlili va muhimlik
"""7-qadam: segmentlar, kalibrlash, muhimlik (real pandas/sklearn)."""
import numpy as np
import pandas as pd
from sklearn.calibration import calibration_curve
from sklearn.compose import ColumnTransformer
from sklearn.ensemble import HistGradientBoostingClassifier
from sklearn.impute import SimpleImputer
from sklearn.inspection import permutation_importance
from sklearn.metrics import brier_score_loss, roc_auc_score
from sklearn.model_selection import train_test_split
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import OneHotEncoder
def yarat(seed: int = 42, n: int = 12000) -> pd.DataFrame:
rng = np.random.default_rng(seed)
hudud = rng.choice(["toshkent", "samarqand", "buxoro", "fargona",
"namangan"], n, p=[0.35, 0.2, 0.15, 0.2, 0.1])
tur = rng.choice(["oddiy", "tezkor", "yirik"], n, p=[0.6, 0.3, 0.1])
kanal = rng.choice(["ilova", "sayt", "qongiroq"], n, p=[0.5, 0.35, 0.15])
masofa = rng.gamma(3, 60, n)
ogirlik = rng.gamma(2, 4, n)
soat = rng.integers(0, 24, n).astype(float)
hafta_kuni = rng.integers(0, 7, n).astype(float)
kuryer_tajribasi = rng.gamma(2, 8, n)
hq = pd.Series(hudud).map({"toshkent": 0.0, "samarqand": 0.5,
"buxoro": 0.9, "fargona": 0.4,
"namangan": 0.6}).to_numpy()
tt = pd.Series(tur).map({"oddiy": 0.0, "tezkor": -0.9,
"yirik": 1.1}).to_numpy()
tig = ((soat >= 7) & (soat <= 10)) | ((soat >= 17) & (soat <= 19))
kuch = (-3.2 + 0.005 * masofa + 0.04 * ogirlik + hq + tt + 1.0 * tig
+ 0.8 * (tig & (hq > 0.4)) - 0.04 * kuryer_tajribasi
+ 0.4 * (hafta_kuni >= 5))
# namangan: belgilar bog'liqligi deyarli yo'q - modellash qiyin segment
qiyin = (hudud == "namangan")
kuch[qiyin] = -1.5 + rng.normal(0, 0.4, int(qiyin.sum()))
y = (rng.random(n) < 1 / (1 + np.exp(-kuch))).astype(int)
df = pd.DataFrame({"hudud": hudud, "tur": tur, "kanal": kanal,
"masofa": masofa, "ogirlik": ogirlik, "soat": soat,
"hafta_kuni": hafta_kuni,
"kuryer_tajribasi": kuryer_tajribasi, "kechikdi": y})
df.loc[rng.random(n) < 0.05, "kuryer_tajribasi"] = np.nan
return df
def main() -> None:
df = yarat()
X = df.drop(columns="kechikdi")
y = df["kechikdi"]
Xtr, Xte, ytr, yte = train_test_split(X, y, test_size=0.25, random_state=0,
stratify=y)
kategoriya = ["hudud", "tur", "kanal"]
sonli = ["masofa", "ogirlik", "soat", "hafta_kuni", "kuryer_tajribasi"]
quvur = Pipeline([
("t", ColumnTransformer([
("k", OneHotEncoder(handle_unknown="ignore"), kategoriya),
("s", SimpleImputer(strategy="median"), sonli)])),
("m", HistGradientBoostingClassifier(learning_rate=0.05, max_iter=1000,
max_depth=5, min_samples_leaf=40,
early_stopping=True,
validation_fraction=0.15,
n_iter_no_change=30,
random_state=0))]).fit(Xtr, ytr)
p = quvur.predict_proba(Xte)[:, 1]
print("=== 1. Umumiy natija ===")
print(f" test ROC AUC: {roc_auc_score(yte, p):.4f}")
print(f" Brier: {brier_score_loss(yte, p):.5f}")
print(f" o'rtacha bashorat {p.mean():.4f}, haqiqiy {yte.mean():.4f}")
print("\n=== 2. Segmentlar bo'yicha ===")
print(f" {'hudud':<12} {'namunalar':>10} {'kechikish':>10} {'AUC':>8}")
for h in sorted(Xte["hudud"].unique()):
m = (Xte["hudud"] == h).to_numpy()
if yte[m].nunique() < 2:
continue
print(f" {h:<12} {int(m.sum()):>10} {yte[m].mean():>9.2%} "
f"{roc_auc_score(yte[m], p[m]):>8.4f}")
print(" (namangan segmentida signal yo'q - AUC tasodifiyga yaqin)")
print("\n=== 3. Kalibrlash ===")
haqiqiy, bashorat = calibration_curve(yte, p, n_bins=8, strategy="quantile")
print(f" {'bashorat':>10} {'haqiqiy':>10} {'farq':>9}")
for b, h in zip(bashorat, haqiqiy):
print(f" {b:>10.4f} {h:>10.4f} {h - b:>+9.4f}")
print("\n=== 4. Permutation importance (test) ===")
r = permutation_importance(quvur, Xte, yte, n_repeats=10,
scoring="roc_auc", random_state=0, n_jobs=1)
tartib = np.argsort(-r.importances_mean)
print(f" {'belgi':<20} {'muhimlik':>10} {'std':>8}")
for i in tartib:
print(f" {X.columns[i]:<20} {r.importances_mean[i]:>+10.4f} "
f"{r.importances_std[i]:>8.4f}")
print(" (bu bashorat foydaliligi, sababiy ta'sir EMAS)")
print(" ⭐ Segment tahlili umumiy metrika yashirgan muammoni ochadi")
if __name__ == "__main__":
main()Natijaning muhim qismi:
=== 1. Umumiy natija ===
test ROC AUC: 0.7394
Brier: 0.12813
o'rtacha bashorat 0.1770, haqiqiy 0.1780
=== 2. Segmentlar bo'yicha ===
hudud namunalar kechikish AUC
buxoro 479 22.55% 0.7553
fargona 597 16.92% 0.7394
namangan 258 21.71% 0.4860
samarqand 593 22.26% 0.7744
toshkent 1073 12.77% 0.7452
(namangan segmentida signal yo'q - AUC tasodifiyga yaqin)
=== 3. Kalibrlash ===
bashorat haqiqiy farq
0.0332 0.0347 +0.0014
0.0647 0.0853 +0.0206
0.0897 0.0667 -0.0230
0.1159 0.1253 +0.0094
0.1523 0.1600 +0.0077
0.2052 0.2347 +0.0295
0.2855 0.2427 -0.0429
0.4697 0.4747 +0.0050
=== 4. Permutation importance (test) ===
belgi muhimlik std
soat +0.0562 0.0069
masofa +0.0548 0.0027
tur +0.0533 0.0080
hudud +0.0418 0.0049
kuryer_tajribasi +0.0217 0.0058
ogirlik +0.0091 0.0026
hafta_kuni +0.0032 0.0013
kanal +0.0006 0.0009
(bu bashorat foydaliligi, sababiy ta'sir EMAS)
⭐ Segment tahlili umumiy metrika yashirgan muammoni ochadiNima ko'rsatdi: 2.4, 2.5-bo'limlar.
Misol 4 — Chegara, taqqoslash va chiqarish
"""8-9 qadamlar: chegara, yakuniy baho, saqlash (real pandas/sklearn/joblib)."""
import io
import pickle
import numpy as np
import pandas as pd
import sklearn
from sklearn.compose import ColumnTransformer
from sklearn.ensemble import HistGradientBoostingClassifier, RandomForestClassifier
from sklearn.impute import SimpleImputer
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import (average_precision_score, f1_score,
precision_recall_curve, precision_score,
recall_score, roc_auc_score)
from sklearn.model_selection import train_test_split
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import OneHotEncoder, StandardScaler
def yarat(seed: int = 42, n: int = 12000) -> pd.DataFrame:
rng = np.random.default_rng(seed)
hudud = rng.choice(["toshkent", "samarqand", "buxoro", "fargona",
"namangan"], n, p=[0.35, 0.2, 0.15, 0.2, 0.1])
tur = rng.choice(["oddiy", "tezkor", "yirik"], n, p=[0.6, 0.3, 0.1])
kanal = rng.choice(["ilova", "sayt", "qongiroq"], n, p=[0.5, 0.35, 0.15])
masofa = rng.gamma(3, 60, n)
ogirlik = rng.gamma(2, 4, n)
soat = rng.integers(0, 24, n).astype(float)
hafta_kuni = rng.integers(0, 7, n).astype(float)
kuryer_tajribasi = rng.gamma(2, 8, n)
hq = pd.Series(hudud).map({"toshkent": 0.0, "samarqand": 0.5,
"buxoro": 0.9, "fargona": 0.4,
"namangan": 0.6}).to_numpy()
tt = pd.Series(tur).map({"oddiy": 0.0, "tezkor": -0.9,
"yirik": 1.1}).to_numpy()
tig = ((soat >= 7) & (soat <= 10)) | ((soat >= 17) & (soat <= 19))
kuch = (-3.2 + 0.005 * masofa + 0.04 * ogirlik + hq + tt + 1.0 * tig
+ 0.8 * (tig & (hq > 0.4)) - 0.04 * kuryer_tajribasi
+ 0.4 * (hafta_kuni >= 5))
y = (rng.random(n) < 1 / (1 + np.exp(-kuch))).astype(int)
df = pd.DataFrame({"hudud": hudud, "tur": tur, "kanal": kanal,
"masofa": masofa, "ogirlik": ogirlik, "soat": soat,
"hafta_kuni": hafta_kuni,
"kuryer_tajribasi": kuryer_tajribasi, "kechikdi": y})
df.loc[rng.random(n) < 0.05, "kuryer_tajribasi"] = np.nan
return df
def tayyorlagich(kategoriya, sonli, masshtab: bool = False):
sonli_quvur = ([("imp", SimpleImputer(strategy="median")),
("sc", StandardScaler())] if masshtab
else [("imp", SimpleImputer(strategy="median"))])
return ColumnTransformer([
("k", OneHotEncoder(handle_unknown="ignore"), kategoriya),
("s", Pipeline(sonli_quvur), sonli)])
def hajm_kb(model) -> float:
bufer = io.BytesIO()
pickle.dump(model, bufer, protocol=pickle.HIGHEST_PROTOCOL)
return bufer.tell() / 1024
def main() -> None:
df = yarat()
X = df.drop(columns="kechikdi")
y = df["kechikdi"]
Xtr, Xqol, ytr, yqol = train_test_split(X, y, test_size=0.4,
random_state=0, stratify=y)
Xval, Xte, yval, yte = train_test_split(Xqol, yqol, test_size=0.5,
random_state=0, stratify=yqol)
kategoriya = ["hudud", "tur", "kanal"]
sonli = ["masofa", "ogirlik", "soat", "hafta_kuni", "kuryer_tajribasi"]
print("=== 1. Yakuniy nomzodlar (test da BIR MARTA) ===")
nomzodlar = {
"LogReg": Pipeline([("t", tayyorlagich(kategoriya, sonli, True)),
("m", LogisticRegression(max_iter=2000))]),
"RandomForest": Pipeline([("t", tayyorlagich(kategoriya, sonli)),
("m", RandomForestClassifier(
n_estimators=400, max_features="sqrt",
min_samples_leaf=5, random_state=0,
n_jobs=1))]),
"HistGB": Pipeline([("t", tayyorlagich(kategoriya, sonli)),
("m", HistGradientBoostingClassifier(
learning_rate=0.05, max_iter=1000, max_depth=5,
min_samples_leaf=40, early_stopping=True,
validation_fraction=0.15, n_iter_no_change=30,
random_state=0))]),
}
print(f" {'model':<14} {'ROC AUC':>9} {'PR AUC':>9} {'hajm KB':>10}")
ehtimolliklar = {}
for nom, m in nomzodlar.items():
m.fit(Xtr, ytr)
pv = m.predict_proba(Xval)[:, 1]
pt = m.predict_proba(Xte)[:, 1]
ehtimolliklar[nom] = (pv, pt)
print(f" {nom:<14} {roc_auc_score(yte, pt):>9.4f} "
f"{average_precision_score(yte, pt):>9.4f} "
f"{hajm_kb(m):>10.0f}")
print("\n=== 2. Chegarani validatsiyada tanlash (HistGB) ===")
pv, pt = ehtimolliklar["HistGB"]
pr, rc, ch = precision_recall_curve(yval, pv)
f1 = 2 * pr[:-1] * rc[:-1] / np.maximum(pr[:-1] + rc[:-1], 1e-12)
eng_f1 = float(ch[int(np.argmax(f1))])
# biznes cheklovi: aniqlik (precision) >= 0.60
mos = np.where(pr[:-1] >= 0.60)[0]
eng_biz = float(ch[mos[int(np.argmax(rc[:-1][mos]))]]) if len(mos) else 0.5
print(f" F1 bo'yicha: {eng_f1:.4f}")
print(f" precision>=0.60 sharti bilan: {eng_biz:.4f}")
print("\n=== 3. Chegaralar test da ===")
print(f" {'chegara':>9} {'precision':>11} {'recall':>9} {'F1':>8} "
f"{'belgilangan %':>15}")
for nom, t in [("0.50", 0.5), ("F1", eng_f1), ("biznes", eng_biz)]:
tahmin = (pt > t).astype(int)
print(f" {nom:>9} {precision_score(yte, tahmin):>11.4f} "
f"{recall_score(yte, tahmin):>9.4f} "
f"{f1_score(yte, tahmin):>8.4f} {tahmin.mean():>14.1%}")
print("\n=== 4. Chiqarish paketi ===")
yakuniy = nomzodlar["HistGB"]
paket = {
"quvur": yakuniy,
"belgilar": list(X.columns),
"chegara": round(eng_biz, 4),
"chegara_asosi": "validatsiyada precision >= 0.60 sharti bilan",
"versiyalar": {"sklearn": sklearn.__version__,
"numpy": np.__version__, "pandas": pd.__version__},
"metrika": {"test_roc_auc": round(float(roc_auc_score(yte, pt)), 4),
"test_pr_auc": round(float(
average_precision_score(yte, pt)), 4)},
"o_quv_hajmi": len(Xtr),
}
print(f" kalitlar: {sorted(paket)}")
print(f" chegara: {paket['chegara']} ({paket['chegara_asosi']})")
print(f" metrika: {paket['metrika']}")
print(f" model hajmi: {hajm_kb(yakuniy):.0f} KB")
print(f" belgilar soni: {len(paket['belgilar'])}")
print(" ⭐ Model + chegara + metama'lumot = chiqarishga tayyor")
if __name__ == "__main__":
main()Natijaning muhim qismi:
=== 1. Yakuniy nomzodlar (test da BIR MARTA) ===
model ROC AUC PR AUC hajm KB
LogReg 0.7453 0.3874 3
RandomForest 0.7725 0.4408 30639
HistGB 0.7851 0.4653 272
=== 2. Chegarani validatsiyada tanlash (HistGB) ===
F1 bo'yicha: 0.2323
precision>=0.60 sharti bilan: 0.4403
=== 3. Chegaralar test da ===
chegara precision recall F1 belgilangan %
0.50 0.6350 0.2033 0.3080 5.7%
F1 0.4058 0.5888 0.4805 25.9%
biznes 0.5950 0.2780 0.3790 8.3%
=== 4. Chiqarish paketi ===
kalitlar: ['belgilar', 'chegara', 'chegara_asosi', 'metrika', 'o_quv_hajmi', 'quvur', 'versiyalar']
chegara: 0.4403 (validatsiyada precision >= 0.60 sharti bilan)
metrika: {'test_roc_auc': 0.7851, 'test_pr_auc': 0.4653}
model hajmi: 272 KB
belgilar soni: 8
⭐ Model + chegara + metama'lumot = chiqarishga tayyorNima ko'rsatdi: 2.6-bo'lim.
5. To'g'ri va noto'g'ri tushunishlar
| Noto'g'ri fikr | To'g'risi |
|---|---|
| "Bazaviy model keraksiz" | Realistik kutish beradi |
| "Yuqori AUC — tayyor model" | Chegara, kechikish, drift ham kerak |
| "Kichik farq — yaxshilanish" | CV std bilan solishtiring |
| "Testda bir necha marta baholash mumkin" | Bir marta |
| "Umumiy metrika yetarli" | Segmentlarga bo'ling |
| "0.5 chegara standart" | Vazifaga qarab tanlang |
| "Sozlash qancha uzoq bo'lsa, shuncha yaxshi" | Validatsiyaga overfitting |
| "Eng aniq model eng yaxshi" | Narx-foyda |
6. Keng tarqalgan xatolar va yechimlari
1. Bazaviy modelsiz
# to'g'ridan-to'g'ri boosting sozlash # ⚠️
# DummyClassifier + LogisticRegression bilan boshlang # ✅2. Testda ko'p marta baholash
for m in modellar: print(m.score(Xte, yte)) # tanlov uchun # ⚠️
# CV da tanlang, testda faqat yakuniy baho # ✅3. std ni hisobga olmaslik
# "0.847 > 0.842, demak yaxshiroq" # ⚠️
# std 0.008 bo'lsa - farq ahamiyatsiz # ✅4. Turli shartlarda taqqoslash
# RF sozlangan, LogReg standart # ⚠️
# har modelga mos byudjet ajrating # ✅5. Chegarani 0.5 da qoldirish
model.predict(Xte) # ⚠️
(model.predict_proba(Xte)[:, 1] > tanlangan).astype(int) # ✅6. Segment tahlilisiz
print(roc_auc_score(yte, p)) # ⚠️
# hudud/kanal/davr bo'yicha bo'lib ko'ring # ✅7. Hujjatsiz topshirish
joblib.dump(model, "model.joblib") # ⚠️
# quvur + chegara + metrika + versiyalar + tahlil hisoboti # ✅7. Integratsiya — bu bilim qayerda kerak bo'ladi
- 12-qism (o'tilgan): ML asoslari
- 15.5, 15.8-darslar (o'tilgan): RF va boosting
- 15.11-dars (o'tilgan): Muhimlik
- 15.13-dars (o'tilgan): Ishlab chiqarish
- Keyingi qism: Nazoratsiz o'rganish
8. Eng yaxshi amaliyotlar
Bazaviy modeldan boshlang.
Bir xil shartlarda taqqoslang.
CV std ni ko'ring.
Sozlashni tartib bilan qiling.
Segmentlarga bo'ling.
Chegarani asoslang.
Testni oxirida ishlating.
Hamma narsani hujjatlashtiring.
9. Amaliy topshiriq
Vazifa 1: Bashorat qiling
1. # oqimning birinchi qadami?
2. # bazaviy modellar?
3. # sozlanmagan RF nima uchun?
4. # halol taqqoslash sharti?
5. # farq muhimligini qanday bilish?
6. # RF uchun byudjet?
7. # boosting uchun?
8. # xatolar tahlilida birinchi nima?
9. # muhimlik qaysi to'plamda?
10. # chegara qayerda tanlanadi?
11. # test necha marta?
12. # chiqarishda nima saqlanadi?Javoblar
- Ma'lumotni ko'rish
- Dummy va LogisticRegression
- Haqiqiy boshlang'ich nuqta
- Bir xil CV, metrika, tayyorlash
- CV std bilan solishtirish
- 5-10 konfiguratsiya
- 40-80
- Segmentlarga bo'lish
- Test
- Validatsiyada
- Bir marta
- Pipeline + metama'lumot + chegara
Vazifa 2: Xatolarni tuzating
1. # to'g'ridan-to'g'ri boosting sozlashdan boshlash
2. for m in modellar: print(m.score(Xte, yte))
3. # "0.847 > 0.842, yaxshiroq" (std 0.008)
4. model.predict(Xte) # chegara 0.5
5. joblib.dump(model, "model.joblib")Javoblar
1. # DummyClassifier + LogisticRegression bilan boshlang
2. # CV da tanlang, testda bir marta
3. # farq std dan kichik - ahamiyatsiz
4. (model.predict_proba(Xte)[:, 1] > tanlangan).astype(int)
5. joblib.dump({"quvur": ..., "chegara": ..., "versiyalar": ...}, ...)Vazifa 3: Bazaviy
Modellang:
- Ma'lumot
- Ajratish
- Bazaviy modellar
- Sozlanmagan RF
Vazifa 4: Ansambllar
Modellang:
- Uch ansambl
- max_features
- Tasodifiy qidiruv
- eta
Vazifa 5: Tahlil
Modellang:
- Umumiy natija
- Segmentlar
- Kalibrlash
- Muhimlik
Vazifa 6: Chiqarish
Modellang:
- Nomzodlar
- Chegara
- Testda chegaralar
- Paket
Vazifa 7: O'ylash
Loyihada HistGB 0.8412, Random Forest 0.8389, logistik regressiya 0.8201 AUC berdi (CV std 0.006). Qaysi birini ishlab chiqarishga chiqarish kerak?
Javob
Qisqa javob: HistGB va Random Forest farqi (0.0023) CV standart og'ishidan (0.006) kichik — ya'ni statistik jihatdan ular teng. Tanlov shuning uchun muhandislik mezonlari bo'yicha qilinadi. Logistik regressiya esa sezilarli ortda (0.021 ≈ 3.5 std) — lekin u ham butunlay rad etilmaydi.
1. Farqlarni baholash
| Taqqoslash | Farq | std ga nisbatan | Xulosa |
|---|---|---|---|
| HistGB vs RF | 0.0023 | 0.4 std | Teng |
| HistGB vs LogReg | 0.0211 | 3.5 std | Sezilarli |
2. Teng bo'lganda nima hal qiladi
- Model hajmi: RF odatda ancha katta (15.13)
- Kechikish: GB odatda tezroq (sayoz daraxtlar)
- OOB: RF da bepul validatsiya bor
- Sozlash barqarorligi: RF kam sozlanadi, qayta o'qitishda ishonchliroq
- Jamoaning tajribasi: qo'llab-quvvatlash osonligi
3. Logistik regressiyani qachon tanlash mumkin
- Talqin qilish majburiy (regulyator talabi)
- Kechikish juda qattiq (mikrosekundlar)
- Model juda tez-tez qayta o'qitiladi
- 0.021 AUC farqi biznes uchun ahamiyatsiz bo'lsa
Bu qarorni biznes qiymati bilan hisoblang: 0.021 AUC necha so'mga teng?
4. Amaliy tavsiya
- HistGB va RF ni kechikish va hajm bo'yicha o'lchang
- Ikkalasini segmentlar bo'yicha solishtiring
- Kalibrlashni tekshiring
- Qolgan mezonlar teng bo'lsa — kichikroq va tezroq modelni tanlang
- Qarorni va uning asosini hujjatlashtiring
5. Xulosa
- Farq std dan kichik bo'lsa — modellar teng
- Tanlov muhandislik mezonlari bo'yicha
- Soddaroq model — kamroq xavf
- Qarorni hujjatlashtiring
Nimani mustahkamlaydi: 2.2, 2.6-bo'limlar.
Xulosa
Bu darsda to'liq ansambl loyihasini qurdik.
Eng muhim uch fikr:
Tartib bilan ishlang. Ma'lumot → ajratish → bazaviy (Dummy + LogReg) → sozlanmagan RF → boosting + erta to'xtatish → tartibli sozlash → xatolar tahlili → chegara → chiqarish. Bazaviy modellar sizga realistik kutish beradi: ularsiz 0.87 AUC yaxshimi yoki yomonmi — bilib bo'lmaydi.
Halol taqqoslang va std ni ko'ring. Modellarni bir xil CV bo'linishi, metrika va tayyorlash bilan solishtiring, har biriga mos sozlash byudjeti ajrating. Eng muhimi: yutuqni CV standart og'ishi bilan taqqoslang — farq std dan kichik bo'lsa, modellar teng va soddaroq/tezroq bo'lganini tanlang.
Metrika — ishning yarmi. Umumiy AUC segmentlar bo'yicha bo'linganda ko'pincha bitta muammoli guruhni yashiradi. Chegarani validatsiyada, biznes sharti bilan tanlang va asoslang. Yakunda Pipeline + chegara + metrikalar + versiyalar + tahlil hisoboti topshiriladi — modelning o'zi emas.
Bu bilan 15-qism — Daraxtlar va ansambllar yakunlandi. Keyingi qismda nazoratsiz o'rganishni o'rganamiz: klasterlash, o'lchamni kamaytirish va anomaliyalarni aniqlash.
Izohlar (0)
Izoh yozish uchun kiring.
- Hozircha izoh yo'q. Birinchi bo'ling!