IlmHamroh
Data Science va sun'iy intellekt/Model baholash sozlash3/12-dars20 daqiqa
Mundarija (22)

18.3-dars: CV dispersiyasi va necha fold

18-QISM — MODEL BAHOLASH VA SOZLASH · 3-dars


1. Kirish va motivatsiya

cross_val_score bir massiv qaytaradi va deyarli hamma shunday yozadi:

python
print(f"{ball.mean():.3f} +- {ball.std():.3f}")

Bu qator noto'g'ri talqin qilinadi. Chiqqan std — foldlar orasidagi tarqoqlik, bahoning standart xatosi emas. Va std / sqrt(k) deb bo'lish ham noto'g'ri, chunki foldlar mustaqil emas: ular bir-biriga o'xshash o'quv to'plamlarini ishlatadi va xatolari korrelyatsiyali.

Ikkinchi savol — necha fold. 5 mi, 10 mi, 20 mi? Javob "ko'proq yaxshiroq" emas: fold soni ortgani sari bias kamayadi, lekin narx chiziqli o'sadi va bahoning dispersiyasi ma'lum nuqtadan keyin kamaymaydi.

Bu darsda: CV bahosining dispersiya manbalari, fold soni ta'siri, takroriy CV, nima uchun oddiy std/sqrt(k) ishlamaydi va CV natijasini to'g'ri hisobot qilish.

Real vaziyat. Maqolada "yangi usul 0.847, bazaviy 0.839, ya'ni 0.008 yaxshi" deyilgan. Sharhlovchi bir savol berdi: "CV ni 10 ta boshqa seed bilan takrorlang". Takrorlanganda bazaviy 0.841 ± 0.006, yangi usul 0.845 ± 0.007 chiqdi — farq shovqin ichida qoldi va maqola qayta yozildi.

Bu darsda CV dispersiyasini o'rganamiz.

Bu darsda:

  • Dispersiya manbalari
  • Nima uchun std/sqrt(k) noto'g'ri
  • Fold soni ta'siri
  • Takroriy CV
  • Bias-dispersiya muvozanati
  • To'g'ri hisobot
  • Tuzoqlar
  • Amaliy: ishonchli taqqoslash

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


2. Nazariya — chuqur tushuntirish

2.1. Dispersiya manbalari

text
CV bahosi uchta manbadan tebranadi:

1. MA'LUMOT tasodifiyligi
   sizdagi n qator - katta populyatsiyadan bitta namuna
   boshqa namunada natija boshqacha bo'lardi

2. BO'LINISH tasodifiyligi
   qaysi qator qaysi foldga tushdi (random_state)

3. MODEL tasodifiyligi
   RF, boosting, SGD, neyron tarmoq - ichki seed

TAKRORIY CV faqat 2-manbani kamaytiradi.
1-manba faqat ko'proq MA'LUMOT bilan kamayadi.

Takroriy CV bahoni "aniqroq" qilmaydi, u faqat bo'linish shovqinini kamaytiradi — ma'lumot namunasidan kelgan noaniqlik qoladi.

2.2. Nima uchun std/sqrt(k) noto'g'ri

text
Mustaqil o'lchovlar uchun:  SE = std / sqrt(k)

LEKIN CV foldlari MUSTAQIL EMAS:
  5-fold da har ikki o'quv to'plami qatorlarining
  ~75% ini BAHAM ko'radi -> fold ballari korrelyatsiyali
  ya'ni formulaning SHARTI buzilgan

IKKI BOSHQA NARSA BOR:
  A. bo'linish shovqini - "boshqa CV seed olsam?"
     odatda KICHIK (takroriy CV uni yanada kamaytiradi)
  B. ma'lumot shovqini - "boshqa ma'lumot yig'sam?"
     hisobotdagi ishonch oralig'i AYNAN shuni bildirishi kerak

std/sqrt(k) na A ni, na B ni o'lchaydi:
  ba'zan B ga yaqin chiqadi, ba'zan ikki barobar adashadi
  (Bengio & Grandvalet 2004: k-fold CV dispersiyasining
   xolis baholovchisi UMUMAN mavjud emas)

TO'G'RI YO'L:
  - alohida test to'plamining SE si (eng ishonchli)
  - takroriy CV -> takrorlar orasidagi tarqoqlik (A uchun)
  - konservativ: fold std ning o'zini ishlating

std / sqrt(k) — ishonch oralig'i emas: uning shartlari buzilgan va u qaysi noaniqlikni o'lchayotgani aniq emas.

2.3. Fold soni ta'siri

text
k kichik (2-3):
  o'quv to'plami kichik -> model zaifroq -> baho PESSIMISTIK
  foldlar mustaqilroq

k katta (10-20):
  o'quv to'plami to'liqqa yaqin -> bias kichik
  foldlar juda o'xshash -> korrelyatsiya yuqori
  narx k barobar

k = n (LeaveOneOut):
  bias eng kichik, dispersiya eng katta, narx eng yuqori

AMALIYOT:
  n > 5000  ->  k = 5 yetarli
  n 1000-5000 -> k = 5 yoki 10
  n < 1000  ->  k = 10 + takrorlash

k = 5 amaliyotda deyarli har doim yetarli: 10 ga oshirish bias ni biroz kamaytiradi, lekin narxni ikki barobar oshiradi.

2.4. Takroriy CV

text
RepeatedStratifiedKFold(n_splits=5, n_repeats=R)

Har takror - boshqa aralashtirish:
  R=1   ->  5 ta o'lchov, bahoning std i katta
  R=5   ->  25 ta o'lchov
  R=10  ->  50 ta o'lchov

BAHONING std i taxminan 1/sqrt(R) kabi kamayadi
  (lekin ma'lumot shovqiniga BORIB TO'XTAYDI)

TAKRORLAR o'rtachalari orasidagi std - bo'linish
shovqinining to'g'ri o'lchovi

Takrorlar o'rtachalarining tarqoqligi — "agar boshqa seed olsam, natija qancha o'zgarardi?" degan savolga to'g'ri javob.

2.5. Bias-dispersiya muvozanati

text
                 bias        dispersiya    narx
  k = 2          yuqori      o'rta         1x
  k = 5          o'rta       o'rta         2.5x
  k = 10         past        o'rta         5x
  k = n (LOO)    eng past    yuqori        n/2 x

MA'LUMOT KAM bo'lsa bias muhimroq -> k kattaroq
MA'LUMOT KO'P bo'lsa bias kichik  -> k = 5

ESLATMA: o'rganish egri chizig'i tekis bo'lsa
         (18.8-dars), k ning ta'siri deyarli yo'q

k ning ta'siri o'rganish egri chizig'iga bog'liq: model 80% ma'lumotda 90% dagidek ishlasa, k = 5 va k = 10 farq qilmaydi.

2.6. To'g'ri hisobot

text
YOZING:
  - strategiya: StratifiedKFold(5, shuffle=True, random_state=0)
  - takrorlar: 5
  - o'rtacha va TAKRORLAR orasidagi std
  - alohida test natijasi (bor bo'lsa)
  - sinalgan nomzodlar soni

YOZMANG:
  "AUC 0.8472 +- 0.0031"   (std/sqrt(k) dan olingan)

YAXSHIROQ:
  "5x5 takroriy CV: 0.847 (takrorlar bo'yicha std 0.006),
   alohida testda 0.843"

Raqamni uch xonagacha yaxlitlang: CV bahosi to'rtinchi xonagacha aniq emas, 0.8472 yozish soxta aniqlik beradi.

2.7. Tuzoqlar

Asosiy tuzoqlar: std/sqrt(k) ni SE deb ishlatish; foldlar orasidagi std ni bahoning noaniqligi deb o'ylash; bitta CV natijasiga asoslanib qaror qabul qilish; k ni "ko'proq yaxshiroq" deb oshirish; takroriy CV ni ma'lumot shovqinini ham kamaytiradi deb o'ylash; to'rt xonali aniqlik e'lon qilish; turli seed li natijalarni taqqoslash.

2.8. Ikki raqam: o'rtacha va noaniqlik

CV natijasi bitta raqam emas — u o'rtacha va noaniqlik juftligi. Noaniqlikni to'g'ri o'lchash uchun takroriy CV ishlating va takrorlar o'rtachalari orasidagi tarqoqlikni yozing; foldlar korrelyatsiyali bo'lgani uchun std/sqrt(k) haqiqiy noaniqlikni past ko'rsatadi. k = 5 ko'p hollarda yetarli; kichik ma'lumotda k = 10 va takrorlash. Ikki modelni taqqoslashda farq noaniqlikdan katta bo'lishi shart.


3. Tez ma'lumotnoma

python
import numpy as np
from sklearn.model_selection import RepeatedStratifiedKFold, cross_val_score

cv = RepeatedStratifiedKFold(n_splits=5, n_repeats=5, random_state=0)
ball = cross_val_score(model, X, y, cv=cv, scoring="roc_auc")

# takrorlar bo'yicha o'rtachalar (5 ta)
takrorlar = ball.reshape(5, 5).mean(axis=1)
print(f"{ball.mean():.3f}, takrorlar std: {takrorlar.std():.4f}")

# NOTO'G'RI: ball.std() / np.sqrt(len(ball))
# TO'G'RI:   takrorlar.std()  yoki konservativ  ball.std()
QOIDA: takroriy CV · takrorlar std i · uch xona ·
       farq noaniqlikdan katta bo'lsin

CV dispersiyasi xulosasi

Dispersiya manbalari: ma'lumot, bo'linish, model
Takroriy CV faqat bo'linish shovqinini kamaytiradi
std/sqrt(k) - noto'g'ri (foldlar korrelyatsiyali)
k = 5 odatda yetarli; kichik ma'lumotda 10 + takror

4. Batafsil misollar

Misollar real numpy/sklearn bilan (Python 3.14).

Misol 1 — Fold soni nimaga ta'sir qiladi

python
"""k ning bias, dispersiya va narxga ta'siri (real numpy/sklearn)."""

import numpy as np
from sklearn.datasets import make_classification
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import roc_auc_score
from sklearn.model_selection import StratifiedKFold, cross_val_score
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import StandardScaler


def model():
    return make_pipeline(StandardScaler(),
                         LogisticRegression(max_iter=2000))


def main() -> None:
    X, y = make_classification(n_samples=21000, n_features=20,
                               n_informative=6, n_redundant=4,
                               flip_y=0.15, class_sep=0.9, random_state=0)
    X_ish, y_ish = X[:1000], y[:1000]          # mavjud ma'lumot
    X_haq, y_haq = X[1000:], y[1000:]          # 'haqiqat' to'plami

    m = model().fit(X_ish, y_ish)
    haqiqiy = roc_auc_score(y_haq, m.predict_proba(X_haq)[:, 1])
    print("=== 1. Sozlama ===")
    print(f"  mavjud: {len(y_ish)} qator, 'haqiqat': {len(y_haq)} qator")
    print(f"  to'liq ma'lumotda o'rgatilgan model: {haqiqiy:.4f}")

    print("\n=== 2. Fold soni va baho ===")
    print(f"  {'k':>4} {'CV o_rtacha':>12} {'fold std':>10} "
          f"{'haqiqiydan farq':>17} {'modellar':>9}")
    for k in [2, 3, 5, 10, 20]:
        b = cross_val_score(model(), X_ish, y_ish,
                            cv=StratifiedKFold(k, shuffle=True,
                                               random_state=0),
                            scoring="roc_auc")
        print(f"  {k:>4} {b.mean():>12.4f} {b.std():>10.4f} "
              f"{b.mean() - haqiqiy:>+17.4f} {k:>9}")
    print("  k kichik -> o'quv to'plami kichik -> baho pastroq (bias)")

    print("\n=== 3. Bahoning barqarorligi (20 ta seed) ===")
    print(f"  {'k':>4} {'baho o_rtachasi':>16} {'bahoning std i':>16}")
    for k in [2, 3, 5, 10, 20]:
        ballar = [cross_val_score(model(), X_ish, y_ish,
                                  cv=StratifiedKFold(k, shuffle=True,
                                                     random_state=s),
                                  scoring="roc_auc").mean()
                  for s in range(20)]
        print(f"  {k:>4} {np.mean(ballar):>16.4f} {np.std(ballar):>16.5f}")
    print("  k oshgani sari bahoning tebranishi kamayadi, lekin")
    print("  ma'lum nuqtadan keyin to'xtaydi")

    print("\n=== 4. Ma'lumot hajmi k ning ahamiyatini kamaytiradi ===")
    print(f"  {'n':>7} {'k=2':>9} {'k=5':>9} {'k=10':>9} {'k=2 va k=10':>13}")
    for n in [300, 1000, 4000]:
        Xn, yn = X[:n], y[:n]
        ballar = {}
        for k in [2, 5, 10]:
            ballar[k] = cross_val_score(model(), Xn, yn,
                                        cv=StratifiedKFold(k, shuffle=True,
                                                           random_state=0),
                                        scoring="roc_auc").mean()
        print(f"  {n:>7} {ballar[2]:>9.4f} {ballar[5]:>9.4f} "
              f"{ballar[10]:>9.4f} {ballar[10] - ballar[2]:>+13.4f}")
    print("  ⭐ k = 5 amaliyotda deyarli har doim yetarli")


if __name__ == "__main__":
    main()

Natijaning muhim qismi:

text
=== 1. Sozlama ===
  mavjud: 1000 qator, 'haqiqat': 20000 qator
  to'liq ma'lumotda o'rgatilgan model: 0.8280

=== 2. Fold soni va baho ===
     k  CV o_rtacha   fold std   haqiqiydan farq  modellar
     2       0.8372     0.0042           +0.0091         2
     3       0.8315     0.0056           +0.0034         3
     5       0.8279     0.0158           -0.0001         5
    10       0.8291     0.0266           +0.0010        10
    20       0.8291     0.0513           +0.0011        20
  k kichik -> o'quv to'plami kichik -> baho pastroq (bias)

=== 3. Bahoning barqarorligi (20 ta seed) ===
     k  baho o_rtachasi   bahoning std i
     2           0.8261          0.00558
     3           0.8286          0.00324
     5           0.8295          0.00239
    10           0.8299          0.00207
    20           0.8303          0.00242
  k oshgani sari bahoning tebranishi kamayadi, lekin
  ma'lum nuqtadan keyin to'xtaydi

=== 4. Ma'lumot hajmi k ning ahamiyatini kamaytiradi ===
        n       k=2       k=5      k=10   k=2 va k=10
      300    0.8025    0.8334    0.8416       +0.0391
     1000    0.8372    0.8279    0.8291       -0.0081
     4000    0.8288    0.8312    0.8327       +0.0040
  ⭐ k = 5 amaliyotda deyarli har doim yetarli

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

Misol 2 — std/sqrt(k) nima uchun noto'g'ri

python
"""Uch xil 'noaniqlik' va naiv formula (real numpy/sklearn)."""

import numpy as np
from sklearn.datasets import make_classification
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import StratifiedKFold, cross_val_score
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import StandardScaler


def model():
    return make_pipeline(StandardScaler(),
                         LogisticRegression(max_iter=2000))


N = 800          # bitta ma'lumot to'plamining hajmi


def main() -> None:
    # katta "populyatsiya", undan mustaqil namunalar olamiz
    X_pool, y_pool = make_classification(n_samples=60000, n_features=20,
                                         n_informative=6, n_redundant=4,
                                         flip_y=0.15, class_sep=0.9,
                                         random_state=0)
    rng = np.random.default_rng(0)
    X, y = X_pool[:N], y_pool[:N]

    print("=== 1. Bitta CV natijasi ===")
    b = cross_val_score(model(), X, y,
                        cv=StratifiedKFold(5, shuffle=True, random_state=0),
                        scoring="roc_auc")
    print(f"  fold ballari: {np.round(b, 4).tolist()}")
    print(f"  o'rtacha: {b.mean():.4f}")
    print(f"  fold std: {b.std(ddof=1):.4f}")
    naiv = float(b.std(ddof=1) / np.sqrt(5))
    print(f"  naiv 'SE' = std/sqrt(5): {naiv:.4f}")

    print("\n=== 2. A: faqat BO'LINISH shovqini ===")
    bolinish = np.array([cross_val_score(
        model(), X, y,
        cv=StratifiedKFold(5, shuffle=True, random_state=s),
        scoring="roc_auc").mean() for s in range(150)])
    print(f"  bitta ma'lumot, 150 ta turli CV seed")
    print(f"  bahoning std i: {bolinish.std(ddof=1):.4f}")
    print("  bu FAQAT 'boshqa seed olsam nima bo'lardi' savoliga javob")

    print("\n=== 3. B: MA'LUMOT namunasi shovqini ===")
    namunalar = []
    for _ in range(150):
        idx = rng.choice(len(y_pool), N, replace=False)
        namunalar.append(cross_val_score(
            model(), X_pool[idx], y_pool[idx],
            cv=StratifiedKFold(5, shuffle=True, random_state=0),
            scoring="roc_auc").mean())
    namunalar = np.array(namunalar)
    print(f"  150 ta MUSTAQIL ma'lumot to'plami (n={N})")
    print(f"  bahoning std i: {namunalar.std(ddof=1):.4f}")
    print("  bu 'boshqa ma'lumot yig'sam nima bo'lardi' savoliga javob")
    print("  hisobotdagi ishonch oralig'i AYNAN shuni aks ettirishi kerak")

    print("\n=== 4. Uch raqamni taqqoslash ===")
    print(f"  {'usul':<38} {'qiymat':>9}")
    print(f"  {'naiv std/sqrt(k)':<38} {naiv:>9.4f}")
    print(f"  {'A: bo_linish shovqini':<38} "
          f"{bolinish.std(ddof=1):>9.4f}")
    print(f"  {'B: ma_lumot shovqini (MUHIMI)':<38} "
          f"{namunalar.std(ddof=1):>9.4f}")
    print(f"  {'fold std (konservativ)':<38} {b.std(ddof=1):>9.4f}")
    print(f"  naiv / B nisbati: {naiv / namunalar.std(ddof=1):.2f}")
    print("  naiv formula A ni ham, B ni ham o'lchamaydi -")
    print("  u shunchaki BOSHQA kattalik va unga ishonib bo'lmaydi")

    print("\n=== 5. Ma'lumot hajmi ortganda ===")
    print(f"  {'n':>6} {'naiv SE':>9} {'B (haqiqiy)':>13} "
          f"{'naiv/B':>8}")
    for n in [400, 800, 1600]:
        Xn, yn = X_pool[:n], y_pool[:n]
        bn = cross_val_score(model(), Xn, yn,
                             cv=StratifiedKFold(5, shuffle=True,
                                                random_state=0),
                             scoring="roc_auc")
        naiv_n = float(bn.std(ddof=1) / np.sqrt(5))
        ballar = []
        for _ in range(80):
            idx = rng.choice(len(y_pool), n, replace=False)
            ballar.append(cross_val_score(
                model(), X_pool[idx], y_pool[idx],
                cv=StratifiedKFold(5, shuffle=True, random_state=0),
                scoring="roc_auc").mean())
        haqiqiy_n = float(np.std(ballar, ddof=1))
        print(f"  {n:>6} {naiv_n:>9.4f} {haqiqiy_n:>13.4f} "
              f"{naiv_n / haqiqiy_n:>7.2f}")
    print("  ⭐ std/sqrt(k) ni ishonch oralig'i sifatida yozmang")


if __name__ == "__main__":
    main()

Natijaning muhim qismi:

text
=== 1. Bitta CV natijasi ===
  fold ballari: [0.7729, 0.8436, 0.8339, 0.7731, 0.8336]
  o'rtacha: 0.8114
  fold std: 0.0353
  naiv 'SE' = std/sqrt(5): 0.0158

=== 2. A: faqat BO'LINISH shovqini ===
  bitta ma'lumot, 150 ta turli CV seed
  bahoning std i: 0.0034
  bu FAQAT 'boshqa seed olsam nima bo'lardi' savoliga javob

=== 3. B: MA'LUMOT namunasi shovqini ===
  150 ta MUSTAQIL ma'lumot to'plami (n=800)
  bahoning std i: 0.0169
  bu 'boshqa ma'lumot yig'sam nima bo'lardi' savoliga javob
  hisobotdagi ishonch oralig'i AYNAN shuni aks ettirishi kerak

=== 4. Uch raqamni taqqoslash ===
  usul                                      qiymat
  naiv std/sqrt(k)                          0.0158
  A: bo_linish shovqini                     0.0034
  B: ma_lumot shovqini (MUHIMI)             0.0169
  fold std (konservativ)                    0.0353
  naiv / B nisbati: 0.93
  naiv formula A ni ham, B ni ham o'lchamaydi -
  u shunchaki BOSHQA kattalik va unga ishonib bo'lmaydi

=== 5. Ma'lumot hajmi ortganda ===
       n   naiv SE   B (haqiqiy)   naiv/B
     400    0.0157        0.0224    0.70
     800    0.0158        0.0133    1.18
    1600    0.0180        0.0105    1.71
  ⭐ std/sqrt(k) ni ishonch oralig'i sifatida yozmang

Nima ko'rsatdi: 2.2-bo'lim.

Misol 3 — Takroriy CV

python
"""Takrorlar sonining bahoga ta'siri (real numpy/sklearn)."""

import numpy as np
from sklearn.datasets import make_classification
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import RepeatedStratifiedKFold, cross_val_score
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import StandardScaler


def model():
    return make_pipeline(StandardScaler(),
                         LogisticRegression(max_iter=2000))


def main() -> None:
    X, y = make_classification(n_samples=400, n_features=18, n_informative=6,
                               flip_y=0.15, class_sep=0.95, random_state=0)
    print("=== 1. Kichik ma'lumot ===")
    print(f"  {len(y)} qator, {X.shape[1]} belgi")

    print("\n=== 2. Takrorlar soni va bahoning barqarorligi ===")
    print(f"  {'R':>4} {'o_lchovlar':>11} {'baho':>9} "
          f"{'bahoning std i':>16} {'narx':>7}")
    for R in [1, 2, 5, 10, 20]:
        ballar = []
        for s in range(15):
            cv = RepeatedStratifiedKFold(n_splits=5, n_repeats=R,
                                         random_state=s)
            ballar.append(cross_val_score(model(), X, y, cv=cv,
                                          scoring="roc_auc").mean())
        print(f"  {R:>4} {5 * R:>11} {np.mean(ballar):>9.4f} "
              f"{np.std(ballar, ddof=1):>16.5f} {5 * R:>5}x")
    print("  std ~ 1/sqrt(R) kabi kamayadi, lekin nolga tushmaydi")

    print("\n=== 3. Takrorlar orasidagi tarqoqlik ===")
    R = 10
    cv = RepeatedStratifiedKFold(n_splits=5, n_repeats=R, random_state=0)
    b = cross_val_score(model(), X, y, cv=cv, scoring="roc_auc")
    takrorlar = b.reshape(R, 5).mean(axis=1)
    print(f"  jami o'lchov: {len(b)}")
    print(f"  takror o'rtachalari: {np.round(takrorlar[:5], 4).tolist()} ...")
    print(f"  umumiy o'rtacha: {b.mean():.4f}")
    print(f"  fold std (barcha 50 o'lchov): {b.std(ddof=1):.4f}")
    print(f"  TAKRORLAR std i: {takrorlar.std(ddof=1):.4f}   <- to'g'ri o'lchov")

    print("\n=== 4. Ikki modelni taqqoslash ===")
    modellar = {
        "C=0.03": make_pipeline(StandardScaler(),
                                LogisticRegression(C=0.03, max_iter=2000)),
        "C=1.0": make_pipeline(StandardScaler(),
                               LogisticRegression(C=1.0, max_iter=2000)),
    }
    cv = RepeatedStratifiedKFold(n_splits=5, n_repeats=10, random_state=0)
    natija = {}
    for nom, m in modellar.items():
        bb = cross_val_score(m, X, y, cv=cv, scoring="roc_auc")
        natija[nom] = bb.reshape(10, 5).mean(axis=1)
        print(f"  {nom:<8} {bb.mean():.4f} "
              f"(takrorlar std {natija[nom].std(ddof=1):.4f})")
    farq = natija["C=1.0"] - natija["C=0.03"]
    print(f"  juftlashgan farq: {farq.mean():+.4f} "
          f"(std {farq.std(ddof=1):.4f})")
    muhim = abs(farq.mean()) > 2 * farq.std(ddof=1) / np.sqrt(len(farq))
    print(f"  farq shovqindan katta: {muhim}")
    print("  ⭐ Juftlashgan farq alohida o'rtachalardan ishonchliroq")


if __name__ == "__main__":
    main()

Natijaning muhim qismi:

text
=== 1. Kichik ma'lumot ===
  400 qator, 18 belgi

=== 2. Takrorlar soni va bahoning barqarorligi ===
     R  o_lchovlar      baho   bahoning std i    narx
     1           5    0.7947          0.00805     5x
     2          10    0.7940          0.00520    10x
     5          25    0.7919          0.00362    25x
    10          50    0.7905          0.00198    50x
    20         100    0.7905          0.00173   100x
  std ~ 1/sqrt(R) kabi kamayadi, lekin nolga tushmaydi

=== 3. Takrorlar orasidagi tarqoqlik ===
  jami o'lchov: 50
  takror o'rtachalari: [0.8049, 0.7979, 0.7992, 0.7809, 0.7985] ...
  umumiy o'rtacha: 0.7941
  fold std (barcha 50 o'lchov): 0.0492
  TAKRORLAR std i: 0.0066   <- to'g'ri o'lchov

=== 4. Ikki modelni taqqoslash ===
  C=0.03   0.7822 (takrorlar std 0.0058)
  C=1.0    0.7941 (takrorlar std 0.0066)
  juftlashgan farq: +0.0119 (std 0.0030)
  farq shovqindan katta: True
  ⭐ Juftlashgan farq alohida o'rtachalardan ishonchliroq

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

Misol 4 — Dispersiya manbalarini ajratish

python
"""Ma'lumot, bo'linish va model shovqini (real numpy/sklearn)."""

import numpy as np
from sklearn.datasets import make_classification
from sklearn.ensemble import RandomForestClassifier
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import StratifiedKFold, cross_val_score
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import StandardScaler


def yarat(seed: int, n: int = 600):
    return make_classification(n_samples=n, n_features=18, n_informative=6,
                               n_redundant=4, flip_y=0.15, class_sep=0.9,
                               random_state=seed)


def main() -> None:
    print("=== 1. Manba A: MA'LUMOT namunasi ===")
    ballar = []
    for seed in range(30):
        X, y = yarat(seed)
        m = make_pipeline(StandardScaler(),
                          LogisticRegression(max_iter=2000))
        ballar.append(cross_val_score(
            m, X, y, cv=StratifiedKFold(5, shuffle=True, random_state=0),
            scoring="roc_auc").mean())
    manba_a = float(np.std(ballar, ddof=1))
    print(f"  30 ta turli ma'lumot to'plami (CV seed qat'iy)")
    print(f"  bahoning std i: {manba_a:.4f}")

    print("\n=== 2. Manba B: BO'LINISH tasodifiyligi ===")
    X, y = yarat(0)
    ballar = []
    for s in range(30):
        m = make_pipeline(StandardScaler(),
                          LogisticRegression(max_iter=2000))
        ballar.append(cross_val_score(
            m, X, y, cv=StratifiedKFold(5, shuffle=True, random_state=s),
            scoring="roc_auc").mean())
    manba_b = float(np.std(ballar, ddof=1))
    print(f"  bitta ma'lumot, 30 ta turli CV seed")
    print(f"  bahoning std i: {manba_b:.4f}")

    print("\n=== 3. Manba C: MODEL tasodifiyligi ===")
    ballar = []
    for s in range(30):
        m = RandomForestClassifier(n_estimators=100, min_samples_leaf=3,
                                   random_state=s, n_jobs=1)
        ballar.append(cross_val_score(
            m, X, y, cv=StratifiedKFold(5, shuffle=True, random_state=0),
            scoring="roc_auc").mean())
    manba_c = float(np.std(ballar, ddof=1))
    print(f"  bitta ma'lumot, bitta CV seed, 30 ta model seed")
    print(f"  bahoning std i: {manba_c:.4f}")

    print("\n=== 4. Taqqoslash va xulosa ===")
    print(f"  {'manba':<28} {'std':>9} {'kamaytirish usuli':<26}")
    print(f"  {'A. ma_lumot namunasi':<28} {manba_a:>9.4f} "
          f"{'ko_proq ma_lumot':<26}")
    print(f"  {'B. bo_linish (CV seed)':<28} {manba_b:>9.4f} "
          f"{'takroriy CV':<26}")
    print(f"  {'C. model seed (RF)':<28} {manba_c:>9.4f} "
          f"{'kop daraxt / seed o_rtacha':<26}")
    eng = max([("A", manba_a), ("B", manba_b), ("C", manba_c)],
              key=lambda kv: kv[1])
    print(f"  eng katta manba: {eng[0]} ({eng[1]:.4f})")
    print("  takroriy CV faqat B ni kamaytiradi")
    print("  ⭐ Eng katta manbani bilmasangiz, noto'g'ri narsani tuzatasiz")


if __name__ == "__main__":
    main()

Natijaning muhim qismi:

text
=== 1. Manba A: MA'LUMOT namunasi ===
  30 ta turli ma'lumot to'plami (CV seed qat'iy)
  bahoning std i: 0.0701

=== 2. Manba B: BO'LINISH tasodifiyligi ===
  bitta ma'lumot, 30 ta turli CV seed
  bahoning std i: 0.0036

=== 3. Manba C: MODEL tasodifiyligi ===
  bitta ma'lumot, bitta CV seed, 30 ta model seed
  bahoning std i: 0.0027

=== 4. Taqqoslash va xulosa ===
  manba                              std kamaytirish usuli
  A. ma_lumot namunasi            0.0701 ko_proq ma_lumot
  B. bo_linish (CV seed)          0.0036 takroriy CV
  C. model seed (RF)              0.0027 kop daraxt / seed o_rtacha
  eng katta manba: A 0.0701-bob
  takroriy CV faqat B ni kamaytiradi
  ⭐ Eng katta manbani bilmasangiz, noto'g'ri narsani tuzatasiz

Nima ko'rsatdi: 2.1-bo'lim.


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

Noto'g'ri fikr To'g'risi
"std/sqrt(k) — SE" Formulaning sharti buzilgan
"Fold std — bahoning noaniqligi" Foldlar orasidagi tarqoqlik
"Ko'proq fold — aniqroq baho" 5 dan keyin foyda kam
"LOO eng aniq" Dispersiyasi yuqori
"Takroriy CV noaniqlikni yo'qotadi" Faqat bo'linish shovqinini
"0.8472 — aniq raqam" Uch xona yetarli
"Bitta CV yetarli" Kichik ma'lumotda yo'q
"Turli seed li natijalar taqqoslanadi" Bir xil bo'linish kerak

6. Keng tarqalgan xatolar va yechimlari

1. Naiv standart xato

python
print(f"{b.mean():.4f} +- {b.std()/np.sqrt(len(b)):.4f}")        # ⚠️
print(f"{b.mean():.3f} (takrorlar std {takrorlar.std():.3f})")   # ✅

2. Bitta CV ga asoslanish

python
ball = cross_val_score(m, X, y, cv=5).mean()                     # ⚠️
cv = RepeatedStratifiedKFold(n_splits=5, n_repeats=5,
                             random_state=0)                     # ✅

3. Fold sonini oshirish bilan "aniqlik" izlash

python
cross_val_score(m, X, y, cv=50)                                  # ⚠️
cross_val_score(m, X, y, cv=RepeatedStratifiedKFold(5, 10))      # ✅

4. Turli seed li natijalarni taqqoslash

python
a = cross_val_score(m1, X, y, cv=KFold(5, shuffle=True))
b = cross_val_score(m2, X, y, cv=KFold(5, shuffle=True))         # ⚠️
CV = KFold(5, shuffle=True, random_state=0)  # ikkalasiga ham    # ✅

5. Soxta aniqlik

python
print(f"AUC: {ball.mean():.6f}")                                 # ⚠️
print(f"AUC: {ball.mean():.3f}")                                 # ✅

6. Model seed ini e'tiborsiz qoldirish

python
RandomForestClassifier()            # har safar boshqa natija    # ⚠️
RandomForestClassifier(random_state=0)                           # ✅

7. Farqni noaniqliksiz e'lon qilish

python
print("Yangi model 0.008 ga yaxshi")                             # ⚠️
print(f"farq {d.mean():+.3f}, takrorlar std {d.std():.3f}")      # ✅

7. Integratsiya — bu bilim qayerda kerak bo'ladi

  • 18.1-dars (o'tilgan): Baholash dizayni
  • 18.2-dars (o'tilgan): CV turlari
  • 18.4-dars: Nested CV
  • 18.10-dars: Modellarni taqqoslash
  • 18.11-dars: Validatsiyaga overfitting

8. Eng yaxshi amaliyotlar

  1. Takroriy CV ishlating.

  2. Takrorlar std ini yozing.

  3. std/sqrt(k) ni ishlatmang.

  4. k = 5 dan boshlang.

  5. Bir xil bo'linishda taqqoslang.

  6. Model random_state ini qo'ying.

  7. Uch xonagacha yaxlitlang.

  8. Farqni noaniqlik bilan birga bering.


9. Amaliy topshiriq

Vazifa 1: Bashorat qiling

python
1.  # dispersiyaning uch manbasi?
2.  # takroriy CV qaysi manbani kamaytiradi?
3.  # nima uchun std/sqrt(k) noto'g'ri?
4.  # k kichik bo'lsa baho qanday?
5.  # k = n nima deyiladi?
6.  # amaliyotda qaysi k?
7.  # R oshsa std qanday kamayadi?
8.  # takrorlar std i nimani o'lchaydi?
9.  # necha xonagacha yaxlitlash?
10. # ikki modelni qanday taqqoslash?
11. # juftlashgan farq nima uchun yaxshi?
12. # model seed qachon muhim?
Javoblar
  1. Ma'lumot, bo'linish, model
  2. Bo'linish
  3. Foldlar korrelyatsiyali — formula sharti buzilgan
  4. Pessimistik (bias)
  5. LeaveOneOut
  6. 5
  7. ~`1/sqrt(R)`
  8. Bo'linish shovqinini
  9. Uch
  10. Bir xil bo'linishda, juftlashgan
  11. Bo'linish shovqini o'zaro qisqaradi
  12. RF, boosting, SGD, neyron tarmoq

Vazifa 2: Xatolarni tuzating

python
1.  print(f"{b.mean():.4f} +- {b.std()/np.sqrt(len(b)):.4f}")

2.  ball = cross_val_score(m, X, y, cv=5).mean()

3.  cross_val_score(m, X, y, cv=50)

4.  a = cross_val_score(m1, X, y, cv=KFold(5, shuffle=True))
    b = cross_val_score(m2, X, y, cv=KFold(5, shuffle=True))

5.  print("Yangi model 0.008 ga yaxshi")
Javoblar
python
1.  print(f"{b.mean():.3f} (takrorlar std {takrorlar.std():.3f})")

2.  cv = RepeatedStratifiedKFold(n_splits=5, n_repeats=5, random_state=0)

3.  cross_val_score(m, X, y, cv=RepeatedStratifiedKFold(5, 10))

4.  CV = KFold(5, shuffle=True, random_state=0)   # ikkalasiga ham

5.  print(f"farq {d.mean():+.3f}, takrorlar std {d.std():.3f}")

Vazifa 3: Fold soni

Modellang:

  1. Sozlama
  2. k va baho
  3. Barqarorlik
  4. Ma'lumot hajmi

Vazifa 4: Naiv SE

Modellang:

  1. Bitta CV
  2. Haqiqiy tebranish
  3. Taqqoslash
  4. k bo'yicha

Vazifa 5: Takroriy CV

Modellang:

  1. Ma'lumot
  2. R va barqarorlik
  3. Takrorlar tarqoqligi
  4. Ikki model

Vazifa 6: Manbalar

Modellang:

  1. Ma'lumot
  2. Bo'linish
  3. Model
  4. Taqqoslash

Vazifa 7: O'ylash

Hamkasbingiz aytdi: "CV da 5 ta fold 0.84, 0.86, 0.83, 0.87, 0.85 chiqdi. Demak model 0.85 ± 0.008 (std/sqrt(5)). Bizning maqsadimiz 0.84 edi, shuning uchun maqsadga yetdik." Qaysi joyda xato bor?

Javob

Qisqa javob: ikkita xato bor — noto'g'ri SE va noto'g'ri savol.

1. std/sqrt(5) — noto'g'ri

Fold ballari: 0.84, 0.86, 0.83, 0.87, 0.85. Ularning std i ≈ 0.016, std/sqrt(5) ≈ 0.007.

Lekin 5-fold CV da har ikki o'quv to'plami qatorlarning 75% ini baham ko'radi. Bu foldlar mustaqil emas, ya'ni markaziy limit teoremasining sharti buzilgan. Haqiqiy tebranish (turli random_state bilan) odatda bu bahodan 2-3 barobar katta chiqadi — bu holda ~0.015-0.020.

2. "Maqsadga yetdik" — noto'g'ri savol

Savol Javob
"CV bahosi 0.84 dan katta bo'ldimi?" Ha, lekin bu muhim emas
"Model ishlab chiqarishda 0.84 beradimi?" Noma'lum

CV bahosi — o'tmishdagi ma'lumot bo'yicha va sozlash qilingan bo'lsa optimistik. Ishlab chiqarish natijasi odatda pastroq.

3. To'g'ri yondashuv

python
# 1. takroriy CV
cv = RepeatedStratifiedKFold(n_splits=5, n_repeats=10, random_state=0)
b = cross_val_score(model, X_ish, y_ish, cv=cv, scoring="roc_auc")
takrorlar = b.reshape(10, 5).mean(axis=1)
print(f"{b.mean():.3f} (takrorlar std {takrorlar.std(ddof=1):.3f})")

# 2. alohida test to'plamida tasdiqlash (bir marta)
test_ball = roc_auc_score(y_test, model.fit(X_ish, y_ish)
                          .predict_proba(X_test)[:, 1])

4. Qanday hisobot qilish kerak

"5×10 takroriy CV: 0.851, takrorlar bo'yicha std 0.014. Alohida test to'plamida (n=4000): 0.846. Sinalgan nomzodlar: 14 ta. Maqsad 0.84 — test natijasi undan yuqori, lekin farq 0.006-bob test SE sidan (≈0.008) kichik, shuning uchun maqsadga yetilganini ishonchli deb aytib bo'lmaydi."

5. Xulosa

  1. std/sqrt(k) ni ishlatmang
  2. Takroriy CV va takrorlar std i
  3. Yakuniy raqamni alohida testdan oling
  4. Maqsad bilan taqqoslashda ham SE ni hisobga oling

Nimani mustahkamlaydi: 2.2, 2.6-bo'limlar.


Xulosa

Bu darsda CV bahosining dispersiyasini o'rgandik.

Eng muhim uch fikr:

  1. std / sqrt(k) — standart xato emas. CV foldlari o'quv ma'lumotining katta qismini baham ko'radi, shuning uchun ballari korrelyatsiyali va formulaning sharti buzilgan. Amalda u ikki boshqa kattalikning hech birini o'lchamaydi: bo'linish shovqinidan bir necha barobar katta, ma'lumot shovqiniga esa ba'zan yaqin, ba'zan ikki barobar uzoq. Ishonchli raqam alohida test to'plamidan, bo'linish shovqini esa takroriy CV dan olinadi.

  2. Dispersiyaning uch manbasi bor: ma'lumot, bo'linish, model. Takroriy CV faqat bo'linish shovqinini kamaytiradi; ma'lumot namunasidan kelgan noaniqlik faqat ko'proq ma'lumot bilan kamayadi. Shuning uchun n_repeats ni cheksiz oshirish bahoni "aniq" qilmaydi — u ma'lum chegaraga borib to'xtaydi.

  3. k = 5 dan boshlang, kichik ma'lumotda takrorlang. Fold sonini oshirish bias ni kamaytiradi, lekin narxni chiziqli oshiradi va ma'lum nuqtadan keyin bahoni barqarorlashtirmaydi. Ikki modelni taqqoslashda bir xil bo'linish va juftlashgan farq ishlating — shunda bo'linish shovqini o'zaro qisqaradi.

Keyingi darsda nested cross-validationni o'rganamiz: giperparametr sozlash CV bahosini qanchalik optimistik qiladi va bu optimizmdan qanday qutulish mumkin.

Ulashish:Telegram'da

Izohlar (0)

Izoh yozish uchun kiring.

  • Hozircha izoh yo'q. Birinchi bo'ling!