Modelni saqlash va yuklash
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.
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]}")
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".
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")
quvur.joblib 3978 bayt faqat_model.joblib 975 bayt
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.")
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.
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.")
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.
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))
| holat | natija | asl bilan mos |
|---|---|---|
| asl holat | ishladi | 1.0000 |
| ustunlar aralashtirildi | ishladi | 1.0000 |
| sonlar teskari tartibda | ishladi | 1.0000 |
| bitta ustun YO'Q | ValueError | - |
| qo'shimcha ustun qo'shildi | ishladi | 1.0000 |
| ikki ustun NOMI almashtirildi | ishladi | 0.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
"""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
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'}")
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
"""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)
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")
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.
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.")
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:
- Faqat o'zingiz yasagan modelni yuklang. Internetdan, Telegramdan, noma'lum hamkasbdan kelgan
.joblibni ochmang. - Foydalanuvchi yuklagan faylni hech qachon
joblib.loadqilmang. Agar xizmatingiz model qabul qilsa — bu to'g'ridan-to'g'ri masofadan kod bajarish zaifligi. - Model fayllarini yozish huquqini cheklang. Kim model faylini almashtira olsa, u sizning serveringizda kod bajara oladi.
- Nazoratsiz muhitda ONNX ishlating — u faqat ma'lumot, kod emas (pastda).
- 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.
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'}")
quvur.joblib SHA-256: 38d5b7e4fdac6b30c73c13b7ae5601d5... Yuklashdan oldin tekshiruv: mos
ONNX: kodsiz, tezroq, portativ
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}")
ONNX va sklearn bashoratlari mos: 1.0000
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")
ONNX tezlik ustunligi: 164 barobar
| format | hajmi (bayt) | bitta bashorat (ms) | kod bajaradimi |
|---|---|---|---|
| joblib (sklearn) | 3 978 | 1.335 | ha |
| ONNX | 1 428 | 0.008 | yo'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
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")
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
| Fayl | Nima uchun |
|---|---|
| quvur.joblib | to'liq Pipeline — tayyorlash + model |
| manifest.json | versiyalar, ustunlar, metrikalar, sana |
| quvur.sha256 | fayl almashtirilmaganini tekshirish |
| quvur.onnx | xizmat uchun tez va kodsiz format |
| DATASHEET.md | 37-bob: ma'lumot qayerdan kelgan |
| requirements.txt | muhitni qayta tiklash |
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.")
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
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"}, ]))
| Qadam | Holati |
|---|---|
| 36. Ma'lumot yig'ildi | bajarildi |
| 37. Dataset qurildi | bajarildi |
| 38. Model moslashtirildi | bajarildi |
| 39. Model saqlandi | quvur + manifest + xesh + ONNX |
| 40. Xizmatga chiqarish | keyingi bob — Django API |
Bugun nima o'rgandik
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"}, ]))
| Tushuncha | Bitta jumla |
|---|---|
| Butun Pipeline saqlanadi | O'lchandi: faqat model jim noto'g'ri javob beradi |
| Ustun tartibi | O'lchandi: DataFrame da muhim EMAS — nom bo'yicha tanlanadi |
| Yetishmagan ustun | Baland ovozda ValueError — xavfsiz |
| Nom almashishi | O'lchandi: JIM buziladi — shartnoma bilan ushlanadi |
| Manifest | Versiya, ustun, metrika model yonida turadi |
| Aylanma sinov | Saqlash ishlaganini isbotlaydi, taxmin qilmaydi |
| pickle xavfsizligi | joblib.load kod bajaradi — begona fayl ochilmaydi |
| ONNX | Kodsiz, 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.