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

18.2-dars: Cross-validation turlari

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


1. Kirish va motivatsiya

cross_val_score(model, X, y, cv=5) — eng ko'p yoziladigan qator. Lekin cv=5 aslida nimani anglatadi? sklearn u yerda sizning o'rningizga qaror qabul qiladi: klassifikatsiya bo'lsa StratifiedKFold(5), aks holda KFold(5) — aralashtirmasdan.

Bu jim qaror ko'p xatoga sabab bo'ladi. Ma'lumot sanaga qarab tartiblangan bo'lsa, aralashtirmagan KFold vaqt bo'yicha bo'ladi — bu ba'zan to'g'ri, ba'zan halokatli. Ma'lumotda bir foydalanuvchining o'nlab qatori bo'lsa, KFold guruh leakage i beradi va CV 0.05 ga optimistik chiqadi.

Bu darsda: KFold, StratifiedKFold, ShuffleSplit, GroupKFold, StratifiedGroupKFold, TimeSeriesSplit, LeaveOneOut va RepeatedKFold — har birining ishlash tartibi, kuchli va zaif tomonlari, hamda qaysi biri qachon kerakligi.

Real vaziyat. Tibbiy tasvirlar loyihasida CV 0.94 ko'rsatdi. Kasalxonada 0.71 chiqdi. Sabab: bitta bemorning 8-12 ta tasviri bor edi va KFold ularni foldlar orasida bo'lib yuborgan — model kasallikni emas, bemorni tanigan. GroupKFold(groups=bemor_id) bilan CV darhol 0.73 ga tushdi va haqiqatni ko'rsatdi.

Bu darsda CV turlarini o'rganamiz.

Bu darsda:

  • KFold va StratifiedKFold
  • GroupKFold
  • TimeSeriesSplit
  • ShuffleSplit va Repeated
  • LeaveOneOut
  • Qaysi biri qachon
  • Tuzoqlar
  • Amaliy: strategiya tanlash

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


2. Nazariya — chuqur tushuntirish

2.1. KFold va StratifiedKFold

text
KFold(n_splits=5, shuffle=False)
  ma'lumotni KETMA-KET 5 bo'lakka bo'ladi
  shuffle=False -> tartib saqlanadi (xavfli!)
  shuffle=True  -> random_state bilan aralashtiriladi

StratifiedKFold(n_splits=5, shuffle=False)
  har foldda sinf ulushlari umumiy ulushga yaqin
  klassifikatsiya uchun DEFAULT

NIMA UCHUN stratifikatsiya:
  musbat ulushi 3% bo'lsa, oddiy KFold da
  bitta foldda 1%, boshqasida 5% chiqishi mumkin
  -> foldlar orasida katta dispersiya

cv=5 yozsangiz, klassifikatsiyada StratifiedKFold(5, shuffle=False) ishlaydi — aralashtirish YO'Q, ya'ni ma'lumot tartiblangan bo'lsa natija noto'g'ri bo'lishi mumkin.

2.2. GroupKFold

text
GroupKFold(n_splits=5)
  bir GURUHning barcha qatorlari BITTA foldda qoladi

QACHON KERAK:
  bir foydalanuvchining ko'p sessiyasi
  bir bemorning ko'p tasviri
  bir do'konning ko'p kuni
  bir hujjatning ko'p jumlasi

StratifiedGroupKFold - guruhni saqlaydi VA sinf ulushini
  (sklearn 0.24+, guruhlar soni yetarli bo'lsa)

GroupShuffleSplit - guruh bo'yicha tasodifiy bo'lish

"Bu qator kim/nimaga tegishli?" degan savolga javob bor bo'lsa — deyarli har doim GroupKFold kerak.

2.3. TimeSeriesSplit

text
TimeSeriesSplit(n_splits=5)
  fold 1: o'quv [0:100]      test [100:200]
  fold 2: o'quv [0:200]      test [200:300]
  fold 3: o'quv [0:300]      test [300:400]
  ...
  test HAR DOIM o'quvdan KEYIN

max_train_size=N -> siljuvchi oyna (kengayuvchi emas)
gap=N            -> o'quv va test orasida bo'shliq

QACHON KERAK: bashorat kelajakka qaratilgan bo'lsa
  narx, talab, nosozlik, churn (kelasi oy)

Vaqt qatorida aralashtirish — leakage: kelajakdagi qatorlarda o'rganib, o'tmishni bashorat qilish real vaziyatga mos emas.

2.4. ShuffleSplit va Repeated

text
ShuffleSplit(n_splits=10, test_size=0.2)
  har safar mustaqil tasodifiy bo'linish
  qatorlar bir necha marta testga tushishi mumkin
  + foldlar soni va test hajmi ALOHIDA sozlanadi

RepeatedStratifiedKFold(n_splits=5, n_repeats=3)
  5-fold CV ni 3 marta, har safar boshqa aralashtirish bilan
  + baho dispersiyasi kamayadi (15 ta o'lchov)
  + har qator har takrorda aynan bir marta testda

KICHIK MA'LUMOTDA (<1000) Repeated eng yaxshi tanlov

RepeatedStratifiedKFold kichik ma'lumotda CV bahosining tebranishini sezilarli kamaytiradi — narxi n_repeats barobar.

2.5. LeaveOneOut

text
LeaveOneOut()  -> n ta fold, har birida 1 ta test qatori

+ deyarli xolis (o'quv hajmi n-1)
- n ta model o'rgatish (juda qimmat)
- BAHO DISPERSIYASI YUQORI (foldlar deyarli bir xil
  o'quv to'plamiga ega -> xatolar korrelyatsiyali)

LeaveOneGroupOut - bitta guruhni chiqarib qoldirish
  (masalan bitta kasalxona, bitta yil)

AMALIYOTDA: 5 yoki 10 fold deyarli har doim afzal

LOO "eng aniq" emas: bias past, lekin dispersiya yuqori va narxi juda katta — 5-10 fold odatda yaxshiroq muvozanat.

2.6. Qaysi biri qachon

text
Savol: ma'lumotda vaqt tartibi bormi?
  ha  -> TimeSeriesSplit (+ gap)
  yo'q -> keyingi savol

Savol: takrorlanuvchi obyektlar bormi (user, bemor, do'kon)?
  ha  -> GroupKFold / StratifiedGroupKFold
  yo'q -> keyingi savol

Savol: klassifikatsiyami?
  ha  -> StratifiedKFold(shuffle=True, random_state=...)
  yo'q -> KFold(shuffle=True, random_state=...)

Savol: ma'lumot kichikmi (<1000)?
  ha  -> Repeated... (n_repeats=3..10)

Uch savol: vaqt bormi → guruh bormi → klassifikatsiyami. Shu tartibda javob bersangiz, to'g'ri strategiyani tanlaysiz.

2.7. Tuzoqlar

Asosiy tuzoqlar: cv=5 yozib, aralashtirish yo'qligini unutish; guruh tuzilmasini e'tiborsiz qoldirish; vaqt qatorida shuffle=True; GroupKFold da groups= ni fit ga uzatmaslik; TimeSeriesSplit da gap qo'ymaslik (bashorat ufqi bor bo'lsa); nomutanosib sinfda stratifikatsiyasiz bo'lish; LOO ni "eng yaxshi" deb ishlatish; har tajribada boshqa random_state.

2.8. Strategiya — birinchi qaror

CV strategiyasi modeldan oldin tanlanadi va butun loyihada bir xil qoladi. Uch savol: vaqt bormi (TimeSeriesSplit), guruh bormi (GroupKFold), klassifikatsiyami (StratifiedKFold). Barchasida shuffle=True va aniq random_state. Kichik ma'lumotda Repeated.... cv=5 deb yozish — strategiyani sklearn ga topshirish demak, va u sizning ma'lumotingiz haqida hech narsa bilmaydi.


3. Tez ma'lumotnoma

python
from sklearn.model_selection import (GroupKFold, GroupShuffleSplit, KFold,
                                     LeaveOneGroupOut, LeaveOneOut,
                                     RepeatedStratifiedKFold, ShuffleSplit,
                                     StratifiedGroupKFold, StratifiedKFold,
                                     TimeSeriesSplit, cross_val_score)

KFold(5, shuffle=True, random_state=0)              # regressiya
StratifiedKFold(5, shuffle=True, random_state=0)    # klassifikatsiya
GroupKFold(5)                                       # guruh bor
StratifiedGroupKFold(5, shuffle=True, random_state=0)
TimeSeriesSplit(n_splits=5, gap=7)                  # vaqt qatori
RepeatedStratifiedKFold(n_splits=5, n_repeats=3, random_state=0)

cross_val_score(model, X, y, cv=cv, groups=guruh, scoring="roc_auc")
for tr, te in cv.split(X, y, groups=guruh): ...     # qo'lda
QOIDA: vaqt? -> guruh? -> stratifikatsiya? ·
       shuffle=True + random_state · strategiya bitta bo'lsin

CV turlari xulosasi

KFold            oddiy, shuffle=True qo'ying
StratifiedKFold  klassifikatsiya uchun default
GroupKFold       takrorlanuvchi obyektlar
TimeSeriesSplit  kelajakni bashorat qilish
Repeated...      kichik ma'lumot
LeaveOneOut      qimmat, dispersiyasi yuqori

4. Batafsil misollar

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

Misol 1 — KFold va StratifiedKFold farqi

python
"""Stratifikatsiya va aralashtirishning 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 (KFold, 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,
                                            class_weight="balanced"))


def main() -> None:
    X, y = make_classification(n_samples=1200, n_features=15,
                               n_informative=5, weights=[0.94, 0.06],
                               flip_y=0.05, class_sep=1.0, random_state=0)
    # ma'lumot maqsad bo'yicha TARTIBLANGAN (tez-tez uchraydigan holat)
    tartib = np.argsort(y, kind="stable")
    X_t, y_t = X[tartib], y[tartib]
    print("=== 1. Ma'lumot ===")
    print(f"  {len(y)} qator, musbat ulushi {y.mean():.2%}")
    print(f"  tartiblangan nusxada birinchi 10 ta y: {y_t[:10].tolist()}")
    print(f"  oxirgi 10 ta y: {y_t[-10:].tolist()}")

    print("\n=== 2. Foldlardagi musbat ulushi ===")
    strategiyalar = {
        "KFold(shuffle=False)": KFold(5),
        "KFold(shuffle=True)": KFold(5, shuffle=True, random_state=0),
        "StratifiedKFold": StratifiedKFold(5, shuffle=True, random_state=0),
    }
    for nom, cv in strategiyalar.items():
        ulushlar = [float(y_t[te].mean())
                    for _, te in cv.split(X_t, y_t)]
        print(f"  {nom:<22} {[f'{u:.3f}' for u in ulushlar]}")

    print("\n=== 3. Tartiblangan ma'lumotda CV natijasi ===")
    # cross_val_score buzilgan foldda ogohlantirish chiqaradi,
    # shuning uchun foldlarni qo'lda aylanamiz
    print(f"  {'strategiya':<22} {'ishlagan fold':>14} {'CV AUC':>9} "
          f"{'std':>8}")
    for nom, cv in strategiyalar.items():
        ballar = []
        for tr, te in cv.split(X_t, y_t):
            if len(np.unique(y_t[tr])) < 2 or len(np.unique(y_t[te])) < 2:
                continue                       # fold yaroqsiz
            m = model().fit(X_t[tr], y_t[tr])
            ballar.append(roc_auc_score(y_t[te],
                                        m.predict_proba(X_t[te])[:, 1]))
        if ballar:
            print(f"  {nom:<22} {len(ballar):>7}/5 {np.mean(ballar):>15.4f} "
                  f"{np.std(ballar):>8.4f}")
        else:
            print(f"  {nom:<22} {0:>7}/5 {'-':>15} {'-':>8}")
    print("  shuffle=False bilan foldda bitta sinf qolishi mumkin ->")
    print("  model o'rgatilmaydi va AUC hisoblanmaydi")

    print("\n=== 4. Aralashtirilgan ma'lumotda ===")
    print(f"  {'strategiya':<22} {'CV AUC':>9} {'std':>8}")
    for nom, cv in strategiyalar.items():
        b = cross_val_score(model(), X, y, cv=cv, scoring="roc_auc")
        print(f"  {nom:<22} {b.mean():>9.4f} {b.std():>8.4f}")
    print("  tartib tasodifiy bo'lsa farq kichik, lekin")
    print("  stratifikatsiya std ni baribir kamaytiradi")
    print("  ⭐ cv=5 - bu shuffle=False degani")


if __name__ == "__main__":
    main()

Natijaning muhim qismi:

text
=== 1. Ma'lumot ===
  1200 qator, musbat ulushi 8.00%
  tartiblangan nusxada birinchi 10 ta y: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
  oxirgi 10 ta y: [1, 1, 1, 1, 1, 1, 1, 1, 1, 1]

=== 2. Foldlardagi musbat ulushi ===
  KFold(shuffle=False)   ['0.000', '0.000', '0.000', '0.000', '0.400']
  KFold(shuffle=True)    ['0.100', '0.071', '0.067', '0.075', '0.087']
  StratifiedKFold        ['0.079', '0.079', '0.079', '0.079', '0.083']

=== 3. Tartiblangan ma'lumotda CV natijasi ===
  strategiya              ishlagan fold    CV AUC      std
  KFold(shuffle=False)         0/5               -        -
  KFold(shuffle=True)          5/5          0.8097   0.0332
  StratifiedKFold              5/5          0.8076   0.0530
  shuffle=False bilan foldda bitta sinf qolishi mumkin ->
  model o'rgatilmaydi va AUC hisoblanmaydi

=== 4. Aralashtirilgan ma'lumotda ===
  strategiya                CV AUC      std
  KFold(shuffle=False)      0.8119   0.0473
  KFold(shuffle=True)       0.8035   0.0544
  StratifiedKFold           0.8076   0.0530
  tartib tasodifiy bo'lsa farq kichik, lekin
  stratifikatsiya std ni baribir kamaytiradi
  ⭐ cv=5 - bu shuffle=False degani

Nima ko'rsatdi: 2.1-bo'lim.

Misol 2 — GroupKFold va guruh leakage i

python
"""Takrorlanuvchi obyektlar CV ni qanday buzadi (real numpy/pandas/sklearn)."""

import numpy as np
import pandas as pd
from sklearn.ensemble import HistGradientBoostingClassifier
from sklearn.model_selection import (GroupKFold, StratifiedGroupKFold,
                                     StratifiedKFold, cross_val_score)


def yarat(seed: int = 0, obyektlar: int = 500, qatorlar_soni: int = 10):
    """Har obyektning bir necha o'lchovi; obyektga xos siljish bor."""
    rng = np.random.default_rng(seed)
    qatorlar = []
    for o in range(obyektlar):
        siljish = rng.normal(0, 1.4)          # obyektga xos daraja
        imzo = rng.normal(0, 1, 3)            # obyektga xos "barmoq izi"
        for _ in range(qatorlar_soni):
            x = rng.normal(0, 1, 3)
            kuch = -0.2 + 0.9 * x[0] + 0.6 * x[1] + siljish
            y = int(rng.random() < 1 / (1 + np.exp(-kuch)))
            qatorlar.append([o, *x, *imzo, y])
    ustunlar = (["obyekt"] + [f"x{i}" for i in range(3)]
                + [f"imzo{i}" for i in range(3)] + ["y"])
    return pd.DataFrame(qatorlar, columns=ustunlar)


def main() -> None:
    df = yarat()
    belgilar = [c for c in df.columns if c not in ("obyekt", "y")]
    X, y = df[belgilar], df["y"].to_numpy()
    guruh = df["obyekt"].to_numpy()

    print("=== 1. Ma'lumot ===")
    print(f"  {len(df)} qator, {df['obyekt'].nunique()} obyekt")
    print(f"  har obyektda {len(df) // df['obyekt'].nunique()} qator")
    print(f"  belgilar: {belgilar}")
    print("  imzo0-2 - obyektga xos, maqsadga bevosita ta'sir qilmaydi")

    def model():
        return HistGradientBoostingClassifier(learning_rate=0.1, max_iter=200,
                                              random_state=0)

    print("\n=== 2. Strategiyalarni taqqoslash ===")
    variantlar = {
        "StratifiedKFold": (StratifiedKFold(5, shuffle=True, random_state=0),
                            None),
        "GroupKFold": (GroupKFold(5), guruh),
        "StratifiedGroupKFold": (
            StratifiedGroupKFold(5, shuffle=True, random_state=0), guruh),
    }
    print(f"  {'strategiya':<24} {'CV AUC':>9} {'std':>8}")
    ballar = {}
    for nom, (cv, g) in variantlar.items():
        b = cross_val_score(model(), X, y, cv=cv, groups=g,
                            scoring="roc_auc")
        ballar[nom] = b.mean()
        print(f"  {nom:<24} {b.mean():>9.4f} {b.std():>8.4f}")
    print(f"  leakage: {ballar['StratifiedKFold'] - ballar['GroupKFold']:+.4f}")

    print("\n=== 3. Foldlarda obyektlar kesishadimi ===")
    for nom, (cv, g) in variantlar.items():
        kesishish = 0
        for tr, te in cv.split(X, y, groups=g):
            kesishish += len(np.intersect1d(guruh[tr], guruh[te]))
        print(f"  {nom:<24} kesishgan obyekt-fold: {kesishish}")

    print("\n=== 4. Guruh hajmi leakage ni qanday oshiradi ===")
    print(f"  {'qator/obyekt':>13} {'StratifiedKFold':>17} "
          f"{'GroupKFold':>12} {'farq':>8}")
    for nechta in [2, 5, 10, 20]:
        d = yarat(obyektlar=300, qatorlar_soni=nechta)
        b_l = [c for c in d.columns if c not in ("obyekt", "y")]
        Xd, yd, gd = d[b_l], d["y"].to_numpy(), d["obyekt"].to_numpy()
        a = cross_val_score(model(), Xd, yd,
                            cv=StratifiedKFold(5, shuffle=True,
                                               random_state=0),
                            scoring="roc_auc").mean()
        b = cross_val_score(model(), Xd, yd, cv=GroupKFold(5), groups=gd,
                            scoring="roc_auc").mean()
        print(f"  {nechta:>13} {a:>17.4f} {b:>12.4f} {a - b:>+8.4f}")
    print("  ⭐ Guruh qancha katta - leakage shuncha kuchli")


if __name__ == "__main__":
    main()

Natijaning muhim qismi:

text
=== 1. Ma'lumot ===
  5000 qator, 500 obyekt
  har obyektda 10 qator
  belgilar: ['x0', 'x1', 'x2', 'imzo0', 'imzo1', 'imzo2']
  imzo0-2 - obyektga xos, maqsadga bevosita ta'sir qilmaydi

=== 2. Strategiyalarni taqqoslash ===
  strategiya                  CV AUC      std
  StratifiedKFold             0.7457   0.0056
  GroupKFold                  0.6426   0.0130
  StratifiedGroupKFold        0.6322   0.0104
  leakage: +0.1031

=== 3. Foldlarda obyektlar kesishadimi ===
  StratifiedKFold          kesishgan obyekt-fold: 2242
  GroupKFold               kesishgan obyekt-fold: 0
  StratifiedGroupKFold     kesishgan obyekt-fold: 0

=== 4. Guruh hajmi leakage ni qanday oshiradi ===
   qator/obyekt   StratifiedKFold   GroupKFold     farq
              2            0.5940       0.5516  +0.0423
              5            0.6992       0.6236  +0.0755
             10            0.7477       0.6635  +0.0843
             20            0.7746       0.6359  +0.1387
  ⭐ Guruh qancha katta - leakage shuncha kuchli

Nima ko'rsatdi: 2.2-bo'lim.

Misol 3 — TimeSeriesSplit

python
"""Vaqt bo'yicha bo'lish va gap (real numpy/pandas/sklearn)."""

import numpy as np
import pandas as pd
from sklearn.ensemble import HistGradientBoostingRegressor
from sklearn.metrics import mean_absolute_error
from sklearn.model_selection import KFold, TimeSeriesSplit, cross_val_score


def yarat(n: int = 1200, seed: int = 0) -> pd.DataFrame:
    """Trend + mavsumiylik + rejim o'zgarishi bo'lgan kunlik qator."""
    rng = np.random.default_rng(seed)
    t = np.arange(n)
    trend = 0.02 * t
    mavsum = 3.0 * np.sin(2 * np.pi * t / 7) + 1.5 * np.sin(2 * np.pi * t / 365)
    rejim = np.where(t > 800, 4.0, 0.0)          # 800-kunda daraja siljidi
    shovqin = rng.normal(0, 1.0, n)
    y = 20 + trend + mavsum + rejim + shovqin
    df = pd.DataFrame({"kun": t, "qiymat": y})
    for lag in [1, 2, 3, 7, 14]:
        df[f"lag{lag}"] = df["qiymat"].shift(lag)
    df["oyna7"] = df["qiymat"].shift(1).rolling(7).mean()
    df["hafta_kuni"] = df["kun"] % 7
    return df.dropna().reset_index(drop=True)


def main() -> None:
    df = yarat()
    belgilar = [c for c in df.columns if c not in ("kun", "qiymat")]
    X, y = df[belgilar], df["qiymat"].to_numpy()

    print("=== 1. Ma'lumot ===")
    print(f"  {len(df)} kun, belgilar: {len(belgilar)}")
    print(f"  qiymat: {y.min():.1f} .. {y.max():.1f}")
    print("  800-kunda daraja siljishi bor")

    def model():
        return HistGradientBoostingRegressor(learning_rate=0.1, max_iter=200,
                                             random_state=0)

    print("\n=== 2. KFold va TimeSeriesSplit ===")
    variantlar = {
        "KFold(shuffle=True)": KFold(5, shuffle=True, random_state=0),
        "TimeSeriesSplit(5)": TimeSeriesSplit(5),
        "TimeSeriesSplit(gap=7)": TimeSeriesSplit(5, gap=7),
        "TSS(oyna=300)": TimeSeriesSplit(5, max_train_size=300),
    }
    print(f"  {'strategiya':<24} {'MAE':>8} {'std':>8}")
    for nom, cv in variantlar.items():
        b = -cross_val_score(model(), X, y, cv=cv,
                             scoring="neg_mean_absolute_error")
        print(f"  {nom:<24} {b.mean():>8.4f} {b.std():>8.4f}")
    print("  aralashtirilgan KFold kelajakdan o'rganadi -> optimistik")

    print("\n=== 3. TimeSeriesSplit foldlari ===")
    print(f"  {'fold':>5} {'o_quv':>8} {'test':>7} {'o_quv oxiri':>13} "
          f"{'test boshi':>12}")
    for i, (tr, te) in enumerate(TimeSeriesSplit(5).split(X), 1):
        print(f"  {i:>5} {len(tr):>8} {len(te):>7} "
              f"{int(df['kun'].iloc[tr[-1]]):>13} "
              f"{int(df['kun'].iloc[te[0]]):>12}")

    print("\n=== 4. Kengayuvchi va siljuvchi oyna ===")
    print(f"  {'fold':>5} {'kengayuvchi MAE':>17} {'siljuvchi(300) MAE':>20}")
    keng = list(TimeSeriesSplit(5).split(X))
    silj = list(TimeSeriesSplit(5, max_train_size=300).split(X))
    for i, ((tr1, te1), (tr2, te2)) in enumerate(zip(keng, silj), 1):
        m1 = model().fit(X.iloc[tr1], y[tr1])
        m2 = model().fit(X.iloc[tr2], y[tr2])
        e1 = mean_absolute_error(y[te1], m1.predict(X.iloc[te1]))
        e2 = mean_absolute_error(y[te2], m2.predict(X.iloc[te2]))
        print(f"  {i:>5} {e1:>17.4f} {e2:>20.4f}")
    print("  rejim o'zgargandan keyin siljuvchi oyna tezroq moslashadi")
    print("  ⭐ Vaqt qatorida test har doim o'quvdan keyin")


if __name__ == "__main__":
    main()

Natijaning muhim qismi:

text
=== 1. Ma'lumot ===
  1186 kun, belgilar: 7
  qiymat: 17.5 .. 53.3
  800-kunda daraja siljishi bor

=== 2. KFold va TimeSeriesSplit ===
  strategiya                    MAE      std
  KFold(shuffle=True)        1.0169   0.0530
  TimeSeriesSplit(5)         2.3502   0.4588
  TimeSeriesSplit(gap=7)     2.4940   0.7949
  TSS(oyna=300)              2.3817   0.5169
  aralashtirilgan KFold kelajakdan o'rganadi -> optimistik

=== 3. TimeSeriesSplit foldlari ===
   fold    o_quv    test   o_quv oxiri   test boshi
      1      201     197           214          215
      2      398     197           411          412
      3      595     197           608          609
      4      792     197           805          806
      5      989     197          1002         1003

=== 4. Kengayuvchi va siljuvchi oyna ===
   fold   kengayuvchi MAE   siljuvchi(300) MAE
      1            2.0385               2.0385
      2            1.7106               1.7499
      3            2.3942               2.5602
      4            2.5463               2.2950
      5            3.0615               3.2648
  rejim o'zgargandan keyin siljuvchi oyna tezroq moslashadi
  ⭐ Vaqt qatorida test har doim o'quvdan keyin

Nima ko'rsatdi: 2.3-bo'lim.

Misol 4 — Repeated, ShuffleSplit va LeaveOneOut

python
"""Kichik ma'lumotda qaysi strategiya barqaror (real numpy/sklearn)."""

import numpy as np
from sklearn.datasets import make_classification
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import (LeaveOneOut, RepeatedStratifiedKFold,
                                     ShuffleSplit, 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=300, n_features=12, n_informative=5,
                               flip_y=0.12, class_sep=1.0, random_state=0)
    print("=== 1. Kichik ma'lumot ===")
    print(f"  {len(y)} qator, {X.shape[1]} belgi")

    print("\n=== 2. Strategiyalar va ularning bahosi ===")
    print(f"  {'strategiya':<28} {'baho':>8} {'foldlar':>8} "
          f"{'model soni':>11}")
    natija = {}
    variantlar = {
        "StratifiedKFold(5)": StratifiedKFold(5, shuffle=True,
                                              random_state=0),
        "StratifiedKFold(10)": StratifiedKFold(10, shuffle=True,
                                               random_state=0),
        "Repeated(5x5)": RepeatedStratifiedKFold(n_splits=5, n_repeats=5,
                                                 random_state=0),
        "ShuffleSplit(20, 0.2)": ShuffleSplit(n_splits=20, test_size=0.2,
                                              random_state=0),
        "LeaveOneOut": LeaveOneOut(),
    }
    for nom, cv in variantlar.items():
        b = cross_val_score(model(), X, y, cv=cv, scoring="accuracy")
        natija[nom] = b
        print(f"  {nom:<28} {b.mean():>8.4f} {len(b):>8} {len(b):>11}")

    print("\n=== 3. Baho qanchalik barqaror (10 ta turli seed) ===")
    print(f"  {'strategiya':<28} {'bahoning std i':>16}")
    for nom, yasovchi in [
            ("StratifiedKFold(5)",
             lambda s: StratifiedKFold(5, shuffle=True, random_state=s)),
            ("StratifiedKFold(10)",
             lambda s: StratifiedKFold(10, shuffle=True, random_state=s)),
            ("Repeated(5x5)",
             lambda s: RepeatedStratifiedKFold(n_splits=5, n_repeats=5,
                                               random_state=s)),
            ("ShuffleSplit(20, 0.2)",
             lambda s: ShuffleSplit(n_splits=20, test_size=0.2,
                                    random_state=s))]:
        ballar = [cross_val_score(model(), X, y, cv=yasovchi(s),
                                  scoring="accuracy").mean()
                  for s in range(10)]
        print(f"  {nom:<28} {np.std(ballar):>16.5f}")
    print("  (LeaveOneOut da tasodif yo'q - std = 0, lekin bu")
    print("   barqarorlik emas: baho o'zi yuqori dispersiyali)")

    print("\n=== 4. LeaveOneOut ning fold ballari ===")
    loo = natija["LeaveOneOut"]
    print(f"  fold ballari faqat 0 yoki 1: "
          f"{sorted(set(loo.tolist()))}")
    print(f"  shuning uchun 'std' ma'nosiz: {loo.std():.4f}")
    print(f"  o'rtacha aniqlik: {loo.mean():.4f}")
    print(f"  narx: {len(loo)} ta model (5-fold da atigi 5 ta)")
    print("  ⭐ Kichik ma'lumotda Repeated - eng yaxshi muvozanat")


if __name__ == "__main__":
    main()

Natijaning muhim qismi:

text
=== 1. Kichik ma'lumot ===
  300 qator, 12 belgi

=== 2. Strategiyalar va ularning bahosi ===
  strategiya                       baho  foldlar  model soni
  StratifiedKFold(5)             0.7900        5           5
  StratifiedKFold(10)            0.8100       10          10
  Repeated(5x5)                  0.7933       25          25
  ShuffleSplit(20, 0.2)          0.8108       20          20
  LeaveOneOut                    0.8100      300         300

=== 3. Baho qanchalik barqaror (10 ta turli seed) ===
  strategiya                     bahoning std i
  StratifiedKFold(5)                    0.00844
  StratifiedKFold(10)                   0.00733
  Repeated(5x5)                         0.00298
  ShuffleSplit(20, 0.2)                 0.00806
  (LeaveOneOut da tasodif yo'q - std = 0, lekin bu
   barqarorlik emas: baho o'zi yuqori dispersiyali)

=== 4. LeaveOneOut ning fold ballari ===
  fold ballari faqat 0 yoki 1: [0.0, 1.0]
  shuning uchun 'std' ma'nosiz: 0.3923
  o'rtacha aniqlik: 0.8100
  narx: 300 ta model (5-fold da atigi 5 ta)
  ⭐ Kichik ma'lumotda Repeated - eng yaxshi muvozanat

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


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

Noto'g'ri fikr To'g'risi
"cv=5 yetarli" shuffle=False — tartib muhim bo'lishi mumkin
"Stratifikatsiya faqat nomutanosibda" Har doim std ni kamaytiradi
"Guruh tuzilmasi kamdan-kam" Juda tez-tez uchraydi
"Vaqt qatorini ham aralashtirsa bo'ladi" Leakage
"LeaveOneOut eng aniq" Dispersiyasi yuqori, qimmat
"Ko'proq fold — yaxshiroq" 5-10 dan keyin foyda kam
"ShuffleSplit va KFold bir xil" Qatorlar takrorlanishi mumkin
"groups ixtiyoriy" GroupKFold da majburiy

6. Keng tarqalgan xatolar va yechimlari

1. Aralashtirishni unutish

python
cross_val_score(model, X, y, cv=5)                               # ⚠️
cross_val_score(model, X, y,
                cv=StratifiedKFold(5, shuffle=True,
                                   random_state=0))              # ✅

2. Guruhni e'tiborsiz qoldirish

python
cross_val_score(model, X, y, cv=StratifiedKFold(5))              # ⚠️
cross_val_score(model, X, y, cv=GroupKFold(5), groups=user_id)   # ✅

3. Vaqt qatorida aralashtirish

python
KFold(5, shuffle=True)                                           # ⚠️
TimeSeriesSplit(5, gap=ufq)                                      # ✅

4. groups ni uzatmaslik

python
cross_val_score(m, X, y, cv=GroupKFold(5))          # ValueError  # ⚠️
cross_val_score(m, X, y, cv=GroupKFold(5), groups=g)             # ✅

5. Har tajribada boshqa seed

python
StratifiedKFold(5, shuffle=True)     # random_state yo'q         # ⚠️
CV = StratifiedKFold(5, shuffle=True, random_state=0)  # umumiy  # ✅

6. LeaveOneOut ni sukut bo'yicha ishlatish

python
cross_val_score(model, X, y, cv=LeaveOneOut())   # 10000 model   # ⚠️
cross_val_score(model, X, y, cv=RepeatedStratifiedKFold(...))    # ✅

7. gap siz vaqt CV si

python
TimeSeriesSplit(5)          # 7 kunlik ufq uchun leakage         # ⚠️
TimeSeriesSplit(5, gap=7)                                        # ✅

7. Integratsiya — bu bilim qayerda kerak bo'ladi

  • 12.3-dars (o'tilgan): Train/test/validatsiya
  • 17.8-dars (o'tilgan): Leakage
  • 18.1-dars (o'tilgan): Baholash dizayni
  • 18.3-dars: CV dispersiyasi
  • 18.4-dars: Nested CV
  • 18.12-dars: Amaliyot

8. Eng yaxshi amaliyotlar

  1. Uch savolga javob bering.

  2. shuffle=True va random_state.

  3. CV obyektini umumiy modulda saqlang.

  4. Guruh bo'lsa — GroupKFold.

  5. Vaqt bo'lsa — TimeSeriesSplit + gap.

  6. Kichik ma'lumotda Repeated....

  7. Foldlardagi sinf ulushlarini tekshiring.

  8. Strategiyani hujjatlashtiring.


9. Amaliy topshiriq

Vazifa 1: Bashorat qiling

python
1.  # cv=5 klassifikatsiyada nima beradi?
2.  # shuffle sukut bo'yicha qanday?
3.  # stratifikatsiya nima qiladi?
4.  # GroupKFold qachon kerak?
5.  # StratifiedGroupKFold nima?
6.  # TimeSeriesSplit da test qayerda?
7.  # gap nima uchun?
8.  # max_train_size nima beradi?
9.  # ShuffleSplit va KFold farqi?
10. # Repeated nima uchun?
11. # LeaveOneOut kamchiligi?
12. # uch savol tartibi?
Javoblar
  1. StratifiedKFold(5, shuffle=False)
  2. False
  3. Foldlarda sinf ulushini saqlaydi
  4. Takrorlanuvchi obyektlar bo'lsa
  5. Guruh va sinf ulushini birga saqlaydi
  6. O'quvdan keyin
  7. Bashorat ufqi uchun leakage ni to'sish
  8. Siljuvchi oyna
  9. ShuffleSplit da qatorlar takrorlanishi mumkin
  10. Baho dispersiyasini kamaytirish
  11. Qimmat, dispersiyasi yuqori
  12. Vaqt → guruh → stratifikatsiya

Vazifa 2: Xatolarni tuzating

python
1.  cross_val_score(model, X, y, cv=5)

2.  cross_val_score(model, X, y, cv=StratifiedKFold(5))   # user_id bor

3.  KFold(5, shuffle=True)   # kunlik narx qatori

4.  cross_val_score(m, X, y, cv=GroupKFold(5))

5.  cross_val_score(model, X, y, cv=LeaveOneOut())   # n=50000
Javoblar
python
1.  cross_val_score(model, X, y,
                    cv=StratifiedKFold(5, shuffle=True, random_state=0))

2.  cross_val_score(model, X, y, cv=GroupKFold(5), groups=user_id)

3.  TimeSeriesSplit(5, gap=ufq)

4.  cross_val_score(m, X, y, cv=GroupKFold(5), groups=g)

5.  cross_val_score(model, X, y, cv=StratifiedKFold(5, shuffle=True,
                                                    random_state=0))

Vazifa 3: Stratifikatsiya

Modellang:

  1. Ma'lumot
  2. Fold ulushlari
  3. Tartiblangan CV
  4. Aralashtirilgan CV

Vazifa 4: Guruh

Modellang:

  1. Ma'lumot
  2. Taqqoslash
  3. Kesishish
  4. Guruh hajmi

Vazifa 5: Vaqt

Modellang:

  1. Ma'lumot
  2. KFold va TSS
  3. Foldlar
  4. Oyna turlari

Vazifa 6: Kichik ma'lumot

Modellang:

  1. Ma'lumot
  2. Strategiyalar
  3. Barqarorlik
  4. LOO tahlili

Vazifa 7: O'ylash

Elektron tijorat loyihasida har bir qator — bitta buyurtma. Ma'lumotda user_id, sana va do'kon_id bor. Model kelasi oyda buyurtma qaytarilishini bashorat qiladi. Qanday CV strategiyasi kerak?

Javob

Qisqa javob: vaqt bo'yicha bo'lish birinchi o'rinda — model kelajakni bashorat qiladi. Guruh (user_id) tuzilmasi ham bor, shuning uchun eng to'g'ri yechim — vaqt bo'yicha bo'lib, chegarada foydalanuvchilarni ajratish.

1. Nima uchun vaqt birinchi

Uch savol tartibi: vaqt → guruh → stratifikatsiya. Bu yerda bashorat kelasi oyga qaratilgan, ya'ni ishlab chiqarishda model o'tmishda o'rganib, kelajakni baholaydi. Agar CV aralashtirilgan bo'lsa:

Muammo Ta'siri
Kelajakdan o'rganish Mavsumiylik, aksiya, narx o'zgarishi "ma'lum" bo'ladi
Rejim o'zgarishi ko'rinmaydi Yangi toifa, yangi do'kon effekti yashirinadi
Baho optimistik Odatda 0.03-0.10 AUC

2. Guruh muammosi qanday qoladi

Vaqt bo'yicha bo'lganda ham bitta foydalanuvchining iyul va avgust buyurtmalari turli tomonlarda bo'ladi — bu to'g'ri, chunki ishlab chiqarishda ham shunday. Lekin chegaraga yaqin kunlarda bir foydalanuvchining ikki buyurtmasi ikki tomonda bo'lishi mumkin va agar belgilar orasida "foydalanuvchining umumiy qaytarish darajasi" bo'lsa, bu leakage beradi.

Yechim: gap qo'ying (bashorat ufqi + belgilar oynasi) yoki chegara atrofidagi foydalanuvchilarni tashlab yuboring.

3. Tavsiya etiladigan dizayn

python
from sklearn.model_selection import TimeSeriesSplit

# 1. qatorlarni sana bo'yicha tartiblang
df = df.sort_values("sana").reset_index(drop=True)

# 2. vaqt bo'yicha bo'lish, ufq uchun gap bilan
cv = TimeSeriesSplit(n_splits=5, gap=30)      # 30 kunlik ufq

# 3. barcha belgilar FAQAT o'tmishdan (shift/rolling)
# 4. yakuniy test - eng so'nggi oy, alohida va yopiq

4. Qo'shimcha tekshiruvlar

  1. Har foldda musbat ulushini chop eting — vaqt bo'yicha o'zgarishi mumkin (drift).
  2. Fold natijalarini vaqt bo'yicha chizing: pasayish trendi bo'lsa — drift bor.
  3. do'kon_id bo'yicha ham baholang: yangi do'konlarda model qanday ishlaydi.

5. Qachon GroupKFold afzal

Agar vazifa "kelajak" emas, balki "yangi foydalanuvchi" bo'lsa (model ilgari ko'rilmagan mijozga qo'llanadi), unda GroupKFold(groups=user_id) to'g'ri bo'ladi. Savol har doim bitta: ishlab chiqarishda model nimani ko'rmagan bo'ladi?

6. Xulosa

  1. Vaqt birinchi: TimeSeriesSplit + gap
  2. Belgilar faqat o'tmishdan
  3. Yakuniy test — eng so'nggi davr
  4. Drift ni foldlar bo'yicha kuzating

Nimani mustahkamlaydi: 2.2, 2.3, 2.6-bo'limlar.


Xulosa

Bu darsda cross-validation turlarini o'rgandik.

Eng muhim uch fikr:

  1. cv=5 — bu strategiyani sklearn ga topshirish. Klassifikatsiyada u StratifiedKFold(5, **shuffle=False**) beradi, ya'ni ma'lumot tartiblangan bo'lsa foldlar buziladi. Har doim aniq yozing: StratifiedKFold(5, shuffle=True, random_state=0) — va bu obyektni loyihada bitta joyda saqlab, hamma joydan import qiling.

  2. Uch savol tartibi: vaqt → guruh → stratifikatsiya. Bashorat kelajakka qaratilgan bo'lsa TimeSeriesSplit (bashorat ufqi bor bo'lsa gap bilan). Takrorlanuvchi obyektlar bo'lsa GroupKFold yoki StratifiedGroupKFold — bitta foydalanuvchi/bemor/do'konning barcha qatorlari bitta foldda qolishi kerak, aks holda model obyektni tanib oladi.

  3. Kichik ma'lumotda takroriy CV. 1000 qatordan kam bo'lsa bitta 5-fold CV ning o'zi tebranadi; RepeatedStratifiedKFold(n_splits=5, n_repeats=5) bahoning standart og'ishini bir necha barobar kamaytiradi. LeaveOneOut esa "eng aniq" emas: narxi n barobar va fold ballari faqat 0/1 bo'lgani uchun dispersiyasi yuqori.

Keyingi darsda CV bahosining dispersiyasini o'rganamiz: necha fold kerak, standart xato qanday hisoblanadi va nima uchun foldlar orasidagi std ni to'g'ridan-to'g'ri ishonch oralig'i sifatida ishlatib bo'lmaydi.

Ulashish:Telegram'da

Izohlar (0)

Izoh yozish uchun kiring.

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