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

14.9-dars: Nomutanosib sinflar

14-QISM — KLASSIFIKATSIYA · 9-dars


1. Kirish va motivatsiya

Firibgarlik 0.3%, kasallik 2%, nosozlik 1.5% — eng qimmatli klassifikatsiya vazifalarida musbat sinf kam uchraydi. 12.7-darsda buning metrik tomonini ko'rdik (accuracy aldaydi, PR AUC kerak). Endi algoritmik tomonni ko'ramiz: model o'zi nima qilishi va biz unga qanday yordam berishimiz mumkin.

Muhim ogohlantirish: nomutanosiblik atrofida ko'p afsona bor. "Har doim SMOTE qiling", "sinflarni tenglashtiring" degan maslahatlar ko'p hollarda zarar keltiradi. Bu darsda nima ishlashini va nima faqat ko'rinishni o'zgartirishini sonlar bilan ajratamiz.

Bu darsda: nomutanosiblik qachon haqiqatan muammo, chegara siljitish (eng arzon yechim), class_weight, resampling (oversampling, undersampling, SMOTE), ularning kalibrlashga ta'siri va amaliy tartib.

Real vaziyat. Bank firibgarlik modelida jamoa SMOTE ishlatdi: CV da F1 0.31 dan 0.44 ga ko'tarildi. Ishlab chiqarishda esa natija yomonlashdi. Sabab: SMOTE CV ning ichida emas, tashqarisida qo'llanilgan (leakage — 12.9), va model ehtimollari butunlay siljigan edi. To'g'ri qo'llanganda SMOTE foydasi 0.01 bo'ldi — chegarani to'g'ri tanlash esa 0.12 berdi.

Bu darsda nomutanosiblik bilan ishlashni o'rganamiz.

Bu darsda:

  • Qachon muammo
  • Chegara siljitish
  • class_weight
  • Resampling va SMOTE
  • Kalibrlashga ta'siri
  • Amaliy tartib
  • Tuzoqlar
  • Amaliy: 1% musbat sinf

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


2. Nazariya — chuqur tushuntirish

2.1. Qachon muammo

text
Nomutanosiblik O'ZI muammo EMAS. Muammo quyidagilar birga kelganda:

  1. musbat sinf MUTLAQ soni kam (100 ta emas, 20 ta)
  2. sinflar belgilar fazosida ustma-ust tushadi (14.1 — Bayes xatosi)
  3. metrika noto'g'ri tanlangan (accuracy — 12.7)
  4. chegara 0.5 qoldirilgan

1 000 000 namunada 1% musbat = 10 000 ta musbat — bu KO'P, muammo yo'q
1 000 namunada 1% musbat = 10 ta — MANA MUAMMO

Birinchi savol: musbat sinf nechta? — ulush emas, mutlaq son. Ko'p hollarda "nomutanosiblik muammosi" deb atalgan narsa aslida kam ma'lumot muammosi yoki noto'g'ri metrika muammosi bo'lib chiqadi.

2.2. Chegara siljitish

text
Eng arzon va ko'pincha eng samarali yechim 12.7-bob:

  model o'qitiladi (hech qanday o'zgarishsiz)
  chegara VALIDATSIYADA tanlanadi: narx, byudjet yoki maqsadli recall bo'yicha

Nega ishlaydi: reyting (AUC) o'zgarmaydi, faqat qaror nuqtasi siljiydi
Nega afzal: ehtimollar KALIBRLANGAN qoladi (9.9, 13.10)

Chegara siljitish — nomutanosiblik bilan ishlashning birinchi qadami: u modelni o'zgartirmaydi, ma'lumotni buzmaydi va ehtimollarni saqlaydi. Ko'p amaliy vazifalarda resampling va class_weight berganidan ko'p foyda aynan shundan keladi.

2.3. class_weight

python
LogisticRegression(class_weight="balanced")
SVC(class_weight="balanced")
RandomForestClassifier(class_weight="balanced_subsample")

"balanced": vazn_k = n / (K · n_k)   — kam sinf ko'proq vazn oladi

Ta'siri: yo'qotish funksiyasida kam sinf xatolari qimmatroq
  → chegara amalda siljiydi
  → EHTIMOLLAR ham siljiydi (kalibrlash buziladi — 13.10)

class_weight="balanced" — chegara siljitishning yo'qotish darajasidagi varianti: natija ko'pincha o'xshash bo'ladi. Farqi: u ehtimollarni siljitadi, shuning uchun predict_proba ni qaror formulalarida ishlatish uchun qayta kalibrlash kerak.

2.4. Resampling va SMOTE

text
OVERSAMPLING (kam sinfni takrorlash):
  + ma'lumot yo'qolmaydi;  - overfitting xavfi (aynan nusxalar)

UNDERSAMPLING (ko'p sinfni kamaytirish):
  + tez;  - ma'lumot yo'qoladi (ba'zan juda ko'p)

SMOTE (sun'iy kam sinf namunalari):
  kam sinf nuqtalari orasida interpolyatsiya
  + nusxalash emas;  - shovqinli chegarani kuchaytiradi, yuqori o'lchamda yomon

MUHIM: resampling FAQAT O'QUV qismida, CV ICHIDA 12.9-bob
       validatsiya va test HAR DOIM asl taqsimotda

Resampling — oxirgi chora, birinchi emas. Zamonaviy tadqiqotlar ko'rsatadiki, kuchli modellar uchun (boosting, regularizatsiyalangan chiziqli) resampling foydasi kichik yoki nol, chegarani to'g'ri tanlash esa doimiy foyda beradi. SMOTE ayniqsa yuqori o'lchamda va shovqinli ma'lumotda xavfli.

2.5. Kalibrlashga ta'siri

text
Har qanday sinf nisbatini o'zgartirish → ehtimollar SILJIYDI

  model o'rtacha bashorati ~ o'quv to'plamidagi musbat ulush
  50/50 ga tenglashtirsangiz → bashoratlar 0.5 atrofida to'planadi

Tuzatish:
  · oldingi ehtimol (prior) bo'yicha qayta hisoblash
  · CalibratedClassifierCV ni ASL taqsimotdagi validatsiyada 14.10-bob
  · yoki resampling qilmaslik

Bu — resampling ning eng ko'p unutiladigan oqibati: model reyting bo'yicha yaxshi qolsa ham, uning ehtimollari endi haqiqatni aks ettirmaydi. Agar siz ehtimolni narxga, zaxiraga yoki qarorga aylantirsangiz (9.9, 12.7), bu jiddiy xato manbai.

2.6. Amaliy tartib

text
1. Musbat sinf MUTLAQ sonini hisoblang
2. Metrikani to'g'ri tanlang: PR AUC, recall@precision, narx 12.7-bob
3. Stratifikatsiyalangan CV 12.3-bob
4. Modelni o'zgarishsiz o'qiting → CHEGARANI validatsiyada tanlang
5. class_weight="balanced" ni sinab ko'ring (CV bilan solishtiring)
6. Faqat shundan keyin resampling (pipeline ichida!)
7. Ehtimol kerak bo'lsa — kalibrlang 14.10-bob
8. Ko'proq musbat namuna yig'ish imkonini tekshiring — eng kuchli yechim

Tartib muhim: eng arzon va xavfsiz qadamlardan boshlanadi. Amaliyotda 4-qadam (chegara) ko'p hollarda yetarli bo'ladi; 6-qadamga yetib borish kamdan-kam kerak.

2.7. Tuzoqlar

Asosiy tuzoqlar: resampling ni CV dan tashqarida (leakage — 12.9); test/validatsiyani ham resampling qilish; accuracy bilan baholash 12.7-bob; resampling dan keyin ehtimolga ishonish; SMOTE ni yuqori o'lchamda yoki kategorik belgilar bilan; stratifikatsiyasiz CV; musbat sinf mutlaq sonini tekshirmaslik; chegarani testda tanlash.

2.8. Avval chegara, keyin qolgani

Nomutanosiblik o'zi muammo emas — muammo musbat sinfning mutlaq soni kamligi, sinflarning ustma-ust tushishi va noto'g'ri metrika/chegara. Eng arzon yechim — chegara siljitish (modelni o'zgartirmaydi, ehtimollarni saqlaydi). Keyin class_weight, va faqat oxirida resampling (albatta pipeline/CV ichida, faqat o'quv qismida). Har qanday sinf nisbatini o'zgartirish kalibrlashni buzadi — ehtimol kerak bo'lsa qayta kalibrlang. Keyingi dars — kalibrlash.


3. Tez ma'lumotnoma

python
import numpy as np
from sklearn.metrics import average_precision_score, precision_recall_curve
from sklearn.model_selection import StratifiedKFold
from sklearn.utils import resample

# 1. chegara — validatsiyada
p, r, t = precision_recall_curve(y_val, proba_val)
mos = r[:-1] >= 0.80
chegara = t[mos][np.argmax(p[:-1][mos])]

# 2. class_weight
LogisticRegression(class_weight="balanced", max_iter=2000)

# 3. resampling — FAQAT o'quv qismida (imbalanced-learn bo'lmasa qo'lda)
musbat = X_tr[y_tr == 1]
kopaytirilgan = resample(musbat, n_samples=len(X_tr[y_tr == 0]),
                         replace=True, random_state=0)

average_precision_score(y_te, proba)        # baza = musbat sinf ulushi
QOIDA: mutlaq sonni sana · PR AUC · chegarani validatsiyada · resampling CV ichida

Nomutanosiblik xulosasi

Muammo: mutlaq son kam + ustma-ustlik + noto'g'ri metrika/chegara
Tartib: metrika → chegara → class_weight → resampling (oxirgi)
Resampling faqat o'quvda, CV ichida; test asl taqsimotda
Har qanday nisbat o'zgarishi kalibrlashni buzadi

4. Batafsil misollar

Misollar real numpy/sklearn bilan (Python 3.14). Misollar bitta firibgarlik ma'lumotidan foydalanadi.

Misol 1 — Nomutanosiblik qachon muammo

python
"""Ulush emas, mutlaq son muhim (real numpy/sklearn)."""

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


def yarat(n: int, ulush: float, seed: int = 5, p: int = 8, ajralish: float = 1.1):
    """Musbat sinf 'ulush' nisbatda; sinflar 'ajralish' masofada."""
    rng = np.random.default_rng(seed)
    y = (rng.random(n) < ulush).astype(int)
    siljish = np.zeros(p)
    siljish[:4] = ajralish
    X = rng.normal(0, 1, (n, p)) + y[:, None] * siljish
    return X, y


def baho(n: int, ulush: float) -> tuple[int, float, float]:
    X, y = yarat(n, ulush)
    Xtr, Xte, ytr, yte = train_test_split(X, y, test_size=0.4, random_state=0,
                                          stratify=y)
    m = Pipeline([("sc", StandardScaler()),
                  ("m", LogisticRegression(max_iter=3000))]).fit(Xtr, ytr)
    p_te = m.predict_proba(Xte)[:, 1]
    return int(ytr.sum()), roc_auc_score(yte, p_te), average_precision_score(yte, p_te)


def main() -> None:
    print("=== 1. Bir xil ulush (1%), turli hajm ===")
    print(f"  {'n':>8} {'musbat (o_quv)':>16} {'ROC AUC':>9} {'PR AUC':>9}")
    for n in [1000, 5000, 20_000, 100_000]:
        musbat, auc, pr = baho(n, 0.01)
        print(f"  {n:>8} {musbat:>16} {auc:>9.4f} {pr:>9.4f}")
    print("  (ulush bir xil, lekin natija mutlaq songa bog'liq)")

    print("\n=== 2. Bir xil musbat son (~200), turli ulush ===")
    print(f"  {'ulush':>8} {'n':>8} {'musbat':>8} {'ROC AUC':>9} {'PR AUC':>9}")
    for ulush in [0.5, 0.1, 0.02, 0.005]:
        n = int(200 / ulush / 0.6)
        musbat, auc, pr = baho(n, ulush)
        print(f"  {ulush:>8.1%} {n:>8} {musbat:>8} {auc:>9.4f} {pr:>9.4f}")
    print("  (ROC AUC barqaror — reyting sifati musbat sonidan kelib chiqadi)")
    print("  (PR AUC tushadi, chunki uning BAZASI ham tushadi)")

    print("\n=== 3. PR AUC ni baza bilan solishtirish ===")
    for ulush in [0.5, 0.1, 0.02, 0.005]:
        n = int(200 / ulush / 0.6)
        _, _, pr = baho(n, ulush)
        print(f"  ulush {ulush:>6.1%}: PR AUC {pr:.4f}, baza {ulush:.4f}, "
              f"nisbat {pr / ulush:.1f}x")

    print("\n=== 4. Sinflar ustma-ust tushganda ===")
    print(f"  {'ajralish':>9} {'ROC AUC':>9} {'PR AUC':>9}")
    for a in [0.4, 1.1, 2.5]:
        X, y = yarat(20_000, 0.02, ajralish=a)
        Xtr, Xte, ytr, yte = train_test_split(X, y, test_size=0.4, random_state=0,
                                              stratify=y)
        m = Pipeline([("sc", StandardScaler()),
                      ("m", LogisticRegression(max_iter=3000))]).fit(Xtr, ytr)
        p_te = m.predict_proba(Xte)[:, 1]
        print(f"  {a:>9.1f} {roc_auc_score(yte, p_te):>9.4f} "
              f"{average_precision_score(yte, p_te):>9.4f}")
    print("  ⭐ Asosiy savol — musbat sinf nechta va sinflar ajraladimi")


if __name__ == "__main__":
    main()

Natijaning muhim qismi:

text
=== 1. Bir xil ulush (1%), turli hajm ===
         n   musbat (o_quv)   ROC AUC    PR AUC
      1000                8    0.9619    0.3848
      5000               29    0.9552    0.2847
     20000              116    0.9513    0.3585
    100000              623    0.9309    0.3396
  (ulush bir xil, lekin natija mutlaq songa bog'liq)

=== 2. Bir xil musbat son (~200), turli ulush ===
     ulush        n   musbat   ROC AUC    PR AUC
     50.0%      666      216    0.9242    0.9379
     10.0%     3333      220    0.9279    0.7029
      2.0%    16666      203    0.9432    0.4789
      0.5%    66666      204    0.9491    0.2524
  (ROC AUC barqaror — reyting sifati musbat sonidan kelib chiqadi)
  (PR AUC tushadi, chunki uning BAZASI ham tushadi)

=== 3. PR AUC ni baza bilan solishtirish ===
  ulush  50.0%: PR AUC 0.9379, baza 0.5000, nisbat 1.9x
  ulush  10.0%: PR AUC 0.7029, baza 0.1000, nisbat 7.0x
  ulush   2.0%: PR AUC 0.4789, baza 0.0200, nisbat 23.9x
  ulush   0.5%: PR AUC 0.2524, baza 0.0050, nisbat 50.5x

=== 4. Sinflar ustma-ust tushganda ===
   ajralish   ROC AUC    PR AUC
        0.4    0.7416    0.0726
        1.1    0.9487    0.5200
        2.5    0.9999    0.9968
  ⭐ Asosiy savol — musbat sinf nechta va sinflar ajraladimi

Nima ko'rsatdi: 2.1-bo'lim.

Misol 2 — Chegara, class_weight va resampling

python
"""Uch yondashuvni bir xil sharoitda solishtirish (real numpy/sklearn)."""

import numpy as np
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import (average_precision_score, f1_score,
                             precision_recall_curve, recall_score,
                             roc_auc_score)
from sklearn.model_selection import train_test_split
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import StandardScaler
from sklearn.utils import resample


def yarat(seed: int = 7, n: int = 30_000, ulush: float = 0.015, p: int = 10):
    rng = np.random.default_rng(seed)
    y = (rng.random(n) < ulush).astype(int)
    siljish = np.zeros(p)
    siljish[:5] = [1.1, -0.9, 0.8, -0.7, 0.6]
    X = rng.normal(0, 1, (n, p)) + y[:, None] * siljish
    return X, y


def chegara_tanla(y_val, p_val, maqsad_recall: float = 0.7) -> float:
    p, r, t = precision_recall_curve(y_val, p_val)
    mos = r[:-1] >= maqsad_recall
    if not mos.any():
        return 0.5
    return float(t[mos][np.argmax(p[:-1][mos])])


def main() -> None:
    X, y = yarat()
    X_qolgan, X_te, y_qolgan, y_te = train_test_split(X, y, test_size=0.3,
                                                      random_state=0, stratify=y)
    X_tr, X_val, y_tr, y_val = train_test_split(X_qolgan, y_qolgan, test_size=0.3,
                                                random_state=0, stratify=y_qolgan)

    print("=== 1. Ma'lumot ===")
    print(f"  o'quv {len(X_tr)} (musbat {y_tr.sum()}), "
          f"validatsiya {len(X_val)} (musbat {y_val.sum()}), "
          f"test {len(X_te)} (musbat {y_te.sum()})")

    print("\n=== 2. Uch yondashuv ===")
    natijalar = {}

    # (a) oddiy model + chegara siljitish
    oddiy = Pipeline([("sc", StandardScaler()),
                      ("m", LogisticRegression(max_iter=3000))]).fit(X_tr, y_tr)
    ch = chegara_tanla(y_val, oddiy.predict_proba(X_val)[:, 1])
    natijalar["oddiy 0.5-bob"] = (oddiy, 0.5)
    natijalar["oddiy + chegara"] = (oddiy, ch)

    # (b) class_weight
    balans = Pipeline([("sc", StandardScaler()),
                       ("m", LogisticRegression(max_iter=3000,
                                                class_weight="balanced"))]).fit(X_tr, y_tr)
    natijalar["balanced 0.5-bob"] = (balans, 0.5)
    natijalar["balanced + chegara"] = (
        balans, chegara_tanla(y_val, balans.predict_proba(X_val)[:, 1]))

    # (c) oversampling (faqat o'quvda)
    musbat_idx = np.flatnonzero(y_tr == 1)
    manfiy_idx = np.flatnonzero(y_tr == 0)
    kopaytirilgan = resample(musbat_idx, n_samples=len(manfiy_idx), replace=True,
                             random_state=0)
    idx = np.concatenate([manfiy_idx, kopaytirilgan])
    over = Pipeline([("sc", StandardScaler()),
                     ("m", LogisticRegression(max_iter=3000))]).fit(X_tr[idx], y_tr[idx])
    natijalar["oversample 0.5-bob"] = (over, 0.5)
    natijalar["oversample + chegara"] = (
        over, chegara_tanla(y_val, over.predict_proba(X_val)[:, 1]))

    print(f"  {'yondashuv':<21} {'chegara':>8} {'recall':>8} {'precision':>10} "
          f"{'F1':>7} {'PR AUC':>8}")
    from sklearn.metrics import precision_score
    for nom, (model, t) in natijalar.items():
        p_te = model.predict_proba(X_te)[:, 1]
        pred = (p_te >= t).astype(int)
        print(f"  {nom:<21} {t:>8.4f} {recall_score(y_te, pred):>8.3f} "
              f"{precision_score(y_te, pred, zero_division=0):>10.3f} "
              f"{f1_score(y_te, pred, zero_division=0):>7.3f} "
              f"{average_precision_score(y_te, p_te):>8.4f}")

    print("\n=== 3. Reyting sifati (PR AUC) deyarli o'zgarmaydi ===")
    for nom in ["oddiy 0.5-bob", "balanced 0.5-bob", "oversample 0.5-bob"]:
        model = natijalar[nom][0]
        p_te = model.predict_proba(X_te)[:, 1]
        print(f"  {nom:<18}: ROC AUC {roc_auc_score(y_te, p_te):.4f}, "
              f"PR AUC {average_precision_score(y_te, p_te):.4f}")

    print("\n=== 4. Xulosa ===")
    print("  chegara siljitish — eng katta o'zgarishni beradi")
    print("  class_weight va oversampling reytingni deyarli o'zgartirmaydi")
    print("  ⭐ Avval chegara, keyin qolgani")


if __name__ == "__main__":
    main()

Natijaning muhim qismi:

text
=== 1. Ma'lumot ===
  o'quv 14700 (musbat 207), validatsiya 6300 (musbat 88), test 9000 (musbat 126)

=== 2. Uch yondashuv ===
  yondashuv              chegara   recall  precision      F1   PR AUC
  oddiy 0.5-bob             0.5000    0.056      0.636   0.102   0.2687
  oddiy + chegara         0.0289    0.690      0.097   0.171   0.2687
  balanced 0.5-bob          0.5000    0.802      0.064   0.119   0.2739
  balanced + chegara      0.7145    0.643      0.103   0.177   0.2739
  oversample 0.5-bob        0.5000    0.802      0.064   0.118   0.2731
  oversample + chegara    0.7195    0.643      0.104   0.179   0.2731

=== 3. Reyting sifati (PR AUC) deyarli o'zgarmaydi ===
  oddiy 0.5-bob       : ROC AUC 0.9060, PR AUC 0.2687
  balanced 0.5-bob    : ROC AUC 0.9059, PR AUC 0.2739
  oversample 0.5-bob  : ROC AUC 0.9056, PR AUC 0.2731

=== 4. Xulosa ===
  chegara siljitish — eng katta o'zgarishni beradi
  class_weight va oversampling reytingni deyarli o'zgartirmaydi
  ⭐ Avval chegara, keyin qolgani

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

Misol 3 — Resampling va kalibrlash

python
"""Sinf nisbatini o'zgartirish ehtimollarni qanday siljitadi (real numpy/sklearn)."""

import numpy as np
from sklearn.calibration import calibration_curve
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import brier_score_loss, roc_auc_score
from sklearn.model_selection import train_test_split
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import StandardScaler
from sklearn.utils import resample


def yarat(seed: int = 7, n: int = 30_000, ulush: float = 0.015, p: int = 10):
    rng = np.random.default_rng(seed)
    y = (rng.random(n) < ulush).astype(int)
    siljish = np.zeros(p)
    siljish[:5] = [1.1, -0.9, 0.8, -0.7, 0.6]
    X = rng.normal(0, 1, (n, p)) + y[:, None] * siljish
    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)

    musbat_idx = np.flatnonzero(ytr == 1)
    manfiy_idx = np.flatnonzero(ytr == 0)
    over = np.concatenate([manfiy_idx,
                           resample(musbat_idx, n_samples=len(manfiy_idx),
                                    replace=True, random_state=0)])
    under = np.concatenate([musbat_idx,
                            resample(manfiy_idx, n_samples=len(musbat_idx),
                                     replace=False, random_state=0)])

    modellar = {}
    for nom, idx in [("asl", np.arange(len(Xtr))), ("oversample", over),
                     ("undersample", under)]:
        modellar[nom] = Pipeline([("sc", StandardScaler()),
                                  ("m", LogisticRegression(max_iter=3000))]).fit(
            Xtr[idx], ytr[idx])

    print("=== 1. O'quv to'plamidagi musbat ulush ===")
    print(f"  asl        : {ytr.mean():.4f} ({len(Xtr)} namuna)")
    print(f"  oversample : {ytr[over].mean():.4f} ({len(over)} namuna)")
    print(f"  undersample: {ytr[under].mean():.4f} ({len(under)} namuna)")

    print("\n=== 2. Test'dagi o'rtacha bashorat ===")
    print(f"  haqiqiy musbat ulush: {yte.mean():.4f}")
    for nom, m in modellar.items():
        p = m.predict_proba(Xte)[:, 1]
        print(f"  {nom:<12}: o'rtacha bashorat {p.mean():.4f}, "
              f"AUC {roc_auc_score(yte, p):.4f}, "
              f"Brier {brier_score_loss(yte, p):.5f}")

    print("\n=== 3. Kalibrlash egri chizig'i ===")
    for nom in ["asl", "oversample"]:
        p = modellar[nom].predict_proba(Xte)[:, 1]
        haqiqiy, bashorat = calibration_curve(yte, p, n_bins=4, strategy="quantile")
        juftlar = ", ".join(f"{b:.3f}->{h:.3f}" for b, h in zip(bashorat, haqiqiy))
        print(f"  {nom:<12}: {juftlar}")

    print("\n=== 4. Prior tuzatish ===")
    # oversample modelining ehtimollarini asl nisbatga qaytarish
    p_over = modellar["oversample"].predict_proba(Xte)[:, 1]
    asl_ulush = ytr.mean()
    over_ulush = ytr[over].mean()
    odds = p_over / (1 - p_over)
    tuzatish = (asl_ulush / (1 - asl_ulush)) / (over_ulush / (1 - over_ulush))
    p_tuz = (odds * tuzatish) / (1 + odds * tuzatish)
    print(f"  oversample o'rtacha bashorat: {p_over.mean():.4f}")
    print(f"  prior tuzatishdan keyin:      {p_tuz.mean():.4f} "
          f"(haqiqiy {yte.mean():.4f})")
    print(f"  Brier: {brier_score_loss(yte, p_over):.5f} → "
          f"{brier_score_loss(yte, p_tuz):.5f}")
    print("  ⭐ Resampling reytingni saqlaydi, ehtimolni buzadi")


if __name__ == "__main__":
    main()

Natijaning muhim qismi:

text
=== 1. O'quv to'plamidagi musbat ulush ===
  asl        : 0.0140 (21000 namuna)
  oversample : 0.5000 (41410 namuna)
  undersample: 0.5000 (590 namuna)

=== 2. Test'dagi o'rtacha bashorat ===
  haqiqiy musbat ulush: 0.0140
  asl         : o'rtacha bashorat 0.0133, AUC 0.9061, Brier 0.01170
  oversample  : o'rtacha bashorat 0.2460, AUC 0.9075, Brier 0.11848
  undersample : o'rtacha bashorat 0.2568, AUC 0.9045, Brier 0.12573

=== 3. Kalibrlash egri chizig'i ===
  asl         : 0.000->0.000, 0.002->0.001, 0.005->0.005, 0.046->0.049
  oversample  : 0.020->0.000, 0.088->0.002, 0.244->0.004, 0.632->0.049

=== 4. Prior tuzatish ===
  oversample o'rtacha bashorat: 0.2460
  prior tuzatishdan keyin:      0.0135 (haqiqiy 0.0140)
  Brier: 0.11848 → 0.01161
  ⭐ Resampling reytingni saqlaydi, ehtimolni buzadi

Nima ko'rsatdi: 2.5-bo'lim.

Misol 4 — Resampling ni CV ichida qilish

python
"""Leakage: resampling CV dan tashqarida qilinganda (real numpy/sklearn)."""

import numpy as np
from sklearn.base import BaseEstimator, ClassifierMixin, clone
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import average_precision_score
from sklearn.model_selection import StratifiedKFold, cross_val_score, train_test_split
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import StandardScaler
from sklearn.utils import resample


class Oversampler(ClassifierMixin, BaseEstimator):   # tartib muhim (sklearn teglari)
    """Faqat fit ichida oversampling qiladi — CV bilan xavfsiz."""

    def __init__(self, asos=None, random_state: int = 0):
        self.asos = asos
        self.random_state = random_state

    def fit(self, X, y):
        y = np.asarray(y)
        musbat = np.flatnonzero(y == 1)
        manfiy = np.flatnonzero(y == 0)
        kop = resample(musbat, n_samples=len(manfiy), replace=True,
                       random_state=self.random_state)
        idx = np.concatenate([manfiy, kop])
        self.model_ = clone(self.asos).fit(np.asarray(X)[idx], y[idx])
        self.classes_ = self.model_.classes_
        return self

    def predict(self, X):
        return self.model_.predict(X)

    def predict_proba(self, X):
        return self.model_.predict_proba(X)


def yarat(seed: int = 21, n: int = 6000, ulush: float = 0.02, p: int = 10):
    rng = np.random.default_rng(seed)
    y = (rng.random(n) < ulush).astype(int)
    siljish = np.zeros(p)
    siljish[:4] = [0.8, -0.6, 0.7, -0.5]
    X = rng.normal(0, 1, (n, p)) + y[:, None] * siljish
    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)
    cv = StratifiedKFold(5, shuffle=True, random_state=0)
    asos = lambda: Pipeline([("sc", StandardScaler()),
                             ("m", LogisticRegression(max_iter=3000))])

    print("=== 1. Ma'lumot ===")
    print(f"  o'quv {len(Xtr)} (musbat {ytr.sum()}), test {len(Xte)} "
          f"(musbat {yte.sum()})")

    print("\n=== 2. NOTO'G'RI: oversampling CV dan TASHQARIDA ===")
    musbat = np.flatnonzero(ytr == 1)
    manfiy = np.flatnonzero(ytr == 0)
    idx = np.concatenate([manfiy, resample(musbat, n_samples=len(manfiy),
                                           replace=True, random_state=0)])
    X_over, y_over = Xtr[idx], ytr[idx]
    soxta = cross_val_score(asos(), X_over, y_over, cv=cv,
                            scoring="average_precision").mean()
    print(f"  CV PR AUC = {soxta:.4f}  ← soxta (aynan nusxalar fold'lar orasida)")

    print("\n=== 3. TO'G'RI: oversampling fit ICHIDA ===")
    haqiqiy = cross_val_score(Oversampler(asos=asos()), Xtr, ytr, cv=cv,
                              scoring="average_precision").mean()
    print(f"  CV PR AUC = {haqiqiy:.4f}")
    oddiy = cross_val_score(asos(), Xtr, ytr, cv=cv,
                            scoring="average_precision").mean()
    print(f"  oversampling'siz CV PR AUC = {oddiy:.4f}")

    print("\n=== 4. Test to'plamida haqiqat ===")
    for nom, model in [("oddiy", asos()),
                       ("oversample (to'g'ri)", Oversampler(asos=asos()))]:
        model.fit(Xtr, ytr)
        p = model.predict_proba(Xte)[:, 1]
        print(f"  {nom:<22}: test PR AUC {average_precision_score(yte, p):.4f}")
    print(f"  soxta CV bahosi {soxta:.4f} test natijasidan ancha yuqori edi")
    print("  ⭐ Resampling har doim fit/pipeline ichida 12.9-bob")


if __name__ == "__main__":
    main()

Natijaning muhim qismi:

text
=== 1. Ma'lumot ===
  o'quv 4200 (musbat 80), test 1800 (musbat 34)

=== 2. NOTO'G'RI: oversampling CV dan TASHQARIDA ===
  CV PR AUC = 0.8163  ← soxta (aynan nusxalar fold'lar orasida)

=== 3. TO'G'RI: oversampling fit ICHIDA ===
  CV PR AUC = 0.1578
  oversampling'siz CV PR AUC = 0.1467

=== 4. Test to'plamida haqiqat ===
  oddiy                 : test PR AUC 0.0724
  oversample (to'g'ri)  : test PR AUC 0.0677
  soxta CV bahosi 0.8163 test natijasidan ancha yuqori edi
  ⭐ Resampling har doim fit/pipeline ichida (12.9)

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


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

Noto'g'ri fikr To'g'risi
"Nomutanosiblik — har doim muammo" Mutlaq son muhim
"Sinflarni tenglashtirish kerak" Kamdan-kam foydali
"SMOTE — standart yechim" Oxirgi chora
"Resampling AUC ni oshiradi" Deyarli o'zgartirmaydi
"Chegara — texnik tafsilot" Eng katta foyda shundan
"Resampling ehtimolni saqlaydi" Siljitadi
"Test'ni ham balanslash kerak" Hech qachon
"class_weight = resampling" O'xshash, lekin bir xil emas

6. Keng tarqalgan xatolar va yechimlari

1. Resampling CV dan tashqarida

python
X_bal, y_bal = smote.fit_resample(X, y); cross_val_score(m, X_bal, y_bal)  # ⚠️
# resampling ni fit ichida yoki imblearn Pipeline bilan                    # ✅

2. Test'ni balanslash

python
X_te, y_te = resample_balanced(X_te, y_te)                        # ⚠️
# test har doim asl taqsimotda                                    # ✅

3. accuracy bilan baholash

python
print(model.score(X_te, y_te))         # 98.5%                    # ⚠️
print(average_precision_score(y_te, proba))                       # ✅

4. Chegarani 0.5 qoldirish

python
pred = model.predict(X_te)                                        # ⚠️
pred = (proba >= chegara_validatsiyadan).astype(int)              # ✅

5. Resampling dan keyin ehtimolga ishonish

python
foyda = over_model.predict_proba(X)[:, 1] * LTV                   # ⚠️
# prior tuzatish yoki kalibrlash 14.10-bob                          # ✅

6. Mutlaq sonni tekshirmaslik

python
# "1% musbat — SMOTE kerak"                                       # ⚠️
print(f"musbat namunalar: {y.sum()}")   # 10 000 bo'lsa muammo yo'q # ✅

7. Stratifikatsiyasiz CV

python
KFold(5, shuffle=True)                                            # ⚠️
StratifiedKFold(5, shuffle=True, random_state=0)                  # ✅

7. Integratsiya — bu bilim qayerda kerak bo'ladi

  • 9.9-dars (o'tilgan): Kalibrlash va qaror
  • 12.7-dars (o'tilgan): Metrikalar va chegara
  • 12.9-dars (o'tilgan): Leakage
  • 14.10-dars: Kalibrlash usullari
  • 14.14-dars: Amaliy loyiha

8. Eng yaxshi amaliyotlar

  1. Musbat sinf mutlaq sonini hisoblang.

  2. Metrikani to'g'ri tanlang.

  3. Chegarani validatsiyada tanlang.

  4. class_weight ni sinang.

  5. Resampling ni faqat pipeline ichida.

  6. Test'ni hech qachon balanslamang.

  7. Ehtimol kerak bo'lsa kalibrlang.

  8. Ko'proq musbat namuna yig'ishni ko'rib chiqing.


9. Amaliy topshiriq

Vazifa 1: Bashorat qiling

python
1.  # nomutanosiblik qachon muammo?
2.  # birinchi savol nima?
3.  # eng arzon yechim?
4.  # chegara qayerda tanlanadi?
5.  # class_weight="balanced" formulasi?
6.  # oversampling kamchiligi?
7.  # undersampling kamchiligi?
8.  # SMOTE nima qiladi?
9.  # resampling qayerda bajariladi?
10. # test balanslanadimi?
11. # resampling ehtimolga ta'siri?
12. # prior tuzatish nima?
Javoblar
  1. Mutlaq son kam + ustma-ustlik + noto'g'ri metrika
  2. Musbat sinf nechta
  3. Chegara siljitish
  4. Validatsiyada
  5. n/(K·n_k)
  6. Overfitting (aynan nusxalar)
  7. Ma'lumot yo'qoladi
  8. Kam sinf nuqtalari orasida interpolyatsiya
  9. Faqat o'quvda, CV ichida
  10. Hech qachon
  11. Siljitadi
  12. Ehtimolni asl nisbatga qaytarish

Vazifa 2: Xatolarni tuzating

python
1.  X_bal, y_bal = smote.fit_resample(X, y); cross_val_score(m, X_bal, y_bal)

2.  print(model.score(X_te, y_te))   # 1.5% musbat sinf

3.  pred = model.predict(X_te)

4.  foyda = over_model.predict_proba(X)[:, 1] * 900000

5.  KFold(5, shuffle=True)   # 0.8% musbat sinf
Javoblar
python
1.  # resampling ni fit ichida (imblearn Pipeline)

2.  print(average_precision_score(y_te, proba))

3.  pred = (proba >= chegara).astype(int)

4.  # prior tuzatish yoki kalibrlash (14.10)

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

Vazifa 3: Qachon muammo

Modellang:

  1. Turli hajm va ulush
  2. Mutlaq son ta'siri
  3. PR AUC va baza
  4. Ustma-ustlik

Vazifa 4: Uch yondashuv

Modellang:

  1. Chegara siljitish
  2. class_weight
  3. Oversampling
  4. Taqqoslash

Vazifa 5: Kalibrlash

Modellang:

  1. Uch xil o'quv to'plami
  2. O'rtacha bashorat
  3. Kalibrlash egri chizig'i
  4. Prior tuzatish

Vazifa 6: Leakage

Modellang:

  1. CV dan tashqarida resampling
  2. Fit ichida
  3. Test natijasi
  4. Xulosa

Vazifa 7: O'ylash

SMOTE 2002-yilda taklif qilingan va bugun ham nomutanosiblik haqidagi har qanday qo'llanmada birinchi maslahat sifatida keltiriladi. Lekin so'nggi tadqiqotlar uning foydasi kichik ekanini ko'rsatmoqda. Nega noto'g'ri maslahat shunchalik uzoq yashaydi?

Javob

Qisqa javob: SMOTE ko'rinadigan natija beradi (CV metrikasi o'sadi), oson qo'llanadi va "muammoni hal qilyapmiz" hissini beradi. Uning foydasi ko'pincha noto'g'ri o'lchov (leakage) yoki chegara effekti bilan chalkashtiriladi.

1. Nega u yaxshi ko'rinadi

  • CV dan tashqarida qo'llansa — natija sun'iy oshadi (leakage)
  • F1 va accuracy o'sadi, chunki chegara amalda siljiydi
  • Vizual tushunarli: "sinflarni tenglashtirdik"
  • Ko'plab qo'llanmalarda takrorlanadi (sitatalar zanjiri)

2. Nima haqiqatan foyda beradi

Qadam Odatiy foyda
To'g'ri metrika (PR AUC, narx) Katta — qarorni to'g'rilaydi
Chegara tanlash Katta
class_weight O'rtacha
Ko'proq musbat ma'lumot Eng katta
SMOTE (to'g'ri qo'llangan) Kichik yoki nol

3. Qachon SMOTE oqlanadi

  • Juda kam musbat namuna (< 100) va past o'lcham
  • Modelda class_weight yo'q va chegara o'zgartirib bo'lmaydi
  • Tajriba bilan CV da to'g'ri o'lchanib, foyda tasdiqlangan

4. Umumiy saboq

  1. "Hamma shunday qiladi" — dalil emas
  2. Har maslahatni o'z ma'lumotingizda o'lchang
  3. Leakage natijani chiroyli qiladi — shubha qiling
  4. Sodda yechimlarni (chegara) avval sinang

5. Xulosa

  1. Afsona ko'rinadigan natijadan tug'iladi
  2. To'g'ri o'lchov ko'p maslahatni rad etadi
  3. Chegara — eng arzon va eng samarali
  4. Eng kuchli yechim — ko'proq ma'lumot

Nimani mustahkamlaydi: 2.4, 2.6-bo'limlar.


Xulosa

Bu darsda nomutanosib sinflar bilan ishlashni o'rgandik.

Eng muhim uch fikr:

  1. Ulush emas, mutlaq son. 1 000 000 namunada 1% musbat — bu 10 000 ta namuna, muammo yo'q; 1 000 namunada 1% — 10 ta, mana bu muammo. "Nomutanosiblik" deb atalgan narsa ko'pincha kam ma'lumot yoki noto'g'ri metrika muammosi bo'lib chiqadi 12.7-bob.

  2. Avval chegara. Chegara siljitish modelni o'zgartirmaydi, ma'lumotni buzmaydi va ehtimollarni saqlaydi — shuning uchun u birinchi qadam. Keyin class_weight="balanced", va faqat oxirida resampling. Amalda reyting sifati (ROC/PR AUC) bu uchala usulda deyarli bir xil qoladi — farq faqat qaror nuqtasida.

  3. Resampling ehtimolni buzadi va leakage manbai. Sinf nisbatini o'zgartirish bashoratlarni siljitadi (Brier yomonlashadi) — ehtimol kerak bo'lsa prior tuzatish yoki qayta kalibrlash zarur 14.10-bob. Va resampling har doim fit ichida, CV bo'lagi ichida bajarilishi kerak; test va validatsiya hech qachon balanslanmaydi.

Keyingi darsda kalibrlashni o'rganamiz: Platt sigmoid, izotonik regressiya, kalibrlash egri chiziqlari va qachon kalibrlash kerak.

Ulashish:Telegram'da

Izohlar (0)

Izoh yozish uchun kiring.

  • Hozircha izoh yo'q. Birinchi bo'ling!
14.9-dars: Nomutanosib sinflar — IlmHamroh