IlmHamroh
Data Science va sun'iy intellekt/scikit learn toliq9/10-dars19 daqiqa
Mundarija (22)

19.9-dars: Tezlik va xotira

19-QISM — SCIKIT-LEARN TO'LIQ · 9-dars


1. Kirish va motivatsiya

Ma'lumot o'sgani sari "ishlaydi" degan javob yetarli bo'lmay qoladi: o'rgatish tunda tugamaydi, xotira tugaydi, bashorat so'roviga javob kech keladi.

Yaxshi xabar shuki, sklearn da tezlik va xotira muammolarining katta qismi algoritmni almashtirmasdan hal qilinadi: to'g'ri dtype (float64 o'rniga float32 — ikki barobar kam xotira), siyrak matritsalar, n_jobs ni to'g'ri qo'yish, qaysi model qaysi hajmga mos ekanini bilish va partial_fit bilan bo'laklab o'rgatish.

Yomon xabar shuki, noto'g'ri qadamlar ham osongina qilinadi: n_jobs=-1 ni har joyga qo'yish sekinlashtirishi mumkin, siyrakni zichga aylantirish xotirani portlatadi, KNeighbors esa butun o'quv to'plamini saqlaydi.

Bu darsda: dtype, siyraklik, n_jobs va ichma-ich parallellik, model tanlash, partial_fit, xotirani o'lchash va qayerni optimallashtirish kerakligini aniqlash.

Real vaziyat. Matn klassifikatsiyasi quvuri 12 GB xotira so'rardi va serverda ishlamasdi. Sabab bitta qatorda edi: TfidfVectorizer chiqishi siyrak edi, lekin keyingi StandardScaler(with_mean=True) uni zichga aylantirishga majbur qilardi. with_mean=False qo'yilgach xotira 400 MB ga tushdi.

Bu darsda tezlik va xotirani o'rganamiz.

Bu darsda:

  • dtype va xotira
  • Siyraklikni saqlash
  • n_jobs va ichma-ich parallellik
  • Model va hajm
  • partial_fit
  • Qayerni optimallashtirish
  • Tuzoqlar
  • Amaliy: quvurni tezlashtirish

ℹ Misollar real numpy/scipy/sklearn bilan (Python 3.14, sklearn 1.9).


2. Nazariya — chuqur tushuntirish

2.1. dtype va xotira

text
float64 (sukut)  8 bayt/element
float32          4 bayt/element  -> IKKI BAROBAR kam
float16          2 bayt          -> aniqlik juda past, ehtiyot

1 000 000 x 100 matritsa:
  float64 -> 800 MB
  float32 -> 400 MB

sklearn ning KO'PCHILIGI float32 ni qabul qiladi va
ichkarida float64 ga AYLANTIRMAYDI (tekshiring!)

INT uchun ham: int64 -> int32 / int8 (kategoriya kodlari)

pandas da:
  df = df.astype({"a": "float32", "k": "category"})
  "category" dtype takroriy satrlarni bir marta saqlaydi

float32 ga o'tish xotirani ikki barobar kamaytiradi va ko'pchilik model uchun aniqlikka ta'sir qilmaydi.

2.2. Siyraklikni saqlash

text
SIYRAK MATRITSA: faqat nolmas elementlar saqlanadi
  CSR - qatorlar bo'yicha (sklearn ko'pchiligi shuni kutadi)
  CSC - ustunlar bo'yicha

QACHON FOYDALI: nolmas ulush < 10%
  matn (TF-IDF), one-hot (ko'p daraja), grafik

SIYRAKNI BUZADIGANLAR:
  StandardScaler(with_mean=True)     -> markazlashtirish nolni buzadi
  PCA (oddiy)                        -> TruncatedSVD ishlating
  PolynomialFeatures                 -> portlash
  toarray() / todense()              -> ochiq aylantirish
  set_output("pandas")               -> siyrak qo'llab-quvvatlanmaydi

SIYRAKNI QABUL QILADIGAN MODELLAR:
  LogisticRegression, LinearSVC, SGDClassifier
  MultinomialNB, ComplementNB
  RandomForest (qabul qiladi, lekin sekin)

QABUL QILMAYDIGANLAR:
  HistGradientBoosting, KNeighbors (ba'zi metrika bilan)

StandardScaler(with_mean=False) siyraklikni saqlaydi — matn quvurlarida bu eng ko'p uchraydigan tuzatish.

2.3. n_jobs va ichma-ich parallellik

text
n_jobs=None (sukut)  bitta protsess
n_jobs=-1            barcha yadrolar
n_jobs=k             k ta protsess

QACHON FOYDA BERADI:
  RandomForest, ExtraTrees (daraxtlar mustaqil)
  cross_val_score, GridSearchCV (foldlar mustaqil)
  KNeighbors (so'rovlar mustaqil)

QACHON FOYDA BERMAYDI yoki ZARAR QILADI:
  kichik ma'lumot (protsess yaratish narxi kattaroq)
  HistGradientBoosting (ichkarida OpenMP ishlatadi)
  ichma-ich: GridSearchCV(n_jobs=-1) + RF(n_jobs=-1)
    -> protsesslar YADRO UCHUN KURASHADI, sekinlashadi

QOIDA: FAQAT BIR pog'onada parallellik
  tashqi (GridSearchCV) n_jobs=-1, ichki n_jobs=1

Ichma-ich parallellik sekinlashtiradi: tashqi halqada n_jobs=-1 bo'lsa, ichki modelda n_jobs=1 qo'ying.

2.4. Model va hajm

text
n < 10 000:
  hamma narsa ishlaydi, SVC(rbf) ham

n = 10 000 - 100 000:
  SVC(rbf) sekin (O(n^2)) -> LinearSVC yoki SGD
  RandomForest ishlaydi
  HistGradientBoosting - eng yaxshi tanlov

n = 100 000 - 1 000 000:
  HistGradientBoosting (binlash tufayli tez)
  SGDClassifier / SGDRegressor
  KNeighbors SEKIN (har so'rov uchun qidiruv)

n > 1 000 000:
  SGD + partial_fit
  HistGradientBoosting (xotira yetsa)
  namuna olish (subsampling) - ko'pincha yetarli

XOTIRA TALABI:
  KNeighbors - BUTUN o'quv to'plami
  SVC - support vektorlar
  RandomForest - daraxtlar (n_estimators x chuqurlik)
  chiziqli model - faqat koeffitsiyentlar (eng kichik)

HistGradientBoosting katta ma'lumot uchun sukut tanlov: u qiymatlarni binlaydi va n bo'yicha deyarli chiziqli ishlaydi.

2.5. partial_fit

python
model = SGDClassifier(loss="log_loss", random_state=0)
sinflar = np.unique(y)
for bolak_X, bolak_y in bolaklar:
    model.partial_fit(bolak_X, bolak_y, classes=sinflar)

QO'LLAB-QUVVATLAYDIGANLAR:
  SGDClassifier, SGDRegressor
  MultinomialNB, BernoulliNB
  MiniBatchKMeans
  StandardScaler, IncrementalPCA (transformerlar ham)

MUHIM:
  birinchi chaqiruvda classes= berish SHART (klassifikatsiyada)
  ma'lumot ARALASHTIRILGAN bo'lishi kerak
  bir necha EPOXA kerak bo'lishi mumkin
  Pipeline partial_fit ni QO'LLAB-QUVVATLAMAYDI

Pipeline da partial_fit yo'q — bo'laklab o'rgatishda tayyorlashni qo'lda boshqarish kerak.

2.6. Qayerni optimallashtirish

text
QOIDA: OLDIN O'LCHANG, KEYIN OPTIMALLASHTIRING

O'LCHASH:
  import time; t0 = time.perf_counter(); ...; time.perf_counter() - t0
  cProfile / py-spy (funksiya darajasida)
  quvur qadamlari: cross_validate -> fit_time

XOTIRA:
  X.nbytes (zich), (data.nbytes + indices.nbytes + indptr.nbytes) (siyrak)
  tracemalloc, memory_profiler

ODATIY TAQSIMOT:
  ma'lumot o'qish/tayyorlash   40%
  belgi hisoblash              30%
  model fit                    25%
  qolgani                       5%

Ya'ni model emas, TAYYORLASH ko'pincha eng qimmat

Model fit ko'pincha eng qimmat qadam emas — avval o'lchang, keyin optimallashtiring.

2.7. Tuzoqlar

Asosiy tuzoqlar: ichma-ich n_jobs=-1; siyrakni zichga aylantirish; StandardScaler(with_mean=True) ni siyrak bilan; float64 ni keraksiz saqlash; kichik ma'lumotda n_jobs=-1; KNeighbors ni katta ma'lumotda; o'lchamasdan optimallashtirish; PolynomialFeatures ni ko'p belgi bilan.

2.8. Uch arzon yutuq

Tezlik va xotira muammolarining katta qismi uchta arzon qadam bilan hal bo'ladi: float32 ga o'tish (xotira yarmiga), siyraklikni saqlash (with_mean=False, TruncatedSVD) va parallellikni bir pog'onada qoldirish. To'rtinchisi — to'g'ri modelni tanlash: n > 100 000 da HistGradientBoosting yoki SGD. Va har doim oldin o'lchang.


3. Tez ma'lumotnoma

python
import numpy as np
from scipy import sparse
from sklearn.decomposition import TruncatedSVD
from sklearn.preprocessing import StandardScaler

X32 = X.astype(np.float32)                 # xotira yarmiga
StandardScaler(with_mean=False)            # siyraklikni saqlaydi
TruncatedSVD(n_components=100)             # siyrak uchun PCA
OneHotEncoder(sparse_output=True)          # siyrak chiqish

GridSearchCV(..., n_jobs=-1)               # TASHQI parallellik
RandomForestClassifier(..., n_jobs=1)      # ICHKI ketma-ket

def hajm_mb(m):
    if sparse.issparse(m):
        return (m.data.nbytes + m.indices.nbytes
                + m.indptr.nbytes) / 1024**2
    return m.nbytes / 1024**2
QOIDA: oldin o'lcha · float32 · siyrakni saqla ·
       parallellik bir pog'onada

Tezlik va xotira xulosasi

float32: xotira yarmiga
Siyraklik: with_mean=False, TruncatedSVD
n_jobs: faqat BIR pog'onada
n > 100k: HistGradientBoosting yoki SGD
partial_fit: bo'laklab (Pipeline qo'llab-quvvatlamaydi)
Oldin o'lchang

4. Batafsil misollar

Misollar real numpy/scipy/sklearn bilan (Python 3.14).

Misol 1 — dtype va xotira

python
"""float64, float32 va kategoriya (real numpy/pandas/sklearn)."""

import numpy as np
import pandas as pd
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import roc_auc_score
from sklearn.model_selection import train_test_split
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import StandardScaler


def main() -> None:
    rng = np.random.default_rng(0)
    n, p = 60_000, 40
    X64 = rng.normal(0, 1, (n, p))
    y = (X64[:, 0] + 0.5 * X64[:, 1] + rng.normal(0, 1, n) > 0).astype(int)

    print("=== 1. Massiv hajmi ===")
    print(f"  {'dtype':<12} {'hajm (MB)':>11} {'nisbat':>8}")
    asos = X64.nbytes / 1024**2
    for nom, dt in [("float64", np.float64), ("float32", np.float32),
                    ("float16", np.float16)]:
        Xd = X64.astype(dt)
        mb = Xd.nbytes / 1024**2
        print(f"  {nom:<12} {mb:>11.2f} {asos / mb:>7.1f}x")

    print("\n=== 2. Aniqlikka ta'siri ===")
    Xtr64, Xte64, ytr, yte = train_test_split(X64, y, test_size=0.3,
                                              random_state=0)
    print(f"  {'dtype':<12} {'test AUC':>10} {'farq':>10}")
    asos_auc = None
    for nom, dt in [("float64", np.float64), ("float32", np.float32)]:
        m = make_pipeline(StandardScaler(),
                          LogisticRegression(max_iter=2000))
        m.fit(Xtr64.astype(dt), ytr)
        auc = roc_auc_score(yte, m.predict_proba(Xte64.astype(dt))[:, 1])
        asos_auc = asos_auc if asos_auc is not None else auc
        print(f"  {nom:<12} {auc:>10.6f} {auc - asos_auc:>+10.6f}")

    print("\n=== 3. sklearn dtype ni saqlaydimi ===")
    for nom, dt in [("float64", np.float64), ("float32", np.float32)]:
        sc = StandardScaler().fit(X64[:1000].astype(dt))
        chiqish = sc.transform(X64[:1000].astype(dt))
        print(f"  kirish {nom:<9} -> chiqish {chiqish.dtype}, "
              f"mean_ {sc.mean_.dtype}")

    print("\n=== 4. pandas: category dtype ===")
    df = pd.DataFrame({
        "kod": rng.choice([f"m{i}" for i in range(50)], n),
        "son": rng.normal(0, 1, n),
    })
    xom_mb = df.memory_usage(deep=True).sum() / 1024**2
    df2 = df.astype({"kod": "category", "son": "float32"})
    yangi_mb = df2.memory_usage(deep=True).sum() / 1024**2
    print(f"  {'variant':<24} {'hajm (MB)':>11}")
    print(f"  {'xom (str + float64)':<24} {xom_mb:>11.2f}")
    print(f"  {'category + float32':<24} {yangi_mb:>11.2f}")
    print(f"  tejash: {xom_mb / yangi_mb:.1f}x")
    print("  ⭐ float32 - eng arzon optimallashtirish")


if __name__ == "__main__":
    main()

Natijaning muhim qismi:

text
=== 1. Massiv hajmi ===
  dtype          hajm (MB)   nisbat
  float64            18.31     1.0x
  float32             9.16     2.0x
  float16             4.58     4.0x

=== 2. Aniqlikka ta'siri ===
  dtype          test AUC       farq
  float64        0.848591  +0.000000
  float32        0.848591  +0.000000

=== 3. sklearn dtype ni saqlaydimi ===
  kirish float64   -> chiqish float64, mean_ float64
  kirish float32   -> chiqish float32, mean_ float64

=== 4. pandas: category dtype ===
  variant                    hajm (MB)
  xom (str + float64)             3.42
  category + float32              0.29
  tejash: 11.9x
  ⭐ float32 - eng arzon optimallashtirish

Nima ko'rsatdi: 2.1-bo'lim.

Misol 2 — Siyraklikni saqlash

python
"""Siyraklikni nima buzadi (real numpy/scipy/sklearn)."""

import numpy as np
from scipy import sparse
from sklearn.decomposition import PCA, TruncatedSVD
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.linear_model import LogisticRegression
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import MaxAbsScaler, StandardScaler


def matnlar_yasa(n: int = 6000, seed: int = 0):
    rng = np.random.default_rng(seed)
    lugat = [f"soz{i}" for i in range(4000)]
    matnlar, y = [], []
    for _ in range(n):
        sinf = int(rng.integers(0, 2))
        asos = rng.choice(lugat[:200] if sinf else lugat[200:400], 8)
        shovqin = rng.choice(lugat, 12)
        matnlar.append(" ".join(np.concatenate([asos, shovqin])))
        y.append(sinf)
    return matnlar, np.asarray(y)


def hajm_mb(m) -> float:
    if sparse.issparse(m):
        return (m.data.nbytes + m.indices.nbytes
                + m.indptr.nbytes) / 1024**2
    return m.nbytes / 1024**2


def main() -> None:
    matnlar, y = matnlar_yasa()
    vek = TfidfVectorizer(max_features=4000)
    X = vek.fit_transform(matnlar)

    print("=== 1. Siyrak matritsa ===")
    print(f"  shakl: {X.shape}, turi: {type(X).__name__}")
    print(f"  nolmas elementlar: {X.nnz:,}")
    toldirilgan = X.nnz / (X.shape[0] * X.shape[1])
    print(f"  to'ldirilganlik: {toldirilgan:.3%}")
    print(f"  siyrak hajm: {hajm_mb(X):.2f} MB")
    print(f"  zich bo'lsa: "
          f"{X.shape[0] * X.shape[1] * 8 / 1024**2:.2f} MB")

    print("\n=== 2. Nima siyraklikni buzadi ===")
    sinashlar = {
        "StandardScaler(with_mean=True)": StandardScaler(),
        "StandardScaler(with_mean=False)": StandardScaler(with_mean=False),
        "MaxAbsScaler": MaxAbsScaler(),
        "TruncatedSVD(50)": TruncatedSVD(n_components=50, random_state=0),
    }
    print(f"  {'transformer':<34} {'natija':<22}")
    for nom, tr in sinashlar.items():
        try:
            chiqish = tr.fit_transform(X)
            turi = "siyrak" if sparse.issparse(chiqish) else "ZICH"
            print(f"  {nom:<34} {turi + f', {hajm_mb(chiqish):.1f} MB':<22}")
        except Exception as xato:
            print(f"  {nom:<34} {type(xato).__name__:<22}")

    print("\n=== 3. PCA siyrak bilan ===")
    try:
        PCA(n_components=50).fit(X)
        print("  PCA siyrakni qabul qildi (kutilmagan)")
    except TypeError as xato:
        print(f"  PCA: {type(xato).__name__}")
        print(f"    {str(xato).splitlines()[0][:60]}")
    svd = TruncatedSVD(n_components=50, random_state=0).fit(X)
    print(f"  TruncatedSVD ishladi: "
          f"tushuntirilgan dispersiya "
          f"{svd.explained_variance_ratio_.sum():.4f}")

    print("\n=== 4. To'g'ri va noto'g'ri quvur ===")
    from sklearn.model_selection import StratifiedKFold, cross_val_score
    cv = StratifiedKFold(3, shuffle=True, random_state=0)
    variantlar = {
        "with_mean=False (siyrak)": Pipeline([
            ("sc", StandardScaler(with_mean=False)),
            ("m", LogisticRegression(max_iter=1000))]),
        "MaxAbsScaler (siyrak)": Pipeline([
            ("sc", MaxAbsScaler()),
            ("m", LogisticRegression(max_iter=1000))]),
    }
    print(f"  {'variant':<28} {'CV AUC':>9} {'oraliq hajm (MB)':>18}")
    for nom, q in variantlar.items():
        b = cross_val_score(q, X, y, cv=cv, scoring="roc_auc")
        oraliq = q.fit(X, y)[:-1].transform(X)
        print(f"  {nom:<28} {b.mean():>9.4f} {hajm_mb(oraliq):>18.2f}")
    zich_mb = X.shape[0] * X.shape[1] * 8 / 1024**2
    print(f"  {'zich bo_lsa':<28} {'-':>9} {zich_mb:>18.2f}")
    print("  ⭐ with_mean=False siyraklikni saqlaydi")


if __name__ == "__main__":
    main()

Natijaning muhim qismi:

text
=== 1. Siyrak matritsa ===
  shakl: (6000, 4000), turi: csr_matrix
  nolmas elementlar: 118,934
  to'ldirilganlik: 0.496%
  siyrak hajm: 1.38 MB
  zich bo'lsa: 183.11 MB

=== 2. Nima siyraklikni buzadi ===
  transformer                        natija
  StandardScaler(with_mean=True)     ValueError
  StandardScaler(with_mean=False)    siyrak, 1.4 MB
  MaxAbsScaler                       siyrak, 1.4 MB
  TruncatedSVD(50)                   ZICH, 2.3 MB

=== 3. PCA siyrak bilan ===
  PCA siyrakni qabul qildi (kutilmagan)
  TruncatedSVD ishladi: tushuntirilgan dispersiya 0.0611

=== 4. To'g'ri va noto'g'ri quvur ===
  variant                         CV AUC   oraliq hajm (MB)
  with_mean=False (siyrak)        1.0000               1.38
  MaxAbsScaler (siyrak)           1.0000               1.38
  zich bo_lsa                          -             183.11
  ⭐ with_mean=False siyraklikni saqlaydi

Nima ko'rsatdi: 2.2-bo'lim.

Misol 3 — Model tanlash va hajm

python
"""Qaysi model qaysi hajmga mos (real numpy/sklearn)."""

import io
import pickle

import numpy as np
from sklearn.datasets import make_classification
from sklearn.ensemble import HistGradientBoostingClassifier, RandomForestClassifier
from sklearn.linear_model import LogisticRegression, SGDClassifier
from sklearn.metrics import roc_auc_score
from sklearn.model_selection import train_test_split
from sklearn.neighbors import KNeighborsClassifier
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import StandardScaler
from sklearn.svm import SVC


def hajm_kb(model) -> float:
    buf = io.BytesIO()
    pickle.dump(model, buf, protocol=pickle.HIGHEST_PROTOCOL)
    return buf.tell() / 1024


def main() -> None:
    X, y = make_classification(n_samples=12_000, n_features=25,
                               n_informative=10, n_redundant=5,
                               flip_y=0.15, class_sep=0.85, random_state=0)
    Xtr, Xte, ytr, yte = train_test_split(X, y, test_size=0.3,
                                          random_state=0)

    print("=== 1. Model hajmi va sifati (n=8400) ===")
    modellar = {
        "LogisticRegression": make_pipeline(
            StandardScaler(), LogisticRegression(max_iter=2000)),
        "SGDClassifier": make_pipeline(
            StandardScaler(),
            SGDClassifier(loss="log_loss", max_iter=1500, tol=1e-4,
                          random_state=0)),
        "HistGradientBoosting": HistGradientBoostingClassifier(
            max_iter=150, early_stopping=False, random_state=0),
        "RandomForest(150)": RandomForestClassifier(
            n_estimators=150, min_samples_leaf=5, random_state=0, n_jobs=1),
        "KNN(k=25)": make_pipeline(StandardScaler(),
                                   KNeighborsClassifier(n_neighbors=25)),
    }
    print(f"  {'model':<24} {'test AUC':>10} {'hajm (KB)':>11} "
          f"{'hajm/n':>10}")
    for nom, m in modellar.items():
        m.fit(Xtr, ytr)
        p = (m.predict_proba(Xte)[:, 1] if hasattr(m, "predict_proba")
             else m.decision_function(Xte))
        kb = hajm_kb(m)
        print(f"  {nom:<24} {roc_auc_score(yte, p):>10.4f} {kb:>11.1f} "
              f"{kb / len(ytr) * 1000:>10.3f}")

    print("\n=== 2. Hajm o'quv to'plamiga bog'liqmi ===")
    print(f"  {'n':>8} {'KNN (KB)':>11} {'LogReg (KB)':>13} "
          f"{'HistGB (KB)':>13}")
    for n in [1000, 3500, 8400]:
        knn = make_pipeline(StandardScaler(),
                            KNeighborsClassifier(n_neighbors=25))
        knn.fit(Xtr[:n], ytr[:n])
        lr = make_pipeline(StandardScaler(),
                           LogisticRegression(max_iter=2000))
        lr.fit(Xtr[:n], ytr[:n])
        gb = HistGradientBoostingClassifier(max_iter=150,
                                            early_stopping=False,
                                            random_state=0)
        gb.fit(Xtr[:n], ytr[:n])
        print(f"  {n:>8} {hajm_kb(knn):>11.1f} {hajm_kb(lr):>13.1f} "
              f"{hajm_kb(gb):>13.1f}")
    print("  KNN hajmi n ga PROPORSIONAL - butun to'plamni saqlaydi")

    print("\n=== 3. RandomForest hajmi nimaga bog'liq ===")
    print(f"  {'n_estimators':>13} {'min_samples_leaf':>18} "
          f"{'hajm (KB)':>11} {'test AUC':>10}")
    for n_est, min_leaf in [(50, 1), (150, 1), (150, 20), (300, 20)]:
        rf = RandomForestClassifier(n_estimators=n_est,
                                    min_samples_leaf=min_leaf,
                                    random_state=0, n_jobs=1).fit(Xtr, ytr)
        auc = roc_auc_score(yte, rf.predict_proba(Xte)[:, 1])
        print(f"  {n_est:>13} {min_leaf:>18} {hajm_kb(rf):>11.1f} "
              f"{auc:>10.4f}")
    print("  min_samples_leaf ni oshirish hajmni keskin kamaytiradi")

    print("\n=== 4. Tavsiya jadvali ===")
    tavsiya = [
        ("n < 10 000", "hamma narsa, SVC(rbf) ham"),
        ("10k - 100k", "HistGradientBoosting, LinearSVC, RF"),
        ("100k - 1M", "HistGradientBoosting, SGD"),
        ("n > 1M", "SGD + partial_fit, namuna olish"),
    ]
    print(f"  {'hajm':<14} {'tavsiya'}")
    for hajm, matn in tavsiya:
        print(f"  {hajm:<14} {matn}")
    print("  ⭐ KNN va SVC hajmi o'quv to'plamiga bog'liq")


if __name__ == "__main__":
    main()

Natijaning muhim qismi:

text
=== 1. Model hajmi va sifati (n=8400) ===
  model                      test AUC   hajm (KB)     hajm/n
  LogisticRegression           0.7168         1.8      0.217
  SGDClassifier                0.6949         2.0      0.243
  HistGradientBoosting         0.8968       570.1     67.869
  RandomForest(150)            0.8981     12561.8   1495.447
  KNN(k=25)                    0.8864      1707.9    203.318

=== 2. Hajm o'quv to'plamiga bog'liqmi ===
         n    KNN (KB)   LogReg (KB)   HistGB (KB)
      1000       204.7           1.8         570.1
      3500       712.5           1.8         570.1
      8400      1707.9           1.8         570.1
  KNN hajmi n ga PROPORSIONAL - butun to'plamni saqlaydi

=== 3. RandomForest hajmi nimaga bog'liq ===
   n_estimators   min_samples_leaf   hajm (KB)   test AUC
             50                  1      7468.6     0.8963
            150                  1     22395.2     0.9005
            150                 20      4595.3     0.8897
            300                 20      9184.8     0.8906
  min_samples_leaf ni oshirish hajmni keskin kamaytiradi

=== 4. Tavsiya jadvali ===
  hajm           tavsiya
  n < 10 000     hamma narsa, SVC(rbf) ham
  10k - 100k     HistGradientBoosting, LinearSVC, RF
  100k - 1M      HistGradientBoosting, SGD
  n > 1M         SGD + partial_fit, namuna olish
  ⭐ KNN va SVC hajmi o'quv to'plamiga bog'liq

Nima ko'rsatdi: 2.4-bo'lim.

Misol 4 — partial_fit bilan bo'laklab o'rgatish

python
"""Xotiraga sig'maydigan ma'lumot uchun (real numpy/sklearn)."""

import numpy as np
from sklearn.datasets import make_classification
from sklearn.linear_model import LogisticRegression, SGDClassifier
from sklearn.metrics import roc_auc_score
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler


def bolaklar(X, y, hajm: int):
    for boshi in range(0, len(y), hajm):
        yield X[boshi:boshi + hajm], y[boshi:boshi + hajm]


def main() -> None:
    X, y = make_classification(n_samples=40_000, n_features=20,
                               n_informative=10, flip_y=0.15,
                               class_sep=0.8, random_state=0)
    Xtr, Xte, ytr, yte = train_test_split(X, y, test_size=0.25,
                                          random_state=0)
    sinflar = np.unique(y)

    print("=== 1. Bo'laklab masshtablash ===")
    sc = StandardScaler()
    for bX, _ in bolaklar(Xtr, ytr, 5000):
        sc.partial_fit(bX)
    sc_toliq = StandardScaler().fit(Xtr)
    print(f"  bo'laklab o'rtacha (birinchi 3): "
          f"{np.round(sc.mean_[:3], 6).tolist()}")
    print(f"  to'liq o'rtacha (birinchi 3):   "
          f"{np.round(sc_toliq.mean_[:3], 6).tolist()}")
    print(f"  maksimal farq: "
          f"{np.abs(sc.mean_ - sc_toliq.mean_).max():.2e}")

    print("\n=== 2. Bo'laklab o'rgatish ===")
    Xtr_s = sc.transform(Xtr)
    Xte_s = sc.transform(Xte)
    model = SGDClassifier(loss="log_loss", random_state=0)
    print(f"  {'epoxa':>7} {'test AUC':>10}")
    rng = np.random.default_rng(0)
    for epoxa in range(1, 6):
        tartib = rng.permutation(len(ytr))       # ARALASHTIRISH muhim
        for bX, by in bolaklar(Xtr_s[tartib], ytr[tartib], 2000):
            model.partial_fit(bX, by, classes=sinflar)
        auc = roc_auc_score(yte, model.predict_proba(Xte_s)[:, 1])
        print(f"  {epoxa:>7} {auc:>10.4f}")

    print("\n=== 3. To'liq o'rgatish bilan solishtirish ===")
    toliq = LogisticRegression(max_iter=2000).fit(Xtr_s, ytr)
    toliq_auc = roc_auc_score(yte, toliq.predict_proba(Xte_s)[:, 1])
    bolak_auc = roc_auc_score(yte, model.predict_proba(Xte_s)[:, 1])
    print(f"  {'usul':<28} {'test AUC':>10}")
    print(f"  {'LogisticRegression (to_liq)':<28} {toliq_auc:>10.4f}")
    print(f"  {'SGD + partial_fit (5 epoxa)':<28} {bolak_auc:>10.4f}")
    print(f"  farq: {bolak_auc - toliq_auc:+.4f}")

    print("\n=== 4. Aralashtirishning ahamiyati ===")
    # maqsad bo'yicha TARTIBLANGAN ma'lumot
    tartib = np.argsort(ytr, kind="stable")
    tartibli = SGDClassifier(loss="log_loss", random_state=0)
    for _ in range(5):
        for bX, by in bolaklar(Xtr_s[tartib], ytr[tartib], 2000):
            if len(np.unique(by)) < 2:
                tartibli.partial_fit(bX, by, classes=sinflar)
            else:
                tartibli.partial_fit(bX, by, classes=sinflar)
    tartibli_auc = roc_auc_score(
        yte, tartibli.predict_proba(Xte_s)[:, 1])
    print(f"  {'ma_lumot tartibi':<28} {'test AUC':>10}")
    print(f"  {'aralashtirilgan':<28} {bolak_auc:>10.4f}")
    print(f"  {'maqsad bo_yicha tartibli':<28} {tartibli_auc:>10.4f}")
    print(f"  farq: {tartibli_auc - bolak_auc:+.4f}")

    print("\n=== 5. Qaysi obyektlar partial_fit ni qo'llab-quvvatlaydi ===")
    from sklearn.cluster import MiniBatchKMeans
    from sklearn.decomposition import IncrementalPCA
    from sklearn.naive_bayes import MultinomialNB
    from sklearn.pipeline import Pipeline
    obyektlar = {
        "SGDClassifier": SGDClassifier(),
        "MultinomialNB": MultinomialNB(),
        "StandardScaler": StandardScaler(),
        "IncrementalPCA": IncrementalPCA(n_components=3),
        "MiniBatchKMeans": MiniBatchKMeans(n_clusters=3, n_init=3),
        "LogisticRegression": LogisticRegression(),
        "Pipeline": Pipeline([("sc", StandardScaler())]),
    }
    print(f"  {'obyekt':<22} {'partial_fit':>12}")
    for nom, ob in obyektlar.items():
        print(f"  {nom:<22} "
              f"{str(hasattr(ob, 'partial_fit')):>12}")
    print("  ⭐ Pipeline partial_fit ni qo'llab-quvvatlamaydi")


if __name__ == "__main__":
    main()

Natijaning muhim qismi:

text
=== 1. Bo'laklab masshtablash ===
  bo'laklab o'rtacha (birinchi 3): [0.002846, 0.389144, -0.40801]
  to'liq o'rtacha (birinchi 3):   [0.002846, 0.389144, -0.40801]
  maksimal farq: 5.44e-15

=== 2. Bo'laklab o'rgatish ===
    epoxa   test AUC
        1     0.6359
        2     0.6586
        3     0.6339
        4     0.6857
        5     0.6812

=== 3. To'liq o'rgatish bilan solishtirish ===
  usul                           test AUC
  LogisticRegression (to_liq)      0.7309
  SGD + partial_fit (5 epoxa)      0.6812
  farq: -0.0498

=== 4. Aralashtirishning ahamiyati ===
  ma_lumot tartibi               test AUC
  aralashtirilgan                  0.6812
  maqsad bo_yicha tartibli         0.7271
  farq: +0.0459

=== 5. Qaysi obyektlar partial_fit ni qo'llab-quvvatlaydi ===
  obyekt                  partial_fit
  SGDClassifier                  True
  MultinomialNB                  True
  StandardScaler                 True
  IncrementalPCA                 True
  MiniBatchKMeans                True
  LogisticRegression            False
  Pipeline                      False
  ⭐ Pipeline partial_fit ni qo'llab-quvvatlamaydi

Nima ko'rsatdi: 2.5-bo'lim.


5. To'g'ri va noto'g'ri tushunishlar

Noto'g'ri fikr To'g'risi
"n_jobs=-1 har doim tezroq" Ichma-ich parallellik sekinlashtiradi
"float64 kerak" float32 ko'pincha yetarli
"Siyrakni zichga aylantirsa qulay" Xotira portlashi mumkin
"StandardScaler siyrak bilan ishlaydi" with_mean=False kerak
"PCA siyrakni qabul qiladi" TruncatedSVD kerak
"Model fit eng qimmat" Tayyorlash ko'pincha qimmatroq
"Pipeline da partial_fit bor" Yo'q
"KNN hajmi kichik" Butun o'quv to'plami

6. Keng tarqalgan xatolar va yechimlari

1. Ichma-ich parallellik

python
GridSearchCV(RandomForestClassifier(n_jobs=-1), ..., n_jobs=-1)  # ⚠️
GridSearchCV(RandomForestClassifier(n_jobs=1), ..., n_jobs=-1)   # ✅

2. Siyraklikni buzish

python
Pipeline([("tfidf", TfidfVectorizer()), ("sc", StandardScaler())])  # ⚠️
Pipeline([("tfidf", TfidfVectorizer()),
          ("sc", StandardScaler(with_mean=False))])                 # ✅

3. Siyrak bilan PCA

python
Pipeline([("tfidf", TfidfVectorizer()), ("p", PCA(n_components=50))])  # ⚠️
Pipeline([("tfidf", TfidfVectorizer()),
          ("p", TruncatedSVD(n_components=50))])                       # ✅

4. Keraksiz float64

python
X = df.to_numpy()                      # float64                 # ⚠️
X = df.to_numpy(dtype=np.float32)                                # ✅

5. Katta ma'lumotda KNN

python
KNeighborsClassifier().fit(X_1M, y)                              # ⚠️
HistGradientBoostingClassifier().fit(X_1M, y)                    # ✅

6. O'lchamasdan optimallashtirish

python
# modelni almashtiramiz, chunki sekin                            # ⚠️
n = cross_validate(...); print(n["fit_time"], n["score_time"])   # ✅

7. partial_fit da classes yo'q

python
model.partial_fit(bX, by)          # birinchi chaqiruvda xato    # ⚠️
model.partial_fit(bX, by, classes=np.unique(y))                  # ✅

7. Integratsiya — bu bilim qayerda kerak bo'ladi

  • 19.3-dars (o'tilgan): Siyrak chiqish
  • 19.6-dars (o'tilgan): Model hajmi
  • 19.7-dars (o'tilgan): n_jobs va takrorlanuvchanlik
  • 19.10-dars: To'liq loyiha
  • 29-qism: MLOps va ishlab chiqarish

8. Eng yaxshi amaliyotlar

  1. Oldin o'lchang.

  2. float32 ga o'ting.

  3. Siyraklikni saqlang.

  4. Parallellik bir pog'onada.

  5. Hajmga mos model.

  6. category dtype ishlating.

  7. Katta ma'lumotda partial_fit yoki namuna.

  8. Model hajmini kuzating.


9. Amaliy topshiriq

Vazifa 1: Bashorat qiling

python
1.  # float32 nima beradi?
2.  # siyraklik qachon foydali?
3.  # nima siyraklikni buzadi?
4.  # StandardScaler siyrak bilan qanday?
5.  # PCA o'rniga nima?
6.  # ichma-ich n_jobs muammosi?
7.  # n > 100k da qaysi model?
8.  # KNN hajmi nimaga bog'liq?
9.  # partial_fit da nima shart?
10. # Pipeline partial_fit ni qo'llaydimi?
11. # odatda eng qimmat qadam?
12. # RF hajmini nima kamaytiradi?
Javoblar
  1. Xotirani ikki barobar kamaytiradi
  2. Nolmas ulush < 10%
  3. with_mean=True, PCA, toarray()
  4. with_mean=False
  5. TruncatedSVD
  6. Protsesslar yadro uchun kurashadi
  7. HistGradientBoosting yoki SGD
  8. O'quv to'plami hajmiga
  9. Birinchi chaqiruvda classes=
  10. Yo'q
  11. Tayyorlash
  12. min_samples_leaf ni oshirish

Vazifa 2: Xatolarni tuzating

python
1.  GridSearchCV(RandomForestClassifier(n_jobs=-1), ..., n_jobs=-1)

2.  Pipeline([("tfidf", TfidfVectorizer()), ("sc", StandardScaler())])

3.  Pipeline([("tfidf", TfidfVectorizer()), ("p", PCA(n_components=50))])

4.  KNeighborsClassifier().fit(X_1M, y)

5.  model.partial_fit(bX, by)
Javoblar
python
1.  GridSearchCV(RandomForestClassifier(n_jobs=1), ..., n_jobs=-1)

2.  ("sc", StandardScaler(with_mean=False))

3.  ("p", TruncatedSVD(n_components=50))

4.  HistGradientBoostingClassifier().fit(X_1M, y)

5.  model.partial_fit(bX, by, classes=np.unique(y))

Vazifa 3: dtype

Modellang:

  1. Hajm
  2. Aniqlik
  3. sklearn saqlaydimi
  4. category

Vazifa 4: Siyraklik

Modellang:

  1. Siyrak matritsa
  2. Nima buzadi
  3. PCA
  4. Quvur

Vazifa 5: Model tanlash

Modellang:

  1. Hajm va sifat
  2. n ga bog'liqlik
  3. RF hajmi
  4. Tavsiya

Vazifa 6: partial_fit

Modellang:

  1. Masshtablash
  2. O'rgatish
  3. Solishtirish
  4. Aralashtirish

Vazifa 7: O'ylash

Quvuringiz 40 daqiqa ishlaydi. Modelni RandomForest dan HistGradientBoosting ga o'tkazdingiz — vaqt 38 daqiqa bo'ldi. Nima qilishingiz kerak edi?

Javob

Qisqa javob: avval o'lchash kerak edi. 40 daqiqadan model fit atigi 2 daqiqa ekan — qolgan 38 daqiqa boshqa joyda.

1. Qayerni o'lchash

python
import time

t0 = time.perf_counter()
df = pd.read_parquet("data.parquet")
t_oqish = time.perf_counter() - t0

t0 = time.perf_counter()
X = belgilar_yasa(df)
t_belgilar = time.perf_counter() - t0

natija = cross_validate(quvur, X, y, cv=cv, return_train_score=False)
print(f"o'qish: {t_oqish:.1f}s, belgilar: {t_belgilar:.1f}s")
print(f"fit: {natija['fit_time'].sum():.1f}s, "
      f"score: {natija['score_time'].sum():.1f}s")

2. Odatiy taqsimot

Bosqich Ulush
Ma'lumot o'qish/tayyorlash ~40%
Belgi hisoblash ~30%
Model fit ~25%
Qolgani ~5%

Model fit kamdan-kam eng qimmat qadam bo'ladi.

3. Har bosqich uchun arzon yutuqlar

O'qish:

python
pd.read_parquet(...)                 # CSV o'rniga (5-10x tez)
pd.read_csv(..., usecols=[...], dtype={...})   # faqat keraklisi

Belgilar:

python
# apply o'rniga vektorlashtirilgan amallar
df["nisbat"] = df["a"] / df["b"].clip(lower=1)     # emas: df.apply(...)
# takroriy hisoblashni keshlang (Pipeline memory - 19.2)

CV:

python
cross_validate(..., n_jobs=-1)       # foldlar parallel
cv=StratifiedKFold(3)                # 5 o'rniga 3 (18.3-dars)

Ma'lumot hajmi:

python
X = X.astype(np.float32)             # xotira va tezlik
# yoki ishlab chiqish paytida namuna oling
Xn, yn = X[:50_000], y[:50_000]

4. Umumiy qoida

Optimallashtirishdan oldin profil oling. Profil olmasdan qilingan optimallashtirish o'rtacha taxmin bo'yicha bajariladi va ko'pincha noto'g'ri joyga tushadi.

5. Ishlab chiqish sikli

  1. Kichik namunada ishlang (10-50 ming qator)
  2. Mantiq to'g'ri bo'lgach to'liq ma'lumotga o'ting
  3. Sekin bo'lsa — o'lchang
  4. Eng katta ulushni optimallashtiring
  5. Qayta o'lchang

6. Xulosa

  1. Avval o'lchang
  2. Model fit odatda eng qimmat emas
  3. O'qish va belgilar ko'pincha katta ulush
  4. Ishlab chiqishda namuna bilan ishlang

Nimani mustahkamlaydi: 2.6-bo'lim.


Xulosa

Bu darsda tezlik va xotirani o'rgandik.

Eng muhim uch fikr:

  1. Uch arzon yutuq: float32, siyraklik, bir pog'onali parallellik. float64 dan float32 ga o'tish xotirani ikki barobar kamaytiradi va ko'pchilik model uchun aniqlikka ta'sir qilmaydi. Siyrak matritsani saqlash (StandardScaler(with_mean=False), TruncatedSVD) matn va ko'p darajali kategoriya quvurlarida o'nlab barobar tejaydi.

  2. n_jobs faqat bir pog'onada. GridSearchCV(n_jobs=-1) va RandomForest(n_jobs=-1) birga ishlatilsa, protsesslar yadro uchun kurashadi va natija ketma-ket ishdan ham sekinroq bo'lishi mumkin. Tashqi halqada parallellik, ichkida n_jobs=1.

  3. Oldin o'lchang. Model fit odatda eng qimmat qadam emas — ma'lumot o'qish va belgi hisoblash ko'pincha ko'proq vaqt oladi. cross_validate ning fit_time va score_time maydonlari hamda oddiy perf_counter o'lchovlari optimallashtirishni to'g'ri joyga yo'naltiradi.

Keyingi darsda to'liq loyiha: xom CSV dan boshlab, tayyorlash quvuri, sozlash, baholash va ishlab chiqarishga tayyor paketgacha bo'lgan yo'lni bir joyda quramiz.

Ulashish:Telegram'da

Izohlar (0)

Izoh yozish uchun kiring.

  • Hozircha izoh yo'q. Birinchi bo'ling!
19.9-dars: Tezlik va xotira — IlmHamroh