Mundarija (22)
- 1. Kirish va motivatsiya
- 2. Nazariya — chuqur tushuntirish
- 2.1. KFold va StratifiedKFold
- 2.2. GroupKFold
- 2.3. TimeSeriesSplit
- 2.4. ShuffleSplit va Repeated
- 2.5. LeaveOneOut
- 2.6. Qaysi biri qachon
- 2.7. Tuzoqlar
- 2.8. Strategiya — birinchi qaror
- 3. Tez ma'lumotnoma
- 4. Batafsil misollar
- Misol 1 — KFold va StratifiedKFold farqi
- Misol 2 — GroupKFold va guruh leakage i
- Misol 3 — TimeSeriesSplit
- Misol 4 — Repeated, ShuffleSplit va LeaveOneOut
- 5. To'g'ri va noto'g'ri tushunishlar
- 6. Keng tarqalgan xatolar va yechimlari
- 7. Integratsiya — bu bilim qayerda kerak bo'ladi
- 8. Eng yaxshi amaliyotlar
- 9. Amaliy topshiriq
- Xulosa
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
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
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
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
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
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 afzalLOO "eng aniq" emas: bias past, lekin dispersiya yuqori va narxi juda katta — 5-10 fold odatda yaxshiroq muvozanat.
2.6. Qaysi biri qachon
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
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'lsinCV 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 yuqori4. Batafsil misollar
Misollar real numpy/pandas/sklearn bilan (Python 3.14).
Misol 1 — KFold va StratifiedKFold farqi
"""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:
=== 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 deganiNima ko'rsatdi: 2.1-bo'lim.
Misol 2 — GroupKFold va guruh leakage i
"""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:
=== 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 kuchliNima ko'rsatdi: 2.2-bo'lim.
Misol 3 — TimeSeriesSplit
"""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:
=== 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 keyinNima ko'rsatdi: 2.3-bo'lim.
Misol 4 — Repeated, ShuffleSplit va LeaveOneOut
"""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:
=== 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 muvozanatNima 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
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
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
KFold(5, shuffle=True) # ⚠️
TimeSeriesSplit(5, gap=ufq) # ✅4. groups ni uzatmaslik
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
StratifiedKFold(5, shuffle=True) # random_state yo'q # ⚠️
CV = StratifiedKFold(5, shuffle=True, random_state=0) # umumiy # ✅6. LeaveOneOut ni sukut bo'yicha ishlatish
cross_val_score(model, X, y, cv=LeaveOneOut()) # 10000 model # ⚠️
cross_val_score(model, X, y, cv=RepeatedStratifiedKFold(...)) # ✅7. gap siz vaqt CV si
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
Uch savolga javob bering.
shuffle=Truevarandom_state.CV obyektini umumiy modulda saqlang.
Guruh bo'lsa —
GroupKFold.Vaqt bo'lsa —
TimeSeriesSplit+gap.Kichik ma'lumotda
Repeated....Foldlardagi sinf ulushlarini tekshiring.
Strategiyani hujjatlashtiring.
9. Amaliy topshiriq
Vazifa 1: Bashorat qiling
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
StratifiedKFold(5, shuffle=False)False- Foldlarda sinf ulushini saqlaydi
- Takrorlanuvchi obyektlar bo'lsa
- Guruh va sinf ulushini birga saqlaydi
- O'quvdan keyin
- Bashorat ufqi uchun leakage ni to'sish
- Siljuvchi oyna
- ShuffleSplit da qatorlar takrorlanishi mumkin
- Baho dispersiyasini kamaytirish
- Qimmat, dispersiyasi yuqori
- Vaqt → guruh → stratifikatsiya
Vazifa 2: Xatolarni tuzating
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=50000Javoblar
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:
- Ma'lumot
- Fold ulushlari
- Tartiblangan CV
- Aralashtirilgan CV
Vazifa 4: Guruh
Modellang:
- Ma'lumot
- Taqqoslash
- Kesishish
- Guruh hajmi
Vazifa 5: Vaqt
Modellang:
- Ma'lumot
- KFold va TSS
- Foldlar
- Oyna turlari
Vazifa 6: Kichik ma'lumot
Modellang:
- Ma'lumot
- Strategiyalar
- Barqarorlik
- 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
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 yopiq4. Qo'shimcha tekshiruvlar
- Har foldda musbat ulushini chop eting — vaqt bo'yicha o'zgarishi mumkin (drift).
- Fold natijalarini vaqt bo'yicha chizing: pasayish trendi bo'lsa — drift bor.
do'kon_idbo'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
- Vaqt birinchi:
TimeSeriesSplit+gap - Belgilar faqat o'tmishdan
- Yakuniy test — eng so'nggi davr
- 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:
cv=5— bu strategiyani sklearn ga topshirish. Klassifikatsiyada uStratifiedKFold(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.Uch savol tartibi: vaqt → guruh → stratifikatsiya. Bashorat kelajakka qaratilgan bo'lsa
TimeSeriesSplit(bashorat ufqi bor bo'lsagapbilan). Takrorlanuvchi obyektlar bo'lsaGroupKFoldyokiStratifiedGroupKFold— bitta foydalanuvchi/bemor/do'konning barcha qatorlari bitta foldda qolishi kerak, aks holda model obyektni tanib oladi.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.LeaveOneOutesa "eng aniq" emas: narxinbarobar 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.
Izohlar (0)
Izoh yozish uchun kiring.
- Hozircha izoh yo'q. Birinchi bo'ling!