Asosiy qismga o'tish

Modelni saqlash va yuklash

40-bob 17 kod bloki 3495 so'z

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

Sahna: noutbukni yopdingiz

Siz uch soat ishladingiz. Pipeline qurdingiz, tuning qildingiz, natija yaxshi. Keyin noutbukni yopdingiz.

Ertaga ochasiz — hamma narsa yo'q. Qaytadan o'qitasiz.

Bu hali eng yomoni emas. Eng yomoni: modelni saqladingiz, lekin noto'g'ri saqladingiz, va buni olti oydan keyin, u allaqachon ishlab chiqarishda noto'g'ri javob berayotganda bilib qolasiz.

Bu bob shu xatolarni o'lchab ko'rsatadi.

python33 qator
import time
import joblib
import numpy as np
import pandas as pd
import sklearn
from pathlib import Path
from sklearn.compose import ColumnTransformer
from sklearn.impute import SimpleImputer
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import accuracy_score
from sklearn.model_selection import train_test_split
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import OneHotEncoder, StandardScaler

uy = pd.read_csv(DATA / "uy_ijara.csv")
SON = ["maydon_m2", "xonalar", "qavat", "metro_km", "qurilgan_yil"]
MATN = ["tuman"]
X = uy[SON + MATN]
y = uy["ijaraga_berildi_30k"]
Xtr, Xte, ytr, yte = train_test_split(X, y, test_size=0.25, random_state=42, stratify=y)

tayyorlash = ColumnTransformer([
    ("son", Pipeline([("imp", SimpleImputer(strategy="median")),
                      ("sc", StandardScaler())]), SON),
    ("matn", OneHotEncoder(handle_unknown="ignore"), MATN),
])
quvur = Pipeline([("tayyorlash", tayyorlash),
                  ("model", LogisticRegression(max_iter=2000))]).fit(Xtr, ytr)
asl_bashorat = quvur.predict(Xte)

print(f"sklearn versiyasi : {sklearn.__version__}")
print(f"Quvur aniqligi    : {accuracy_score(yte, asl_bashorat):.4f}")
print(f"Quvur qadamlari   : {[nom for nom, _ in quvur.steps]}")
Chiqish
sklearn versiyasi : 1.7.2
Quvur aniqligi    : 0.7900
Quvur qadamlari   : ['tayyorlash', 'model']

Birinchi xato: faqat modelni saqlash

Eng ko'p uchraydigan xato — quvur.named_steps["model"] ni saqlash. Model "aqlli qism" bo'lib tuyuladi, tayyorlash esa "shunchaki tozalash".

python8 qator
SAQLASH = ISH / "modellar"
SAQLASH.mkdir(parents=True, exist_ok=True)

joblib.dump(quvur, SAQLASH / "quvur.joblib")
joblib.dump(quvur.named_steps["model"], SAQLASH / "faqat_model.joblib")

for nom in ["quvur.joblib", "faqat_model.joblib"]:
    print(f"  {nom:<22} {(SAQLASH / nom).stat().st_size:>7} bayt")
Chiqish
  quvur.joblib              3978 bayt
  faqat_model.joblib         975 bayt
python9 qator
faqat_model = joblib.load(SAQLASH / "faqat_model.joblib")
print("Faqat modelga XOM ma'lumot beramiz:")
try:
    faqat_model.predict(Xte)
    print("  xato chiqmadi")
except Exception as x:
    print(f"  {type(x).__name__}: {str(x)[:95]}")
print()
print("Yaxshi xabar: bu xato BALAND OVOZDA chiqdi — 'tuman' matn ustuni sonli emas.")
Chiqish
Faqat modelga XOM ma'lumot beramiz:
  ValueError: could not convert string to float: 'Olmazor'

Yaxshi xabar: bu xato BALAND OVOZDA chiqdi — 'tuman' matn ustuni sonli emas.

Lekin shakl to'g'ri bo'lsa, xato JIM qoladi

Yuqoridagi xato bizni qutqardi. Endi shunday holatni ko'raylik: kimdir "modelga son kerak ekan" deb o'ylab, ustunlarni o'zi tayyorlaydi — lekin o'qitishda ishlatilgan tayyorlashdan boshqacha qilib.

Shakl to'g'ri bo'ladi, xato chiqmaydi, model javob qaytaradi. Javob esa noto'g'ri.

python14 qator
xom_son = Xte[SON].fillna(Xte[SON].median())          # scaling YO'Q
kerakli = faqat_model.n_features_in_
qoshimcha = np.zeros((len(xom_son), kerakli - len(SON)))   # OneHot o'rniga nollar
soxta_kirish = np.hstack([xom_son.values, qoshimcha])

soxta_bashorat = faqat_model.predict(soxta_kirish)
mos = float((soxta_bashorat == asl_bashorat).mean())
print(f"Model kutgan ustunlar soni : {kerakli}")
print(f"Xato chiqdimi              : yo'q")
print(f"To'g'ri javob bilan mos    : {mos:.4f}")
print(f"Haqiqiy aniqlik            : {accuracy_score(yte, soxta_bashorat):.4f}")
print(f"Asl quvur aniqligi         : {accuracy_score(yte, asl_bashorat):.4f}")
print()
print("Model ishladi, javob berdi, hech kim xato ko'rmadi — va javoblar buzuq.")
Chiqish
Model kutgan ustunlar soni : 13
Xato chiqdimi              : yo'q
To'g'ri javob bilan mos    : 0.4433
Haqiqiy aniqlik            : 0.4933
Asl quvur aniqligi         : 0.7900

Model ishladi, javob berdi, hech kim xato ko'rmadi — va javoblar buzuq.

Qoida: saqlanadigan narsa — butun Pipeline, model emas. Tayyorlash modelning ajralmas qismi.

Yuklangan quvur nimaga chidaydi, nimaga yo'q

Men bu bo'limga "ustunlar tartibi o'zgarsa, bashorat jim buziladi" deb yozmoqchi edim. O'lchab ko'rdim.

python22 qator
yuklangan = joblib.load(SAQLASH / "quvur.joblib")
sinovlar = []

def sina(nom, kadr):
    try:
        b = yuklangan.predict(kadr)
        return {"holat": nom, "natija": "ishladi",
                "asl bilan mos": f"{float((b == asl_bashorat).mean()):.4f}"}
    except Exception as x:
        return {"holat": nom, "natija": type(x).__name__,
                "asl bilan mos": "-"}

sinovlar.append(sina("asl holat", Xte))
sinovlar.append(sina("ustunlar aralashtirildi", Xte[MATN + SON]))
sinovlar.append(sina("sonlar teskari tartibda", Xte[SON[::-1] + MATN]))
sinovlar.append(sina("bitta ustun YO'Q", Xte.drop(columns=["metro_km"])))
qoshimchali = Xte.copy()
qoshimchali["yangi_ustun"] = 1.0
sinovlar.append(sina("qo'shimcha ustun qo'shildi", qoshimchali))
nomi_almashgan = Xte.rename(columns={"maydon_m2": "xonalar", "xonalar": "maydon_m2"})
sinovlar.append(sina("ikki ustun NOMI almashtirildi", nomi_almashgan))
show(pd.DataFrame(sinovlar))
holatnatijaasl bilan mos
asl holatishladi1.0000
ustunlar aralashtirildiishladi1.0000
sonlar teskari tartibdaishladi1.0000
bitta ustun YO'QValueError-
qo'shimcha ustun qo'shildiishladi1.0000
ikki ustun NOMI almashtirildiishladi0.4433

"Ustunlar tartibi muhim" — bu afsona, haqiqiy xavf boshqa joyda

Jadvalga qarang. Ustunlarni aralashtirdim, sonlarni teskari qildim — bashorat o'zgarmadi.

Sababi: ColumnTransformer ustunlarni nomi bo'yicha tanlaydi, o'rni bo'yicha emas. Shuning uchun DataFrame bersangiz, tartib ahamiyatsiz.

Yetishmayotgan ustun ham xavfsiz: ValueError baland ovozda chiqadi.

Qo'shimcha ustun ham xavfsiz: e'tiborsiz qoldiriladi.

Haqiqiy xavf — oxirgi qator: ustun NOMLARI almashtirilsa, kod ishlaydi, xato chiqmaydi, va javoblar buziladi. Chunki ColumnTransformer nomga ishonadi — nom to'g'ri bo'lsa, ichidagi qiymat noto'g'ri ekanini u bilmaydi.

Shuning uchun himoya nomda emas, tekshiruvda bo'lishi kerak: 37-bobdagi ma'lumot shartnomasi (tozalash/shartnoma.py) aynan shuni ushlaydi — maydon 15..400 oralig'ida, xona 1..10 oralig'ida. Nomlar almashsa, diapazon buziladi va tekshiruv qichqiradi.

Va yana bitta: DataFrame emas, numpy massiv bersangiz, nom yo'qoladi va tartib yana muhim bo'lib qoladi. Shuning uchun ishlab chiqarishga DataFrame uzating.

Model yolg'iz saqlanmaydi: manifest

Model fayli o'zi haqida hech narsa aytmaydi. Olti oydan keyin kerak bo'ladigan savollar: qaysi versiyada o'qitilgan? qaysi ustunlarni kutadi? natijasi qanaqa edi?

shu faylni yaratingmodellar/manifest.py

python39 qator
"""Model bilan birga saqlanadigan ma'lumot."""
import json
import platform
from datetime import date
from pathlib import Path


def manifest_yoz(yol, model, ustunlar, metrikalar, izoh=""):
    """Model yonidagi `manifest.json` ni yozadi.

    Nega kerak: `.joblib` fayl o'zi haqida hech narsa aytmaydi. Versiya mos
    kelmasa, yuklash jim buzilishi mumkin — manifest buni ko'rsatadi.
    """
    import sklearn
    import numpy
    yozuv = {
        "yaratilgan": str(date.today()),
        "python": platform.python_version(),
        "sklearn": sklearn.__version__,
        "numpy": numpy.__version__,
        "model_turi": type(model).__name__,
        "kutilgan_ustunlar": list(ustunlar),
        "metrikalar": metrikalar,
        "izoh": izoh,
    }
    Path(yol).write_text(json.dumps(yozuv, ensure_ascii=False, indent=2), encoding="utf-8")
    return yozuv


def versiyani_tekshir(manifest_yoli):
    """Hozirgi muhit manifestga mos keladimi. Mos kelmasa — ogohlantirish ro'yxati."""
    import sklearn
    import numpy
    m = json.loads(Path(manifest_yoli).read_text(encoding="utf-8"))
    ogoh = []
    for nom, hozir in [("sklearn", sklearn.__version__), ("numpy", numpy.__version__)]:
        if m.get(nom) != hozir:
            ogoh.append(f"{nom}: model {m.get(nom)} bilan saqlangan, hozir {hozir}")
    return ogoh
python14 qator
import sys
sys.path.insert(0, str(ISH))
from modellar.manifest import manifest_yoz, versiyani_tekshir

yozuv = manifest_yoz(
    SAQLASH / "manifest.json", quvur, SON + MATN,
    {"aniqlik": round(accuracy_score(yte, asl_bashorat), 4),
     "test_hajmi": int(len(yte))},
    izoh="uy_ijara.csv, ijaraga_berildi_30k target",
)
for k, v in yozuv.items():
    print(f"  {k:<18} {v}")
print()
print(f"Versiya tekshiruvi: {versiyani_tekshir(SAQLASH / 'manifest.json') or 'mos keladi'}")
Chiqish
  yaratilgan         2026-10-03
  python             3.10.12
  sklearn            1.7.2
  numpy              2.2.6
  model_turi         Pipeline
  kutilgan_ustunlar  ['maydon_m2', 'xonalar', 'qavat', 'metro_km', 'qurilgan_yil', 'tuman']
  metrikalar         {'aniqlik': 0.79, 'test_hajmi': 300}
  izoh               uy_ijara.csv, ijaraga_berildi_30k target

Versiya tekshiruvi: mos keladi

Saqlash ishlaganini TEKSHIRISH

Saqlash kodini yozib, "tayyor" deyish yetarli emas. Fayl diskda turgani — u to'g'ri yuklangani degani emas.

shu faylni yaratingmodellar/aylanma_sinov.py

python18 qator
"""Saqlash -> yuklash -> bashorat zanjiri buzilmaganini tekshiradi."""
import joblib
import numpy as np


def aylanma_sinov(model, X_namuna, yol):
    """Modelni saqlaydi, qayta yuklaydi va bashoratlar AYNAN mos kelishini tekshiradi.

    Bu sinov har deploy dan oldin ishga tushiriladi. U arzon, lekin
    "saqladim deb o'yladim" turidagi xatolarning hammasini ushlaydi.
    """
    oldin = model.predict(X_namuna)
    joblib.dump(model, yol)
    keyin = joblib.load(yol).predict(X_namuna)
    if not np.array_equal(oldin, keyin):
        farq = int((oldin != keyin).sum())
        raise AssertionError(f"saqlash/yuklash bashoratni o'zgartirdi: {farq} qatorda farq")
    return len(X_namuna)
python4 qator
from modellar.aylanma_sinov import aylanma_sinov

n = aylanma_sinov(quvur, Xte, SAQLASH / "quvur.joblib")
print(f"Aylanma sinov: {n} qatorda bashorat aynan bir xil — OK")
Chiqish
Aylanma sinov: 300 qatorda bashorat aynan bir xil — OK

Endi sinov haqiqiy xatoni ushlaydimi? Eng ko'p uchraydigan deploy xatosi — noto'g'ri artefakt: papkada eski model qolib ketgan, yangisi esa yozilmagan.

python17 qator
eski_model = Pipeline([
    ("tayyorlash", tayyorlash),
    ("model", LogisticRegression(max_iter=2000, C=0.0005)),   # boshqa sozlama
]).fit(Xtr, ytr)
joblib.dump(eski_model, SAQLASH / "eski_artefakt.joblib")

oldin = quvur.predict(Xte)
keyin = joblib.load(SAQLASH / "eski_artefakt.joblib").predict(Xte)
farq = int((oldin != keyin).sum())
print(f"Kutilgan model aniqligi : {accuracy_score(yte, oldin):.4f}")
print(f"Papkadagi eski model    : {accuracy_score(yte, keyin):.4f}")
print(f"Nechta qatorda farq     : {farq} / {len(Xte)}")
print()
if farq:
    print("Aylanma sinov shu holatda AssertionError beradi va deploy to'xtaydi.")
else:
    print("Farq yo'q — bu sozlamada ikki model bir xil javob berdi.")
Chiqish
Kutilgan model aniqligi : 0.7900
Papkadagi eski model    : 0.7967
Nechta qatorda farq     : 12 / 300

Aylanma sinov shu holatda AssertionError beradi va deploy to'xtaydi.

Sinovni deploy zanjiriga ulang

aylanma_sinov o'zi yozilgani yetarli emas — u har deploy dan oldin ishga tushishi kerak.

Amalda bu CI bosqichi bo'ladi: model qurildi -> aylanma sinov -> xesh yozildi -> serverga yuborildi. Sinov yiqilsa, zanjir to'xtaydi va buzuq model ishlab chiqarishga chiqmaydi.

Qo'lda "men tekshirdim" degan yondashuv birinchi shoshilinch tuzatishda buziladi.

Xavfsizlik: pickle kod ishga tushiradi

`joblib.load` — bu kod BAJARISH, shunchaki fayl o'qish emas

joblib va pickle fayl ichidagi ko'rsatmalarni bajaradi. Maxsus tayyorlangan .joblib fayl yuklanganda ixtiyoriy kod ishga tushishi mumkin: fayllaringizni o'chirishi, kalitlaringizni yuborishi, serverga kirish ochishi.

Shuning uchun qoidalar qat'iy:

  1. Faqat o'zingiz yasagan modelni yuklang. Internetdan, Telegramdan, noma'lum hamkasbdan kelgan .joblib ni ochmang.
  2. Foydalanuvchi yuklagan faylni hech qachon joblib.load qilmang. Agar xizmatingiz model qabul qilsa — bu to'g'ridan-to'g'ri masofadan kod bajarish zaifligi.
  3. Model fayllarini yozish huquqini cheklang. Kim model faylini almashtira olsa, u sizning serveringizda kod bajara oladi.
  4. Nazoratsiz muhitda ONNX ishlating — u faqat ma'lumot, kod emas (pastda).
  5. Model fayli xeshini saqlang va yuklashdan oldin solishtiring.

Bu nazariy xavf emas. "Modelni yuklash" eng ko'p e'tibordan chetda qoladigan hujum yo'llaridan biri.

python11 qator
import hashlib

def fayl_xeshi(yol):
    return hashlib.sha256(Path(yol).read_bytes()).hexdigest()

xesh = fayl_xeshi(SAQLASH / "quvur.joblib")
print(f"quvur.joblib SHA-256: {xesh[:32]}...")
(SAQLASH / "quvur.sha256").write_text(xesh, encoding="utf-8")

kutilgan = (SAQLASH / "quvur.sha256").read_text(encoding="utf-8").strip()
print(f"Yuklashdan oldin tekshiruv: {'mos' if fayl_xeshi(SAQLASH / 'quvur.joblib') == kutilgan else 'MOS EMAS — YUKLAMANG'}")
Chiqish
quvur.joblib SHA-256: 38d5b7e4fdac6b30c73c13b7ae5601d5...
Yuklashdan oldin tekshiruv: mos

ONNX: kodsiz, tezroq, portativ

python17 qator
from skl2onnx import to_onnx
from skl2onnx.common.data_types import FloatTensorType, StringTensorType
import onnxruntime as ort

turlar = [(c, (StringTensorType if c in MATN else FloatTensorType)([None, 1]))
          for c in SON + MATN]
onx = to_onnx(quvur, initial_types=turlar,
              options={id(quvur.named_steps["model"]): {"zipmap": False}})
(SAQLASH / "quvur.onnx").write_bytes(onx.SerializeToString())

seans = ort.InferenceSession(str(SAQLASH / "quvur.onnx"),
                             providers=["CPUExecutionProvider"])
kirish = {c: (Xte[c].values.reshape(-1, 1).astype(np.float32) if c in SON
              else Xte[c].values.reshape(-1, 1).astype(object))
          for c in SON + MATN}
onnx_bashorat = seans.run(None, kirish)[0].ravel()
print(f"ONNX va sklearn bashoratlari mos: {float((onnx_bashorat == asl_bashorat).mean()):.4f}")
Chiqish
ONNX va sklearn bashoratlari mos: 1.0000
python19 qator
bitta = {c: v[:1] for c, v in kirish.items()}
t0 = time.perf_counter()
for _ in range(300):
    seans.run(None, bitta)
onnx_ms = (time.perf_counter() - t0) / 300 * 1000

bitta_df = Xte.iloc[:1]
t0 = time.perf_counter()
for _ in range(300):
    yuklangan.predict(bitta_df)
skl_ms = (time.perf_counter() - t0) / 300 * 1000

show(pd.DataFrame([
    {"format": "joblib (sklearn)", "hajmi (bayt)": (SAQLASH / "quvur.joblib").stat().st_size,
     "bitta bashorat (ms)": round(skl_ms, 3), "kod bajaradimi": "ha"},
    {"format": "ONNX", "hajmi (bayt)": (SAQLASH / "quvur.onnx").stat().st_size,
     "bitta bashorat (ms)": round(onnx_ms, 3), "kod bajaradimi": "yo'q"},
]))
print(f"ONNX tezlik ustunligi: {skl_ms / onnx_ms:.0f} barobar")
Chiqish
ONNX tezlik ustunligi: 164 barobar
formathajmi (bayt)bitta bashorat (ms)kod bajaradimi
joblib (sklearn)3 9781.335ha
ONNX1 4280.008yo'q

Tezlik farqi qayerdan — halol izoh

ONNX bitta qator uchun sezilarli tez chiqdi, lekin bu "ONNX matematikani tezroq hisoblaydi" degani emas.

Vaqtning katta qismi hisob emas, ortiqcha yuk: sklearn har chaqiruvda DataFrame tekshiradi, ColumnTransformer ustunlarni ajratadi, har bosqich o'z tekshiruvini qiladi. ONNX da bu ishlar oldindan bitta grafga aylantirilgan.

Shuning uchun farq bitta qator bashoratida eng katta — ya'ni aynan API xizmatida (40-bob). Minglab qatorni birdan bashorat qilsangiz, farq ancha kichik bo'ladi.

Qachon ONNX: real vaqtli API, mobil/brauzer, Python bo'lmagan muhit, xavfsizlik muhim joy.

Qachon joblib: tadqiqot, offline paketli bashorat, murakkab maxsus transformerlar (ularni ONNX ko'pincha qo'llab-quvvatlamaydi).

Nima saqlanadi — to'liq ro'yxat

python13 qator
show(pd.DataFrame([
    {"Fayl": "quvur.joblib", "Nima uchun": "to'liq Pipeline — tayyorlash + model"},
    {"Fayl": "manifest.json", "Nima uchun": "versiyalar, ustunlar, metrikalar, sana"},
    {"Fayl": "quvur.sha256", "Nima uchun": "fayl almashtirilmaganini tekshirish"},
    {"Fayl": "quvur.onnx", "Nima uchun": "xizmat uchun tez va kodsiz format"},
    {"Fayl": "DATASHEET.md", "Nima uchun": "37-bob: ma'lumot qayerdan kelgan"},
    {"Fayl": "requirements.txt", "Nima uchun": "muhitni qayta tiklash"},
]))
print()
print("Hammasi bitta papkada: ish/modellar/")
for p in sorted(SAQLASH.iterdir()):
    if p.is_file():
        print(f"  {p.name:<22} {p.stat().st_size:>7} bayt")
Chiqish
Hammasi bitta papkada: ish/modellar/
  aylanma_sinov.py           693 bayt
  eski_artefakt.joblib      3978 bayt
  faqat_model.joblib         975 bayt
  manifest.json              368 bayt
  manifest.py               1335 bayt
  quvur.joblib              3978 bayt
  quvur.onnx                1428 bayt
  quvur.sha256                64 bayt
FaylNima uchun
quvur.joblibto'liq Pipeline — tayyorlash + model
manifest.jsonversiyalar, ustunlar, metrikalar, sana
quvur.sha256fayl almashtirilmaganini tekshirish
quvur.onnxxizmat uchun tez va kodsiz format
DATASHEET.md37-bob: ma'lumot qayerdan kelgan
requirements.txtmuhitni qayta tiklash
python9 qator
muhit = f"""scikit-learn=={sklearn.__version__}
numpy=={np.__version__}
pandas=={pd.__version__}
joblib=={joblib.__version__}
"""
(SAQLASH / "requirements.txt").write_text(muhit, encoding="utf-8")
print(muhit)
print("Versiyalar QAT'IY qadalgan (==), chunki sklearn ichki formati versiyalar")
print("orasida o'zgaradi va eski model yangi versiyada jim buzilishi mumkin.")
Chiqish
scikit-learn==1.7.2
numpy==2.2.6
pandas==2.3.3
joblib==1.5.3

Versiyalar QAT'IY qadalgan (==), chunki sklearn ichki formati versiyalar
orasida o'zgaradi va eski model yangi versiyada jim buzilishi mumkin.

Protokolga ulanish

python7 qator
show(pd.DataFrame([
    {"Qadam": "36. Ma'lumot yig'ildi", "Holati": "bajarildi"},
    {"Qadam": "37. Dataset qurildi", "Holati": "bajarildi"},
    {"Qadam": "38. Model moslashtirildi", "Holati": "bajarildi"},
    {"Qadam": "39. Model saqlandi", "Holati": "quvur + manifest + xesh + ONNX"},
    {"Qadam": "40. Xizmatga chiqarish", "Holati": "keyingi bob — Django API"},
]))
QadamHolati
36. Ma'lumot yig'ildibajarildi
37. Dataset qurildibajarildi
38. Model moslashtirildibajarildi
39. Model saqlandiquvur + manifest + xesh + ONNX
40. Xizmatga chiqarishkeyingi bob — Django API

Bugun nima o'rgandik

python11 qator
show(pd.DataFrame([
    {"Tushuncha": "Butun Pipeline saqlanadi", "Bitta jumla": "O'lchandi: faqat model jim noto'g'ri javob beradi"},
    {"Tushuncha": "Ustun tartibi", "Bitta jumla": "O'lchandi: DataFrame da muhim EMAS — nom bo'yicha tanlanadi"},
    {"Tushuncha": "Yetishmagan ustun", "Bitta jumla": "Baland ovozda ValueError — xavfsiz"},
    {"Tushuncha": "Nom almashishi", "Bitta jumla": "O'lchandi: JIM buziladi — shartnoma bilan ushlanadi"},
    {"Tushuncha": "Manifest", "Bitta jumla": "Versiya, ustun, metrika model yonida turadi"},
    {"Tushuncha": "Aylanma sinov", "Bitta jumla": "Saqlash ishlaganini isbotlaydi, taxmin qilmaydi"},
    {"Tushuncha": "pickle xavfsizligi", "Bitta jumla": "joblib.load kod bajaradi — begona fayl ochilmaydi"},
    {"Tushuncha": "ONNX", "Bitta jumla": "Kodsiz, portativ, bitta qatorda ancha tez"},
    {"Tushuncha": "Versiya qadash", "Bitta jumla": "== bilan, chunki ichki format o'zgaradi"},
]))
TushunchaBitta jumla
Butun Pipeline saqlanadiO'lchandi: faqat model jim noto'g'ri javob beradi
Ustun tartibiO'lchandi: DataFrame da muhim EMAS — nom bo'yicha tanlanadi
Yetishmagan ustunBaland ovozda ValueError — xavfsiz
Nom almashishiO'lchandi: JIM buziladi — shartnoma bilan ushlanadi
ManifestVersiya, ustun, metrika model yonida turadi
Aylanma sinovSaqlash ishlaganini isbotlaydi, taxmin qilmaydi
pickle xavfsizligijoblib.load kod bajaradi — begona fayl ochilmaydi
ONNXKodsiz, portativ, bitta qatorda ancha tez
Versiya qadash== bilan, chunki ichki format o'zgaradi

Keyingi bobda shu saqlangan modelni xizmatga chiqaramiz: Django API, validatsiya va xatolarni to'g'ri qaytarish.

Bu bobni bepul o'qidingiz.

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

Telegramda yozish Narxlar