Asosiy qismga o'tish

Yakuniy loyiha: hammasini bitta tizimga

38-bob 10 kod bloki 1943 so'z

Bu bobdagi kod shu fayllar bilan ishlaydio'z kompyuteringizda ishga tushiring

Topshiriq

Bu bobda yangi tushuncha yo'q. Bu — imtihon, 21-bobdagi kabi, lekin endi butun kitob bo'yicha.

RostUy sizga aniq talab qo'ydi:

Broker aniqlash modeli ishlab chiqarishda ishlasin. Uni har hafta qayta o'qitish mumkin bo'lsin. Yomon model chiqmasin. Buzilsa o'zi qaytsin. Eskirganini o'zi aytsin.

To'rtta talab. Har biri bir necha bobga tegishli.

python13 qator
import pandas as pd
show(pd.DataFrame([
    {"Talab": "Takrorlanadigan o'qitish", "Qaysi boblar": "2, 5, 24",
     "Nima kerak": "Seed, ma'lumot xeshi, bitta buyruq"},
    {"Talab": "Yomon model chiqmasin", "Qaysi boblar": "6, 24, 25, 31",
     "Nima kerak": "Sifat tekshiruvi, model testlari, darvoza"},
    {"Talab": "Qaysi model ishlayapti", "Qaysi boblar": "22, 23",
     "Nima kerak": "Kuzatuv, registr, alias"},
    {"Talab": "Buzilsa qaytsin", "Qaysi boblar": "27, 28, 30, 32",
     "Nima kerak": "Sog'liq, konteyner, avtomatik qaytarish"},
    {"Talab": "Eskirganini aytsin", "Qaysi boblar": "34, 35",
     "Nima kerak": "O'lchov, PSI, ogohlantirish"},
]))
TalabQaysi boblarNima kerak
Takrorlanadigan o'qitish2, 5, 24Seed, ma'lumot xeshi, bitta buyruq
Yomon model chiqmasin6, 24, 25, 31Sifat tekshiruvi, model testlari, darvoza
Qaysi model ishlayapti22, 23Kuzatuv, registr, alias
Buzilsa qaytsin27, 28, 30, 32Sog'liq, konteyner, avtomatik qaytarish
Eskirganini aytsin34, 35O'lchov, PSI, ogohlantirish

Tizim ko'rinishi

python22 qator
diagramma(
    {
        "Ma'lumot\n(manifest)": (0, 2.6, 2.1, 1.0, "manba"),
        "Sifat\n(6-bob)": (2.8, 2.6, 1.9, 1.0, "qayta"),
        "O'qitish\n(24-bob)": (5.4, 2.6, 2.0, 1.0, "qayta"),
        "Darvoza\n(31-bob)": (8.1, 2.6, 2.0, 1.0, "kuzatuv"),
        "Registr\n(23-bob)": (10.8, 2.6, 2.0, 1.0, "saqlash"),
        "Servis\n(27-28)": (10.8, 0.9, 2.0, 1.0, "xizmat"),
        "Monitoring\n(34-35)": (8.1, 0.9, 2.0, 1.0, "kuzatuv"),
    },
    [
        ("Ma'lumot\n(manifest)", "Sifat\n(6-bob)"),
        ("Sifat\n(6-bob)", "O'qitish\n(24-bob)"),
        ("O'qitish\n(24-bob)", "Darvoza\n(31-bob)"),
        ("Darvoza\n(31-bob)", "Registr\n(23-bob)", "o'tdi"),
        ("Registr\n(23-bob)", "Servis\n(27-28)"),
        ("Servis\n(27-28)", "Monitoring\n(34-35)"),
        ("Monitoring\n(34-35)", "O'qitish\n(24-bob)", "drift"),
    ],
    nom="yakuniy_tizim.png", kenglik=14,
    izoh="Halqa yopiq: monitoring driftni sezsa, o'qitish qaytadan boshlanadi.",
)

Halqa yopiq: monitoring driftni sezsa, o'qitish qaytadan boshlanadi.

Halqa yopiq: monitoring driftni sezsa, o'qitish qaytadan boshlanadi.

Diqqat qiling — bu halqa, chiziq emas. Oxirgi strelka monitoringdan o'qitishga qaytadi.

Chiziqli tizimda model bir marta chiqariladi va unutiladi. Halqali tizimda u yashaydi.

1-bosqich: ma'lumot va uning qaydnomasi

shu faylni yaratingsrc/quvur/loyiha_oxiri.py

python64 qator
"""Yakuniy quvur: ma'lumotdan monitoring darajasigacha."""
import hashlib
import json
import time
from dataclasses import dataclass, asdict
from pathlib import Path
from typing import Any

import numpy as np
import pandas as pd
from sklearn.ensemble import RandomForestClassifier
from sklearn.metrics import f1_score, precision_score, recall_score
from sklearn.model_selection import train_test_split

from .xususiyat import XUSUSIYATLAR, tayyorla


@dataclass(frozen=True, slots=True)
class Pasport:
    """2-bobdagi pasport + 5-bobdagi ma'lumot xeshi."""
    malumot_xesh: str
    qatorlar: int
    seed: int
    parametrlar: dict[str, Any]
    metrikalar: dict[str, float]
    vaqt: str


def malumot_xesh(yol: str | Path) -> str:
    h = hashlib.sha256()
    with open(yol, "rb") as f:
        for blok in iter(lambda: f.read(1 << 20), b""):
            h.update(blok)
    return h.hexdigest()[:16]


QOIDA = {
    "narx_mln": lambda s: s.between(0.5, 200),
    "maydon": lambda s: s.between(10, 500),
    "xonalar": lambda s: s.between(1, 10),
}


def sifat(d: pd.DataFrame) -> tuple[pd.DataFrame, int]:
    """6-bob: chegarada tekshirish."""
    yaxshi = pd.Series(True, index=d.index)
    for ustun, tekshir in QOIDA.items():
        yaxshi &= tekshir(d[ustun])
    return d[yaxshi], int((~yaxshi).sum())


def oqit(d: pd.DataFrame, seed: int = 42) -> tuple[Any, dict[str, float]]:
    """24-bob: bitta funksiya, takrorlanadigan."""
    x, y = tayyorla(d), d["broker"]
    Xtr, Xte, ytr, yte = train_test_split(
        x, y, test_size=0.25, stratify=y, random_state=seed)
    m = RandomForestClassifier(n_estimators=150, min_samples_leaf=3,
                               random_state=seed, n_jobs=1).fit(Xtr, ytr)
    p = m.predict(Xte)
    return m, {
        "f1": float(f1_score(yte, p)),
        "aniqlik": float(precision_score(yte, p)),
        "toliqlik": float(recall_score(yte, p)),
    }
python15 qator
python3 -c "
import sys; sys.path.insert(0, 'src')
import pandas as pd
from quvur.loyiha_oxiri import malumot_xesh, sifat

YOL = '../datasets/ijara.csv'
d = pd.read_csv(YOL)
xesh = malumot_xesh(YOL)
toza, rad = sifat(d)
print(f'1-BOSQICH: MALUMOT')
print(f'  malumot xeshi : {xesh}')
print(f'  kirgan qator  : {len(d):,}')
print(f'  rad etilgan   : {rad:,}')
print(f'  toza          : {len(toza):,}')
"
Chiqish (qaytish kodi 0, 801 ms)
1-BOSQICH: MALUMOT
  malumot xeshi : 03dbeed95e47e74c
  kirgan qator  : 12,000
  rad etilgan   : 0
  toza          : 12,000

Xesh — bu qaysi ma'lumot ekanining isboti (5-bob). U pasportga yoziladi va olti oydan keyin "o'sha ma'lumotmi?" degan savolga javob beradi.

2-bosqich: o'qitish va darvoza

python27 qator
python3 -c "
import sys, json, time; sys.path.insert(0, 'src')
import joblib, pandas as pd
from quvur.loyiha_oxiri import Pasport, malumot_xesh, oqit, sifat
from quvur.darvoza import Chegara, asos_oqi, darvoza, hisobot
from dataclasses import asdict

YOL = '../datasets/ijara.csv'
d, _ = sifat(pd.read_csv(YOL))
m, metrikalar = oqit(d, seed=42)

p = Pasport(
    malumot_xesh=malumot_xesh(YOL), qatorlar=len(d), seed=42,
    parametrlar={'n_estimators': 150, 'min_samples_leaf': 3},
    metrikalar=metrikalar,
    vaqt=time.strftime('%Y-%m-%d %H:%M'),
)

ok, sabablar = darvoza(metrikalar, Chegara(), asos_oqi('ci_asos.json'))
print('2-BOSQICH: OQITISH VA DARVOZA')
print(hisobot(metrikalar, ok, sabablar))
if ok:
    joblib.dump(m, 'yakuniy_model.joblib')
    open('yakuniy_pasport.json','w').write(json.dumps(asdict(p), indent=1))
    print()
    print('  model va pasport saqlandi')
"
Chiqish (qaytish kodi 0, 2312 ms)
2-BOSQICH: OQITISH VA DARVOZA
==============================================
SIFAT DARVOZASI
==============================================
  f1          : 0.8595
  aniqlik     : 0.8929
  toliqlik    : 0.8285
----------------------------------------------
  NATIJA: O'TDI

  model va pasport saqlandi

Darvoza (31-bob) — bu yerda eng muhim tugun. Undan o'tmagan model hech qayerga bormaydi.

3-bosqich: model testlari

Darvoza F1 ni tekshirdi. Lekin 25-bobda ko'rgandik: yaxshi F1 mantiqiy xulqni kafolatlamaydi.

python22 qator
python3 -c "
import sys; sys.path.insert(0, 'src')
import joblib
from quvur.sinov_yordam import ehtimol

m = joblib.load('yakuniy_model.joblib')

# 25-bob: minimal funksionallik
ega = ehtimol(m, telefon_takrorlanishi=1, kunlik_elonlar=1,
              tavsif_uzunligi=600, rasmlar_soni=15, egasi_tasdiqlangan=1)
brok = ehtimol(m, telefon_takrorlanishi=18, kunlik_elonlar=9,
               tavsif_uzunligi=60, rasmlar_soni=2, egasi_tasdiqlangan=0)

# 25-bob: yonalish
past = ehtimol(m, telefon_takrorlanishi=1)
yuqori = ehtimol(m, telefon_takrorlanishi=20)

print('3-BOSQICH: MODEL TESTLARI')
print(f'  aniq ega          : {ega:.4f}  {\"OK\" if ega < 0.3 else \"XATO\"}')
print(f'  aniq broker       : {brok:.4f}  {\"OK\" if brok > 0.7 else \"XATO\"}')
print(f'  telefon 1 -> 20   : {past:.4f} -> {yuqori:.4f}  {\"OK\" if yuqori > past else \"XATO\"}')
"
Chiqish (qaytish kodi 0, 1401 ms)
3-BOSQICH: MODEL TESTLARI
  aniq ega          : 0.0563  OK
  aniq broker       : 0.8983  OK
  telefon 1 -> 20   : 0.0516 -> 0.6446  OK

Uchala test ham mantiqni tekshiradi, aniqlikni emas. Tarozi yonyaptimi emas, raqam ma'nolimi.

4-bosqich: chiqarishga tayyorlash

python26 qator
python3 -c "
import sys, json, pathlib; sys.path.insert(0, 'src')
import joblib
from quvur.xususiyat import XUSUSIYATLAR
from quvur.format import onnx_ga, onnx_sessiya, onnx_ehtimol
import numpy as np, pandas as pd
from quvur.xususiyat import tayyorla

m = joblib.load('yakuniy_model.joblib')
onnx_ga(m, len(XUSUSIYATLAR), 'yakuniy.onnx')

# 26-bob: ONNX bir xil javob beradimi
d = pd.read_csv('../datasets/ijara.csv').head(500)
x = tayyorla(d)
a = m.predict_proba(x)[:, 1]
s = onnx_sessiya('yakuniy.onnx')
b = onnx_ehtimol(s, x.values)
farq = float(np.max(np.abs(a - b)))
qaror = int(np.sum((a >= 0.5) == (b >= 0.5)))

print('4-BOSQICH: ONNX')
print(f'  joblib hajmi : {pathlib.Path(\"yakuniy_model.joblib\").stat().st_size/1024/1024:.1f} MB')
print(f'  onnx hajmi   : {pathlib.Path(\"yakuniy.onnx\").stat().st_size/1024/1024:.1f} MB')
print(f'  eng katta farq: {farq:.2e}')
print(f'  qaror mosligi : {qaror}/{len(d)}')
"
Chiqish (qaytish kodi 0, 2691 ms)
4-BOSQICH: ONNX
  joblib hajmi : 14.0 MB
  onnx hajmi   : 6.8 MB
  eng katta farq: 4.61e-07
  qaror mosligi : 500/500
2026-09-29 21:38:24.084126709 [W:onnxruntime:Default, device_discovery.cc:164 DiscoverDevicesForPlatform] GPU device discovery failed: device_discovery.cc:89 ReadFileContents Failed to open file: "/sys/class/drm/card0/device/vendor"

5-bosqich: drift kuzatuvi

python20 qator
python3 -c "
import sys; sys.path.insert(0, 'src')
import pandas as pd
from quvur.drift import hisobot, KATTA

eski = pd.read_csv('../datasets/ijara.csv')
yangi = pd.read_csv('../datasets/ijara_yangi.csv')
USTUN = ['tavsif_uzunligi','rasmlar_soni','telefon_takrorlanishi','kunlik_elonlar','narx_mln']
h = hisobot(eski, yangi, USTUN)

print('5-BOSQICH: DRIFT')
print(h.to_string(index=False))
ogoh = h[h['psi'] >= 0.10]
print()
if len(ogoh):
    print(f'  OGOHLANTIRISH: {len(ogoh)} ustunda siljish')
    print(f'  qaror: qayta oqitish kerak (35-bob)')
else:
    print('  siljish yoq - tegmang')
"
Chiqish (qaytish kodi 0, 376 ms)
5-BOSQICH: DRIFT
                ustun    psi     baho
         rasmlar_soni 0.2251 o'rtacha
      tavsif_uzunligi 0.1262 o'rtacha
telefon_takrorlanishi 0.0089 barqaror
       kunlik_elonlar 0.0075 barqaror
             narx_mln 0.0046 barqaror

  OGOHLANTIRISH: 2 ustunda siljish
  qaror: qayta oqitish kerak (35-bob)

Yakuniy hisobot

python19 qator
python3 -c "
import json, pathlib
p = json.load(open('yakuniy_pasport.json'))
print('=' * 50)
print('LOYIHA HISOBOTI')
print('=' * 50)
print(f'  malumot xeshi : {p[\"malumot_xesh\"]}')
print(f'  qatorlar      : {p[\"qatorlar\"]:,}')
print(f'  seed          : {p[\"seed\"]}')
print(f'  F1            : {p[\"metrikalar\"][\"f1\"]:.4f}')
print(f'  aniqlik       : {p[\"metrikalar\"][\"aniqlik\"]:.4f}')
print(f'  toliqlik      : {p[\"metrikalar\"][\"toliqlik\"]:.4f}')
print(f'  vaqt          : {p[\"vaqt\"]}')
print('-' * 50)
for f in ('yakuniy_model.joblib', 'yakuniy.onnx', 'yakuniy_pasport.json'):
    bor = pathlib.Path(f).exists()
    print(f'  {f:26s} {\"bor\" if bor else \"YOQ\"}')
print('=' * 50)
"
Chiqish (qaytish kodi 0, 22 ms)
==================================================
LOYIHA HISOBOTI
==================================================
  malumot xeshi : 03dbeed95e47e74c
  qatorlar      : 12,000
  seed          : 42
  F1            : 0.8595
  aniqlik       : 0.8929
  toliqlik      : 0.8285
  vaqt          : 2026-09-29 21:38
--------------------------------------------------
  yakuniy_model.joblib       bor
  yakuniy.onnx               bor
  yakuniy_pasport.json       bor
==================================================

Bu hisobot — loyihaning javobgarligi. Unda uchta savolga javob bor: qaysi ma'lumot, qanday sozlama, qanday natija.

Olti oydan keyin kimdir so'rasa, javob faylda turibdi — kimningdir esida emas.

Nimani qurdingiz

Kitob boshida model bor edi va u jim buzilardi. Endi sizda tizim bor:

Xususiyat Qayerdan
Bir xil natija ikki marta Seed + ma'lumot xeshi (2, 5-bob)
Buzuq ma'lumot kirmaydi Chegarada tekshiruv (6-bob)
Bitta buyruq bilan o'qitish Quvur (24-bob)
Yomon model chiqmaydi Darvoza (31-bob)
Mantiqsiz xulq ushlanadi Model testlari (25-bob)
Qaysi model ishlayapti Registr va alias (23-bob)
Tez va kichik xizmat ONNX va konteyner (26, 28-bob)
Buzilsa qaytadi Avtomatik qaytarish (32-bob)
Foydalanuvchi ko'rmaydi Shadow va canary (33-bob)
Eskirgani bilinadi O'lchov va PSI (34, 35-bob)

Hammasini birdan qurmang

Bu ro'yxat to'liq ko'rinadi va yangi loyihada uni butunlay qurish vasvasasi bo'ladi.

Qilmang. Har element muammo paydo bo'lganda qo'shiladi:

  1. Avval takrorlanish (2-bob) — usiz qolgani ma'nosiz.
  2. Keyin sifat tekshiruvi (6-bob) — eng ko'p muammo shundan.
  3. Keyin darvoza (31-bob) — birinchi marta yomon model chiqqanda.
  4. Keyin monitoring (34-bob) — birinchi marta "qachondan beri buzuq?" deb so'raganda.

Qolganlari kerak bo'lganda qo'shiladi. 19-bobdagi qoida bu yerda ham amal qiladi: murakkablikni muammo talab qilganda qo'shing, oldindan emas.

Tozalash

matn2 qator
rm -f yakuniy_model.joblib yakuniy.onnx yakuniy_pasport.json
echo "vaqtinchalik fayllar tozalandi"
Chiqish (qaytish kodi 0, 27 ms)
vaqtinchalik fayllar tozalandi

Talaba ishi

Bu bobning talaba ishi — o'z loyihangiz.

  1. O'z ma'lumotingizni oling (yoki ballon.csv / buyurtma.csv). Masalani aniqlang: nimani bashorat qilasiz va qaror kim uchun?
  2. 1–8 boblarni takrorlang: manifest, sifat qoidalari, xususiyat kodi, quvur.
  3. Darvoza chegarasini o'zingiz tanlang va nega shu raqam ekanini yozib qo'ying.
  4. Servisni ko'taring va p99 ni o'lchang (29-bob). Sizning talabingiz qancha?
  5. Bir oy ishlatib, PSI ni o'lchang. Nimadir siljidimi?

Oxirgi punkt eng muhimi. Kitobdagi hamma narsa vaqt bilan ma'noga ega bo'ladi — bir oy ishlatmaguncha, siz faqat kodni ko'rgan bo'lasiz, tizimni emas.

Bu bobni bepul o'qidingiz.

Kitob o'rgatadi, lekin sizning kodingiz nega ishlamaganini ayta olmaydi. Kursda uy vazifangiz tekshiriladi.

Telegramda yozish Narxlar