IlmHamroh
Data Science va sun'iy intellekt/Klassifikatsiya8/14-dars20 daqiqa
Mundarija (22)

14.8-dars: Ko'p sinfli strategiyalar

14-QISM — KLASSIFIKATSIYA · 8-dars


1. Kirish va motivatsiya

Ko'pchilik algoritmlar tabiatan binar: SVM ikki sinfni ajratadi, logistik regressiya bitta sigmoid beradi. Lekin real vazifalarda sinflar ko'p: 10 ta raqam, 18 ta rubrika, 200 ta mahsulot kategoriyasi. Ularni qanday hal qilamiz?

Uch yo'l bor: OvR (har sinf qolganlarga qarshi), OvO (har juftlik), va tabiiy ko'p sinfli modellar (softmax, daraxtlar, KNN, NB). Tanlov aniqlikka, tezlikka va ehtimollar izchilligiga ta'sir qiladi.

Bu darsda: uch strategiya va ularning narxi, ehtimollar izchilligi, chalkashlik matritsasi tahlili 12.7-bob, macro/micro/weighted metrikalari, ko'p sinfda nomutanosiblik va ierarxik yondashuv.

Real vaziyat. Marketpleys mahsulotlarni 320 ta kategoriyaga ajratadi. OvR bilan 320 ta model o'qitildi — 45 daqiqa, F1 macro 0.71. Chalkashlik matritsasi ko'rsatdiki, xatolarning 62% i bitta darax shoxida ("elektronika" ichida). Ierarxik yondashuv (avval 12 ta yirik guruh, keyin guruh ichida) F1 ni 0.78 ga ko'tardi va o'qitishni 12 daqiqaga tushirdi.

Bu darsda ko'p sinfli strategiyalarni o'rganamiz.

Bu darsda:

  • OvR, OvO va softmax
  • Ehtimollar izchilligi
  • Chalkashlik matritsasi tahlili
  • macro, micro, weighted
  • Ko'p sinfda nomutanosiblik
  • Ierarxik yondashuv
  • Tuzoqlar
  • Amaliy: 10 sinfli vazifa

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


2. Nazariya — chuqur tushuntirish

2.1. Uch strategiya

text
OvR (One-vs-Rest):   K ta binar model — "sinf k" va "qolganlari"
  bashorat: eng yuqori ball bergan model
  + K ta model (tez), har model butun ma'lumotda
  - sinflar nomutanosib bo'lib qoladi, ballar solishtirilmaydigan

OvO (One-vs-One):    K(K-1)/2 ta model — har juftlik uchun
  bashorat: ovoz berish
  + har model faqat 2 sinf ma'lumotida (kichik, tez)
  - model soni kvadratik (K=50 → 1225 model)

SOFTMAX (multinomial): bitta model, K ta chiqish
  p_k = exp(z_k) / sum(exp(z_j))
  + izchil ehtimollar, bitta optimallashtirish
  - faqat ba'zi algoritmlarda mavjud (LogReg, tarmoqlar)

Tabiatan ko'p sinfli: daraxtlar, KNN, NB, LDA/QDA

sklearn da ko'pchilik algoritmlar avtomatik ko'p sinfni qo'llab-quvvatlaydi: LogisticRegression — softmax, SVC — OvO, LinearSVC — OvR. Buni bilmaslik natijani noto'g'ri talqin qilishga olib keladi: masalan, SVC da decision_function shakli (n, K(K-1)/2) bo'lishi mumkin.

2.2. Ehtimollar izchilligi

text
SOFTMAX:  ehtimollar yig'indisi 1 — izchil, kalibrlanishi mumkin 13.10-bob

OvR:      har model o'z ehtimolini beradi → yig'indi 1 GA TENG EMAS
          sklearn ularni normallashtiradi (bo'ladi), lekin bu kalibrlashni buzadi

OvO:      ovozlardan ehtimol tiklash (Wu-Lin-Weng) — taxminiy

Natija: ehtimol muhim bo'lsa → softmax yoki keyin kalibrlash 14.10-bob
        faqat yorliq kerak bo'lsa → farq kichik

Izchillik — OvR ning asosiy kamchiligi: K ta mustaqil model bir-biridan bexabar, ularning ballari bir shkalada emas. Amalda bu aniqlikka kam ta'sir qiladi, lekin ehtimollar ishlatilganda (narx formulalari, chegaralar — 12.7) muammo bo'ladi.

2.3. Chalkashlik matritsasi tahlili

python
from sklearn.metrics import ConfusionMatrixDisplay, confusion_matrix

cm = confusion_matrix(y_te, pred)
cm_norm = confusion_matrix(y_te, pred, normalize="true")     # qatorlar bo'yicha

# eng ko'p chalkashadigan juftliklar
np.fill_diagonal(cm, 0)
eng = np.dstack(np.unravel_index(np.argsort(cm, axis=None)[::-1], cm.shape))[0][:5]

Ko'p sinfda chalkashlik matritsasi — eng ma'lumotli diagnostika 12.7-bob: u qaysi sinflar bir-biriga o'xshashligini ko'rsatadi. Tipik topilmalar: ikki sinf doim chalkashadi (ularni birlashtirish yoki maxsus belgi kerak), bitta sinf hech qachon bashorat qilinmaydi (juda kam namuna), xatolar ierarxiya ichida to'plangan.

2.4. macro, micro, weighted

text
macro    — har sinf uchun metrikani hisoblab, oddiy o'rtacha
           → kam namunali sinflar KATTA vazn oladi
micro    — barcha TP/FP/FN ni yig'ib hisoblash
           → ko'p namunali sinflar hukmron; ko'p sinfda micro-F1 = accuracy
weighted — sinf hajmiga qarab vaznlangan o'rtacha

Tanlov:
  barcha sinflar teng muhim      → macro
  umumiy ishlash muhim           → weighted yoki accuracy
  kam uchraydigan sinf muhim     → macro yoki shu sinf uchun alohida metrika

macro va weighted farqi nomutanosib ko'p sinfli vazifalarda juda katta bo'lishi mumkin: 20 ta sinfning 15 tasi kichik bo'lsa, weighted 0.92 va macro 0.54 bo'lishi normal. Hisobotda ikkalasini ham bering va qaysi biri muhimligini oldindan kelishing 12.7-bob.

2.5. Ko'p sinfda nomutanosiblik

text
Muammo: sinflar hajmi 10 000 dan 50 gacha farq qiladi

Yechimlar:
  · class_weight="balanced" — har sinf teskari chastota bilan vaznlanadi
  · kam sinflarni BIRLASHTIRISH ("boshqa" kategoriyasi)
  · ierarxik yondashuv
  · har sinf uchun alohida chegara 12.7-bob
  · macro metrikalar bilan baholash

Diqqat: stratifikatsiya majburiy 12.3-bob — aks holda kichik sinf foldda yo'q bo'lishi mumkin

Ko'p sinfli nomutanosiblik binar holatdan qiyinroq: kichik sinflar hech qachon bashorat qilinmasligi mumkin. class_weight="balanced" birinchi qadam, lekin ko'pincha yetarli emas — sinflarni birlashtirish yoki ierarxiya amaliy yechim bo'ladi.

2.6. Ierarxik yondashuv

text
Sinflar tabiiy ierarxiyaga ega bo'lsa (kategoriya → kichik kategoriya):

  1-bosqich: yirik guruhni bashorat qilish (12 sinf)
  2-bosqich: guruh ichida aniq sinfni (har guruh uchun alohida model)

+ har model oddiyroq vazifani yechadi, tezroq o'qitiladi
+ xatolar "yaqin" sinflar orasida qoladi (narxi kamroq)
- xato birinchi bosqichda tarqaladi (kaskad xatosi)
- ko'proq model va murakkabroq infratuzilma

Ierarxiya — ko'p sinfli katta vazifalarning amaliy yechimi (marketpleys kategoriyalari, tibbiy kodlar, tillar). Uning asosiy xavfi — kaskad xatosi: birinchi bosqichda yanglishsangiz, ikkinchisi tuzata olmaydi. Shuning uchun birinchi bosqich aniqroq bo'lishi kerak.

2.7. Tuzoqlar

Asosiy tuzoqlar: average ni ko'rsatmaslik 12.7-bob; macro va weighted ni chalkashtirish; stratifikatsiyasiz CV (kichik sinf yo'qoladi); OvR ehtimollarini kalibrlangan deb hisoblash; SVC da decision_function shaklini noto'g'ri talqin qilish; katta K da OvO ishlatish (kvadratik); chalkashlik matritsasini ko'rmaslik; ierarxiyada kaskad xatosini hisobga olmaslik.

2.8. Strategiya — narx va izchillik

Ko'p sinfli vazifa uch yo'l bilan hal qilinadi: OvR (K model, tez, lekin ballar solishtirilmaydigan), OvO (K(K-1)/2 model, kichik vazifalar, katta K da qimmat) va softmax (bitta model, izchil ehtimollar). sklearn da bu avtomatik tanlanadi — qaysi biri ishlatilayotganini bilish kerak. Baholashda macro va weighted ni birga bering, chalkashlik matritsasini albatta ko'ring. Katta va ierarxik sinf to'plamlarida ierarxik yondashuv tezlik va sifat beradi. Keyingi dars — nomutanosib sinflar.


3. Tez ma'lumotnoma

python
import numpy as np
from sklearn.metrics import classification_report, confusion_matrix, f1_score
from sklearn.multiclass import OneVsOneClassifier, OneVsRestClassifier

OneVsRestClassifier(LinearSVC())            # K model
OneVsOneClassifier(SVC())                   # K(K-1)/2 model
LogisticRegression()                        # softmax (tabiiy)

f1_score(y, pred, average="macro")          # sinflar teng
f1_score(y, pred, average="weighted")       # hajmga qarab
print(classification_report(y, pred, digits=3))

cm = confusion_matrix(y, pred, normalize="true")
np.fill_diagonal(cm, 0)
eng_chalkash = np.unravel_index(np.argsort(cm, axis=None)[::-1][:5], cm.shape)
QOIDA: strategiyani bil · macro+weighted ber · CM ni ko'r · stratifikatsiya qil

Ko'p sinf xulosasi

OvR: K model, tez, ballar izchil emas · OvO: K(K-1)/2, kichik vazifalar
Softmax: bitta model, izchil ehtimollar · Daraxt/KNN/NB: tabiatan ko'p sinfli
macro (sinflar teng) / weighted (hajmga qarab) / micro (= accuracy)
Ierarxiya: tez va aniqroq, lekin kaskad xatosi

4. Batafsil misollar

Misollar real numpy/sklearn bilan (Python 3.14).

Misol 1 — OvR, OvO va softmax

python
"""Uch strategiya: aniqlik va ichki modellar soni (real numpy/sklearn)."""

import numpy as np
from sklearn.datasets import load_digits
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import accuracy_score, f1_score
from sklearn.model_selection import train_test_split
from sklearn.multiclass import OneVsOneClassifier, OneVsRestClassifier
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import StandardScaler
from sklearn.svm import SVC


def main() -> None:
    X, y = load_digits(return_X_y=True)
    Xtr, Xte, ytr, yte = train_test_split(X, y, test_size=0.3, random_state=0,
                                          stratify=y)
    K = len(np.unique(y))

    print("=== 1. Vazifa ===")
    print(f"  {len(X)} namuna, {X.shape[1]} belgi, {K} sinf")
    print(f"  OvR: {K} model, OvO: {K * (K - 1) // 2} model, softmax: 1 model")

    print("\n=== 2. Logistik regressiya: softmax va OvR ===")
    asos = lambda: LogisticRegression(max_iter=5000, C=0.1)
    modellar = {
        "softmax": Pipeline([("sc", StandardScaler()), ("m", asos())]),
        "OvR": Pipeline([("sc", StandardScaler()),
                         ("m", OneVsRestClassifier(asos()))]),
        "OvO": Pipeline([("sc", StandardScaler()),
                         ("m", OneVsOneClassifier(asos()))]),
    }
    for nom, m in modellar.items():
        m.fit(Xtr, ytr)
        pred = m.predict(Xte)
        ichki = getattr(m.named_steps["m"], "estimators_", None)
        soni = 1 if ichki is None else len(ichki)
        print(f"  {nom:<8}: aniqlik {accuracy_score(yte, pred):.4f}, "
              f"F1 macro {f1_score(yte, pred, average='macro'):.4f}, "
              f"ichki modellar {soni:>2}")

    print("\n=== 3. SVC ichki strategiyasi ===")
    svc = Pipeline([("sc", StandardScaler()), ("m", SVC(C=10.0))]).fit(Xtr, ytr)
    df = svc.decision_function(Xte)
    print(f"  SVC decision_function shakli: {df.shape}")
    print(f"  (sklearn SVC ichida OvO, lekin natijani OvR shakliga keltiradi)")
    svc_ovo = OneVsOneClassifier(SVC(C=10.0))
    Xtr_s = StandardScaler().fit(Xtr).transform(Xtr)
    Xte_s = StandardScaler().fit(Xtr).transform(Xte)
    svc_ovo.fit(Xtr_s, ytr)
    print(f"  aniq OvO: {len(svc_ovo.estimators_)} ta ichki model, "
          f"aniqlik {(svc_ovo.predict(Xte_s) == yte).mean():.4f}")

    print("\n=== 4. Ehtimollar izchilligi ===")
    soft = modellar["softmax"]
    ovr = modellar["OvR"]
    ps = soft.predict_proba(Xte[:5])
    po = ovr.predict_proba(Xte[:5])
    print(f"  softmax yig'indilari: {ps.sum(axis=1).round(6)}")
    print(f"  OvR yig'indilari (normallashtirilgandan keyin): "
          f"{po.sum(axis=1).round(6)}")
    # normallashtirishdan oldingi xom ballar
    xom = np.column_stack([e.predict_proba(
        StandardScaler().fit(Xtr).transform(Xte[:5]))[:, 1]
        for e in ovr.named_steps["m"].estimators_])
    print(f"  OvR XOM ehtimollar yig'indisi: {xom.sum(axis=1).round(4)}")
    print("  ⭐ OvR da yig'indi 1 emas — normallashtirish kalibrlashni buzadi")


if __name__ == "__main__":
    main()

Natijaning muhim qismi:

text
=== 1. Vazifa ===
  1797 namuna, 64 belgi, 10 sinf
  OvR: 10 model, OvO: 45 model, softmax: 1 model

=== 2. Logistik regressiya: softmax va OvR ===
  softmax : aniqlik 0.9722, F1 macro 0.9725, ichki modellar  1
  OvR     : aniqlik 0.9648, F1 macro 0.9652, ichki modellar 10
  OvO     : aniqlik 0.9778, F1 macro 0.9779, ichki modellar 45

=== 3. SVC ichki strategiyasi ===
  SVC decision_function shakli: (540, 10)
  (sklearn SVC ichida OvO, lekin natijani OvR shakliga keltiradi)
  aniq OvO: 45 ta ichki model, aniqlik 0.9852

=== 4. Ehtimollar izchilligi ===
  softmax yig'indilari: [1. 1. 1. 1. 1.]
  OvR yig'indilari (normallashtirilgandan keyin): [1. 1. 1. 1. 1.]
  OvR XOM ehtimollar yig'indisi: [1.4367 1.0675 1.0989 1.0343 0.9899]
  ⭐ OvR da yig'indi 1 emas — normallashtirish kalibrlashni buzadi

Nima ko'rsatdi: 2.1, 2.2-bo'limlar.

Misol 2 — Chalkashlik matritsasi tahlili

python
"""Qaysi sinflar chalkashadi va nega (real numpy/sklearn)."""

import numpy as np
from sklearn.datasets import load_digits
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import classification_report, confusion_matrix, f1_score
from sklearn.model_selection import train_test_split
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import StandardScaler


def main() -> None:
    X, y = load_digits(return_X_y=True)
    Xtr, Xte, ytr, yte = train_test_split(X, y, test_size=0.3, random_state=0,
                                          stratify=y)
    m = Pipeline([("sc", StandardScaler()),
                  ("m", LogisticRegression(max_iter=5000, C=0.05))]).fit(Xtr, ytr)
    pred = m.predict(Xte)

    print("=== 1. Umumiy natija ===")
    print(f"  aniqlik = {(pred == yte).mean():.4f}")
    print(f"  F1 macro = {f1_score(yte, pred, average='macro'):.4f}, "
          f"weighted = {f1_score(yte, pred, average='weighted'):.4f}")

    print("\n=== 2. Eng ko'p chalkashadigan juftliklar ===")
    cm = confusion_matrix(yte, pred)
    cm_toza = cm.copy()
    np.fill_diagonal(cm_toza, 0)
    tartib = np.argsort(cm_toza, axis=None)[::-1][:6]
    for idx in tartib:
        a, b = np.unravel_index(idx, cm.shape)
        if cm_toza[a, b] > 0:
            print(f"  haqiqiy {a} → bashorat {b}: {cm_toza[a, b]} marta "
                  f"({cm_toza[a, b] / cm[a].sum():.1%} shu sinfdan)")

    print("\n=== 3. Har sinf bo'yicha ===")
    hisobot = classification_report(yte, pred, output_dict=True, zero_division=0)
    qatorlar = [(k, v) for k, v in hisobot.items() if k.isdigit()]
    qatorlar.sort(key=lambda t: t[1]["f1-score"])
    print(f"  eng qiyin uchta sinf:")
    for k, v in qatorlar[:3]:
        print(f"    sinf {k}: F1 {v['f1-score']:.3f}, recall {v['recall']:.3f}, "
              f"support {int(v['support'])}")
    print(f"  eng oson uchta sinf:")
    for k, v in qatorlar[-3:]:
        print(f"    sinf {k}: F1 {v['f1-score']:.3f}, recall {v['recall']:.3f}, "
              f"support {int(v['support'])}")

    print("\n=== 4. Qatorlar bo'yicha normallashtirilgan matritsa ===")
    cmn = confusion_matrix(yte, pred, normalize="true")
    eng_qiyin = int(qatorlar[0][0])
    qator = cmn[eng_qiyin]
    top = np.argsort(qator)[::-1][:3]
    print(f"  sinf {eng_qiyin} qayerga ketadi: "
          f"{ {int(i): round(float(qator[i]), 3) for i in top} }")
    print(f"  diagonal o'rtachasi (har sinf recall i): {np.diag(cmn).mean():.4f}")
    print(f"  eng past diagonal: sinf {int(np.argmin(np.diag(cmn)))} "
          f"({np.diag(cmn).min():.3f})")
    print("  ⭐ Chalkashlik matritsasi — ko'p sinfli diagnostikaning markazi")


if __name__ == "__main__":
    main()

Natijaning muhim qismi:

text
=== 1. Umumiy natija ===
  aniqlik = 0.9630
  F1 macro = 0.9634, weighted = 0.9635

=== 2. Eng ko'p chalkashadigan juftliklar ===
  haqiqiy 8 → bashorat 1: 5 marta (9.6% shu sinfdan)
  haqiqiy 9 → bashorat 5: 2 marta (3.7% shu sinfdan)
  haqiqiy 4 → bashorat 8: 2 marta (3.7% shu sinfdan)
  haqiqiy 6 → bashorat 1: 2 marta (3.7% shu sinfdan)
  haqiqiy 3 → bashorat 8: 2 marta (3.6% shu sinfdan)
  haqiqiy 1 → bashorat 8: 1 marta (1.8% shu sinfdan)

=== 3. Har sinf bo'yicha ===
  eng qiyin uchta sinf:
    sinf 8: F1 0.904, recall 0.904, support 52
    sinf 1: F1 0.908, recall 0.982, support 55
    sinf 3: F1 0.962, recall 0.927, support 55
  eng oson uchta sinf:
    sinf 7: F1 0.982, recall 1.000, support 54
    sinf 2: F1 0.990, recall 0.981, support 53
    sinf 0: F1 1.000, recall 1.000, support 54

=== 4. Qatorlar bo'yicha normallashtirilgan matritsa ===
  sinf 8 qayerga ketadi: {8: 0.904, 1: 0.096, 9: 0.0}
  diagonal o'rtachasi (har sinf recall i): 0.9628
  eng past diagonal: sinf 8 0.904-bob
  ⭐ Chalkashlik matritsasi — ko'p sinfli diagnostikaning markazi

Nima ko'rsatdi: 2.3, 2.4-bo'limlar.

Misol 3 — Nomutanosib ko'p sinf

python
"""Kichik sinflar yo'qolganda nima bo'ladi (real numpy/sklearn)."""

import numpy as np
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import classification_report, f1_score
from sklearn.model_selection import train_test_split
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import StandardScaler


def yarat(seed: int = 7, n: int = 6000, K: int = 8):
    """Sinf hajmlari keskin farq qiladi."""
    rng = np.random.default_rng(seed)
    ulush = np.array([0.40, 0.25, 0.15, 0.08, 0.05, 0.04, 0.02, 0.01])
    y = rng.choice(K, n, p=ulush)
    markazlar = rng.normal(0, 2.0, (K, 6))
    X = markazlar[y] + rng.normal(0, 1.6, (n, 6))
    return X, y


def main() -> None:
    X, y = yarat()
    Xtr, Xte, ytr, yte = train_test_split(X, y, test_size=0.3, random_state=0,
                                          stratify=y)

    print("=== 1. Sinf hajmlari ===")
    sinf, soni = np.unique(ytr, return_counts=True)
    print(f"  {dict(zip(sinf.tolist(), soni.tolist()))}")
    print(f"  eng katta / eng kichik nisbati: {soni.max() / soni.min():.0f}x")

    print("\n=== 2. Oddiy model ===")
    oddiy = Pipeline([("sc", StandardScaler()),
                      ("m", LogisticRegression(max_iter=5000))]).fit(Xtr, ytr)
    p1 = oddiy.predict(Xte)
    print(f"  aniqlik {(p1 == yte).mean():.4f}, "
          f"F1 macro {f1_score(yte, p1, average='macro'):.4f}, "
          f"weighted {f1_score(yte, p1, average='weighted'):.4f}")
    bashorat_qilingan = set(np.unique(p1).tolist())
    yoq = sorted(set(range(8)) - bashorat_qilingan)
    print(f"  hech qachon bashorat qilinmagan sinflar: {yoq}")

    print("\n=== 3. class_weight='balanced' ===")
    balans = Pipeline([("sc", StandardScaler()),
                       ("m", LogisticRegression(max_iter=5000,
                                                class_weight="balanced"))]).fit(Xtr, ytr)
    p2 = balans.predict(Xte)
    print(f"  aniqlik {(p2 == yte).mean():.4f}, "
          f"F1 macro {f1_score(yte, p2, average='macro'):.4f}, "
          f"weighted {f1_score(yte, p2, average='weighted'):.4f}")
    print(f"  bashorat qilingan sinflar: {sorted(np.unique(p2).tolist())}")

    print("\n=== 4. Kichik sinflar bo'yicha recall ===")
    h1 = classification_report(yte, p1, output_dict=True, zero_division=0)
    h2 = classification_report(yte, p2, output_dict=True, zero_division=0)
    print(f"  {'sinf':>5} {'support':>8} {'oddiy R':>9} {'balanced R':>11}")
    for k in ["4", "5", "6", "7"]:
        if k in h1:
            print(f"  {k:>5} {int(h1[k]['support']):>8} "
                  f"{h1[k]['recall']:>9.3f} {h2[k]['recall']:>11.3f}")
    print("  ⭐ macro va weighted farqi nomutanosiblikni ko'rsatadi")


if __name__ == "__main__":
    main()

Natijaning muhim qismi:

text
=== 1. Sinf hajmlari ===
  {0: 1727, 1: 1003, 2: 618, 3: 353, 4: 215, 5: 170, 6: 70, 7: 44}
  eng katta / eng kichik nisbati: 39x

=== 2. Oddiy model ===
  aniqlik 0.9494, F1 macro 0.8820, weighted 0.9486
  hech qachon bashorat qilinmagan sinflar: []

=== 3. class_weight='balanced' ===
  aniqlik 0.9244, F1 macro 0.8341, weighted 0.9299
  bashorat qilingan sinflar: [0, 1, 2, 3, 4, 5, 6, 7]

=== 4. Kichik sinflar bo'yicha recall ===
   sinf  support   oddiy R  balanced R
      4       92     0.880       0.826
      5       73     0.918       0.973
      6       30     0.567       0.867
      7       19     0.684       0.947
  ⭐ macro va weighted farqi nomutanosiblikni ko'rsatadi

Nima ko'rsatdi: 2.4, 2.5-bo'limlar.

Misol 4 — Ierarxik yondashuv

python
"""Ikki bosqichli tasniflash va kaskad xatosi (real numpy/sklearn)."""

import numpy as np
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import f1_score
from sklearn.model_selection import train_test_split
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import StandardScaler


def yarat(seed: int = 9, n: int = 9000):
    """12 ta sinf, 3 ta yirik guruhga birlashgan (guruh ichida o'xshash)."""
    rng = np.random.default_rng(seed)
    K = 12
    y = rng.integers(0, K, n)
    guruh = y // 4                                    # 0,1,2 — yirik guruhlar
    # guruhlar yaqin - 1-bosqich ham yanglishadi (kaskad xatosi ko'rinsin)
    guruh_markaz = np.array([[1.0, 0.0], [-0.5, 0.87], [-0.5, -0.87]]) * 2.0
    sinf_siljish = rng.normal(0, 0.9, (K, 2))
    X2 = guruh_markaz[guruh] + sinf_siljish[y] + rng.normal(0, 0.75, (n, 2))
    qoshimcha = rng.normal(0, 1, (n, 4))
    X = np.column_stack([X2, qoshimcha])
    return X, y, guruh


def main() -> None:
    X, y, guruh = yarat()
    Xtr, Xte, ytr, yte, gtr, gte = train_test_split(X, y, guruh, test_size=0.3,
                                                    random_state=0, stratify=y)

    print("=== 1. Vazifa ===")
    print(f"  {len(X)} namuna, 12 sinf, 3 yirik guruh (har guruhda 4 sinf)")

    print("\n=== 2. Yassi (flat) model: 12 sinf birdaniga ===")
    yassi = Pipeline([("sc", StandardScaler()),
                      ("m", LogisticRegression(max_iter=5000))]).fit(Xtr, ytr)
    p_yassi = yassi.predict(Xte)
    print(f"  aniqlik {(p_yassi == yte).mean():.4f}, "
          f"F1 macro {f1_score(yte, p_yassi, average='macro'):.4f}, "
          f"modellar: 1 ta (12 sinf uchun)")

    print("\n=== 3. Ierarxik model ===")
    bosqich1 = Pipeline([("sc", StandardScaler()),
                         ("m", LogisticRegression(max_iter=5000))]).fit(Xtr, gtr)
    bosqich2 = {}
    for g in range(3):
        mos = gtr == g
        bosqich2[g] = Pipeline([("sc", StandardScaler()),
                                ("m", LogisticRegression(max_iter=5000))]).fit(
            Xtr[mos], ytr[mos])

    g_bash = bosqich1.predict(Xte)
    p_ier = np.empty(len(Xte), dtype=int)
    for g in range(3):
        mos = g_bash == g
        if mos.any():
            p_ier[mos] = bosqich2[g].predict(Xte[mos])
    print(f"  1-bosqich (guruh) aniqligi: {(g_bash == gte).mean():.4f}")
    print(f"  yakuniy aniqlik {(p_ier == yte).mean():.4f}, "
          f"F1 macro {f1_score(yte, p_ier, average='macro'):.4f}, "
          f"modellar: 4 ta (1 + 3), har biri kichikroq vazifada")

    print("\n=== 4. Kaskad xatosi ===")
    togri_guruh = g_bash == gte
    print(f"  guruh to'g'ri topilganda aniqlik: "
          f"{(p_ier[togri_guruh] == yte[togri_guruh]).mean():.4f}")
    if (~togri_guruh).any():
        print(f"  guruh noto'g'ri bo'lganda aniqlik: "
              f"{(p_ier[~togri_guruh] == yte[~togri_guruh]).mean():.4f}")
    else:
        print("  guruh noto'g'ri bo'lgan holat yo'q (1-bosqich xatosiz)")
    print(f"  guruh xatosi ulushi: {(~togri_guruh).mean():.1%}")
    print("\n  yassi model xatolari qanday taqsimlangan:")
    yassi_xato = p_yassi != yte
    guruh_ichida = (p_yassi[yassi_xato] // 4) == (yte[yassi_xato] // 4)
    print(f"    guruh ichida: {guruh_ichida.mean():.1%}, "
          f"guruhlar orasida: {1 - guruh_ichida.mean():.1%}")
    print("  ⭐ Bu yerda ierarxiya YUTMADI - 1-bosqich xatosi qaytarilmaydi")


if __name__ == "__main__":
    main()

Natijaning muhim qismi:

text
=== 1. Vazifa ===
  9000 namuna, 12 sinf, 3 yirik guruh (har guruhda 4 sinf)

=== 2. Yassi (flat) model: 12 sinf birdaniga ===
  aniqlik 0.5219, F1 macro 0.5099, modellar: 1 ta (12 sinf uchun)

=== 3. Ierarxik model ===
  1-bosqich (guruh) aniqligi: 0.9037
  yakuniy aniqlik 0.5019, F1 macro 0.4938, modellar: 4 ta (1 + 3), har biri kichikroq vazifada

=== 4. Kaskad xatosi ===
  guruh to'g'ri topilganda aniqlik: 0.5553
  guruh noto'g'ri bo'lganda aniqlik: 0.0000
  guruh xatosi ulushi: 9.6%

  yassi model xatolari qanday taqsimlangan:
    guruh ichida: 81.6%, guruhlar orasida: 18.4%
  ⭐ Bu yerda ierarxiya YUTMADI - 1-bosqich xatosi qaytarilmaydi

Nima ko'rsatdi: 2.6-bo'lim.


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

Noto'g'ri fikr To'g'risi
"OvR va softmax bir xil" Ehtimollar izchilligi farq qiladi
"OvO har doim sekin" Har model kichikroq ma'lumotda
"macro va weighted yaqin" Nomutanosiblikda juda farqli
"micro-F1 — alohida metrika" Ko'p sinfda = accuracy
"Chalkashlik matritsasi — formallik" Asosiy diagnostika
"class_weight yetarli" Ko'pincha emas
"Ierarxiya har doim yaxshi" Kaskad xatosi bor
"sklearn o'zi hal qiladi" Qaysi strategiya — bilish kerak

6. Keng tarqalgan xatolar va yechimlari

1. average ni ko'rsatmaslik

python
f1_score(y, pred)                      # ko'p sinfda xato        # ⚠️
f1_score(y, pred, average="macro")                                # ✅

2. Stratifikatsiyasiz CV

python
KFold(5, shuffle=True)                 # kichik sinf yo'qoladi   # ⚠️
StratifiedKFold(5, shuffle=True, random_state=0)                  # ✅

3. OvR ehtimollarini kalibrlangan deb bilish

python
foyda = ovr.predict_proba(X) * qiymatlar                          # ⚠️
# softmax ishlating yoki kalibrlang 14.10-bob                       # ✅

4. Katta K da OvO

python
OneVsOneClassifier(SVC()).fit(X, y)    # K = 200 → 19900 model    # ⚠️
LinearSVC()  # yoki ierarxik yondashuv                            # ✅

5. Chalkashlik matritsasini ko'rmaslik

python
print("aniqlik:", acc)                                            # ⚠️
print(confusion_matrix(y, pred, normalize="true"))                # ✅

6. Faqat weighted berish

python
print(f1_score(y, pred, average="weighted"))                      # ⚠️
# macro ni ham bering (kichik sinflar ko'rinadi)                  # ✅

7. Kichik sinflarni e'tiborsiz qoldirish

python
# 3 ta sinf hech qachon bashorat qilinmayapti                     # ⚠️
# class_weight, birlashtirish yoki ierarxiya                      # ✅

7. Integratsiya — bu bilim qayerda kerak bo'ladi

  • 12.7-dars (o'tilgan): Ko'p sinfli metrikalar
  • 13.10-dars (o'tilgan): Softmax
  • 14.9-dars: Nomutanosib sinflar
  • 14.10-dars: Kalibrlash
  • 14.13-dars: Xatolar tahlili

8. Eng yaxshi amaliyotlar

  1. Qaysi strategiya ishlatilayotganini biling.

  2. macro va weighted ni birga bering.

  3. Chalkashlik matritsasini ko'ring.

  4. Stratifikatsiya qiling.

  5. Ehtimol kerak bo'lsa softmax yoki kalibrlash.

  6. Katta K da ierarxiyani o'ylang.

  7. Kichik sinflarni alohida tekshiring.

  8. Model va vaqt narxini hisoblang.


9. Amaliy topshiriq

Vazifa 1: Bashorat qiling

python
1.  # OvR nechta model?
2.  # OvO nechta model?
3.  # softmax formulasi?
4.  # OvR ehtimollari yig'indisi?
5.  # sklearn LogReg qaysi strategiya?
6.  # SVC qaysi?
7.  # macro nima?
8.  # weighted nima?
9.  # ko'p sinfda micro-F1?
10. # stratifikatsiya nega kerak?
11. # ierarxiyaning xavfi?
12. # kichik sinf bashorat qilinmasa?
Javoblar
  1. K
  2. K(K-1)/2
  3. exp(z_k)/sum(exp(z_j))
  4. 1 ga teng emas
  5. Softmax
  6. OvO
  7. Sinflar bo'yicha oddiy o'rtacha
  8. Hajmga qarab vaznlangan
  9. Accuracy ga teng
  10. Kichik sinf foldda yo'qolmasligi uchun
  11. Kaskad xatosi
  12. class_weight, birlashtirish, ierarxiya

Vazifa 2: Xatolarni tuzating

python
1.  f1_score(y, pred)   # 12 sinf

2.  KFold(5).split(X)   # kichik sinflar bor

3.  OneVsOneClassifier(SVC()).fit(X, y)   # 150 sinf

4.  print(f"aniqlik {acc:.3f}")   # 20 sinf

5.  foyda = ovr.predict_proba(X) @ qiymatlar
Javoblar
python
1.  f1_score(y, pred, average="macro")

2.  StratifiedKFold(5, shuffle=True, random_state=0)

3.  LinearSVC()   # yoki ierarxik yondashuv

4.  print(confusion_matrix(y, pred, normalize="true"))

5.  # softmax model yoki kalibrlash (14.10)

Vazifa 3: Strategiyalar

Modellang:

  1. OvR, OvO, softmax
  2. Aniqlik va vaqt
  3. Model soni
  4. Ehtimollar

Vazifa 4: Chalkashlik

Modellang:

  1. Matritsa
  2. Eng chalkash juftliklar
  3. Sinf bo'yicha F1
  4. Xulosa

Vazifa 5: Nomutanosiblik

Modellang:

  1. Turli hajmli sinflar
  2. Oddiy va balanced
  3. Kichik sinf recall
  4. macro va weighted

Vazifa 6: Ierarxiya

Modellang:

  1. Guruhlangan sinflar
  2. Yassi va ierarxik
  3. Kaskad xatosi
  4. Tavsiya

Vazifa 7: O'ylash

Ko'p sinfli vazifalarda sinflar ro'yxati ko'pincha biznes tomonidan beriladi (masalan, 320 ta mahsulot kategoriyasi) va Data Scientist uni o'zgarmas deb qabul qiladi. Bu to'g'ri yondashuvmi?

Javob

Qisqa javob: yo'q — sinflar ro'yxati modellashtirish qarori hamdir. Agar ikki sinf ma'lumotda ajralmasa yoki bir sinfda 12 ta namuna bo'lsa, ularni o'zgarmas deb qabul qilish modelni ham, foydalanuvchini ham yomon ahvolga soladi.

1. Qachon ro'yxatni qayta ko'rish kerak

Belgi Taklif
Ikki sinf doim chalkashadi Birlashtirish yoki qo'shimcha belgi
Sinfda < 50 namuna "Boshqa" ga qo'shish yoki ma'lumot yig'ish
Sinflar ierarxik Ikki bosqichli model
Sinf ta'rifi noaniq Yorliqlash qo'llanmasini aniqlashtirish

2. Qanday asoslash

  • Chalkashlik matritsasi — eng ishonchli dalil
  • Yorliqlar orasidagi kelishuv (inter-annotator agreement)
  • Har sinf uchun biznes qiymati (ba'zi sinflar qarorga ta'sir qilmaydi)

3. Nima taklif qilish mumkin

  1. Sinflarni birlashtirish (past qiymatli va chalkashadiganlar)
  2. Ierarxiya: yirik guruh + ichki model
  3. "Ishonchsiz" javob: past ehtimolda inson ko'rigiga yuborish
  4. Yorliqlash sifatini yaxshilash

4. Muzokara tili

  • "Model bu sinflarni ajrata olmaydi"
  • "Ushbu ikki kategoriya ma'lumotda 38% holatda chalkashadi; ularni birlashtirsak F1 0.71 dan 0.79 ga ko'tariladi — qaysi biri sizga muhimroq?"

5. Xulosa

  1. Sinflar ro'yxati — muzokara predmeti
  2. Dalil — chalkashlik matritsasi
  3. Ierarxiya va "boshqa" sinfi amaliy yechim
  4. Yorliqlash sifati modeldan muhimroq bo'lishi mumkin

Nimani mustahkamlaydi: 2.3, 2.6-bo'limlar.


Xulosa

Bu darsda ko'p sinfli strategiyalarni o'rgandik.

Eng muhim uch fikr:

  1. Uch strategiya, uch narx. OvR — K ta model, tez, lekin ballar bir shkalada emas; OvO — K(K-1)/2 ta model (har biri kichik ma'lumotda), katta K da qimmat; softmax — bitta model va izchil ehtimollar. sklearn buni avtomatik tanlaydi (LogisticRegression — softmax, SVC — OvO, LinearSVC — OvR), lekin qaysi biri ishlatilayotganini bilish kerak.

  2. macro va weighted birga beriladi. Nomutanosib ko'p sinfli vazifada ular keskin farq qiladi: weighted katta sinflar hisobiga yuqori bo'lib turishi, macro esa kichik sinflar yo'qolganini ko'rsatishi mumkin. Chalkashlik matritsasi — asosiy diagnostika: u qaysi sinflar o'xshashligini, qaysi sinf hech qachon bashorat qilinmasligini ko'rsatadi.

  3. Ierarxiya — katta vazifalar uchun. Sinflar tabiiy guruhlarga bo'linsa, ikki bosqichli model tezroq o'qitiladi va xatolarni "yaqin" sinflar ichida ushlab qoladi. Asosiy xavf — kaskad xatosi: birinchi bosqichdagi yanglishish tuzatilmaydi. Va eslang: sinflar ro'yxati — o'zgarmas berilgan emas, muzokara predmeti.

Keyingi darsda nomutanosib sinflarni chuqurroq o'rganamiz: resampling, SMOTE, chegara siljitish va ularning haqiqiy foydasi.

Ulashish:Telegram'da

Izohlar (0)

Izoh yozish uchun kiring.

  • Hozircha izoh yo'q. Birinchi bo'ling!
14.8-dars: Ko'p sinfli strategiyalar — IlmHamroh