Mundarija (23)
- 1. Kirish va motivatsiya
- 2. Nazariya — chuqur tushuntirish
- 2.1. Notebook modeli va ishlab chiqarishdagi model
- 2.2. ML hayot sikli
- 2.3. Training-serving skew
- 2.4. ML tizimlarining yashirin texnik qarzi
- 2.5. Feedback loop va tanlov siljishi
- 2.6. MLOps yetuklik darajalari
- 2.7. Rollar
- 2.8. Bu qism xaritasi
- 2.9. Tuzoqlar
- 3. Tez ma'lumotnoma
- 4. Batafsil misollar
- Misol 1 — Training-serving skew: notebook modeli va quvur modeli
- Misol 2 — Feedback loop: rad etilganlar uchun belgi yo'q
- Misol 3 — Yashirin bog'liqlik: so'm ming so'mga aylanganda
- Misol 4 — Kichik ML quvuri noldan: bosqichlar, artefaktlar, darvoza, yetuklik
- 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
27.1-dars: MLOps nima
27-QISM — MLOPS VA DEPLOY · 1-dars
1. Kirish va motivatsiya
26-qism oxirida biz loyihani weights_only=True bilan yuklanadigan paket va hisobot bilan yakunladik va keyingi qadamni aytdik: paketni haqiqiy xizmatga aylantirish, versiyalash va tajribalarni kuzatish, modelni API orqali taqdim etish, ishlab chiqarishda sifat va ma'lumot siljishini kuzatish. Shu qismda aynan shu ishni qilamiz. Bu birinchi darsda esa savolni to'g'ri qo'yamiz: notebook dagi yaxshi model bilan ishlab chiqarishda yillar davomida ishlaydigan model o'rtasida qanday farq bor?
Oldingi 26 qismda biz asosan bitta savolga javob berdik: "qaysi model yaxshiroq va buni qanday halol o'lchash mumkin?" Test qulflandi, juftlashgan taqqoslash qilindi, paket tayyorlandi. Bu — ishning muhim, lekin kichik qismi. Ishlab chiqarishda model:
- har kuni yangi ma'lumot oladi — va bu ma'lumotni boshqa jamoa, boshqa kod, boshqa tizim tayyorlaydi;
- boshqa dasturlash muhitida, boshqa kutubxona versiyalari bilan ishga tushadi;
- o'z qarorlari bilan dunyoni o'zgartiradi — va o'zgargan dunyodan keyingi o'quv ma'lumotini oladi;
- oylar o'tib eskiradi va qayta o'qitilishi kerak — va bu har safar xavfsiz bo'lishi kerak.
MLOps (Machine Learning Operations) — shu muammolarni tizimli hal qiladigan amaliyotlar to'plami: ma'lumot quvurlari, takrorlanuvchanlik, eksperiment kuzatuvi, versiyalash, deploy, monitoring va qayta o'qitish. Nomi DevOps dan olingan, lekin ML da qo'shimcha qiyinchilik bor: dasturiy ta'minotda xatti-harakat kod bilan belgilanadi, ML da esa kod + ma'lumot + model bilan — va ularning har biri alohida o'zgaradi.
Real vaziyat. Bankning skoring jamoasi notebook da AUC 0.81 li modelni tayyorladi. Xizmatni boshqa jamoa Java da yozdi va notebook dagi tayyorlash kodini "ko'chirdi". Uch oy davomida model "ishladi": xato yo'q, har so'rovga javob bor. Keyin kvartal hisobotida defoltlar kutilganidan ancha ko'p chiqdi. Tekshiruv ko'rsatdi: xizmatdagi kod tarif kategoriyalarini boshqa tartibda kodlagan — ya'ni model "Premium" mijozga "Yoshlar" tarifining koeffitsiyentini qo'llagan. Hech qanday xato xabari bo'lmagan, chunki raqamlar formati to'g'ri edi. Bu darsning 1-misolida xuddi shu xatoni o'lchaymiz.
Bu darsda ML tizimi nimadan iboratligini, u qayerda va qanday "jim" buzilishini va MLOps bosqichlari bu buzilishlarni qanday oldini olishini ko'ramiz.
Bu darsda:
- Notebook modeli va ishlab chiqarishdagi model
- ML hayot sikli
- Training-serving skew
- ML tizimlarining yashirin texnik qarzi
- Feedback loop va tanlov siljishi
- MLOps yetuklik darajalari
- Rollar
- Amaliy: skew, feedback loop, yashirin bog'liqlik va kichik quvur — hammasi o'lchanadi
ℹ Misollar real sklearn bilan (Python 3.14, scikit-learn 1.9, pandas 3). Ma'lumotlar urug' bilan generatsiya qilinadi; fayllar faqat vaqtinchalik papkada.
2. Nazariya — chuqur tushuntirish
2.1. Notebook modeli va ishlab chiqarishdagi model
NOTEBOOK DAGI MODEL:
CSV fayl (bir marta yuklangan)
-> tayyorlash kataklari (tartibi yashirin)
-> model.fit
-> test aniqligi 0.81
savol: "model yaxshimi?"
ISHLAB CHIQARISHDAGI ML TIZIMI:
+-------------------+ +-------------+ +-----------------+
| ma'lumot yig'ish |-->| tekshiruv |-->| belgi tayyorlash|
+-------------------+ +-------------+ +-----------------+
| |
v v
+-------------------+ +-------------+ +-----------------+
| konfiguratsiya | | MODEL |<--| o'qitish quvuri |
+-------------------+ | (kichik | +-----------------+
| qism!) |
+-------------------+ +-------------+ +-----------------+
| xizmat (API) |<--| registr | | monitoring |
+-------------------+ +-------------+ +-----------------+
savollar: "model bugun ham yaxshimi?", "kim, qachon, qaysi
ma'lumot bilan o'qitgan?", "buzilsa qanday qaytaramiz?"Sculley va boshqalarning mashhur maqolasi ("Hidden Technical Debt in Machine Learning Systems", NeurIPS 2015) shuni rasm bilan ko'rsatgan: haqiqiy ML tizimida model kodi — kichik quticha, uning atrofida esa ma'lumot yig'ish, tekshirish, belgi tayyorlash, konfiguratsiya, xizmat, monitoring kabi ancha katta infratuzilma bor. Notebook faqat o'rtadagi qutichani ko'radi.
| Jihat | Notebook | Ishlab chiqarish |
|---|---|---|
| Ma'lumot | Bir marta yuklangan fayl | Har kuni keladigan, o'zgaradigan oqim |
| Tayyorlash | Kataklar, qo'lda | Kod, test qilingan, versiyalangan |
| Baholash | Bir marta, testda | Doimiy, belgilar kechikib keladi |
| Xato | Ko'zga ko'rinadi (traceback) | Ko'pincha jim — format to'g'ri, ma'no noto'g'ri |
| Takrorlash | "Mening kompyuterimda ishladi" | Istalgan vaqtda, istalgan mashinada |
| Muvaffaqiyat o'lchovi | Test metrikasi | Biznes natija + metrika + ishonchlilik |
ML tizimi — model emas, model atrofidagi butun quvur. Aksariyat nosozliklar modelda emas, uning atrofida bo'ladi.
2.2. ML hayot sikli
+----------------------------------------------------+
| |
v |
1. MA'LUMOT -----> 2. O'QITISH -----> 3. BAHOLASH |
yig'ish, tajribalar, test, bazaviy, |
tekshirish, kuzatuv 27.4-bob darvoza |
versiya 27.3-bob | |
v |
6. QAYTA O'QITISH <---- 5. MONITORING <---- 4. DEPLOY |
trigger, A/B sifat, drift, registr 27.5-bob, |
27.13-bob kechikish paket 27.6-bob, |
| (27.11, 27.12) API (27.7-27.8),|
| Docker 27.9-bob, |
+------------------------------------bulut 27.10-bob---+
HAR BOSQICH SAQLAYDI:
ma'lumot -> xesh, sxema, sifat hisoboti
o'qitish -> konfig, kod versiyasi, muhit, urug'
baholash -> metrikalar, bashorat xeshi, darvoza qarori
deploy -> registr yozuvi (versiya, kim, qachon, nima uchun)
monitoring -> kirish statistikasi, bashorat taqsimoti, kechikkan belgilarSikl yopiq: monitoring topgan muammo yangi ma'lumot yig'ish va qayta o'qitishga olib keladi. Shuning uchun har bosqich keyingisi uchun iz qoldirishi kerak — aks holda uch oydan keyin "bu model qaysi ma'lumotdan o'qitilgan?" degan savolga hech kim javob bera olmaydi.
Deploy — oxiri emas, siklning o'rtasi. Model ishlab chiqarishga chiqqan kundan boshlab eskira boshlaydi.
2.3. Training-serving skew
O'QITISHDA: xom -> tayyorla_A(x) -> model
XIZMATDA: xom -> tayyorla_B(x) -> model tayyorla_B "xuddi shunday" yozilgan
tayyorla_A != tayyorla_B bo'lsa -> SKEW:
model o'qitishda ko'rmagan taqsimotni oladi
xato xabari YO'Q - faqat bashoratlar noto'g'ri
TIPIK MANBALAR:
kategoriya kodlash tartibi (sorted vs uchrash tartibi)
unutilgan o'zgartirish (log, clip)
masshtab statistikasi (o'quvdan emas, so'rov partiyasidan)
bo'sh qiymatni to'ldirish (0 vs median)
birlik (so'm vs ming so'm), vaqt zonasi, matn tozalash
kutubxona farqi: pandas .std() ddof=1, numpy .std() ddof=0
YECHIM:
tayyorlash kodi BIR marta yoziladi va IKKALA joyda ishlatiladi
sklearn Pipeline / ColumnTransformer -> bitta joblib fayl (19-qism)
xizmat XOM ma'lumot oladi, tayyorlashni o'zi qilmaydi
nazorat: bir xil kirish -> offline va xizmat bashorati TENG (test)Skew ning xavfli tomoni — u ko'rinmaydi. Notebook dagi test aniqligi yaxshi, xizmat ham xato bermaydi. Farqni faqat ikki yo'l bilan topish mumkin: (1) bir xil kirishda offline va xizmat bashoratlarini solishtirish (bu testni deploydan oldin avtomatik qilish kerak — 27.8), (2) ishlab chiqarishda kechikib kelgan belgilarni kuzatish 27.11-bob — lekin bu oylar oladi.
Tayyorlash kodi bir marta yoziladi. Model bilan birga bitta obyekt sifatida saqlanadigan Pipeline — skew ga qarshi eng arzon himoya.
2.4. ML tizimlarining yashirin texnik qarzi
Texnik qarz — hozir tezlik uchun qilingan qisqa yo'l, keyin esa "foizi" bilan to'lanadigan ish. Sculley va boshqalar ML ga xos qarz turlarini ajratgan:
1. GLUE CODE (yopishtiruvchi kod)
umumiy kutubxona atrofida o'z ma'lumotini moslashtiruvchi
ko'p kod; tizimning 95% i "yopishtirish", 5% i ML
-> kutubxonani almashtirish yoki yangilash juda qimmat
2. PIPELINE JUNGLE (quvur changalzori)
vaqt o'tib qo'shilgan birlashtirish, oraliq fayl, "vaqtincha"
skriptlar; hech kim butun oqimni bilmaydi
-> bitta ustun o'zgarsa, nima buzilishini hech kim aytolmaydi
3. YASHIRIN (e'lon qilinmagan) BOG'LIQLIKLAR
a) beqaror ma'lumot bog'liqligi: yuqori oqim jadvali egasi
ustun ma'nosini o'zgartiradi (so'm -> ming so'm)
b) e'lon qilinmagan iste'molchilar: sizning bashoratingizni
boshqa jamoa jim ishlatadi -> modelni yangilash ularni buzadi
4. FEEDBACK LOOP (teskari aloqa halqasi)
to'g'ridan-to'g'ri: model qarori keyingi o'quv ma'lumotini
tanlaydi (kredit rad etilgan -> belgi yo'q)
yashirin: ikki model bir-biriga dunyo orqali ta'sir qiladi
(narx modeli -> talab -> talab modeli -> narx)
5. CACE: "Changing Anything Changes Everything"
bitta belgi, giperparametr yoki ma'lumot ulushi o'zgarsa,
barcha belgilarning og'irligi o'zgaradi -> izolyatsiya yo'q
6. KONFIGURATSIYA QARZI
yuzlab sozlama, ular orasida bog'liqlik, versiyasiz
7. O'LIK EKSPERIMENT YO'LLARI
if eski_variant: ... - hech kim o'chirmaydi, bir kun yoqiladiBu qarzlarning umumiy xususiyati: ular kod ko'rib chiqishda (code review) ko'rinmaydi. Kod to'g'ri, testlar o'tadi — muammo ma'lumotda, ma'no o'zgarishida yoki tizim darajasidagi o'zaro ta'sirda.
Yashirin bog'liqlik uchun eng arzon himoya — kirish tekshiruvi: o'quv ma'lumotidan "shartnoma" (median, diapazon, kategoriyalar ro'yxati, null ulushi) yoziladi va har partiya u bilan solishtiriladi. 3-misolda bu tekshiruv birlik o'zgarishini birinchi kunning o'zidayoq ushlaydi — belgilar kelishini kutmasdan.
2.5. Feedback loop va tanlov siljishi
KREDIT SKORINGI:
davr t: model -> tasdiqlash / rad
tasdiqlangan -> kredit oladi -> defolt yoki yo'q (BELGI BOR)
rad etilgan -> kredit olmaydi (BELGI YO'Q)
davr t+1: model faqat tasdiqlanganlarda qayta o'qitiladi
OQIBAT:
model rad etgan hududdan hech qachon yangi dalil olmaydi
model xato qilsa (masalan, eski ma'lumot tufayli) - xato
O'ZINI TASDIQLAYDI va tuzalmaydi
o'quv populyatsiyasi haqiqiy populyatsiyadan uzoqlashadi
YECHIMLAR:
izlanish (exploration): rad etilganlarning kichik tasodifiy
qismi tasdiqlanadi -> xolis belgi (narxi bor!)
IPW (inverse propensity weighting): izlangan har misol
1/q og'irlik oladi (q - izlanish ehtimoli)
reject inference, sabab-oqibat usullari (murakkabroq)
monitoring: o'quv va populyatsiya taqsimotini solishtirishFeedback loop tavsiya tizimlarida (faqat ko'rsatilgan narsa bosiladi), firibgarlikni aniqlashda (bloklangan tranzaksiya haqida belgi yo'q), politsiya patrullarida (ko'p patrul — ko'p qayd — yana ko'p patrul) ham uchraydi. 2-misolda kredit misolida uni simulyatsiya qilamiz va izlanishning narxi va foydasini raqam bilan o'lchaymiz — natija kutilgandek oddiy emas.
Model o'z o'quv ma'lumotini o'zi tanlasa, xatosi o'zini tasdiqlaydi. Izlanish va to'g'ri og'irlik bu halqani uzadi.
2.6. MLOps yetuklik darajalari
Google ning MLOps haqidagi qo'llanmasida ishlatiladigan va keng tarqalgan uch darajali shkala:
DARAJA 0 - QO'LDA
notebook, qo'lda o'qitish, model faylini "tashlab berish"
deploy - kamdan-kam, qo'lda; monitoring yo'q
kim uchun: tadqiqot, birinchi prototip
DARAJA 1 - AVTOMATLASHTIRILGAN QUVUR (continuous training)
o'qitish quvuri kod: yuklash -> tekshirish -> o'qitish ->
baholash -> darvoza -> registr
ma'lumot va model versiyalanadi (xesh), metrikalar qayd qilinadi
quvur jadval yoki trigger bilan qayta ishga tushadi
tayyorlash o'qitish va xizmatda BIR xil
DARAJA 2 - CI/CD/CT
CI: quvur KODI ham testlanadi (birlik testlari, ma'lumot testlari)
CD: quvurning o'zi va xizmat avtomatik yetkaziladi
CT: monitoring signali (drift, sifat) qayta o'qitishni ishga tushiradi
xavfsiz deploy: kanareyka, A/B, tezkor orqaga qaytarish| Savol | 0 | 1 | 2 |
|---|---|---|---|
| Model qanday o'qitiladi? | Qo'lda, notebook | Quvur, jadval bo'yicha | Quvur, trigger bo'yicha |
| Qaysi ma'lumot ishlatilgani ma'lummi? | Yo'q | Ha (xesh) | Ha |
| Yangi model qanday chiqadi? | Fayl yuboriladi | Darvoza + registr | Avtomatik, bosqichma-bosqich |
| Quvur kodi testlanadimi? | Yo'q | Qisman | Ha, CI da |
| Monitoring | Yo'q | Asosiy metrikalar | Drift + sifat + trigger |
Muhim: daraja maqsad emas. Oyda bir marta ishlatiladigan hisobot modeliga 2-daraja kerak emas; har daqiqada millionlab so'rovga javob beradigan modelga esa 0-daraja xavfli. To'g'ri savol: "model buzilsa, qancha zarar va biz buni qancha vaqtda bilamiz?"
Birinchi qadam deyarli har doim bir xil: notebook dan quvurga — takrorlanadigan, tekshiriladigan, iz qoldiradigan bosqichlarga o'tish (4-misol).
2.7. Rollar
| Rol | Asosiy mas'uliyat | MLOps dagi hissasi |
|---|---|---|
| Data engineer | Ma'lumot quvurlari, omborlar | Sxema, sifat tekshiruvi, ma'lumot shartnomalari |
| Data scientist | Muammo, belgi, model, baholash | Tajribalar, metrikalar, model kartasi |
| ML engineer | Modelni ishlab chiqarishga olib chiqish | Paketlash, API, quvur, testlar |
| MLOps / platforma muhandisi | Umumiy infratuzilma | Registr, CI/CD, monitoring vositalari |
| SRE / DevOps | Xizmat ishonchliligi | Kechikish, mavjudlik, alertlar |
| Mahsulot egasi | Biznes maqsad | Muvaffaqiyat metrikasi, xavf chegarasi |
| Soha mutaxassisi | Ma'lumot ma'nosi | Belgilar sifati, "g'alati" natijani tushuntirish |
Kichik jamoada bu rollarning hammasi bitta-ikki odamda bo'ladi. Muhimi — har bir mas'uliyat kimdadir bo'lishi. "Model buzildi, lekin kimga aytish kerakligini hech kim bilmaydi" — tipik 0-daraja muammosi.
2.8. Bu qism xaritasi
27.1 MLOps nima <- siz shu yerdasiz
27.2 Reproduksiya va muhit urug'lar, versiyalar, manifest
27.3 Ma'lumot quvuri ETL, idempotentlik, sifat tekshiruvi
27.4 Eksperiment kuzatuvi MLflow g'oyasi noldan
27.5 Model versiyalash va registr versiya, bosqich, orqaga qaytarish
27.6 Modelni paketlash artefakt + metama'lumot
27.7 FastAPI bilan model API xizmat
27.8 API ni mustahkamlash/testlash validatsiya, xatolar, testlar
27.9 Docker bilan konteynerlash muhitni muzlatish
27.10 Bulut va deploy strategiyalari kanareyka, blue-green
27.11 Monitoring metrikalar, loglar, alertlar
27.12 Drift aniqlash taqsimot siljishi testlari
27.13 Qayta o'qitish va A/B test CT, xavfsiz almashtirish
27.14 Amaliyot hammasi bitta loyihadaMisollarda MLflow, DVC yoki Airflow ishlatilmaydi — ularning g'oyasini sqlite, json va hashlib bilan noldan quramiz. Shunda tayyor vositani ishlatganda u "sehr" emas, tushunarli mexanizm bo'ladi.
2.9. Tuzoqlar
Asosiy tuzoqlar: tayyorlash kodini xizmatda qayta yozish; "xato bermayapti — demak ishlayapti" deb o'ylash; faqat offline metrikaga qarash; model qarori keyingi o'quv ma'lumotini tanlashini unutish; yuqori oqimdagi jadval egasini bilmaslik; handle_unknown="ignore" ni tekshiruvsiz ishlatish (xatoni yashiradi); qaysi ma'lumot va kod bilan o'qitilganini yozmaslik; yetuklik darajasini vazifa xavfidan qat'i nazar tanlash.
3. Tez ma'lumotnoma
import joblib
import numpy as np
from sklearn.compose import ColumnTransformer
from sklearn.linear_model import LogisticRegression
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import FunctionTransformer, OneHotEncoder, StandardScaler
# 1. Skew ga qarshi: tayyorlash + model = BITTA obyekt
quvur = Pipeline([
("tayyorla", ColumnTransformer([
("log", Pipeline([("log1p", FunctionTransformer(np.log1p)),
("z", StandardScaler())]), ["tolov"]),
("son", StandardScaler(), ["muddat"]),
("kat", OneHotEncoder(handle_unknown="ignore"), ["tarif"]),
])),
("model", LogisticRegression(max_iter=1000)),
])
# quvur.fit(X_xom, y); joblib.dump(quvur, "quvur.joblib")
# xizmatda: joblib.load("quvur.joblib").predict_proba(X_xom)
# 2. Yashirin bog'liqlikka qarshi: o'quvdan "shartnoma" va tekshiruv
def shartnoma(df, sonlar):
return {c: {"median": float(df[c].median()),
"past": float(df[c].quantile(0.005)),
"yuqori": float(df[c].quantile(0.995))} for c in sonlar}
def tekshir(df, sh):
muammo = []
for c, r in sh.items():
nisbat = df[c].median() / r["median"]
if not 0.5 <= nisbat <= 2.0:
muammo.append(f"{c}: median nisbati {nisbat:.3g}")
return muammoTuzilma xulosasi
ML tizimi = ma'lumot + tekshiruv + tayyorlash + model + xizmat + monitoring
hayot sikli: ma'lumot -> o'qitish -> baholash -> deploy -> monitoring -> qayta
skew: tayyorlash ikki joyda yozilsa -> Pipeline bilan bitta obyekt
qarz: glue code, pipeline jungle, yashirin bog'liqlik, feedback loop, CACE
feedback loop: belgi faqat tasdiqlanganlarda -> izlanish + IPW
yetuklik: 0 qo'lda -> 1 avtomatik quvur -> 2 CI/CD/CT
har bosqich iz qoldiradi: xesh, konfig, metrika, qaror4. Batafsil misollar
Misollar real sklearn bilan (Python 3.14, scikit-learn 1.9, pandas 3). Har misol bir necha soniyada ishlaydi.
Misol 1 — Training-serving skew: notebook modeli va quvur modeli
Mijoz ketishi (churn) modeli. Notebook da tayyorlash qo'lda yozilgan: oylik to'lovga log1p, uchta son belgini o'quv statistikasi bilan standartlash, tarifni one-hot. Xizmat jamoasi shu kodni "qayta yozdi" — to'rt variantda. Keyin xuddi shu ishni Pipeline bilan qilamiz.
"""Training-serving skew: tayyorlash kodi ikki marta yozilganda."""
import tempfile
from pathlib import Path
import joblib
import numpy as np
import pandas as pd
from sklearn.compose import ColumnTransformer
from sklearn.linear_model import LogisticRegression
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import FunctionTransformer, OneHotEncoder, StandardScaler
TARIFLAR = ["Asosiy", "Biznes", "Premium", "Yoshlar"]
SON = ["oylik_tolov", "muddat_oy", "murojaat"]
def malumot(n, seed):
rng = np.random.default_rng(seed)
tarif = rng.choice(TARIFLAR, size=n, p=[0.4, 0.2, 0.15, 0.25])
tolov = np.round(rng.lognormal(np.log(150_000), 0.6, n), -2)
muddat = rng.integers(1, 73, n)
murojaat = rng.poisson(1.5, n)
tarif_eff = pd.Series(tarif).map(
{"Asosiy": 0.0, "Biznes": -1.2, "Premium": -0.6, "Yoshlar": 1.1}).to_numpy()
logit = (1.2 * (np.log(tolov) - np.log(150_000)) - 0.05 * (muddat - 36)
+ 0.45 * (murojaat - 1.5) + tarif_eff - 0.3)
ketdi = (rng.random(n) < 1 / (1 + np.exp(-logit))).astype(int)
return pd.DataFrame({"oylik_tolov": tolov, "muddat_oy": muddat,
"murojaat": murojaat, "tarif": tarif, "ketdi": ketdi})
# ---------- notebook dagi qo'lda tayyorlash (o'qitishda) ----------
def notebook_tayyorla(df, stat):
son = df[SON].astype(float).copy()
son["oylik_tolov"] = np.log1p(son["oylik_tolov"])
z = (son - stat["ortacha"]) / stat["std"]
oh = np.column_stack([(df["tarif"] == t).to_numpy() for t in TARIFLAR])
return np.column_stack([z.to_numpy(), oh.astype(float)])
# ---------- xizmat (serving) kodida qayta yozilgan variantlar ----------
def xizmat_tayyorla(df, stat, variant):
son = df[SON].astype(float).copy()
if variant != "log unutilgan":
son["oylik_tolov"] = np.log1p(son["oylik_tolov"])
std = stat["std0"] if variant == "std ddof=0" else stat["std"]
z = (son - stat["ortacha"]) / std
tartib = TARIFLAR
if variant == "kategoriya tartibi":
tartib = ["Asosiy", "Yoshlar", "Biznes", "Premium"] # uchrash tartibi
oh = np.column_stack([(df["tarif"] == t).to_numpy() for t in tartib])
return np.column_stack([z.to_numpy(), oh.astype(float)])
def main() -> None:
oquv, test = malumot(6000, 1), malumot(3000, 2)
y_tr, y_te = oquv["ketdi"].to_numpy(), test["ketdi"].to_numpy()
print("=== 1. Notebook modeli (qo'lda tayyorlash) ===")
son = oquv[SON].astype(float).copy()
son["oylik_tolov"] = np.log1p(son["oylik_tolov"])
stat = {"ortacha": son.mean(), "std": son.std(), # pandas: ddof=1
"std0": son.std(ddof=0)} # numpy uslubi
model = LogisticRegression(max_iter=1000).fit(
notebook_tayyorla(oquv, stat), y_tr)
p_off = model.predict_proba(notebook_tayyorla(test, stat))[:, 1]
tog_off = ((p_off > 0.5) == y_te).astype(float)
print(f" offline test aniqligi: {tog_off.mean():.4f}")
print("\n=== 2. Xizmatda qayta yozilgan tayyorlash (o'sha test) ===")
print(f" {'variant':<19} {'aniqlik':>7} {'farq':>8} {'2*SE':>7} "
f"{'max|dp|':>8} {'qaror':>6} xulosa")
for variant in ["aniq nusxa", "std ddof=0", "kategoriya tartibi",
"log unutilgan"]:
p = model.predict_proba(xizmat_tayyorla(test, stat, variant))[:, 1]
tog = ((p > 0.5) == y_te).astype(float)
d = tog - tog_off # juftlashgan farq
se = d.std(ddof=1) / np.sqrt(len(d))
dp = np.abs(p - p_off).max()
ozgardi = float(np.mean((p > 0.5) != (p_off > 0.5)))
if dp == 0:
xulosa = "aynan bir xil"
elif ozgardi == 0:
xulosa = "ehtimol biroz farq, qaror bir xil"
elif abs(d.mean()) > 2 * se:
xulosa = "SEZILARLI buzilish"
else:
xulosa = "qarorlar farq qiladi, aniqlik sezilmas"
print(f" {variant:<19} {tog.mean():>7.4f} {d.mean():>+8.4f} "
f"{2 * se:>7.4f} {dp:>8.1e} {ozgardi:>6.1%} {xulosa}")
print(" qaror = qarori o'zgargan mijozlar ulushi (chegara 0.5)")
print("\n=== 3. Bitta mijoz misolida ===")
bitta = test[test["tarif"] == "Biznes"].iloc[[0]]
for variant in ["aniq nusxa", "kategoriya tartibi", "log unutilgan"]:
p1 = model.predict_proba(xizmat_tayyorla(bitta, stat, variant))[0, 1]
print(f" {variant:<19} ketish ehtimoli {p1:.3f}")
print(f" (mijoz: tarif={bitta['tarif'].iloc[0]}, "
f"tolov={bitta['oylik_tolov'].iloc[0]:.0f})")
print("\n=== 4. Yechim: bitta Pipeline obyekti ikki joyda ===")
quvur = Pipeline([
("tayyorla", ColumnTransformer([
("log", Pipeline([("log1p", FunctionTransformer(
np.log1p, feature_names_out="one-to-one")),
("z", StandardScaler())]), ["oylik_tolov"]),
("son", StandardScaler(), ["muddat_oy", "murojaat"]),
("tarif", OneHotEncoder(handle_unknown="ignore"), ["tarif"]),
])),
("model", LogisticRegression(max_iter=1000)),
])
quvur.fit(oquv.drop(columns="ketdi"), y_tr)
with tempfile.TemporaryDirectory() as papka:
yol = Path(papka) / "quvur.joblib"
joblib.dump(quvur, yol)
xizmat = joblib.load(yol) # xizmat SHU faylni yuklaydi
p_oq = quvur.predict_proba(test.drop(columns="ketdi"))[:, 1]
p_xz = xizmat.predict_proba(test.drop(columns="ketdi"))[:, 1]
print(f" offline aniqlik: {((p_oq > 0.5) == y_te).mean():.4f}")
print(f" xizmat aniqligi: {((p_xz > 0.5) == y_te).mean():.4f}")
print(f" max |p_offline - p_xizmat| = {np.abs(p_oq - p_xz).max():.1e}")
print(f" qo'lda yozilgan notebook modelidan farqi: "
f"max {np.abs(p_oq - p_off).max():.1e}")
print(" xizmat XOM ma'lumot oladi - tayyorlash kodi ikkinchi marta "
"yozilmaydi")
if __name__ == "__main__":
main()Natijaning muhim qismi:
=== 1. Notebook modeli (qo'lda tayyorlash) ===
offline test aniqligi: 0.7617
=== 2. Xizmatda qayta yozilgan tayyorlash (o'sha test) ===
variant aniqlik farq 2*SE max|dp| qaror xulosa
aniq nusxa 0.7617 +0.0000 0.0000 0.0e+00 0.0% aynan bir xil
std ddof=0 0.7617 +0.0000 0.0000 4.1e-05 0.0% ehtimol biroz farq, qaror bir xil
kategoriya tartibi 0.6627 -0.0990 0.0175 5.2e-01 23.9% SEZILARLI buzilish
log unutilgan 0.4467 -0.3150 0.0253 1.0e+00 57.9% SEZILARLI buzilish
qaror = qarori o'zgargan mijozlar ulushi (chegara 0.5)
=== 3. Bitta mijoz misolida ===
aniq nusxa ketish ehtimoli 0.146
kategoriya tartibi ketish ehtimoli 0.230
log unutilgan ketish ehtimoli 1.000
(mijoz: tarif=Biznes, tolov=137000)
=== 4. Yechim: bitta Pipeline obyekti ikki joyda ===
offline aniqlik: 0.7617
xizmat aniqligi: 0.7617
max |p_offline - p_xizmat| = 0.0e+00
qo'lda yozilgan notebook modelidan farqi: max 3.0e-07
xizmat XOM ma'lumot oladi - tayyorlash kodi ikkinchi marta yozilmaydiNima ko'rsatdi: 2.3-bo'lim. Barcha to'rt variant xatosiz ishladi — har mijozga [0, 1] oralig'idagi ehtimol qaytdi. Lekin natijalar juda farq qiladi:
std ddof=0— pandas (ddof=1) va numpy (ddof=0) standart og'ishlari farqi. 6000 qatorda bu nisbatsqrt(6000/5999)ga teng, shuning uchun ehtimollar faqat1e-5tartibida farq qildi va birorta qaror o'zgarmadi. Hamma farq ham xavfli emas — lekin buni o'lchab bilamiz, taxmin qilib emas. Kichik partiyada (masalan, 10 qator) bu farq ancha katta bo'lardi.kategoriya tartibi— tarif ustunlari boshqa tartibda. Model "Yoshlar" ga "Biznes" koeffitsiyentini qo'llaydi va hokazo. Mijozlarning chorakka yaqinining qarori o'zgardi, aniqlik juftlashgan farqda2*SEdan ancha ko'p tushdi.log unutilgan—log1psiz to'lov z-bahoda minglab sigma bo'ladi: ehtimol deyarli har doim 0 yoki 1. Aniqlik tanga tashlashdan ham pastga tushdi.
Pipeline bilan esa offline va xizmat bashoratlari aynan teng (0.0e+00), va u qo'lda yozilgan notebook modeli bilan ham amalda bir xil — chunki matematik jihatdan xuddi shu hisob. Farq shundaki, endi tayyorlash bitta joyda yashaydi va model bilan birga saqlanadi.
Misol 2 — Feedback loop: rad etilganlar uchun belgi yo'q
Kredit skoringi. Eski davrda "yangi hudud" segmenti haqiqatan riskli edi (tarixiy ma'lumot), hozir esa bu segment boshqalardan riskli emas. Model har davr qayta o'qitiladi — lekin belgi faqat tasdiqlangan arizalarda paydo bo'ladi. Uch strategiyani solishtiramiz: faqat tasdiqlanganlar, 3% tasodifiy izlanish, 3% izlanish + IPW og'irliklari.
"""Feedback loop: model qarori keyingi o'quv ma'lumotini o'zgartiradi."""
import numpy as np
from sklearn.linear_model import LogisticRegression
CHEGARA = 0.20 # defolt ehtimoli shundan past - kredit beriladi
FOYDA, ZARAR = 1.0, 4.0 # faraziy birliklar: qaytargan +1, defolt -4
DAVRLAR = 12
def arizalar(rng, n, eski=False):
x = rng.normal(0, 1, n) # kredit reytingi (masshtablangan)
s = (rng.random(n) < 0.3).astype(float) # segment: "yangi hudud"
segment_eff = 3.0 if eski else 0.0 # ESKI davrda segment riskli edi
logit = -1.2 - 1.3 * x + segment_eff * s
defolt = (rng.random(n) < 1 / (1 + np.exp(-logit))).astype(int)
return np.column_stack([x, s]), defolt
def simulyatsiya(seed, izlanish, ipw):
rng = np.random.default_rng(seed)
X_hist, y_hist = arizalar(rng, 800, eski=True)
X_bel, y_bel, w_bel = [X_hist], [y_hist], [np.ones(len(y_hist))]
tarix = []
for davr in range(1, DAVRLAR + 1):
model = LogisticRegression().fit(np.vstack(X_bel), np.concatenate(y_bel),
sample_weight=np.concatenate(w_bel))
X, y = arizalar(rng, 2000)
p = model.predict_proba(X)[:, 1]
tasdiq = p < CHEGARA
vazn = np.ones(len(p))
if izlanish > 0: # rad etilganlarning kichik qismi
izla = ~tasdiq & (rng.random(len(p)) < izlanish)
if ipw:
vazn[izla] = 1 / izlanish # bitta izlangan = 1/q ta rad etilgan
tasdiq |= izla
X_bel.append(X[tasdiq]) # belgi FAQAT tasdiqlanganlarda
y_bel.append(y[tasdiq])
w_bel.append(vazn[tasdiq])
foyda = FOYDA * np.sum(tasdiq & (y == 0)) - ZARAR * np.sum(tasdiq & (y == 1))
s1 = X[:, 1] == 1
tarix.append({"davr": davr, "foyda": foyda / len(y),
"s1_baho": model.predict_proba(np.array([[0.0, 1.0]]))[0, 1],
"s1_tasdiq": tasdiq[s1].mean(),
"s0_tasdiq": tasdiq[~s1].mean(),
"belgili": tasdiq.mean(),
"oquvda_s1": np.vstack(X_bel)[:, 1].mean()})
return tarix
def orakul_foyda(seed):
rng = np.random.default_rng(seed + 1000)
X, y = arizalar(rng, 20000)
p = 1 / (1 + np.exp(-(-1.2 - 1.3 * X[:, 0]))) # haqiqiy HOZIRGI ehtimol
tasdiq = p < CHEGARA
return (FOYDA * np.sum(tasdiq & (y == 0))
- ZARAR * np.sum(tasdiq & (y == 1))) / len(y)
STRATEGIYALAR = [("faqat tasdiqlanganlar", 0.0, False),
("3% izlanish", 0.03, False),
("3% izlanish + IPW", 0.03, True)]
def main() -> None:
haqiqiy = 1 / (1 + np.exp(1.2))
print("=== 1. Bitta simulyatsiya (seed 0) ===")
print(f" segment=1, x=0 uchun HOZIRGI haqiqiy defolt ehtimoli: {haqiqiy:.3f}")
print(" populyatsiyada segment=1 ulushi: 30%")
for nom, iz, ipw in STRATEGIYALAR:
print(f"\n -- {nom} --")
print(f" {'davr':>4} {'s1 bahosi':>9} {'s1 tasdiq':>9} {'s0 tasdiq':>9} "
f"{'belgili':>8} {'oquvda s1':>9} {'foyda':>7}")
for q in simulyatsiya(0, iz, ipw):
if q["davr"] in (1, 2, 3, 6, 9, 12):
print(f" {q['davr']:>4} {q['s1_baho']:>9.3f} "
f"{q['s1_tasdiq']:>9.1%} {q['s0_tasdiq']:>9.1%} "
f"{q['belgili']:>8.1%} {q['oquvda_s1']:>9.1%} "
f"{q['foyda']:>+7.3f}")
print("\n=== 2. 10 seed: 12 davr o'rtacha foydasi (ariza boshiga) ===")
natija = {}
for nom, iz, ipw in STRATEGIYALAR:
natija[nom] = np.array([
np.mean([q["foyda"] for q in simulyatsiya(s, iz, ipw)])
for s in range(10)])
orakul = np.mean([orakul_foyda(s) for s in range(3)])
asos = natija["faqat tasdiqlanganlar"]
for nom, qiymat in natija.items():
if nom == "faqat tasdiqlanganlar":
print(f" {nom:<22} {qiymat.mean():+.4f} asos")
continue
d = qiymat - asos # bir xil seedlar - juftlashgan
se = d.std(ddof=1) / np.sqrt(len(d))
if d.mean() > 2 * se:
baho = "SEZILARLI yaxshi"
elif d.mean() < -2 * se:
baho = "SEZILARLI yomon"
else:
baho = "sezilarli emas"
print(f" {nom:<22} {qiymat.mean():+.4f} farq {d.mean():+.4f} "
f"(SE {se:.4f}) - {baho}")
print(f" {'orakul (haqiqiy model)':<22} {orakul:+.4f} yuqori chegara")
yoq = (orakul - asos.mean()) / orakul
print(f" asos orakulga nisbatan yo'qotgan foyda: {yoq:.0%}")
if __name__ == "__main__":
main()Natijaning muhim qismi:
=== 1. Bitta simulyatsiya (seed 0) ===
segment=1, x=0 uchun HOZIRGI haqiqiy defolt ehtimoli: 0.231
populyatsiyada segment=1 ulushi: 30%
-- faqat tasdiqlanganlar --
davr s1 bahosi s1 tasdiq s0 tasdiq belgili oquvda s1 foyda
1 0.847 0.0% 40.6% 28.2% 17.4% +0.150
2 0.852 0.5% 45.7% 32.8% 11.9% +0.172
3 0.853 0.3% 44.7% 31.4% 9.2% +0.172
6 0.852 2.2% 46.6% 33.5% 5.8% +0.184
9 0.844 1.8% 43.9% 30.9% 4.6% +0.162
12 0.830 1.2% 41.2% 29.3% 4.0% +0.141
-- 3% izlanish --
davr s1 bahosi s1 tasdiq s0 tasdiq belgili oquvda s1 foyda
1 0.847 1.8% 42.5% 30.1% 17.8% +0.141
2 0.830 3.2% 45.0% 33.2% 12.9% +0.152
3 0.795 5.7% 47.5% 34.6% 10.9% +0.123
6 0.710 6.4% 47.0% 34.6% 8.3% +0.164
9 0.631 10.7% 46.6% 36.5% 8.0% +0.115
12 0.554 15.0% 44.8% 35.9% 8.9% +0.131
-- 3% izlanish + IPW --
davr s1 bahosi s1 tasdiq s0 tasdiq belgili oquvda s1 foyda
1 0.847 1.8% 42.5% 30.1% 17.8% +0.141
2 0.495 18.8% 50.5% 41.6% 15.9% +0.186
3 0.347 29.3% 53.6% 46.1% 17.0% +0.176
6 0.305 35.6% 51.4% 46.6% 19.2% +0.208
9 0.283 37.7% 49.3% 46.0% 20.8% +0.142
12 0.265 41.7% 47.6% 45.8% 22.6% +0.153
=== 2. 10 seed: 12 davr o'rtacha foydasi (ariza boshiga) ===
faqat tasdiqlanganlar +0.1525 asos
3% izlanish +0.1473 farq -0.0052 (SE 0.0024) - SEZILARLI yomon
3% izlanish + IPW +0.1856 farq +0.0330 (SE 0.0020) - SEZILARLI yaxshi
orakul (haqiqiy model) +0.2179 yuqori chegara
asos orakulga nisbatan yo'qotgan foyda: 30%Nima ko'rsatdi: 2.5-bo'lim. Ustunlar: s1 bahosi — model segment=1, x=0 mijozga bergan defolt ehtimoli (haqiqiysi 0.231); s1 tasdiq / s0 tasdiq — segmentlar bo'yicha tasdiqlash ulushi; belgili — shu davr arizalarining qanchasi belgi oldi; oquvda s1 — to'plangan o'quv ma'lumotida segment=1 ulushi.
- Faqat tasdiqlanganlar. Model tarixiy ma'lumotdan segment=1 ni riskli deb o'rgandi (
0.847) va 12 davr davomida bu fikrdan deyarli qaytmadi: segment=1 ning atigi 0-2.2% i tasdiqlanadi, demak yangi dalil deyarli kelmaydi. Belgi arizalarning faqat taxminan uchdan birida (28-34%) bor. O'quv ma'lumotidagi segment=1 ulushi17.4%dan4.0%gacha tushdi — populyatsiyada esa u30%. Model o'z xatosini o'zi "himoya qiladi". - 3% izlanish (og'irliksiz). Rad etilganlarning 3% i tasodifan tasdiqlanadi — segment=1 bahosi sekin tushadi (
0.554gacha), lekin 800 ta tarixiy yozuvga (ulardan 240 ga yaqini segment=1) nisbatan har davr qo'shiladigan 10-20 ta xolis segment=1 belgisi juda kam. Natija kutilmagan: 10 seedda o'rtacha foyda asosdan sezilarli past. Izlanish pul turadi (rad etilganlar orasida defolt ko'p), og'irliksiz esa u foydasini bu muddatda qaytarmaydi. - 3% izlanish + IPW. Har izlangan misol
1/0.03og'irlik oladi — ya'ni u rad etilgan 33 ta o'xshash mijozning vakili. Segment=1 bahosi ikkinchi-uchinchi davrdayoq haqiqiyga keskin yaqinlashadi (0.847→0.347, 12-davrda0.265), segment=1 tasdiqlash ulushi segment=0 ga yaqinlashadi va 12 davr o'rtacha foydasi asosdan sezilarli yuqori.
Xulosa ikki qavatli: feedback loop haqiqiy zarar keltiradi (asos haqiqiy model — orakulga nisbatan foydaning taxminan 30% ini yo'qotdi), lekin izlanishning o'zi yetarli emas — xolis belgini to'g'ri og'irlik bilan ishlatish kerak. Bu "narx" ham, "foyda" ham raqam bilan o'lchanishi kerak bo'lgan qaror.
Misol 3 — Yashirin bog'liqlik: so'm ming so'mga aylanganda
Skoring modeli daromad ustunini yuqori oqimdagi jadvaldan oladi. 21-kundan boshlab o'sha jadval egasi daromadni ming so'mda saqlashga o'tdi va hech kimga aytmadi. Model 30 kun ishlaydi; biz aniqlikni (belgilar kechikib keladi — bu ma'lumot aslida oylar o'tib ma'lum bo'ladi) va belgisiz ishlaydigan oddiy kirish tekshiruvini kuzatamiz.
"""Yashirin bog'liqlik: yuqori oqimdagi ustun ma'nosi jim o'zgarganda."""
import numpy as np
import pandas as pd
from sklearn.compose import ColumnTransformer
from sklearn.linear_model import LogisticRegression
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import OneHotEncoder, StandardScaler
HUDUDLAR = ["Andijon", "Buxoro", "Samarqand", "Toshkent"]
SON = ["daromad", "qarz", "yosh"]
def partiya(n, rng):
hudud = rng.choice(HUDUDLAR, size=n, p=[0.2, 0.15, 0.25, 0.4])
daromad = np.round(rng.lognormal(np.log(6_000_000), 0.45, n), -3) # so'm
qarz = np.round(daromad * rng.uniform(0.5, 6.0, n), -3)
yosh = rng.integers(21, 66, n)
eff = pd.Series(hudud).map({"Andijon": 0.3, "Buxoro": 0.1,
"Samarqand": 0.0, "Toshkent": -0.4}).to_numpy()
logit = (0.3 - 0.45 * (daromad / 1e6 - 6) + 0.08 * (qarz / 1e6 - 20)
- 0.03 * (yosh - 40) + eff)
defolt = (rng.random(n) < 1 / (1 + np.exp(-logit))).astype(int)
return pd.DataFrame({"daromad": daromad, "qarz": qarz, "yosh": yosh,
"hudud": hudud, "defolt": defolt})
def yasash_quvur():
return Pipeline([
("tayyorla", ColumnTransformer([
("son", StandardScaler(), SON),
("hudud", OneHotEncoder(handle_unknown="ignore"), ["hudud"])])),
("model", LogisticRegression(max_iter=1000))])
def shartnoma(oquv):
"""O'quv ma'lumotidan kirish 'shartnomasi' (mos yozuv)."""
m = {"son": {}, "kat": {}}
for c in SON:
m["son"][c] = {"median": float(oquv[c].median()),
"q_past": float(oquv[c].quantile(0.005)),
"q_yuqori": float(oquv[c].quantile(0.995)),
"null": float(oquv[c].isna().mean())}
m["kat"]["hudud"] = sorted(oquv["hudud"].unique().tolist())
return m
def kirish_tekshiruvi(df, m):
"""Belgisiz ishlaydi: faqat kirish ma'lumotiga qaraydi."""
muammolar = []
for c, r in m["son"].items():
nisbat = df[c].median() / r["median"]
if not 0.5 <= nisbat <= 2.0:
muammolar.append(f"{c}: median nisbati {nisbat:.3g}")
tashqarida = ((df[c] < r["q_past"]) | (df[c] > r["q_yuqori"])).mean()
if tashqarida > 0.05:
muammolar.append(f"{c}: diapazondan tashqari {tashqarida:.0%}")
if df[c].isna().mean() > r["null"] + 0.05:
muammolar.append(f"{c}: null {df[c].isna().mean():.0%}")
for c, bilgan in m["kat"].items():
yangi = (~df[c].isin(bilgan)).mean()
if yangi > 0.02:
muammolar.append(f"{c}: noma'lum qiymat {yangi:.0%}")
return muammolar
def main() -> None:
rng = np.random.default_rng(7)
oquv = partiya(8000, rng)
X_tr = oquv.drop(columns="defolt")
quvur = yasash_quvur().fit(X_tr, oquv["defolt"])
m = shartnoma(X_tr)
print("=== 1. 30 kunlik partiyalar; 21-kundan daromad MING so'mda ===")
kunlar = []
for kun in range(1, 31):
df = partiya(600, rng)
if kun >= 21:
df["daromad"] = df["daromad"] / 1000 # yuqori oqim o'zgardi
X = df.drop(columns="defolt")
p = quvur.predict_proba(X)[:, 1]
kunlar.append({"kun": kun, "aniqlik": ((p > 0.5) == df["defolt"]).mean(),
"ort_p": p.mean(), "rad": (p > 0.5).mean(),
"bayroq": kirish_tekshiruvi(X, m)})
print(f" {'kun':>3} {'aniqlik':>8} {'ortacha p':>9} {'rad ulushi':>10} "
"kirish tekshiruvi")
for k in kunlar:
if k["kun"] in (1, 2, 10, 19, 20, 21, 22, 30):
holat = "; ".join(k["bayroq"]) if k["bayroq"] else "ok"
print(f" {k['kun']:>3} {k['aniqlik']:>8.3f} {k['ort_p']:>9.3f} "
f"{k['rad']:>10.1%} {holat}")
print("\n=== 2. Jamlanma ===")
oldin = np.array([k["aniqlik"] for k in kunlar if k["kun"] < 21])
keyin = np.array([k["aniqlik"] for k in kunlar if k["kun"] >= 21])
d = keyin.mean() - oldin.mean()
se = np.sqrt(oldin.var(ddof=1) / len(oldin) + keyin.var(ddof=1) / len(keyin))
print(f" aniqlik: oldin {oldin.mean():.3f}, keyin {keyin.mean():.3f}, "
f"farq {d:+.3f} (SE {se:.3f})")
print(" " + ("SEZILARLI tushish" if d < -2 * se else "sezilarli emas"))
yolgon = sum(bool(k["bayroq"]) for k in kunlar if k["kun"] < 21)
ushlandi = sum(bool(k["bayroq"]) for k in kunlar if k["kun"] >= 21)
print(f" normal kunlarda yolg'on signal: {yolgon}/20")
print(f" buzilgan kunlarda ushlangan: {ushlandi}/10")
print(" model xato bermadi: har kuni 600 ta 'to'g'ri formatdagi' bashorat")
print("\n=== 3. Boshqa jim o'zgarish: kategoriya nomi ===")
df = partiya(3000, np.random.default_rng(99))
X = df.drop(columns="defolt")
X2 = X.assign(hudud=X["hudud"].replace({"Toshkent": "Toshkent sh."}))
tosh = (df["hudud"] == "Toshkent").to_numpy()
tog = {}
for nom, xx in [("asl nomlar", X), ("'Toshkent sh.'", X2)]:
p = quvur.predict_proba(xx)[:, 1]
tog[nom] = ((p > 0.5) == df["defolt"]).to_numpy().astype(float)
print(f" {nom:<15} aniqlik {tog[nom].mean():.3f}, "
f"Toshkent: ortacha p {p[tosh].mean():.3f}, "
f"rad {(p[tosh] > 0.5).mean():.1%}")
print(f" {'':<15} tekshiruv: {'; '.join(kirish_tekshiruvi(xx, m)) or 'ok'}")
d = tog["'Toshkent sh.'"] - tog["asl nomlar"]
se = d.std(ddof=1) / np.sqrt(len(d))
baho = "sezilarli" if abs(d.mean()) > 2 * se else "sezilarli emas"
print(f" umumiy aniqlik farqi {d.mean():+.4f} (SE {se:.4f}) - {baho}")
print(" handle_unknown='ignore' xatoni YASHIRDI - tekshiruv esa ko'rsatdi")
if __name__ == "__main__":
main()Natijaning muhim qismi:
=== 1. 30 kunlik partiyalar; 21-kundan daromad MING so'mda ===
kun aniqlik ortacha p rad ulushi kirish tekshiruvi
1 0.713 0.495 53.3% ok
2 0.665 0.498 52.5% ok
10 0.662 0.493 52.8% ok
19 0.708 0.514 56.0% ok
20 0.665 0.523 56.5% ok
21 0.527 0.921 100.0% daromad: median nisbati 0.000992; daromad: diapazondan tashqari 100%
22 0.485 0.920 100.0% daromad: median nisbati 0.000955; daromad: diapazondan tashqari 100%
30 0.498 0.918 100.0% daromad: median nisbati 0.000961; daromad: diapazondan tashqari 100%
=== 2. Jamlanma ===
aniqlik: oldin 0.685, keyin 0.506, farq -0.180 (SE 0.008)
SEZILARLI tushish
normal kunlarda yolg'on signal: 0/20
buzilgan kunlarda ushlangan: 10/10
model xato bermadi: har kuni 600 ta 'to'g'ri formatdagi' bashorat
=== 3. Boshqa jim o'zgarish: kategoriya nomi ===
asl nomlar aniqlik 0.703, Toshkent: ortacha p 0.433, rad 41.6%
tekshiruv: ok
'Toshkent sh.' aniqlik 0.690, Toshkent: ortacha p 0.520, rad 57.3%
tekshiruv: hudud: noma'lum qiymat 41%
umumiy aniqlik farqi -0.0127 (SE 0.0046) - sezilarli
handle_unknown='ignore' xatoni YASHIRDI - tekshiruv esa ko'rsatdiNima ko'rsatdi: 2.4-bo'lim. Yuqori oqimdagi birlik o'zgarishi modelni jim buzdi: hech qanday istisno yo'q, lekin daromad ming marta kichik ko'ringani uchun model hammani "kam daromadli" deb biladi — o'rtacha bashorat keskin oshdi va 21-kundan boshlab deyarli hamma rad etiladi. Aniqlik sezilarli tushdi — lekin haqiqiy hayotda bu raqam oylar o'tib, defoltlar ma'lum bo'lganda ko'rinadi. Oddiy kirish tekshiruvi esa birinchi kunning o'zida ikki signal berdi (median nisbati taxminan 0.001, 100% qiymat diapazondan tashqarida) va 20 normal kunda birorta ham yolg'on signal bermadi.
3-qismdagi kategoriya nomi o'zgarishi yanada ayyor: handle_unknown="ignore" tufayli "Toshkent sh." shunchaki barcha nol vektorga aylanadi va model uni "bazaviy" hudud sifatida ko'radi. Umumiy aniqlik atigi -0.0127 ga tushdi (bu 2*SE dan katta, ya'ni sezilarli, lekin dashboardda ko'zga tashlanmaydigan darajada). Toshkent mijozlari (partiyaning 41% i) uchun esa o'rtacha bashorat 0.433 dan 0.520 ga, rad ulushi 41.6% dan 57.3% ga oshdi. Ya'ni umumiy metrika muammoning ko'lamini yashiradi, segment bo'yicha qarash va noma'lum kategoriya ulushini kuzatish esa uni ochadi.
Misol 4 — Kichik ML quvuri noldan: bosqichlar, artefaktlar, darvoza, yetuklik
Notebook dan quvurga birinchi qadam: har bosqich — funksiya, har bosqich faylga iz qoldiradi, quvur manifest yozadi va xeshlaydi. To'rt yurish: asos, aynan takror, buzilgan ma'lumot va zaif model. Oxirida yetuklik chek-listini manifestdan hisoblaymiz.
"""Kichik ML quvuri noldan: bosqichlar, artefaktlar, darvoza, yetuklik."""
import hashlib
import json
import tempfile
from dataclasses import dataclass
from pathlib import Path
import joblib
import numpy as np
import pandas as pd
from sklearn.compose import ColumnTransformer
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import roc_auc_score
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import OneHotEncoder, StandardScaler
def xesh_bayt(b):
return hashlib.sha256(b).hexdigest()[:12]
def xesh_fayl(yol):
return xesh_bayt(Path(yol).read_bytes())
class Toxtatish(Exception):
"""Bosqich quvurni ongli ravishda to'xtatadi."""
@dataclass
class Bosqich:
nom: str
fn: object
saqlaydi: str # nima saqlanishi (hujjat uchun)
class Quvur:
def __init__(self, bosqichlar, papka):
self.bosqichlar = bosqichlar
self.papka = Path(papka)
def ishga_tushir(self, yurish_id, kontekst):
ypapka = self.papka / yurish_id
ypapka.mkdir(parents=True)
manifest = {"yurish": yurish_id, "bosqichlar": []}
holat = "tugadi"
for b in self.bosqichlar:
try:
fayllar = b.fn(kontekst, ypapka) or []
except Toxtatish as sabab:
manifest["bosqichlar"].append({"nom": b.nom, "holat": "TOXTADI",
"sabab": str(sabab)})
holat = f"to'xtadi: {b.nom}"
break
manifest["bosqichlar"].append({
"nom": b.nom, "holat": "ok",
"artefaktlar": {n: xesh_fayl(ypapka / n) for n in sorted(fayllar)}})
manifest["holat"] = holat
(ypapka / "manifest.json").write_text(
json.dumps(manifest, indent=1, ensure_ascii=False), encoding="utf-8")
return manifest
# ------------------------- bosqich funksiyalari -------------------------
def yuklash(k, p):
rng = np.random.default_rng(k["urug"])
n = 4000
df = pd.DataFrame({
"oy": np.sort(rng.integers(1, 13, n)),
"tolov": np.round(rng.lognormal(11.9, 0.5, n), -2),
"muddat": rng.integers(1, 73, n),
"tarif": rng.choice(["A", "B", "C"], n)})
logit = (k["signal"] * (0.8 * np.log(df["tolov"] / 150_000)
- 0.04 * (df["muddat"] - 36))
+ df["tarif"].map({"A": 0.0, "B": 0.5, "C": -0.5}) - 0.4)
df["ketdi"] = (rng.random(n) < 1 / (1 + np.exp(-logit))).astype(int)
if k.get("nulllar"):
df.loc[rng.random(n) < 0.3, "tolov"] = np.nan # yuqori oqim xatosi
df.to_csv(p / "xom.csv", index=False)
k["df"] = df
return ["xom.csv"]
def tekshirish(k, p):
df = k["df"]
hisobot = {"qatorlar": len(df),
"null_ulushi": {c: round(float(df[c].isna().mean()), 4)
for c in df.columns},
"tolov_musbat": bool((df["tolov"].dropna() > 0).all()),
"tarif_malum": bool(df["tarif"].isin(["A", "B", "C"]).all())}
(p / "tekshiruv.json").write_text(json.dumps(hisobot, indent=1))
if max(hisobot["null_ulushi"].values()) > 0.05:
raise Toxtatish("null ulushi 5% dan katta")
if not (hisobot["tolov_musbat"] and hisobot["tarif_malum"]):
raise Toxtatish("sxema buzilgan")
return ["tekshiruv.json"]
def bolish(k, p):
df = k["df"]
k["oquv"], k["test"] = df[df["oy"] <= 9], df[df["oy"] > 9] # vaqt bo'yicha
bolinma = {"oquv": len(k["oquv"]), "test": len(k["test"]),
"qoida": "oy <= 9 oquv, oy > 9 test"}
(p / "bolinma.json").write_text(json.dumps(bolinma))
return ["bolinma.json"]
def oqitish(k, p):
quvur = Pipeline([
("tayyorla", ColumnTransformer([
("son", StandardScaler(), ["tolov", "muddat"]),
("tarif", OneHotEncoder(handle_unknown="ignore"), ["tarif"])])),
("model", LogisticRegression(max_iter=1000))])
quvur.fit(k["oquv"].drop(columns=["ketdi", "oy"]), k["oquv"]["ketdi"])
joblib.dump(quvur, p / "model.joblib")
(p / "konfig.json").write_text(json.dumps({"model": "LogisticRegression",
"urug": k["urug"]}))
k["model"] = quvur
return ["model.joblib", "konfig.json"]
def baholash(k, p):
pr = k["model"].predict_proba(k["test"].drop(columns=["ketdi", "oy"]))[:, 1]
k["auc"] = round(float(roc_auc_score(k["test"]["ketdi"], pr)), 4)
k["bashorat_xesh"] = xesh_bayt(np.round(pr, 6).tobytes())
(p / "metrikalar.json").write_text(json.dumps(
{"auc": k["auc"], "bashorat_xesh": k["bashorat_xesh"]}))
return ["metrikalar.json"]
def darvoza(k, p):
joriy = k["registr"]["ishlab_chiqarish"]
joriy_auc = joriy["auc"] if joriy else 0.0
qaror = {"yangi_auc": k["auc"], "joriy_auc": joriy_auc,
"otdi": k["auc"] >= max(0.70, joriy_auc - 0.005)}
(p / "darvoza.json").write_text(json.dumps(qaror))
if not qaror["otdi"]:
raise Toxtatish(f"AUC {k['auc']} < talab (joriy {joriy_auc}, min 0.70)")
return ["darvoza.json"]
def royxatga(k, p):
model_xesh = xesh_fayl(p / "model.joblib")
reg = k["registr"]
oldingi = [v for v in reg["versiyalar"] if v["model_xesh"] == model_xesh]
if oldingi: # idempotent: dublikat yo'q
yozuv = dict(oldingi[0], izoh="o'zgarish yo'q, yangi versiya yaratilmadi")
else:
yozuv = {"versiya": len(reg["versiyalar"]) + 1, "auc": k["auc"],
"model_xesh": model_xesh, "malumot_xesh": xesh_fayl(p / "xom.csv")}
reg["versiyalar"].append(yozuv)
reg["ishlab_chiqarish"] = yozuv
(p / "registr_yozuvi.json").write_text(json.dumps(yozuv, ensure_ascii=False))
return ["registr_yozuvi.json"]
BOSQICHLAR = [
Bosqich("yuklash", yuklash, "xom ma'lumot (xeshi manifestda)"),
Bosqich("tekshirish", tekshirish, "sifat hisoboti"),
Bosqich("bolish", bolish, "bo'lish qoidasi va hajmlar"),
Bosqich("oqitish", oqitish, "model fayli + konfig"),
Bosqich("baholash", baholash, "metrikalar + bashorat xeshi"),
Bosqich("darvoza", darvoza, "deploy qarori va sababi"),
Bosqich("royxatga", royxatga, "registr yozuvi (versiya)"),
]
YETUKLIK = [
(0, "model faylga saqlanadi"),
(1, "ma'lumot xeshi qayd qilinadi"),
(1, "avtomatik sifat tekshiruvi"),
(1, "metrikalar faylga yoziladi"),
(1, "deploy darvozasi"),
(1, "versiyali registr"),
(1, "takroriy yurish bir xil natija"),
(2, "quvur kodi CI da testlanadi"),
(2, "ishlab chiqarish monitoringi"),
(2, "trigger bo'yicha qayta o'qitish"),
]
def daraja(holat):
natija = -1
for d in range(3):
if all(holat.get(s, False) for dd, s in YETUKLIK if dd <= d):
natija = d
return natija
def main() -> None:
with tempfile.TemporaryDirectory() as papka:
q = Quvur(BOSQICHLAR, papka)
registr = {"versiyalar": [], "ishlab_chiqarish": None}
print("=== 1. Bosqichlar va har birida nima saqlanadi ===")
for i, b in enumerate(BOSQICHLAR, 1):
print(f" {i}. {b.nom:<11} -> {b.saqlaydi}")
print("\n=== 2. To'rt yurish ===")
yurishlar = [("y1_asos", {"urug": 1, "signal": 1.0}),
("y2_takror", {"urug": 1, "signal": 1.0}),
("y3_nulllar", {"urug": 2, "signal": 1.0, "nulllar": True}),
("y4_zaif", {"urug": 3, "signal": 0.3})]
manifestlar = {}
for yid, sozlama in yurishlar:
k = dict(sozlama, registr=registr)
m = q.ishga_tushir(yid, k)
manifestlar[yid] = m
ok = sum(b["holat"] == "ok" for b in m["bosqichlar"])
print(f" {yid:<11} {m['holat']:<22} ok: {ok}/7 "
f"auc={k.get('auc', '-')}")
for b in m["bosqichlar"]:
if b["holat"] != "ok":
print(f" sabab: {b['sabab']}")
print("\n=== 3. Takroriy yurish: artefakt xeshlari ===")
art = {yid: {n: h for b in m["bosqichlar"]
for n, h in b.get("artefaktlar", {}).items()}
for yid, m in manifestlar.items()}
for n in ["xom.csv", "model.joblib", "metrikalar.json"]:
a, b = art["y1_asos"][n], art["y2_takror"][n]
print(f" {n:<16} y1 {a} y2 {b} teng: {a == b}")
y2 = json.loads((Path(papka) / "y2_takror" / "registr_yozuvi.json")
.read_text(encoding="utf-8"))
print(f" y2 registr: {y2.get('izoh', 'yangi versiya')}")
print("\n=== 4. Registr ===")
for v in registr["versiyalar"]:
print(f" v{v['versiya']}: auc {v['auc']}, model {v['model_xesh']}, "
f"malumot {v['malumot_xesh']}")
print(f" ishlab chiqarishda: v{registr['ishlab_chiqarish']['versiya']}")
print(f" y3 fayllari: {sorted(art['y3_nulllar'])}")
print(f" y4 fayllari: {len(art['y4_zaif'])} ta, registr yozuvi yo'q: "
f"{'registr_yozuvi.json' not in art['y4_zaif']}")
print("\n=== 5. Yetuklik chek-listi (manifestdan hisoblangan) ===")
f = art["y1_asos"]
bizniki = {
"model faylga saqlanadi": "model.joblib" in f,
"ma'lumot xeshi qayd qilinadi": "xom.csv" in f,
"avtomatik sifat tekshiruvi": "tekshiruv.json" in f,
"metrikalar faylga yoziladi": "metrikalar.json" in f,
"deploy darvozasi": "darvoza.json" in f,
"versiyali registr": "registr_yozuvi.json" in f,
"takroriy yurish bir xil natija":
art["y1_asos"]["metrikalar.json"] == art["y2_takror"]["metrikalar.json"],
}
noutbuk = {"model faylga saqlanadi": True}
for nom, holat in [("noutbuk jamoa", noutbuk), ("bizning quvur", bizniki)]:
yoq = [s for _, s in YETUKLIK if not holat.get(s, False)]
print(f" {nom:<14} daraja {daraja(holat)}; yetishmaydi {len(yoq)}: "
f"{yoq[0]}" + (f" (+{len(yoq) - 1} ta)" if len(yoq) > 1 else ""))
print(" 2-daraja qismlari: CI 27.8-bob, monitoring 27.11-bob, "
"qayta o'qitish 27.13-bob")
if __name__ == "__main__":
main()Natijaning muhim qismi:
=== 1. Bosqichlar va har birida nima saqlanadi ===
1. yuklash -> xom ma'lumot (xeshi manifestda)
2. tekshirish -> sifat hisoboti
3. bolish -> bo'lish qoidasi va hajmlar
4. oqitish -> model fayli + konfig
5. baholash -> metrikalar + bashorat xeshi
6. darvoza -> deploy qarori va sababi
7. royxatga -> registr yozuvi (versiya)
=== 2. To'rt yurish ===
y1_asos tugadi ok: 7/7 auc=0.7287
y2_takror tugadi ok: 7/7 auc=0.7287
y3_nulllar to'xtadi: tekshirish ok: 1/7 auc=-
sabab: null ulushi 5% dan katta
y4_zaif to'xtadi: darvoza ok: 5/7 auc=0.6402
sabab: AUC 0.6402 < talab (joriy 0.7287, min 0.70)
=== 3. Takroriy yurish: artefakt xeshlari ===
xom.csv y1 076ac1893cf3 y2 076ac1893cf3 teng: True
model.joblib y1 a724798217ba y2 a724798217ba teng: True
metrikalar.json y1 30ae3c6e9e17 y2 30ae3c6e9e17 teng: True
y2 registr: o'zgarish yo'q, yangi versiya yaratilmadi
=== 4. Registr ===
v1: auc 0.7287, model a724798217ba, malumot 076ac1893cf3
ishlab chiqarishda: v1
y3 fayllari: ['xom.csv']
y4 fayllari: 6 ta, registr yozuvi yo'q: True
=== 5. Yetuklik chek-listi (manifestdan hisoblangan) ===
noutbuk jamoa daraja 0; yetishmaydi 9: ma'lumot xeshi qayd qilinadi (+8 ta)
bizning quvur daraja 1; yetishmaydi 3: quvur kodi CI da testlanadi (+2 ta)
2-daraja qismlari: CI 27.8-bob, monitoring 27.11-bob, qayta o'qitish (27.13)Nima ko'rsatdi: 2.2, 2.6-bo'limlar. Quvurning yetti bosqichi har biri o'z izini qoldirdi va manifest ularning xeshlarini yozdi. To'rt yurishning har biri boshqa narsani ko'rsatdi:
y1_asos— barcha bosqichlar o'tdi, model v1 sifatida registrga yozildi.y2_takror— xuddi shu urug' va ma'lumot: xom fayl, model fayli va metrikalar xeshlari aynan teng. Registr bosqichi idempotent — bir xil model uchun yangi versiya yaratmadi. Bu 27.2 va 27.3 darslarining asosiy g'oyasi: qayta ishga tushirish dublikat yoki "boshqa" natija bermasligi kerak.y3_nulllar— yuqori oqimda to'lov ustunining 30% i bo'sh keldi. Tekshiruv bosqichi quvurni to'xtatdi: model ham o'qitilmadi, registrga ham hech narsa yozilmadi. Sabab manifestga yozildi, xom fayl esa tahlil uchun saqlanib qoldi (buzilgan partiyani karantinga olishni 27.3 da ko'ramiz).y4_zaif— ma'lumot signali zaif; model o'qitildi va baholandi, lekin darvoza uni joriy modeldan yomon deb rad etdi. Ishlab chiqarishda v1 qoldi.
Yetuklik chek-listi deklaratsiyadan emas, manifestdan hisoblandi: "noutbuk jamoa" 0-darajada, bizning quvur esa 1-darajada — 2-daraja uchun CI, monitoring va trigger bo'yicha qayta o'qitish yetishmaydi, bular qismning keyingi darslari mavzusi.
5. To'g'ri va noto'g'ri tushunishlar
| Noto'g'ri fikr | To'g'risi |
|---|---|
| "Xizmat xato bermayapti — demak model ishlayapti" | 1-misolda barcha variant xatosiz ishladi; kategoriya tartibi va unutilgan log aniqlikni sezilarli tushirdi |
| "Tayyorlash kodini xizmatda qayta yozish oddiy ish" | Tayyorlash + model — bitta Pipeline; xizmat xom ma'lumot oladi |
| "Har qanday farq — xato" | ddof farqi 6000 qatorda qarorlarni o'zgartirmadi; farqni o'lchash kerak |
| "Model har davr qayta o'qitilsa, o'zi tuzaladi" | 2-misolda feedback loop tufayli 12 davrda tuzalmadi |
| "Biroz izlanish qo'shsak yetarli" | Og'irliksiz 3% izlanish bu muddatda foydani sezilarli kamaytirdi; IPW bilan esa oshirdi |
| "Ma'lumot muammosini aniqlik tushganda ko'ramiz" | Belgilar oylar kechikadi; kirish tekshiruvi birinchi kunning o'zida ushladi |
"handle_unknown='ignore' — xavfsiz tanlov" |
Xatoni yashiradi; noma'lum kategoriya ulushini kuzatish kerak |
| "MLOps = Kubernetes va qimmat vositalar" | Birinchi qadam — kichik quvur, xesh, darvoza va registr |
| "Har loyihaga 2-daraja kerak" | Daraja xavf va chastotaga qarab tanlanadi |
6. Keng tarqalgan xatolar va yechimlari
1. Tayyorlash kodini ikki marta yozish
X = qolda_tayyorla(df); model.fit(X, y) # ⚠️ xizmatda qayta yoziladi
quvur = Pipeline([("tayyorla", ct), ("model", model)]).fit(df, y) # ✅ bitta obyekt2. Masshtab statistikasini so'rovdan hisoblash
z = (x - x.mean()) / x.std() # ⚠️ har so'rovda boshqa
z = (x - stat["ortacha"]) / stat["std"] # ✅ o'quvdan, saqlangan3. Kategoriya tartibiga tayanish
tartib = df["tarif"].unique() # ⚠️ uchrash tartibi
OneHotEncoder(handle_unknown="ignore").fit(df[["tarif"]]) # ✅ o'rganilgan va saqlangan4. Kirish tekshiruvisiz xizmat
p = quvur.predict_proba(X) # ⚠️ ma'no o'zgarsa - jim
muammo = kirish_tekshiruvi(X, shartnoma); log/alert # ✅ belgisiz signal5. Faqat tasdiqlanganlarda qayta o'qitish
model.fit(X[tasdiq], y[tasdiq]) # ⚠️ feedback loop
model.fit(X[tasdiq], y[tasdiq], sample_weight=w[tasdiq]) # ✅ izlanish + IPW6. Manifestsiz o'qitish
joblib.dump(model, "model_final.pkl") # ⚠️ qaysi ma'lumot? qaysi kod?
manifest = {"malumot_xesh": ..., "konfig": ..., "auc": ...} # ✅ har yurishda7. Darvozasiz deploy
registr["ishlab_chiqarish"] = yangi_model # ⚠️ yomonroq bo'lsa ham
if yangi_auc >= max(min_auc, joriy_auc - tolerans): ... # ✅ avtomatik qaror7. Integratsiya — bu bilim qayerda kerak bo'ladi
- 19-qism (o'tilgan):
Pipeline,ColumnTransformer,joblibbilan saqlash — skew ga qarshi asosiy vosita - 18-qism (o'tilgan): test qulfi, juftlashgan taqqoslash, vaqt bo'yicha bo'lish — darvoza qarorlari shu mantiqda
- 21.11, 26.10-darslar (o'tilgan): konfig, jurnal, paket — bu qismda ular quvur bosqichlariga aylanadi
- 27.2-27.4-darslar: reproduksiya, ma'lumot quvuri, eksperiment kuzatuvi — 4-misoldagi quvurning har bosqichi chuqurlashadi
- 27.11-27.13-darslar: monitoring, drift va qayta o'qitish — 3-misoldagi kirish tekshiruvi va 2-misoldagi feedback loop davomi
8. Eng yaxshi amaliyotlar
Tayyorlash va modelni bitta obyektda saqlang; xizmat xom ma'lumot olsin.
Deploydan oldin "bir xil kirish — bir xil bashorat" testini avtomatik qiling.
O'quv ma'lumotidan kirish shartnomasini yozing va har partiyani u bilan solishtiring.
Model qarori o'quv ma'lumotini tanlasa — izlanish va to'g'ri og'irlikni rejalashtiring.
Har yurishda ma'lumot xeshi, konfig, metrika va qarorni manifestga yozing.
Quvurni to'xtata oladigan bosqichlar qo'ying: sifat tekshiruvi va deploy darvozasi.
Yuqori oqimdagi har ma'lumot manbaining egasini va o'zgarish kanalini biling.
Yetuklik darajasini vazifa xavfiga qarab tanlang va bosqichma-bosqich oshiring.
9. Amaliy topshiriq
Vazifa 1: Bashorat qiling
1. # ML tizimida model kodi qancha qismni egallaydi (Sculley)?
2. # hayot siklining olti bosqichi?
3. # training-serving skew nima va nega xato xabari bermaydi?
4. # pandas .std() va numpy .std() farqi?
5. # 1-misolda qaysi ikki variant aniqlikni sezilarli tushirdi?
6. # feedback loop kredit skoringida qanday paydo bo'ladi?
7. # IPW og'irligi izlanish ehtimoli q = 0.03 da nechaga teng?
8. # yuqori oqimda birlik o'zgarsa, belgisiz qanday bilinadi?
9. # handle_unknown="ignore" nimani yashiradi?
10. # MLOps yetuklik darajalari?
11. # darvoza bosqichi nima qiladi?
12. # registr bosqichini nega idempotent qilish kerak?Javoblar
- Kichik qismini; atrofidagi infratuzilma ancha katta
- Ma'lumot → o'qitish → baholash → deploy → monitoring → qayta o'qitish
- O'qitish va xizmatdagi tayyorlash farqi; format to'g'ri, faqat ma'no noto'g'ri
ddof=1vaddof=0- Kategoriya tartibi va unutilgan
log - Belgi faqat tasdiqlanganlarda bo'ladi; rad etilgan hududdan dalil kelmaydi
1 / 0.03 ≈ 33- O'quvdan olingan shartnoma bilan solishtirish (median nisbati, diapazon)
- Noma'lum kategoriyani — u nol vektorga aylanadi
- 0 — qo'lda, 1 — avtomatlashtirilgan quvur, 2 — CI/CD/CT
- Yangi modelni mezon bilan solishtirib, deployga ruxsat beradi yoki to'xtatadi
- Qayta ishga tushirish dublikat versiya yaratmasligi uchun
Vazifa 2: Xatolarni tuzating
1. z = (x - x.mean()) / x.std() # xizmatda, har so'rov partiyasida
2. tartib = df["tarif"].unique() # one-hot ustunlari tartibi
3. model.fit(X[tasdiq], y[tasdiq]) # har davr, faqat tasdiqlanganlar
4. joblib.dump(model, "model_final_v2.pkl")
5. registr["ishlab_chiqarish"] = yangi_modelJavoblar
1. z = (x - stat["ortacha"]) / stat["std"] # o'quvdan saqlangan statistika
# yoki yaxshisi: StandardScaler Pipeline ichida
2. OneHotEncoder(handle_unknown="ignore") # o'rganilgan tartib Pipeline da
# + noma'lum kategoriya ulushini kuzatish
3. izla = ~tasdiq & (rng.random(n) < q); w[izla] = 1 / q
model.fit(X[tasdiq | izla], y[tasdiq | izla], sample_weight=w[tasdiq | izla])
4. manifest = {"malumot_xesh": ..., "konfig": ..., "metrikalar": ...}
joblib.dump(quvur, yurish_papka / "model.joblib")
5. if yangi_auc >= max(min_auc, joriy_auc - tolerans):
registr["ishlab_chiqarish"] = yangi_yozuvVazifa 3: Skew ovchisi
1-misolni kengaytiring:
- Yana ikki skew varianti qo'shing: bo'sh qiymatni o'qitishda median, xizmatda
0bilan to'ldirish;murojaatustunini xizmatdaclip(0, 5)qilish - Har variant uchun aniqlik farqi,
2*SEva qarori o'zgargan ulushni hisoblang std ddof=0variantini 10 qatorli o'quv to'plami bilan takrorlang — farq qanday o'zgaradi?- "Bir xil kirish — bir xil bashorat" testini funksiya sifatida yozing:
assert np.abs(p_off - p_xizmat).max() < 1e-9
Vazifa 4: Feedback loop
2-misolni o'zgartiring:
- Izlanish ulushini
0.01,0.03,0.10qilib, IPW bilan va IPWsiz 12 davr foydasini solishtiring - Tarixiy ma'lumotni 800 dan 200 ga kamaytiring — og'irliksiz izlanish tezroq tuzaladimi?
- Tarixiy ma'lumotga vaqt bo'yicha og'irlik bering (masalan, har davr
0.7ga ko'paytirib kamaytiring) — bu IPW o'rnini bosadimi? - Qaror qoidasini yozing: "eng yaxshisidan sezilarli yomon bo'lmagan eng arzon izlanish"
Vazifa 5: Kirish shartnomasi
3-misoldagi tekshiruvni kuchaytiring:
- Kategoriya ulushlari uchun tekshiruv qo'shing (masalan, "Toshkent" ulushi o'quvdagidan 10 foiz punktdan ko'p farq qilsa)
- 20 normal kunda yolg'on signal sonini hisoblang; chegaralarni shunday tanlangki, yolg'on signal 0 bo'lsin
- Sekin o'zgarishni simulyatsiya qiling: daromad har kuni 3% oshadi — tekshiruv necha kundan keyin ushlaydi?
- Shartnomani JSON faylga yozing va har partiyada fayldan o'qing
Vazifa 6: Quvurni kengaytirish
4-misoldagi quvurga:
- "bazaviy" bosqich qo'shing: eng ko'p uchraydigan sinfni bashorat qiluvchi modelning AUC si (
0.5) va darvozada "bazaviydan kamida 0.05 yaxshi" sharti - Ma'lumot xeshi o'zgarmagan bo'lsa,
oqitishbosqichini o'tkazib yuboring (kesh) - Manifestga har bosqichning "kirish xeshlari" ni ham yozing
- Yetuklik chek-listiga ikki band qo'shing va ularni manifestdan hisoblang
Vazifa 7: O'ylash
Kichik onlayn do'kon ikki model ishlatadi: (A) har kecha ertangi kun uchun omborga buyurtma miqdorini hisoblaydigan talab modeli (xato — ortiqcha zaxira yoki bo'sh javon), (B) oyda bir marta marketing bo'limi uchun "ketish ehtimoli yuqori mijozlar ro'yxati". Jamoa ikki kishidan iborat. Rahbar: "ikkalasini ham to'liq CI/CD/CT ga o'tkazamiz" deydi. Siz nima taklif qilasiz?
Javob
Qisqa javob: yetuklik darajasini har model uchun alohida, xavf va chastotaga qarab tanlash kerak. A modeli har kecha ishlaydi va xatosi to'g'ridan-to'g'ri pul — unga 1-daraja majburiy va 2-darajaning ba'zi qismlari (monitoring, avtomatik darvoza) kerak. B modeli oyda bir marta, inson ko'rib chiqadigan ro'yxat — unga 1-darajaning minimal qismi yetarli.
1. Xavf jadvali
| Savol | A: talab | B: ketish ro'yxati |
|---|---|---|
| Qanchalik tez-tez ishlaydi? | Har kecha | Oyda bir marta |
| Natijani inson ko'radimi? | Yo'q, avtomatik buyurtma | Ha, marketing ko'radi |
| Xato narxi | To'g'ridan-to'g'ri pul | Samarasiz kampaniya |
| Feedback loop bormi? | Ha — buyurtma sotuvni cheklaydi (javonda yo'q narsa sotilmaydi) | Kuchsiz — kampaniya ketishni kamaytirsa, belgi o'zgaradi |
2. A modeli uchun (ustuvor)
# 1-daraja: quvur (yuklash -> tekshirish -> o'qitish -> baholash -> darvoza -> registr)
# kirish shartnomasi: kechagi sotuv ma'lumoti bo'sh yoki birligi o'zgargan bo'lsa - TO'XTAT
# darvoza: yangi model oxirgi 4 hafta backtest xatosi joriydan yomon bo'lmasin
# monitoring: bashorat va haqiqiy sotuv farqi har kuni (27.11)
# feedback loop: "sotuv" emas, "talab" ni o'lchash - javon bo'sh kunlarni belgilashFeedback loop bu yerda muhim: agar model kam buyurtma bersa, mahsulot tugaydi, sotuv past chiqadi va keyingi model yana kam buyurtma beradi — 2-misoldagi mexanizmning o'zi. Bo'sh javon kunlari alohida belgilanishi va o'qitishda boshqacha ishlatilishi (yoki kichik "izlanish" buyurtmasi) kerak.
3. B modeli uchun (minimal)
- Skript +
Pipeline+ manifest (ma'lumot xeshi, metrika) — 4-misoldagi quvurning birinchi beshta bosqichi - Har oy natija bilan birga oldingi oy ro'yxatidagi mijozlarning haqiqiy ketishi solishtiriladi
- CI/CD/CT hozircha shart emas
4. Ikki kishilik jamoa uchun tartib
- Birinchi hafta: ikkala model ham notebook dan skriptga,
Pipelinebilan (skew yo'qoladi) - Ikkinchi hafta: A uchun kirish shartnomasi va darvoza
- Keyin: A uchun kunlik monitoring
- Faqat A barqaror ishlagach: A uchun avtomatik qayta o'qitish triggeri
Rahbarga javob: "To'liq CI/CD/CT ikki kishilik jamoa uchun oylar oladi va B modeliga foyda bermaydi. Avval ikkalasini ham takrorlanadigan quvurga o'tkazamiz (1-2 hafta), keyin pul bilan bog'liq A modeliga himoya — kirish tekshiruvi, darvoza va monitoring — qo'shamiz. Avtomatik qayta o'qitishni A ning monitoringi bir necha oy barqaror ishlagandan keyin yoqamiz."
Nimani mustahkamlaydi: 2.3, 2.5, 2.6-bo'limlar.
Xulosa
Bu darsda notebook modeli bilan ishlab chiqarishdagi ML tizimi o'rtasidagi farqni o'lchadik.
Eng muhim uch fikr:
ML tizimi — model emas, uning atrofidagi quvur; nosozliklar ko'pincha jim. 1-misolda tayyorlash kodini xizmatda qayta yozishning to'rt varianti xatosiz ishladi, lekin kategoriya tartibi va unutilgan
loganiqlikni sezilarli tushirdi;Pipelinebilan esa offline va xizmat bashoratlari aynan teng bo'ldi. 3-misolda yuqori oqimdagi birlik o'zgarishi (so'm → ming so'm) modelni deyarli hammani rad etadigan qildi — hech qanday xato xabarisiz. O'quvdan yozilgan oddiy kirish shartnomasi buni birinchi kunning o'zida ushladi va normal kunlarda yolg'on signal bermadi.Model o'z o'quv ma'lumotini tanlasa, xatosi o'zini tasdiqlaydi. 2-misolda kredit modeli eski ma'lumot tufayli bir segmentni riskli deb o'rgandi va faqat tasdiqlanganlarda qayta o'qitilganda 12 davr davomida tuzalmadi. Og'irliksiz izlanish bu muddatda foydani oshirmadi — aksincha, kamaytirdi; IPW bilan esa segment bahosi ikki-uch davrda haqiqiyga keskin yaqinlashdi va foyda sezilarli oshdi. Izlanishning narxi va foydasi — o'lchanadigan qaror.
Birinchi MLOps qadami — iz qoldiradigan quvur. 4-misolda yetti bosqichli quvur har bosqich artefaktini xeshladi va manifest yozdi: takroriy yurish aynan bir xil xeshlar berdi va registrda dublikat yaratmadi, buzilgan ma'lumot tekshiruvda to'xtatildi, zaif model darvozada rad etildi. Yetuklik chek-listi manifestdan hisoblandi — bu quvur 1-darajada; 2-daraja uchun CI, monitoring va trigger bo'yicha qayta o'qitish kerak.
Keyingi darsda Reproduksiya va muhit: nega bir xil kod ertaga boshqa natija beradi — urug'lar ierarxiyasi, PYTHONHASHSEED tuzog'i, kutubxona versiyalari va ma'lumot xeshi — va natijani istalgan vaqtda qayta olish uchun "reproduksiya manifesti" ni qanday qurish.
Izohlar (0)
Izoh yozish uchun kiring.
- Hozircha izoh yo'q. Birinchi bo'ling!