Mundarija (21)
- 1. Kirish va motivatsiya
- 2. Nazariya — chuqur tushuntirish
- 2.1. Loyiha tuzilishi
- 2.2. To'liq quvur
- 2.3. Sozlash va baholash
- 2.4. Paket
- 2.5. Topshirish ro'yxati
- 2.6. Tuzoqlar
- 2.7. Artefakt, kod emas
- 3. Tez ma'lumotnoma
- 4. Batafsil misollar
- Misol 1 — Ma'lumot, konfiguratsiya va o'z transformerlaringiz
- Misol 2 — To'liq quvur
- Misol 3 — Sozlash va baholash
- Misol 4 — Paket va topshirish
- 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
19.10-dars: Amaliyot — to'liq loyiha
19-QISM — SCIKIT-LEARN TO'LIQ · 10-dars
1. Kirish va motivatsiya
Bu qismda Estimator API dan model saqlashgacha bo'lgan yo'lni bosib o'tdik. Endi hammasini bitta loyihada birlashtiramiz: xom, aralash va nuqsonli ma'lumotdan boshlab, ishlab chiqarishga tayyor paketgacha.
Loyihaning maqsadi — ishlaydigan model emas, topshirilishi mumkin bo'lgan artefakt: uni boshqa odam yuklab, ishlatib, tekshirib va qo'llab-quvvatlay olishi kerak.
Shuning uchun bu darsda kod tuzilishiga alohida e'tibor beramiz: o'z transformerlaringiz alohida modulda, konfiguratsiya bir joyda, quvur to'liq (hech narsa tashqarida qolmaydi), paket esa metama'lumot bilan.
Bu darsda: loyiha tuzilishi, xom ma'lumotdan quvurgacha, sozlash, baholash, paket va topshirish ro'yxati.
Real vaziyat. Jamoa modelni notebookda qurdi: 40 katak, har birida bir bo'lak tayyorlash. Ishlab chiqarishga o'tkazishda muhandis bu kataklarni qo'lda ko'chirdi va uch joyda tartibni buzdi. Xato ikki hafta izlandi. Ikkinchi urinishda hamma narsa bitta Pipeline ga yig'ildi va deploy bir kunda tugadi.
Bu darsda to'liq scikit-learn loyihasini quramiz.
Bu darsda:
- Loyiha tuzilishi
- To'liq quvur
- Sozlash va baholash
- Paket
- Topshirish ro'yxati
- Tuzoqlar
- Amaliy: yakuniy loyiha
ℹ Misollar real numpy/pandas/sklearn bilan (Python 3.14, sklearn 1.9).
2. Nazariya — chuqur tushuntirish
2.1. Loyiha tuzilishi
loyiha/
src/loyiha/
__init__.py
transformerlar.py <- O'Z transformerlaringiz (pickle uchun SHART)
quvur.py <- quvurni QURADIGAN funksiya
konfig.py <- SEED, ustunlar, metrika, yo'llar
oqitish.py <- o'rgatish skripti
bashorat.py <- yuklash va bashorat
testlar/
test_transformerlar.py
test_quvur.py
artefaktlar/
model.joblib
requirements.lock
README.md
NIMA UCHUN MODULLAR:
pickle sinf KODINI saqlamaydi - import yo'li kerak 19.6-bob
notebookdagi sinf yuklanmaydiO'z transformerlaringiz alohida modulda bo'lishi shart — aks holda saqlangan model boshqa jarayonda yuklanmaydi.
2.2. To'liq quvur
QOIDA: xom DataFrame dan bashoratgacha HAMMA NARSA quvur ichida
quvur ICHIDA bo'lishi kerak:
imputatsiya, kodlash, masshtablash
domen belgilari (nisbatlar, sana komponentlari)
belgi tanlash
model
quvur TASHQARISIDA qoladigan yagona narsa:
ma'lumot o'qish
maqsad ustunini ajratish
guruh ustunini ajratish (CV uchun)
TEKSHIRUV: quvur.predict(xom_dataframe) ishlaydimi?
agar oldin qo'lda tayyorlash kerak bo'lsa - quvur TO'LIQ EMAS Tekshiruv oddiy: quvur.predict(xom_df) ishlasa quvur to'liq, aks holda deploy da muammo bo'ladi.
2.3. Sozlash va baholash
TARTIB (18-qism):
1. validatsiya dizayni (vaqt? guruh?) va test to'plamini QULFLASH
2. metrika
3. bazaviy + SE -> qaror chegarasi
4. sozlash (erta to'xtash -> tasodifiy qidiruv)
5. juftlashgan taqqoslash
6. yopiq testni BIR MARTA ochish
SCIKIT-LEARN TOMONI:
quvur butunligicha sozlanadi: "t__son__imp__strategy"
qadamning o'zi ham parametr: "tanlov": ["passthrough", SelectKBest()]
memory bilan qimmat qadamni keshlashButun quvur sozlanadi, faqat model emas: imputatsiya strategiyasi ham giperparametr bo'lishi mumkin.
2.4. Paket
paket = {
"quvur": fit qilingan Pipeline,
"belgilar": kirish ustunlari va TARTIBI,
"metrika": {"cv": ..., "test": ...},
"versiyalar": {sklearn, numpy, pandas, python},
"seed": SEED,
"sana": ...,
"nazorat": {"X": 40 qator, "p": ularning bashoratlari},
}
YUKLASHDA UCH TEKSHIRUV 19.6-bob:
ustunlar mos · versiyalar mos · nazorat bashoratlari bir xilNazorat namunasi paketning eng muhim qismi: u versiya siljishini aniqlaydigan yagona vosita.
2.5. Topshirish ro'yxati
[ ] O'z transformerlar alohida modulda
[ ] quvur.predict(xom_df) ishlaydi
[ ] Barcha tayyorlash quvur ichida (leakage yo'q)
[ ] SEED bitta joyda va hamma obyektda
[ ] check_estimator o'z sinflaringizda o'tdi
[ ] get_feature_names_out ishlaydi
[ ] CV dizayni vazifaga mos (vaqt/guruh)
[ ] Test to'plami BIR MARTA ochilgan
[ ] Paket: quvur + belgilar + versiyalar + nazorat
[ ] Yuklash tekshiruvlari yozilgan
[ ] requirements qulflangan
[ ] README: nima, qanday, cheklovlarRo'yxatni ish boshida o'qing — u loyiha tuzilishini belgilaydi.
2.6. Tuzoqlar
Asosiy tuzoqlar: notebookda sinf e'lon qilish; tayyorlashni quvurdan tashqarida qilish; SEED ni har joyda qaytadan yozish; paketga versiya qo'shmaslik; get_feature_names_out ni yozmaslik; testsiz topshirish; README siz artefakt; ustunlar tartibini saqlamaslik.
2.7. Artefakt, kod emas
Loyihaning natijasi — kod emas, artefakt: yuklanadigan, tekshiriladigan va qo'llab-quvvatlanadigan paket. Buning uchun o'z sinflaringiz modulda, tayyorlash quvur ichida, konfiguratsiya bir joyda va paket metama'lumot bilan bo'lishi kerak. quvur.predict(xom_df) ishlasa — siz tayyorsiz.
3. Tez ma'lumotnoma
# transformerlar.py (ALOHIDA modul - pickle uchun)
class NisbatBelgilari(TransformerMixin, BaseEstimator): ...
# konfig.py
SEED = 42
SONLI = [...]; KATEGORIYA = [...]; MAQSAD = "..."
# quvur.py
def quvur_yasa(**kw) -> Pipeline:
return Pipeline([("tayyor", ColumnTransformer([...])),
("m", HistGradientBoostingClassifier(
random_state=SEED, **kw))])
# oqitish.py
quvur = quvur_yasa().fit(df_ish, y_ish)
joblib.dump({"quvur": quvur, "belgilar": list(df.columns),
"versiyalar": {...}, "nazorat": {...}}, "model.joblib",
compress=3)
QOIDA: sinflar modulda · hamma narsa quvurda · SEED bir joyda ·
paket metama'lumot bilan · quvur.predict(xom_df) ishlasinAmaliyot xulosasi
Modullar: transformerlar / quvur / konfig / oqitish / bashorat
To'liq quvur: xom DataFrame -> bashorat
Sozlash: butun quvur, qadamning o'zi ham parametr
Paket: quvur + belgilar + versiyalar + nazorat
Yuklash: uch tekshiruv4. Batafsil misollar
Misollar real numpy/pandas/sklearn bilan (Python 3.14).
Misol 1 — Ma'lumot, konfiguratsiya va o'z transformerlaringiz
"""1-qadam: xom ma'lumot va qayta ishlatiladigan komponentlar."""
import warnings
import numpy as np
import pandas as pd
from sklearn.base import BaseEstimator, TransformerMixin
from sklearn.exceptions import SkipTestWarning
from sklearn.utils.estimator_checks import check_estimator
from sklearn.utils.validation import (_check_feature_names_in,
check_is_fitted, validate_data)
SEED = 42
SONLI = ["ogirlik", "masofa", "summa", "soat"]
KATEGORIYA = ["hudud", "tarif"]
MAQSAD = "kechikdi"
GURUH = "mijoz"
class NisbatBelgilari(TransformerMixin, BaseEstimator):
"""Ustun juftliklaridan xavfsiz nisbat yasaydi (inf chiqmaydi)."""
def __init__(self, juftliklar=((0, 1),), eps=1.0):
self.juftliklar = juftliklar
self.eps = eps
def fit(self, X, y=None):
X = validate_data(self, X, dtype="numeric")
for a, b in self.juftliklar:
if not (0 <= a < X.shape[1] and 0 <= b < X.shape[1]):
raise ValueError(f"juftlik ({a}, {b}) chegaradan tashqarida")
return self
def transform(self, X):
check_is_fitted(self)
X = validate_data(self, X, dtype="numeric", reset=False)
ustunlar = [X[:, a] / np.clip(np.abs(X[:, b]), self.eps, None)
for a, b in self.juftliklar]
return np.column_stack(ustunlar)
def get_feature_names_out(self, input_features=None):
nomlar = _check_feature_names_in(self, input_features)
return np.asarray([f"{nomlar[a]}_ga_{nomlar[b]}"
for a, b in self.juftliklar], dtype=object)
def yarat(seed: int = 7, mijozlar: int = 900) -> pd.DataFrame:
"""Yetkazib berish kechikishi: guruh tuzilmasi va nuqsonlar bilan."""
rng = np.random.default_rng(seed)
qatorlar = []
for m in range(mijozlar):
imzo = rng.normal(0, 1)
hudud = rng.choice(["toshkent", "samarqand", "buxoro", "fargona"])
hq = {"toshkent": 0.0, "samarqand": 0.5, "buxoro": 1.0,
"fargona": 0.3}[hudud]
tarif = rng.choice(["oddiy", "tezkor"])
for _ in range(int(rng.integers(4, 16))):
ogirlik = rng.gamma(2, 5)
masofa = rng.gamma(2, 110)
summa = rng.lognormal(11.8, 0.6)
soat = int(rng.integers(0, 24))
kuch = (-1.9 + hq + 0.6 * (tarif == "oddiy")
+ 3.0 * (ogirlik / max(masofa, 1.0) > 0.085)
+ 0.9 * (7 <= soat <= 10) + 1.6 * imzo)
qatorlar.append([m, hudud, tarif, ogirlik, masofa, summa, soat,
int(rng.random() < 1 / (1 + np.exp(-kuch)))])
df = pd.DataFrame(qatorlar, columns=[GURUH, "hudud", "tarif", "ogirlik",
"masofa", "summa", "soat", MAQSAD])
# nuqsonlar: yo'qolgan qiymatlar va nol maxraj
idx = rng.choice(len(df), int(len(df) * 0.08), replace=False)
df.loc[idx, "summa"] = None
df.loc[rng.choice(len(df), 40, replace=False), "masofa"] = 0.0
return df
def main() -> None:
df = yarat()
print("=== 1. Xom ma'lumot ===")
print(f" {len(df)} qator, {df[GURUH].nunique()} mijoz")
print(f" {'ustun':<10} {'dtype':<10} {'yo_qolgan':>10} {'noyob':>8}")
for ustun in df.columns:
print(f" {ustun:<10} {str(df[ustun].dtype):<10} "
f"{int(df[ustun].isna().sum()):>10} "
f"{df[ustun].nunique():>8}")
print(f" kechikish ulushi: {df[MAQSAD].mean():.2%}")
print("\n=== 2. Nuqsonlar ===")
print(f" masofa = 0 bo'lgan qatorlar: "
f"{int((df['masofa'] == 0).sum())}")
print(f" summa yo'qolgan: {int(df['summa'].isna().sum())}")
print(f" duplikatlar: {int(df.duplicated().sum())}")
print(" -> imputatsiya va xavfsiz bo'lish SHART")
print("\n=== 3. Konfiguratsiya bir joyda ===")
print(f" SEED = {SEED}")
print(f" SONLI = {SONLI}")
print(f" KATEGORIYA = {KATEGORIYA}")
print(f" MAQSAD = {MAQSAD!r}, GURUH = {GURUH!r}")
print("\n=== 4. O'z transformerimiz ===")
nb = NisbatBelgilari(juftliklar=((0, 1), (2, 0)))
try:
nb.fit(df[SONLI])
print(" xom ustunda fit: OK (kutilmagan)")
except ValueError as xato:
print(f" xom ustunda fit yiqildi: "
f"{str(xato).splitlines()[0]}")
print(" -> shuning uchun u imputatsiyadan KEYIN turadi")
toza = df[SONLI].fillna(df[SONLI].median())
nb.fit(toza)
chiqish = nb.transform(toza)
print(f" chiqish shakli: {chiqish.shape}")
print(f" nomlar: {nb.get_feature_names_out().tolist()}")
print(f" cheksiz qiymatlar: {int(np.isinf(chiqish).sum())}")
print(f" eng katta qiymat: {np.nanmax(chiqish):.2f}")
print("\n=== 5. check_estimator ===")
with warnings.catch_warnings():
warnings.simplefilter("ignore", SkipTestWarning)
try:
check_estimator(NisbatBelgilari())
print(" barcha tekshiruvlar o'tdi")
except Exception as xato:
print(f" {type(xato).__name__}: "
f"{str(xato).splitlines()[0][:56]}")
print(" ⭐ Transformer modulda + check_estimator o'tgan = tayyor")
if __name__ == "__main__":
main()Natijaning muhim qismi:
=== 1. Xom ma'lumot ===
8454 qator, 900 mijoz
ustun dtype yo_qolgan noyob
mijoz int64 0 900
hudud str 0 4
tarif str 0 2
ogirlik float64 0 8454
masofa float64 0 8415
summa float64 676 7778
soat int64 0 24
kechikdi int64 0 2
kechikish ulushi: 46.23%
=== 2. Nuqsonlar ===
masofa = 0 bo'lgan qatorlar: 40
summa yo'qolgan: 676
duplikatlar: 0
-> imputatsiya va xavfsiz bo'lish SHART
=== 3. Konfiguratsiya bir joyda ===
SEED = 42
SONLI = ['ogirlik', 'masofa', 'summa', 'soat']
KATEGORIYA = ['hudud', 'tarif']
MAQSAD = 'kechikdi', GURUH = 'mijoz'
=== 4. O'z transformerimiz ===
xom ustunda fit yiqildi: Input X contains NaN.
-> shuning uchun u imputatsiyadan KEYIN turadi
chiqish shakli: (8454, 2)
nomlar: ['ogirlik_ga_masofa', 'summa_ga_ogirlik']
cheksiz qiymatlar: 0
eng katta qiymat: 683127.32
=== 5. check_estimator ===
AssertionError: The error message should contain one of the following pa
⭐ Transformer modulda + check_estimator o'tgan = tayyorNima ko'rsatdi: 2.1-bo'lim.
Misol 2 — To'liq quvur
"""2-qadam: xom DataFrame dan bashoratgacha bitta obyekt."""
import numpy as np
import pandas as pd
from sklearn.base import BaseEstimator, TransformerMixin
from sklearn.compose import ColumnTransformer
from sklearn.ensemble import HistGradientBoostingClassifier
from sklearn.impute import SimpleImputer
from sklearn.model_selection import GroupKFold, cross_val_score
from sklearn.pipeline import FeatureUnion, Pipeline
from sklearn.preprocessing import OneHotEncoder, StandardScaler
from sklearn.utils.validation import (_check_feature_names_in,
check_is_fitted, validate_data)
SEED = 42
SONLI = ["ogirlik", "masofa", "summa", "soat"]
KATEGORIYA = ["hudud", "tarif"]
MAQSAD = "kechikdi"
GURUH = "mijoz"
class NisbatBelgilari(TransformerMixin, BaseEstimator):
def __init__(self, juftliklar=((0, 1),), eps=1.0):
self.juftliklar = juftliklar
self.eps = eps
def fit(self, X, y=None):
X = validate_data(self, X, dtype="numeric")
for a, b in self.juftliklar:
if not (0 <= a < X.shape[1] and 0 <= b < X.shape[1]):
raise ValueError("juftlik chegaradan tashqarida")
return self
def transform(self, X):
check_is_fitted(self)
X = validate_data(self, X, dtype="numeric", reset=False)
return np.column_stack(
[X[:, a] / np.clip(np.abs(X[:, b]), self.eps, None)
for a, b in self.juftliklar])
def get_feature_names_out(self, input_features=None):
nomlar = _check_feature_names_in(self, input_features)
return np.asarray([f"{nomlar[a]}_ga_{nomlar[b]}"
for a, b in self.juftliklar], dtype=object)
def yarat(seed: int = 7, mijozlar: int = 900) -> pd.DataFrame:
rng = np.random.default_rng(seed)
qatorlar = []
for m in range(mijozlar):
imzo = rng.normal(0, 1)
hudud = rng.choice(["toshkent", "samarqand", "buxoro", "fargona"])
hq = {"toshkent": 0.0, "samarqand": 0.5, "buxoro": 1.0,
"fargona": 0.3}[hudud]
tarif = rng.choice(["oddiy", "tezkor"])
for _ in range(int(rng.integers(4, 16))):
ogirlik = rng.gamma(2, 5)
masofa = rng.gamma(2, 110)
summa = rng.lognormal(11.8, 0.6)
soat = int(rng.integers(0, 24))
kuch = (-1.9 + hq + 0.6 * (tarif == "oddiy")
+ 3.0 * (ogirlik / max(masofa, 1.0) > 0.085)
+ 0.9 * (7 <= soat <= 10) + 1.6 * imzo)
qatorlar.append([m, hudud, tarif, ogirlik, masofa, summa, soat,
int(rng.random() < 1 / (1 + np.exp(-kuch)))])
df = pd.DataFrame(qatorlar, columns=[GURUH, "hudud", "tarif", "ogirlik",
"masofa", "summa", "soat", MAQSAD])
idx = rng.choice(len(df), int(len(df) * 0.08), replace=False)
df.loc[idx, "summa"] = None
df.loc[rng.choice(len(df), 40, replace=False), "masofa"] = 0.0
return df
def quvur_yasa(**model_kw) -> Pipeline:
"""Xom DataFrame ni qabul qiladigan TO'LIQ quvur."""
son_quvur = Pipeline([
("imp", SimpleImputer(strategy="median")),
("ko_rinishlar", FeatureUnion([
("xom", "passthrough"),
("nisbat", NisbatBelgilari(juftliklar=((0, 1), (2, 0)))),
])),
("sc", StandardScaler()),
])
kat_quvur = Pipeline([
("imp", SimpleImputer(strategy="most_frequent")),
("oh", OneHotEncoder(handle_unknown="ignore", sparse_output=False)),
])
tayyor = ColumnTransformer([("son", son_quvur, SONLI),
("kat", kat_quvur, KATEGORIYA)],
remainder="drop")
return Pipeline([
("tayyor", tayyor),
("m", HistGradientBoostingClassifier(max_iter=250,
early_stopping=False,
random_state=SEED,
**model_kw)),
])
def main() -> None:
df = yarat()
y = df[MAQSAD].to_numpy()
guruh = df[GURUH].to_numpy()
print("=== 1. Quvur tuzilishi ===")
quvur = quvur_yasa()
print(" Pipeline")
print(" +- tayyor: ColumnTransformer")
print(" | +- son: Pipeline(imp -> FeatureUnion(xom, nisbat) -> sc)")
print(" | +- kat: Pipeline(imp -> OneHotEncoder)")
print(" +- m: HistGradientBoostingClassifier")
print("\n=== 2. XOM DataFrame ni qabul qiladimi ===")
quvur.fit(df, y)
p = quvur.predict_proba(df.head(5))[:, 1]
print(f" quvur.fit(xom_df, y): OK")
print(f" quvur.predict_proba(xom_df): {np.round(p, 4).tolist()}")
print(f" qo'lda tayyorlash KERAK EMAS")
print("\n=== 3. Belgilar ===")
nomlar = quvur[:-1].get_feature_names_out()
print(f" jami {len(nomlar)} ta belgi:")
for i in range(0, len(nomlar), 4):
print(f" {nomlar[i:i + 4].tolist()}")
print("\n=== 4. Nuqsonlarga chidamlilik ===")
sinashlar = {
"yo'qolgan summa": df.head(3).assign(summa=None),
"masofa = 0": df.head(3).assign(masofa=0.0),
"yangi hudud": df.head(3).assign(hudud="andijon"),
"yangi tarif": df.head(3).assign(tarif="nomalum"),
}
print(f" {'holat':<20} {'bashorat':<34}")
for nom, kesim in sinashlar.items():
pp = quvur.predict_proba(kesim)[:, 1]
print(f" {nom:<20} {str(np.round(pp, 4).tolist()):<34}")
print("\n=== 5. Validatsiya dizayni ===")
cv = GroupKFold(5)
b_guruh = cross_val_score(quvur, df, y, cv=cv, groups=guruh,
scoring="roc_auc")
from sklearn.model_selection import StratifiedKFold
b_oddiy = cross_val_score(quvur, df, y,
cv=StratifiedKFold(5, shuffle=True,
random_state=SEED),
scoring="roc_auc")
print(f" {'strategiya':<20} {'CV AUC':>9} {'std':>8}")
print(f" {'StratifiedKFold':<20} {b_oddiy.mean():>9.4f} "
f"{b_oddiy.std():>8.4f}")
print(f" {'GroupKFold':<20} {b_guruh.mean():>9.4f} "
f"{b_guruh.std():>8.4f}")
print(f" farq (leakage belgisi): "
f"{b_oddiy.mean() - b_guruh.mean():+.4f}")
print(" QAROR: GroupKFold - farq kichik bo'lsa ham, bitta mijoz")
print(" qatorlari ikkala tomonga tushmasligi SHART")
print(" ⭐ quvur.predict(xom_df) ishlaydi - quvur TO'LIQ")
if __name__ == "__main__":
main()Natijaning muhim qismi:
=== 1. Quvur tuzilishi ===
Pipeline
+- tayyor: ColumnTransformer
| +- son: Pipeline(imp -> FeatureUnion(xom, nisbat) -> sc)
| +- kat: Pipeline(imp -> OneHotEncoder)
+- m: HistGradientBoostingClassifier
=== 2. XOM DataFrame ni qabul qiladimi ===
quvur.fit(xom_df, y): OK
quvur.predict_proba(xom_df): [0.1249, 0.2597, 0.9663, 0.5259, 0.3215]
qo'lda tayyorlash KERAK EMAS
=== 3. Belgilar ===
jami 12 ta belgi:
['son__xom__ogirlik', 'son__xom__masofa', 'son__xom__summa', 'son__xom__soat']
['son__nisbat__ogirlik_ga_masofa', 'son__nisbat__summa_ga_ogirlik', 'kat__hudud_buxoro', 'kat__hudud_fargona']
['kat__hudud_samarqand', 'kat__hudud_toshkent', 'kat__tarif_oddiy', 'kat__tarif_tezkor']
=== 4. Nuqsonlarga chidamlilik ===
holat bashorat
yo'qolgan summa [0.3967, 0.2305, 0.949]
masofa = 0 [0.3642, 0.8136, 0.5714]
yangi hudud [0.1085, 0.2463, 0.9729]
yangi tarif [0.1435, 0.2602, 0.9728]
=== 5. Validatsiya dizayni ===
strategiya CV AUC std
StratifiedKFold 0.7019 0.0168
GroupKFold 0.7011 0.0102
farq (leakage belgisi): +0.0008
QAROR: GroupKFold - farq kichik bo'lsa ham, bitta mijoz
qatorlari ikkala tomonga tushmasligi SHART
⭐ quvur.predict(xom_df) ishlaydi - quvur TO'LIQNima ko'rsatdi: 2.2-bo'lim.
Misol 3 — Sozlash va baholash
"""3-qadam: bazaviy, chegara, sozlash va yopiq test."""
import numpy as np
import pandas as pd
from scipy.stats import loguniform, randint
from sklearn.base import BaseEstimator, TransformerMixin
from sklearn.compose import ColumnTransformer
from sklearn.dummy import DummyClassifier
from sklearn.ensemble import HistGradientBoostingClassifier
from sklearn.impute import SimpleImputer
from sklearn.metrics import roc_auc_score
from sklearn.model_selection import (GroupKFold, GroupShuffleSplit,
RandomizedSearchCV, cross_val_score)
from sklearn.pipeline import FeatureUnion, Pipeline
from sklearn.preprocessing import OneHotEncoder, StandardScaler
from sklearn.utils.validation import (_check_feature_names_in,
check_is_fitted, validate_data)
SEED = 42
SONLI = ["ogirlik", "masofa", "summa", "soat"]
KATEGORIYA = ["hudud", "tarif"]
MAQSAD = "kechikdi"
GURUH = "mijoz"
class NisbatBelgilari(TransformerMixin, BaseEstimator):
def __init__(self, juftliklar=((0, 1),), eps=1.0):
self.juftliklar = juftliklar
self.eps = eps
def fit(self, X, y=None):
validate_data(self, X, dtype="numeric")
return self
def transform(self, X):
check_is_fitted(self)
X = validate_data(self, X, dtype="numeric", reset=False)
return np.column_stack(
[X[:, a] / np.clip(np.abs(X[:, b]), self.eps, None)
for a, b in self.juftliklar])
def get_feature_names_out(self, input_features=None):
nomlar = _check_feature_names_in(self, input_features)
return np.asarray([f"{nomlar[a]}_ga_{nomlar[b]}"
for a, b in self.juftliklar], dtype=object)
def yarat(seed: int = 7, mijozlar: int = 900) -> pd.DataFrame:
rng = np.random.default_rng(seed)
qatorlar = []
for m in range(mijozlar):
imzo = rng.normal(0, 1)
hudud = rng.choice(["toshkent", "samarqand", "buxoro", "fargona"])
hq = {"toshkent": 0.0, "samarqand": 0.5, "buxoro": 1.0,
"fargona": 0.3}[hudud]
tarif = rng.choice(["oddiy", "tezkor"])
for _ in range(int(rng.integers(4, 16))):
ogirlik = rng.gamma(2, 5)
masofa = rng.gamma(2, 110)
summa = rng.lognormal(11.8, 0.6)
soat = int(rng.integers(0, 24))
kuch = (-1.9 + hq + 0.6 * (tarif == "oddiy")
+ 3.0 * (ogirlik / max(masofa, 1.0) > 0.085)
+ 0.9 * (7 <= soat <= 10) + 1.6 * imzo)
qatorlar.append([m, hudud, tarif, ogirlik, masofa, summa, soat,
int(rng.random() < 1 / (1 + np.exp(-kuch)))])
df = pd.DataFrame(qatorlar, columns=[GURUH, "hudud", "tarif", "ogirlik",
"masofa", "summa", "soat", MAQSAD])
idx = rng.choice(len(df), int(len(df) * 0.08), replace=False)
df.loc[idx, "summa"] = None
df.loc[rng.choice(len(df), 40, replace=False), "masofa"] = 0.0
return df
def quvur_yasa(**model_kw) -> Pipeline:
son_quvur = Pipeline([
("imp", SimpleImputer(strategy="median")),
("ko_rinishlar", FeatureUnion([
("xom", "passthrough"),
("nisbat", NisbatBelgilari(juftliklar=((0, 1), (2, 0)))),
])),
("sc", StandardScaler()),
])
kat_quvur = Pipeline([
("imp", SimpleImputer(strategy="most_frequent")),
("oh", OneHotEncoder(handle_unknown="ignore", sparse_output=False)),
])
return Pipeline([
("tayyor", ColumnTransformer([("son", son_quvur, SONLI),
("kat", kat_quvur, KATEGORIYA)])),
("m", HistGradientBoostingClassifier(max_iter=150,
early_stopping=False,
random_state=SEED,
**model_kw)),
])
def main() -> None:
df = yarat()
y = df[MAQSAD].to_numpy()
guruh = df[GURUH].to_numpy()
print("=== 1. Test to'plamini QULFLASH ===")
gss = GroupShuffleSplit(n_splits=1, test_size=0.25, random_state=SEED)
tr, te = next(gss.split(df, y, groups=guruh))
dftr, ytr, gtr = df.iloc[tr], y[tr], guruh[tr]
dfte, yte = df.iloc[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: "
f"{len(np.intersect1d(gtr, guruh[te]))}")
cv = GroupKFold(5)
print("\n=== 2. Bazaviy va qaror chegarasi ===")
dummy = cross_val_score(DummyClassifier(strategy="prior"), dftr, ytr,
cv=cv, groups=gtr, scoring="roc_auc").mean()
asos_ballar = cross_val_score(quvur_yasa(), dftr, ytr, cv=cv,
groups=gtr, scoring="roc_auc")
se = float(asos_ballar.std(ddof=1) / np.sqrt(len(asos_ballar)))
print(f" {'model':<24} {'CV AUC':>9}")
print(f" {'Dummy':<24} {dummy:>9.4f}")
print(f" {'bazaviy quvur':<24} {asos_ballar.mean():>9.4f}")
print(f" SE: {se:.4f}, qaror chegarasi (2*SE): {2 * se:.4f}")
print("\n=== 3. Sozlash (12 nomzod) ===")
taqsimot = {
"m__learning_rate": loguniform(0.02, 0.4),
"m__max_leaf_nodes": randint(4, 50),
"m__min_samples_leaf": randint(5, 100),
"tayyor__son__imp__strategy": ["median", "mean"],
}
q = RandomizedSearchCV(quvur_yasa(), taqsimot, n_iter=12, cv=cv,
scoring="roc_auc", random_state=SEED,
n_jobs=1).fit(dftr, ytr, groups=gtr)
qisqa = {k.split("__")[-1]: (round(float(v), 4)
if isinstance(v, (float, np.floating))
else v)
for k, v in sorted(q.best_params_.items())}
print(f" eng yaxshi: {qisqa}")
print(f" best_score_: {q.best_score_:.4f} (hisobotga YOZILMAYDI)")
osish = q.best_score_ - asos_ballar.mean()
print(f" bazaviydan o'sish: {osish:+.4f}, chegara: {2 * se:.4f}")
print(f" qaror: {'QABUL' if osish > 2 * se else 'rad etildi'}")
print("\n=== 4. Yopiq testni BIR MARTA ochish ===")
yakuniy = q.best_estimator_ if osish > 2 * se else quvur_yasa().fit(
dftr, ytr)
test_auc = roc_auc_score(yte, yakuniy.predict_proba(dfte)[:, 1])
cv_ball = q.best_score_ if osish > 2 * se else asos_ballar.mean()
print(f" {'manba':<24} {'AUC':>9}")
print(f" {'CV':<24} {cv_ball:>9.4f}")
print(f" {'TEST (bir marta)':<24} {test_auc:>9.4f}")
print(f" farq: {cv_ball - test_auc:+.4f}")
print(f" sinalgan nomzodlar (T): {12 + 1}")
print("\n=== 5. Belgi muhimligi ===")
from sklearn.inspection import permutation_importance
r = permutation_importance(yakuniy, dfte, yte, n_repeats=8,
scoring="roc_auc", random_state=SEED,
n_jobs=1)
nomlar = list(dfte.columns)
tartib = np.argsort(-r.importances_mean)
print(f" {'belgi':<12} {'muhimlik':>11}")
for i in tartib[:5]:
print(f" {nomlar[i]:<12} {r.importances_mean[i]:>+11.4f}")
print(" ⭐ Butun quvur sozlanadi, imputatsiya strategiyasi ham")
if __name__ == "__main__":
main()Natijaning muhim qismi:
=== 1. Test to'plamini QULFLASH ===
ish to'plami: 6336 qator, 675 mijoz
TEST (yopiq): 2118 qator, 225 mijoz
mijoz kesishishi: 0
=== 2. Bazaviy va qaror chegarasi ===
model CV AUC
Dummy 0.5000
bazaviy quvur 0.6996
SE: 0.0066, qaror chegarasi (2*SE): 0.0132
=== 3. Sozlash (12 nomzod) ===
eng yaxshi: {'learning_rate': 0.0213, 'max_leaf_nodes': 5, 'min_samples_leaf': 92, 'strategy': 'mean'}
best_score_: 0.7352 (hisobotga YOZILMAYDI)
bazaviydan o'sish: +0.0355, chegara: 0.0132
qaror: QABUL
=== 4. Yopiq testni BIR MARTA ochish ===
manba AUC
CV 0.7352
TEST (bir marta) 0.7355
farq: -0.0003
sinalgan nomzodlar (T): 13
=== 5. Belgi muhimligi ===
belgi muhimlik
masofa +0.1259
ogirlik +0.1063
tarif +0.0324
hudud +0.0161
soat +0.0110
⭐ Butun quvur sozlanadi, imputatsiya strategiyasi hamNima ko'rsatdi: 2.3-bo'lim.
Misol 4 — Paket va topshirish
"""4-qadam: saqlash, yuklash, tekshirish va hisobot."""
import shutil
import sys
import tempfile
from pathlib import Path
import joblib
import numpy as np
import pandas as pd
import sklearn
from sklearn.base import BaseEstimator, TransformerMixin
from sklearn.compose import ColumnTransformer
from sklearn.ensemble import HistGradientBoostingClassifier
from sklearn.impute import SimpleImputer
from sklearn.metrics import roc_auc_score
from sklearn.model_selection import (GroupKFold, GroupShuffleSplit,
cross_val_score)
from sklearn.pipeline import FeatureUnion, Pipeline
from sklearn.preprocessing import OneHotEncoder, StandardScaler
from sklearn.utils.validation import (_check_feature_names_in,
check_is_fitted, validate_data)
SEED = 42
SONLI = ["ogirlik", "masofa", "summa", "soat"]
KATEGORIYA = ["hudud", "tarif"]
MAQSAD = "kechikdi"
GURUH = "mijoz"
class NisbatBelgilari(TransformerMixin, BaseEstimator):
def __init__(self, juftliklar=((0, 1),), eps=1.0):
self.juftliklar = juftliklar
self.eps = eps
def fit(self, X, y=None):
validate_data(self, X, dtype="numeric")
return self
def transform(self, X):
check_is_fitted(self)
X = validate_data(self, X, dtype="numeric", reset=False)
return np.column_stack(
[X[:, a] / np.clip(np.abs(X[:, b]), self.eps, None)
for a, b in self.juftliklar])
def get_feature_names_out(self, input_features=None):
nomlar = _check_feature_names_in(self, input_features)
return np.asarray([f"{nomlar[a]}_ga_{nomlar[b]}"
for a, b in self.juftliklar], dtype=object)
def yarat(seed: int = 7, mijozlar: int = 900) -> pd.DataFrame:
rng = np.random.default_rng(seed)
qatorlar = []
for m in range(mijozlar):
imzo = rng.normal(0, 1)
hudud = rng.choice(["toshkent", "samarqand", "buxoro", "fargona"])
hq = {"toshkent": 0.0, "samarqand": 0.5, "buxoro": 1.0,
"fargona": 0.3}[hudud]
tarif = rng.choice(["oddiy", "tezkor"])
for _ in range(int(rng.integers(4, 16))):
ogirlik = rng.gamma(2, 5)
masofa = rng.gamma(2, 110)
summa = rng.lognormal(11.8, 0.6)
soat = int(rng.integers(0, 24))
kuch = (-1.9 + hq + 0.6 * (tarif == "oddiy")
+ 3.0 * (ogirlik / max(masofa, 1.0) > 0.085)
+ 0.9 * (7 <= soat <= 10) + 1.6 * imzo)
qatorlar.append([m, hudud, tarif, ogirlik, masofa, summa, soat,
int(rng.random() < 1 / (1 + np.exp(-kuch)))])
df = pd.DataFrame(qatorlar, columns=[GURUH, "hudud", "tarif", "ogirlik",
"masofa", "summa", "soat", MAQSAD])
idx = rng.choice(len(df), int(len(df) * 0.08), replace=False)
df.loc[idx, "summa"] = None
df.loc[rng.choice(len(df), 40, replace=False), "masofa"] = 0.0
return df
def quvur_yasa() -> Pipeline:
son_quvur = Pipeline([
("imp", SimpleImputer(strategy="median")),
("ko_rinishlar", FeatureUnion([
("xom", "passthrough"),
("nisbat", NisbatBelgilari(juftliklar=((0, 1), (2, 0)))),
])),
("sc", StandardScaler()),
])
kat_quvur = Pipeline([
("imp", SimpleImputer(strategy="most_frequent")),
("oh", OneHotEncoder(handle_unknown="ignore", sparse_output=False)),
])
return Pipeline([
("tayyor", ColumnTransformer([("son", son_quvur, SONLI),
("kat", kat_quvur, KATEGORIYA)])),
("m", HistGradientBoostingClassifier(max_iter=250,
early_stopping=False,
random_state=SEED)),
])
def paket_yasa(quvur, kirish, cv_ball, test_ball, T) -> dict:
nazorat = kirish.head(40)
return {
"quvur": quvur,
"belgilar": list(kirish.columns),
"metrika": {"cv_auc": round(float(cv_ball), 4),
"test_auc": round(float(test_ball), 4)},
"versiyalar": {"sklearn": sklearn.__version__,
"numpy": np.__version__,
"pandas": pd.__version__,
"python": sys.version.split()[0]},
"seed": SEED,
"validatsiya": "GroupKFold(5) mijoz bo'yicha",
"tajribalar": T,
"sana": "2026-09-21",
"nazorat": {"X": nazorat,
"p": quvur.predict_proba(nazorat)[:, 1]},
}
def paketni_tekshir(paket, yangi) -> list:
natija = []
natija.append(("ustunlar va tartibi",
list(yangi.columns) == paket["belgilar"]))
natija.append(("sklearn versiyasi",
sklearn.__version__ == paket["versiyalar"]["sklearn"]))
p = paket["quvur"].predict_proba(paket["nazorat"]["X"])[:, 1]
natija.append(("nazorat namunasi",
bool(np.allclose(p, paket["nazorat"]["p"]))))
return natija
def main() -> None:
df = yarat()
y = df[MAQSAD].to_numpy()
guruh = df[GURUH].to_numpy()
kirish = df.drop(columns=[GURUH, MAQSAD])
tr, te = next(GroupShuffleSplit(1, test_size=0.25, random_state=SEED)
.split(df, y, groups=guruh))
quvur = quvur_yasa()
cv_ball = cross_val_score(quvur, kirish.iloc[tr], y[tr],
cv=GroupKFold(5), groups=guruh[tr],
scoring="roc_auc").mean()
quvur.fit(kirish.iloc[tr], y[tr])
test_ball = roc_auc_score(y[te],
quvur.predict_proba(kirish.iloc[te])[:, 1])
papka = Path(tempfile.mkdtemp(prefix="sk_loyiha_"))
try:
print("=== 1. Paket ===")
paket = paket_yasa(quvur, kirish, cv_ball, test_ball, T=1)
yol = papka / "model.joblib"
joblib.dump(paket, yol, compress=3)
print(f" kalitlar: {sorted(paket)}")
print(f" belgilar: {paket['belgilar']}")
print(f" metrika: {paket['metrika']}")
print(f" fayl hajmi: {yol.stat().st_size / 1024:.1f} KB")
print("\n=== 2. Yuklash va uch tekshiruv ===")
yuklangan = joblib.load(yol)
print(f" {'tekshiruv':<24} {'natija':>8}")
for nom, holat in paketni_tekshir(yuklangan, kirish):
print(f" {nom:<24} {'OK' if holat else 'XATO':>8}")
print("\n=== 3. Ishlab chiqarishda bashorat ===")
yangi = kirish.head(3)
p = yuklangan["quvur"].predict_proba(yangi)[:, 1]
print(f" kirish: xom DataFrame {yangi.shape}")
print(f" bashorat: {np.round(p, 4).tolist()}")
buzilgan = yangi[list(reversed(yuklangan["belgilar"]))]
tekshiruv = paketni_tekshir(yuklangan, buzilgan)
print(f" ustun tartibi buzilganda: "
f"{'OK' if tekshiruv[0][1] else 'XATO aniqlandi'}")
print("\n=== 4. Topshirish ro'yxati ===")
royxat = [
("transformerlar alohida modulda", True),
("quvur.predict(xom_df) ishlaydi", True),
("barcha tayyorlash quvur ichida", True),
("SEED bir joyda", True),
("get_feature_names_out ishlaydi",
len(quvur[:-1].get_feature_names_out()) > 0),
("CV dizayni vazifaga mos (guruh)", True),
("test bir marta ochilgan", True),
("paketda versiyalar bor", "versiyalar" in paket),
("nazorat namunasi bor", "nazorat" in paket),
("yuklash tekshiruvlari yozilgan", True),
]
print(f" {'band':<36} {'holat':>7}")
for nom, holat in royxat:
print(f" {nom:<36} {'OK' if holat else 'XATO':>7}")
print("\n=== 5. Yakuniy hisobot ===")
print(" VAZIFA: yetkazib berish kechikishini bashorat qilish")
print(" CHIQISH: xavf balli -> tezkor yetkazishga yo'naltirish")
print(" METRIKA: roc_auc")
print(f" DIZAYN: {paket['validatsiya']}; "
f"test GroupShuffleSplit 25%")
print(f" NATIJA: CV {paket['metrika']['cv_auc']:.3f}, "
f"TEST {paket['metrika']['test_auc']:.3f}")
print(f" JARAYON: T = {paket['tajribalar']}, "
f"seed = {paket['seed']}")
print(f" VERSIYALAR: sklearn {paket['versiyalar']['sklearn']}, "
f"numpy {paket['versiyalar']['numpy']}")
print(" CHEKLOVLAR: sun'iy ma'lumot; vaqt bo'yicha drift")
print(" tekshirilmagan; yangi hudud/tarif 'ignore' bilan")
print(" o'tkaziladi, lekin monitoring kerak")
print(" ⭐ Artefakt: yuklanadigan, tekshiriladigan, hujjatlangan")
finally:
shutil.rmtree(papka, ignore_errors=True)
if __name__ == "__main__":
main()Natijaning muhim qismi:
=== 1. Paket ===
kalitlar: ['belgilar', 'metrika', 'nazorat', 'quvur', 'sana', 'seed', 'tajribalar', 'validatsiya', 'versiyalar']
belgilar: ['hudud', 'tarif', 'ogirlik', 'masofa', 'summa', 'soat']
metrika: {'cv_auc': 0.6918, 'test_auc': 0.7054}
fayl hajmi: 371.2 KB
=== 2. Yuklash va uch tekshiruv ===
tekshiruv natija
ustunlar va tartibi OK
sklearn versiyasi OK
nazorat namunasi OK
=== 3. Ishlab chiqarishda bashorat ===
kirish: xom DataFrame (3, 6)
bashorat: [0.1843, 0.1293, 0.872]
ustun tartibi buzilganda: XATO aniqlandi
=== 4. Topshirish ro'yxati ===
band holat
transformerlar alohida modulda OK
quvur.predict(xom_df) ishlaydi OK
barcha tayyorlash quvur ichida OK
SEED bir joyda OK
get_feature_names_out ishlaydi OK
CV dizayni vazifaga mos (guruh) OK
test bir marta ochilgan OK
paketda versiyalar bor OK
nazorat namunasi bor OK
yuklash tekshiruvlari yozilgan OK
=== 5. Yakuniy hisobot ===
VAZIFA: yetkazib berish kechikishini bashorat qilish
CHIQISH: xavf balli -> tezkor yetkazishga yo'naltirish
METRIKA: roc_auc
DIZAYN: GroupKFold(5) mijoz bo'yicha; test GroupShuffleSplit 25%
NATIJA: CV 0.692, TEST 0.705
JARAYON: T = 1, seed = 42
VERSIYALAR: sklearn 1.9.1, numpy 2.5.3
CHEKLOVLAR: sun'iy ma'lumot; vaqt bo'yicha drift
tekshirilmagan; yangi hudud/tarif 'ignore' bilan
o'tkaziladi, lekin monitoring kerak
⭐ Artefakt: yuklanadigan, tekshiriladigan, hujjatlanganNima ko'rsatdi: 2.4, 2.5-bo'limlar.
5. To'g'ri va noto'g'ri tushunishlar
| Noto'g'ri fikr | To'g'risi |
|---|---|
| "Notebook yetarli" | Sinflar modulda bo'lishi shart |
| "Tayyorlashni tashqarida qilsa tezroq" | Leakage va deploy muammosi |
| "Model — yakuniy natija" | Paket yakuniy natija |
"README keyin yoziladi" |
Artefaktning bir qismi |
| "Faqat model sozlanadi" | Butun quvur sozlanadi |
| "Testsiz topshirsa bo'ladi" | check_estimator minimal talab |
| "Versiyalar keyin qulflanadi" | Boshida |
"predict ishlasa tayyor" |
Uch tekshiruv kerak |
6. Keng tarqalgan xatolar va yechimlari
1. Notebookda sinf
# notebook katakida class MeningT(...) -> yuklanmaydi # ⚠️
# src/loyiha/transformerlar.py da # ✅2. Tayyorlash tashqarida
df["nisbat"] = df["a"] / df["b"]; quvur.fit(df[belgilar], y) # ⚠️
# NisbatBelgilari quvur ichida # ✅3. SEED tarqoq
train_test_split(..., random_state=1); KFold(..., random_state=7) # ⚠️
SEED = 42 # konfig.py da, hamma joyga import # ✅4. Yolg'iz model
joblib.dump(quvur, "model.joblib") # ⚠️
joblib.dump(paket, "model.joblib", compress=3) # ✅5. Tekshiruvsiz yuklash
q = joblib.load("m.joblib")["quvur"]; q.predict(yangi) # ⚠️
for nom, holat in paketni_tekshir(paket, yangi): assert holat # ✅6. check_estimator siz
# o'z transformerimiz ishlayapti, yetarli # ⚠️
check_estimator(NisbatBelgilari()) # ✅7. Cheklovlarni yozmaslik
# "Model AUC 0.84. Tayyor." # ⚠️
# + ma'lumot davri, drift, yangi kategoriya, T # ✅7. Integratsiya — bu bilim qayerda kerak bo'ladi
- 19.1-19.9-darslar (o'tilgan): Butun qism
- 18.12-dars (o'tilgan): Baholash amaliyoti
- 17.10-dars (o'tilgan): Belgi muhandisligi amaliyoti
- 29-qism: MLOps va deploy
- 31-qism: Loyihalar va karyera
8. Eng yaxshi amaliyotlar
Sinflar alohida modulda.
Hamma narsa quvur ichida.
SEEDbir joyda.check_estimatorni ishga tushiring.Test to'plamini qulflang.
Paket saqlang.
Yuklash tekshiruvlarini yozing.
READMEva cheklovlar.
9. Amaliy topshiriq
Vazifa 1: Bashorat qiling
1. # nima uchun sinflar modulda?
2. # quvur ichida nima bo'lishi kerak?
3. # quvurdan tashqarida nima qoladi?
4. # quvur to'liqligini qanday tekshirish?
5. # SEED qayerda?
6. # butun quvur sozlanadimi?
7. # paketda nima bo'ladi?
8. # yuklashda necha tekshiruv?
9. # nazorat namunasi nima uchun?
10. # check_estimator nimani beradi?
11. # test necha marta ochiladi?
12. # hisobotda nima bo'lishi shart?Javoblar
picklemodul yo'lini saqlaydi- Imputatsiya, kodlash, belgilar, model
- O'qish, maqsad va guruh ustunlari
quvur.predict(xom_df)konfig.pyda- Ha, imputatsiya strategiyasi ham
- Quvur, belgilar, versiyalar, metrika, nazorat
- Uch
- Versiya siljishini aniqlash
- Shartnoma buzilishini
- Bir marta
T, cheklovlar, versiyalar
Vazifa 2: Xatolarni tuzating
1. # notebook katakida class MeningT(...)
2. df["nisbat"] = df["a"] / df["b"]; quvur.fit(df[belgilar], y)
3. joblib.dump(quvur, "model.joblib")
4. q = joblib.load("m.joblib")["quvur"]; q.predict(yangi)
5. # "Model AUC 0.84. Tayyor."Javoblar
1. # src/loyiha/transformerlar.py da
2. # NisbatBelgilari quvur ichida
3. joblib.dump(paket, "model.joblib", compress=3)
4. for nom, holat in paketni_tekshir(paket, yangi): assert holat
5. # + ma'lumot davri, drift, yangi kategoriya, TVazifa 3: Komponentlar
Modellang:
- Xom ma'lumot
- Nuqsonlar
- Konfiguratsiya
- Transformer
Vazifa 4: Quvur
Modellang:
- Tuzilish
- Xom DataFrame
- Belgilar
- Chidamlilik
Vazifa 5: Sozlash
Modellang:
- Qulflash
- Bazaviy
- Sozlash
- Test
Vazifa 6: Paket
Modellang:
- Paket
- Tekshiruvlar
- Bashorat
- Ro'yxat
Vazifa 7: O'ylash
Modelingiz tayyor va paket saqlandi. Ishlab chiqarish muhandisi so'radi: "Bu model bir soniyada nechta so'rovni qayta ishlay oladi va agar yangi hudud kelsa nima bo'ladi?" Qanday javob berasiz?
Javob
Qisqa javob: ikkala savolga ham o'lchov bilan javob berish kerak — taxmin bilan emas. Va ikkalasi ham paketga yozilishi kerak edi.
1. Ish unumdorligi (throughput)
import time
paket = joblib.load("model.joblib")
quvur = paket["quvur"]
for hajm in [1, 10, 100, 1000]:
kesim = df.head(hajm)
quvur.predict_proba(kesim) # isitish
t0 = time.perf_counter()
for _ in range(20):
quvur.predict_proba(kesim)
davomiylik = (time.perf_counter() - t0) / 20
print(f"{hajm:>5} qator: {davomiylik * 1000:6.2f} ms, "
f"{hajm / davomiylik:,.0f} qator/s")Muhim nuans: bitta qator uchun kechikish (latency) va paket uchun unumdorlik (throughput) butunlay boshqa raqamlar. Bitta qatorda Pipeline ning qo'shimcha xarajati (DataFrame yaratish, tekshirishlar) hisobning o'zidan ko'proq bo'lishi mumkin.
2. Yangi hudud kelganda
Quvurda OneHotEncoder(handle_unknown="ignore") bor, ya'ni:
- xato chiqmaydi
- yangi hudud uchun barcha one-hot ustunlar nol bo'ladi
- model uni "hudud noma'lum" deb qabul qiladi
Buni ko'rsatish kerak:
yangi = kirish.head(3).assign(hudud="andijon")
print(quvur.predict_proba(yangi)[:, 1])3. Bu yetarlimi?
Yo'q. Yangi kategoriya jim o'tadi, ya'ni siz bilmaysiz. Shuning uchun monitoring kerak:
def notanish_ulush(quvur, yangi, ustun="hudud"):
oh = quvur["tayyor"].named_transformers_["kat"].named_steps["oh"]
i = KATEGORIYA.index(ustun)
korilganlar = set(oh.categories_[i])
return float((~yangi[ustun].isin(korilganlar)).mean())Agar bu ulush 1% dan oshsa — ogohlantirish.
4. Javobga qo'shiladigan boshqa raqamlar
| Savol | Qanday o'lchanadi |
|---|---|
| Xotira | Paket hajmi + yuklangan model hajmi |
| Sovuq start | joblib.load vaqti |
| Parallel ishlash | Bir nechta jarayon bilan sinov |
| Yo'qolgan qiymat | SimpleImputer o'quv medianasini ishlatadi |
5. Paketga qo'shilishi kerak bo'lgan ma'lumot
paket["ishlash"] = {
"bitta_qator_ms": ...,
"1000_qator_ms": ...,
"yuklash_ms": ...,
"hajm_kb": ...,
}
paket["xatti_harakat"] = {
"yangi_kategoriya": "one-hot nol, xato yo'q",
"yo_qolgan_qiymat": "o'quv medianasi bilan to'ldiriladi",
"nol_maxraj": "eps=1.0 bilan cheklangan",
}6. Xulosa
- Unumdorlikni o'lchang (kechikish va throughput alohida)
- Yangi kategoriya xatti-harakatini ko'rsating
- Monitoring funksiyasini bering
- Bu ma'lumotlarni paketga va
READMEga yozing
Nimani mustahkamlaydi: 2.2, 2.4-bo'limlar.
Xulosa
Bu darsda to'liq scikit-learn loyihasini qurdik.
Eng muhim uch fikr:
O'z sinflaringiz alohida modulda bo'lishi shart.
picklesinf kodini saqlamaydi — faqat import yo'lini. Notebook katakida e'lon qilingan transformer saqlangan modelni boshqa jarayonda yuklanmaydigan qiladi. Shuning uchuntransformerlar.py,quvur.py,konfig.py— bu qulaylik emas, texnik talab.Xom
DataFramedan bashoratgacha hamma narsa quvur ichida. Tekshiruv oddiy:quvur.predict(xom_df)ishlaydimi? Agar oldin qo'lda ustun qo'shish kerak bo'lsa, quvur to'liq emas va deploy da o'sha qo'lda qadamlar takrorlanib, xato kiritadi. Imputatsiya, domen belgilari va kodlash — hammasi ichkarida.Natija — model emas, artefakt. Paketga quvur, kirish ustunlari va tartibi, versiyalar, metrikalar, seed, tajribalar soni va nazorat namunasi kiradi; yuklashda esa uch tekshiruv bajariladi. Bunga
READMEva cheklovlar qo'shilsa, artefakt boshqa odam tomonidan yuklanadigan, tekshiriladigan va qo'llab-quvvatlanadigan bo'ladi.
Bu bilan 19-qism — scikit-learn to'liq yakunlandi va u bilan birga kursning Machine Learning bosqichi ham tugadi. Keyingi qismdan Deep Learning va AI bosqichi boshlanadi: neyron tarmoqlar, PyTorch, kompyuter ko'rish, NLP, transformerlar va katta til modellari.
Izohlar (0)
Izoh yozish uchun kiring.
- Hozircha izoh yo'q. Birinchi bo'ling!