Mundarija (26)
- 1. Kirish va motivatsiya
- 2. Nazariya — chuqur tushuntirish
- 2.1. Drift turlari: nima o'zgaradi?
- 2.2. Vaqt bo'yicha shakllar
- 2.3. Bir o'lchovli o'lchovlar
- 2.4. Bin tanlash tuzog'i
- 2.5. p-qiymat va effekt o'lchami
- 2.6. Ko'p ustun — ko'p testlash
- 2.7. Ko'p o'lchovli drift: domen klassifikatori
- 2.8. Bashorat drifti va belgilarsiz sifat taxmini
- 2.9. Drift va sifat — to'rt holat
- 2.10. Oqim ustida drift aniqlash
- 2.11. Reference oyna va signaldan keyingi harakat
- 2.12. Tuzoqlar
- 3. Tez ma'lumotnoma
- 4. Batafsil misollar
- Misol 1 — PSI, KS, chi-kvadrat va Wasserstein noldan; bin tanlash tuzog'i
- Misol 2 — Katta namuna va ko'p testlash tuzoqlari
- Misol 3 — Drift va model sifati: nima ko'rinadi, nima ko'rinmaydi
- Misol 4 — Oqim ustida: Page-Hinkley va oynali test
- 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
27.12-dars: Drift aniqlash
27-QISM — MLOPS VA DEPLOY · 12-dars
1. Kirish va motivatsiya
Oldingi darsda monitoringni qurdik: xizmat sog'lig'i, kechikish, xatolar, bashoratlar va kirish ma'lumotlari loglarga yoziladi va chegaralar bilan kuzatiladi. Endi monitoringning eng nozik savoliga o'tamiz: ma'lumot o'zgardimi — va bu o'zgarish modelga zarar beradimi?
Model o'qitilgan paytdagi dunyo bilan ishlab chiqarishdagi dunyo hech qachon aynan bir xil emas. Mijozlar tarkibi o'zgaradi, narxlar oshadi, yangi mahsulot chiqadi, raqobatchi aksiya boshlaydi, ilova interfeysi yangilanadi, ma'lumot yig'uvchi xizmatda xato paydo bo'ladi. Bularning hammasi umumiy nom bilan drift (siljish) deyiladi. Muammo shundaki, drift turlari har xil, ularni aniqlash usullari har xil — va eng xavflisi, eng oson aniqlanadigan drift ko'pincha eng zararsizi bo'ladi.
Bu darsda drift turlarini ajratamiz va aniqlash usullarini noldan quramiz: PSI, KS testi, chi-kvadrat, Wasserstein masofa, domen klassifikatori, bashorat taqsimoti drifti va oqim ustida ishlaydigan Page-Hinkley detektori. Lekin darsning asosiy qismi — tuzoqlar. Ularni gapirib emas, o'lchab ko'rsatamiz: katta namunada KS testi arzimas o'zgarishni ham "sezilarli" deydi; 40 ta ustunni har kuni tekshirsangiz, drift bo'lmasa ham kunlarning ko'pchiligida qizil signal chiqadi; ahamiyatsiz ustundagi drift sifatga ta'sir qilmaydi, konsept drift esa kirish detektorlarining birortasiga ko'rinmasdan sifatni tushiradi.
Real vaziyat. Bank kredit skoring modeli uchun drift paneli qurdi: 60 ta ustunning har birida har kuni KS testi. Birinchi haftadanoq panelda har kuni 3-4 ta qizil ustun chiqdi — ko'pi arzimas o'zgarishlar edi. Jamoa signallarga qaramay qo'ydi. Uch oydan keyin defolt ulushi keskin oshdi. Tahlil ko'rsatdiki, kirish ustunlarining birortasi ham sezilarli o'zgarmagan: iqtisodiy vaziyat o'zgargani sababli bir xil daromad va tarixga ega mijozlarning to'lov xulqi o'zgargan edi. Bu — konsept drift, va uni faqat belgilar (haqiqiy natija) kelgandan keyin sifat metrikasi ko'rsatadi. Panel ham noto'g'ri narsani, ham noto'g'ri sezgirlik bilan o'lchagan.
Bu darsda driftni aniqlashni va — undan ham muhimi — signalni to'g'ri talqin qilishni o'rganamiz.
Bu darsda:
- Drift turlari: kovariat drift
P(X), konsept driftP(y|X), belgilar driftiP(y); vaqt bo'yicha shakllari - PSI, KS, chi-kvadrat, Wasserstein — noldan va
scipybilan - Bin tanlash tuzog'i
- p-qiymat va effekt o'lchami; ko'p testlash (Bonferroni, BH)
- Ko'p o'lchovli drift: domen klassifikatori
- Bashorat taqsimoti drifti va belgilarsiz sifatni taxminlash
- Drift va sifat: qaysi drift zarar beradi, qaysi biri ko'rinmaydi
- Oqim ustida: Page-Hinkley, ADWIN g'oyasi, kechikish va yolg'on signal
- Tuzoqlar
ℹ Misollar real numpy/scipy/sklearn bilan (Python 3.14). Ma'lumot sintetik, urug' bilan yaratiladi.
2. Nazariya — chuqur tushuntirish
2.1. Drift turlari: nima o'zgaradi?
Birgalikdagi taqsimotni ikki xil yoyish mumkin, va har bir ko'paytuvchi alohida o'zgarishi mumkin:
P(X, y) = P(y | X) * P(X) = P(X | y) * P(y)
KOVARIAT DRIFT (ma'lumot drifti): P(X) o'zgaradi, P(y|X) o'sha-o'sha
misol: ilova yoshlar orasida mashhur bo'ldi -> yosh taqsimoti siljidi
kirish detektorlari KO'RADI
sifatga ta'siri: model o'sha hududda yaxshi bo'lsa - yo'q;
model yangi hududda xato bo'lsa - katta
KONSEPT DRIFT: P(y|X) o'zgaradi, P(X) o'sha-o'sha
misol: iqtisodiy inqiroz - bir xil daromadli mijoz endi ko'proq
kechiktiradi; firibgarlar yangi usul topdi
kirish detektorlari KO'RMAYDI (X taqsimoti o'zgarmagan!)
sifat albatta tushadi - model o'rgangan qoida endi noto'g'ri
BELGILAR DRIFTI (prior shift): P(y) o'zgaradi, P(X|y) o'sha-o'sha
misol: epidemiya mavsumi - kasallar ulushi oshdi
X taqsimoti ham o'zgaradi (sinflar aralashmasi o'zgargani uchun)
AUC deyarli o'zgarmaydi, lekin chegara va kalibrovka buziladiDrift — bitta hodisa emas, uch xil hodisa. Ular turli detektor bilan ko'rinadi va turli yechim talab qiladi.
Amalda ular aralash keladi. Masalan, yangi marketing kampaniyasi boshqa mijozlarni olib keladi (kovariat), ularning xulqi ham boshqacha (konsept), ketish ulushi ham o'zgaradi (belgilar). Lekin fikrlashda ularni ajratish kerak, chunki savol har doim bitta: qaysi qism o'zgardi va model shu qismga bog'liqmi?
2.2. Vaqt bo'yicha shakllar
TO'SATDAN (abrupt) ASTA-SEKIN (gradual/incremental)
______ ____/
|______ /
(yangi versiya, qonun, (inflyatsiya, auditoriya
ma'lumot manbai buzildi) asta o'zgaradi)
MAVSUMIY (seasonal) QAYTUVCHI (recurring)
/\ /\ /\ /\ ___ ___ ___
/ \/ \/ \/ \ |__| |__|
(hafta kuni, oy, bayram) (aksiya boshlanadi va tugaydi,
eski rejim qaytadi)Har shakl detektorga boshqa talab qo'yadi:
- To'satdan — tez aniqlanadi, lekin tez javob kerak (odatda ma'lumot xatosi yoki tizim o'zgarishi).
- Asta-sekin — har kuni o'zgarish kichik, shuning uchun "kechagi kun bilan solishtirish" uni ko'rmaydi; uzoqroq reference bilan solishtirish kerak.
- Mavsumiy — drift emas, kutilgan o'zgarish. Reference oyna mavsumni qamramasa, detektor har mavsumda yolg'on signal beradi. Yechim: o'tgan yilning o'sha davri bilan solishtirish yoki mavsumni modelga belgi sifatida berish.
- Qaytuvchi — eski rejim qaytadi; eski modellarni arxivda saqlash va qayta ishlatish foydali (27.5 registr).
2.3. Bir o'lchovli o'lchovlar
Bitta ustun uchun reference (masalan, o'quv davri) va current (oxirgi hafta) taqsimotlarini solishtiramiz.
PSI (Population Stability Index):
1. chegaralarni REFERENCE kvantillaridan olamiz (10 bin -> har birida ~10%)
2. p_i = reference ulushi, q_i = current ulushi har binda
3. PSI = sum (q_i - p_i) * ln(q_i / p_i) (bo'sh bin -> eps)
an'anaviy qoida (qonun emas!): < 0.1 barqaror
0.1-0.25 o'rtacha siljish
> 0.25 katta siljish
KS (Kolmogorov-Smirnov), ikki namunali:
D = max_x |F_ref(x) - F_cur(x)| (empirik CDF lar)
p-qiymat: sqrt(n*m/(n+m)) * D ~ Kolmogorov taqsimoti (asimptotik)
D - effekt o'lchami [0, 1]; p - "tasodifmi?" savoliga javob
CHI-KVADRAT (kategoriyalar):
2 x K jadval (reference / current x toifalar)
X^2 = sum (kuzatilgan - kutilgan)^2 / kutilgan, df = K - 1
WASSERSTEIN (W1, "tuproq ko'chirish" masofasi):
W1 = integral |F_ref(x) - F_cur(x)| dx
ustun BIRLIGIDA (so'm, yosh, kun) -> reference std ga bo'lib taqqoslang
PSI va KS dan farqli: siljish QANCHA uzoqqa ekanini ham hisobga oladi| O'lchov | Tur | Nimani o'lchaydi | Kuchli tomoni | Zaif tomoni |
|---|---|---|---|---|
| PSI | son (bin) / kategoriya | ulushlar farqi | talqini oson, sanoatda keng tarqalgan | bin va eps ga bog'liq, kichik n da shovqinli |
| KS D | son | CDF lar orasidagi maksimal farq | binsiz, [0, 1] shkala | dumdagi o'zgarishga kam sezgir |
| chi-kvadrat | kategoriya | toifa ulushlari | klassik test, p-qiymat | katta n da har doim sezilarli |
| W1 | son | massani ko'chirish narxi | masofa ma'nosi bor | birlikka bog'liq, normallash kerak |
Chegaralar faqat reference dan olinadi — ular current ga moslashsa, siljish "yutilib" ketadi.
2.4. Bin tanlash tuzog'i
PSI oddiy ko'rinadi, lekin natija ikki tanlovga kuchli bog'liq:
1. BIN SONI
drift YO'Q bo'lsa ham, kichik n da har bin ulushi tasodifan tebranadi
PSI ning "shovqin tagligi" ~ (bin soni - 1) * (1/n_ref + 1/n_cur)
100 bin + 500 qator -> drift bo'lmasa ham PSI > 0.1
2. BIN TURI
teng kenglik (min..max ni teng bo'lish) QIYSHIQ ustunda:
ma'lumotning ko'pi 2-3 binda, qolganlari deyarli bo'sh
bitta xato yozuv (masalan 1e6) max ni cho'zadi ->
HAMMA ma'lumot bitta binga tushadi -> PSI ~ 0, drift ko'rinmaydi
KVANTIL chegaralar: har binda ~teng ulush, chetlanishga chidamli
3. EPS (bo'sh bin)
reference da yo'q toifa/bin paydo bo'lsa: ln(q / eps) - eps ga bog'liq
yangi toifani ALOHIDA metrika sifatida sanang ("ko'rilmagan ulush")2.5. p-qiymat va effekt o'lchami
p-qiymat savoli: "bu farq tasodifiy tebranishmi?"
biznes savoli: "bu farq modelga/foydalanuvchiga TA'SIR qiladimi?"
n oshgan sari test har qanday nolga teng bo'lmagan farqni ushlaydi:
0.02 std siljish, n = 500 -> p katta, "drift yo'q"
0.02 std siljish, n = 500 000 -> p ~ 1e-18, "sezilarli drift!"
effekt (D, W1, PSI) esa ikkala holatda ham ARZIMAS
AMALIY QOIDA: signal = statistik sezilarli VA effekt chegaradan katta
masalan: p < alfa (tuzatilgan) VA (D >= 0.1 yoki PSI >= 0.1)
effekt chegarasini biznes bilan kelishing, natijani ko'rishdan OLDINKatta ma'lumotda p-qiymat drift o'lchovi emas — u faqat "tasodif emas" deydi. Qaror uchun effekt o'lchami kerak (11-qismdagi "statistik va amaliy ahamiyat" farqi aynan shu).
2.6. Ko'p ustun — ko'p testlash
m = 40 ustun, har biri alfa = 0.05, drift YO'Q:
kutilgan yolg'on signal / kun = 40 * 0.05 = 2
P(kamida bitta signal) = 1 - 0.95^40 = 0.87
-> deyarli HAR KUNI qizil chiroq, lekin hech narsa bo'lmagan
TUZATISHLAR 11.9-bob:
Bonferroni: p < alfa / m FWER <= alfa (qat'iy, quvvat past)
BH (Benjamini-Hochberg): FDR <= alfa (yumshoqroq, quvvat yuqori)
p larni saralang: p_(1) <= ... <= p_(m)
eng katta k: p_(k) <= alfa * k / m -> 1..k rad etiladi
FWER - kamida bitta yolg'on signal ehtimoli
FDR - signallar ichidagi yolg'onlar ULUSHI (kutilgan)Monitoringda qaysi biri? Agar har signal odam vaqtini oladigan tekshiruv bo'lsa — FDR nazorati (BH) ko'pincha yaxshi murosa: haqiqiy driftlarning ko'pini topadi, signallar ichidagi yolg'onlar ulushini past tutadi.
2.7. Ko'p o'lchovli drift: domen klassifikatori
Bir o'lchovli testlar ustunlarni alohida ko'radi. Ikki ustun orasidagi korrelyatsiya o'zgarsa (masalan, avval yosh va daromad birga o'sardi, endi yo'q), har ustunning o'z taqsimoti o'zgarmasligi mumkin — bir o'lchovli testlar jim turadi.
DOMEN KLASSIFIKATORI (adversarial validation):
reference qatorlari -> belgi 0, current qatorlari -> belgi 1
klassifikator (masalan HistGB) cross-validation bilan o'qitiladi
AUC ~ 0.5 -> ikki to'plamni ajratib bo'lmaydi -> drift yo'q
AUC >> 0.5 -> farq bor; belgi ahamiyati - QAYSI ustunlar
AFZALLIGI: barcha ustunlar va ularning o'zaro bog'liqligi birga
bitta raqam; aralash turdagi ustunlar
KAMCHILIGI: model o'qitish kerak (sekinroq)
AUC ham "drift bor" deydi, "zararli" demaydi2.8. Bashorat drifti va belgilarsiz sifat taxmini
Belgilar (haqiqiy natija) ko'pincha kechikib keladi: mijoz ketdimi — bir oydan keyin ma'lum, kredit qaytdimi — yildan keyin. Shu vaqt ichida qo'lda bor narsa — kirishlar va bashoratlar.
BASHORAT TAQSIMOTI DRIFTI:
PSI(bashorat_ref, bashorat_cur)
model "ko'radigan" o'zgarishni ko'rsatadi: ahamiyatsiz ustundagi
drift bashoratni o'zgartirmaydi -> signal yo'q (bu yaxshi!)
ISHONCH ASOSIDA SIFAT TAXMINI (belgilarsiz):
kalibrlangan model uchun: kutilgan aniqlik = mean(max(p, 1 - p))
g'oya: "model 0.9 ishonch bilan aytgan bo'lsa, 90% hollarda to'g'ri"
CHEKLOVI (juda muhim):
bu faqat P(y|X) o'zgarmagan va model yangi hududda ham
KALIBRLANGAN bo'lsa to'g'ri
konsept drift -> model o'zini xuddi avvalgidek ishonchli his qiladi,
taxmin "hammasi joyida" deydi, haqiqiy aniqlik tushgan
yangi hududga siljish -> model ekstrapolyatsiyada ORTIQCHA ishonchliBelgilarsiz taxmin — erta ogohlantirish, dalil emas. Uni belgilar kelgach haqiqiy sifat bilan albatta tekshiring.
2.9. Drift va sifat — to'rt holat
sifat O'ZGARMADI sifat TUSHDI
drift KO'RINDI ahamiyatsiz ustunda muhim ustunda kovariat drift
drift (yolg'on xavotir) (model yangi hududda xato),
belgilar drifti (chegara)
drift KO'RINMADI hammasi joyida KONSEPT DRIFT - eng xavflisiShuning uchun monitoring ikki qavatli bo'ladi:
- Kirish va bashorat drifti — tez, belgisiz, erta ogohlantirish. Muhim ustunlarga (belgi ahamiyati bo'yicha) ko'proq vazn bering.
- Sifat metrikasi — belgilar kelgach, kechikib, lekin haqiqiy javob. Konsept driftni faqat shu ushlaydi.
2.10. Oqim ustida drift aniqlash
Partiyalar (kun, hafta) o'rniga har bir hodisa kelib turadigan oqimda detektor har qadamda qaror qabul qiladi.
OYNALAR:
qat'iy reference oyna vs sirpanuvchi oxirgi-N oyna -> test (z, KS)
oyna kichik -> tez, lekin shovqinli; katta -> barqaror, lekin sekin
PAGE-HINKLEY (o'rtachaning OSHISHI, masalan xato darajasi):
x_t - kuzatuv (1 = model xato qildi, 0 = to'g'ri)
xbar_t - shu paytgacha o'rtacha
m_t = sum_{i<=t} (x_i - xbar_i - delta) (delta - chidam)
M_t = min_{i<=t} m_i
SIGNAL: m_t - M_t > lambda
lambda katta -> kam yolg'on signal, lekin KECHROQ aniqlaydi
xotira O(1): faqat 4 ta son saqlanadi
ADWIN (ADaptive WINdowing) g'oyasi:
oyna o'zgaruvchan uzunlikda; har qadamda oynani ikki qismga
bo'lib ko'radi - o'rtachalar Hoeffding chegarasidan ko'proq farq
qilsa, ESKI qismni tashlaydi. Oyna uzunligi o'zi "barqaror davr"
uzunligiga moslashadi. (river kutubxonasida tayyor; bu darsda g'oya.)
ASOSIY MUROSA:
aniqlash KECHIKISHI <-> YOLG'ON SIGNAL chastotasi
chegarani drift bo'lmagan tarixiy oqimda kalibrlang:
"yiliga ko'pi bilan N ta yolg'on signal" -> lambda2.11. Reference oyna va signaldan keyingi harakat
Har qanday drift o'lchovi "nimaga nisbatan?" degan savolga bog'liq. Reference tanlovi natijani o'lchov turidan kam bo'lmagan darajada belgilaydi.
REFERENCE VARIANTLARI:
o'quv to'plami model "bilgan" dunyo; eng keng tarqalgan
validatsiya/test davri model tekshirilgan dunyo
sirpanuvchi (o'tgan hafta) to'satdan o'zgarishni ko'radi,
asta-sekin driftni KO'RMAYDI
o'tgan yilning o'sha davri mavsumiy ma'lumot uchun
TANLOV QOIDASI:
reference = "model yaxshi ishlagan va biz ishonadigan davr"
hajmi yetarli (bin ulushlari barqaror bo'lishi uchun)
mavsum bo'lsa - mavsumni qamrasin yoki o'sha davr bilan solishtiring
model qayta o'qitilganda reference ham YANGILANADISignal chiqdi. Keyin nima?
1. TEKSHIRISH: ma'lumot xatosimi? (sxema, birlik, bo'sh qiymatlar,
yangi manba) -> ha bo'lsa, bu drift emas, INSIDENT (27.3, 27.11)
2. TA'SIR: bashorat drifti bormi? muhim ustunmi? belgilar kelganmi?
3. QAROR (oldindan yozilgan runbook bo'yicha):
ta'sir yo'q -> kuzatishda davom, reference ni ko'rib chiqish
ma'lumot xatosi -> manbani tuzatish, zarar ko'rgan bashoratlar
haqiqiy drift, sifat tushgan -> qayta o'qitish triggeri 27.13-bob
konsept drift, belgilar kam -> qoida/chegara bilan vaqtinchalik
himoya, belgilar to'planishini kutish
4. HUJJAT: signal, sabab, qaror, natija - keyingi safar uchunDrift signali — savol, javob emas. Uni ma'lumot xatosi, ahamiyatsiz o'zgarish va haqiqiy muammoga ajratish uchun oldindan yozilgan tartib kerak.
2.12. Tuzoqlar
Asosiy tuzoqlar: katta namunada p-qiymatni drift o'lchovi deb olish; har ustunni tuzatishsiz testlash va "signal charchoqligi"; PSI ni teng kenglikdagi binlarda yoki juda ko'p binda hisoblash; chegaralarni current dan olish; yangi toifani eps ichida yashirish; har qanday driftni "qayta o'qitish kerak" deb talqin qilish; kirish detektorlari jim bo'lsa "model yaxshi" deb xulosa qilish (konsept drift); belgilarsiz taxminni haqiqiy sifat o'rniga ishlatish; mavsumiy o'zgarishni drift deb qabul qilish; oqim detektorining chegarasini drift bo'lmagan ma'lumotda kalibrlamaslik.
3. Tez ma'lumotnoma
import numpy as np
from scipy import stats
def psi(ref, cur, bins=10, eps=1e-4):
ichki = np.unique(np.quantile(ref, np.linspace(0, 1, bins + 1)[1:-1]))
k = len(ichki) + 1
p = np.bincount(np.searchsorted(ichki, ref, side="right"), minlength=k) / len(ref)
q = np.bincount(np.searchsorted(ichki, cur, side="right"), minlength=k) / len(cur)
p, q = np.clip(p, eps, None), np.clip(q, eps, None)
return float(np.sum((q - p) * np.log(q / p)))
r = stats.ks_2samp(ref, cur) # r.statistic (D), r.pvalue
w = stats.wasserstein_distance(ref, cur) / ref.std()
chi = stats.chi2_contingency(jadval_2xK, correction=False)
def bh(p, alfa=0.05): # Benjamini-Hochberg
m = len(p)
tartib = np.argsort(p)
otdi = p[tartib] <= alfa * np.arange(1, m + 1) / m
rad = np.zeros(m, dtype=bool)
if otdi.any():
rad[tartib[:np.max(np.nonzero(otdi)[0]) + 1]] = True
return rad
signal = (p_tuzatilgan < 0.05) & (D >= 0.1) # sezilarli VA katta
# domen klassifikatori
X = np.vstack([X_ref, X_cur]); d = np.r_[np.zeros(len(X_ref)), np.ones(len(X_cur))]
s = cross_val_predict(HistGradientBoostingClassifier(), X, d, cv=3,
method="predict_proba")[:, 1]
domen_auc = roc_auc_score(d, s) # ~0.5 -> drift yo'q
taxminiy_aniqlik = np.mean(np.maximum(p, 1 - p)) # faqat kalibrlangan va
# konsept drift yo'q bo'lsaQaysi vaziyatda nima
| Savol | Vosita |
|---|---|
| Son ustun siljidimi? | KS D + p, W1/std, PSI (kvantil binlar) |
| Kategoriya ulushlari? | chi-kvadrat, PSI, "ko'rilmagan toifa ulushi" |
| Ustunlar birgalikda? | domen klassifikatori AUC |
| Model buni sezdimi? | bashorat taqsimoti PSI |
| Sifat tushdimi? | belgilar kelgach — haqiqiy metrika (yagona ishonchli javob) |
| Ko'p ustun | BH yoki Bonferroni + effekt chegarasi |
| Oqim | Page-Hinkley / ADWIN, chegara drift yo'q tarixda kalibrlanadi |
Drift xulosasi
drift turi: P(X) / P(y|X) / P(y) - qaysi qism o'zgardi?
signal = sezilarli (tuzatilgan p) VA katta (effekt chegarasi)
PSI: kvantil chegaralar reference dan; bin soni n ga mos
domen AUC - ko'p o'lchovli; bashorat PSI - model ko'rgan drift
konsept drift - faqat belgilar bilan; belgisiz taxmin - faqat ogohlantirish
oqim: kechikish <-> yolg'on signal4. Batafsil misollar
Misollar real numpy/scipy/sklearn bilan (Python 3.14). Har misol mustaqil ishlaydi.
Misol 1 — PSI, KS, chi-kvadrat va Wasserstein noldan; bin tanlash tuzog'i
"""Drift o'lchovlari noldan: PSI, KS, chi-kvadrat, Wasserstein (scipy bilan solishtirib)."""
import numpy as np
from scipy import stats
def psi(ref, cur, bins=10, usul="kvantil", eps=1e-4):
"""Population Stability Index; chegaralar FAQAT reference dan olinadi."""
if usul == "kvantil":
ichki = np.quantile(ref, np.linspace(0, 1, bins + 1)[1:-1])
else: # teng kenglik
ichki = np.linspace(ref.min(), ref.max(), bins + 1)[1:-1]
ichki = np.unique(ichki)
k = len(ichki) + 1 # chetlar -inf va +inf gacha
p = np.bincount(np.searchsorted(ichki, ref, side="right"),
minlength=k) / len(ref)
q = np.bincount(np.searchsorted(ichki, cur, side="right"),
minlength=k) / len(cur)
p, q = np.clip(p, eps, None), np.clip(q, eps, None)
return float(np.sum((q - p) * np.log(q / p)))
def psi_kat(ref, cur, eps=1e-4):
toifalar = sorted(set(ref) | set(cur))
p = np.array([np.mean(ref == t) for t in toifalar])
q = np.array([np.mean(cur == t) for t in toifalar])
p, q = np.clip(p, eps, None), np.clip(q, eps, None)
return float(np.sum((q - p) * np.log(q / p)))
def kolmogorov_sf(x, hadlar=100):
"""P(K > x) - Kolmogorov taqsimotining dumi (asimptotik)."""
if x < 0.2:
return 1.0
k = np.arange(1, hadlar + 1)
return float(min(1.0, 2 * np.sum((-1) ** (k - 1) * np.exp(-2 * k**2 * x**2))))
def ks_noldan(a, b):
a, b = np.sort(a), np.sort(b)
nuqtalar = np.concatenate([a, b])
fa = np.searchsorted(a, nuqtalar, side="right") / len(a)
fb = np.searchsorted(b, nuqtalar, side="right") / len(b)
d = float(np.max(np.abs(fa - fb)))
en = np.sqrt(len(a) * len(b) / (len(a) + len(b)))
return d, kolmogorov_sf(en * d)
def chi2_noldan(ref, cur):
toifalar = sorted(set(ref) | set(cur))
jadval = np.array([[np.sum(ref == t) for t in toifalar],
[np.sum(cur == t) for t in toifalar]], dtype=float)
kutilgan = jadval.sum(1, keepdims=True) * jadval.sum(0) / jadval.sum()
x2 = float(np.sum((jadval - kutilgan) ** 2 / kutilgan))
df = len(toifalar) - 1
return x2, df, float(stats.chi2.sf(x2, df))
def wasserstein_noldan(a, b):
"""W1 = integral |F_a(x) - F_b(x)| dx (1 o'lchovli)."""
a, b = np.sort(a), np.sort(b)
x = np.sort(np.concatenate([a, b]))
fa = np.searchsorted(a, x[:-1], side="right") / len(a)
fb = np.searchsorted(b, x[:-1], side="right") / len(b)
return float(np.sum(np.abs(fa - fb) * np.diff(x)))
def main() -> None:
rng = np.random.default_rng(0)
ref = rng.lognormal(4.0, 0.5, 5000) # "oylik to'lov" - qiyshiq
std = ref.std()
print("=== 1. Noldan va scipy: bir xil javob ===")
cur = rng.lognormal(4.1, 0.5, 2000)
d, p = ks_noldan(ref, cur)
r = stats.ks_2samp(ref, cur, method="asymp")
print(f" KS D: noldan {d:.5f} scipy {r.statistic:.5f}")
print(f" KS p: noldan {p:.3e} scipy {r.pvalue:.3e}")
w = wasserstein_noldan(ref, cur)
print(f" W1: noldan {w:.5f} scipy "
f"{stats.wasserstein_distance(ref, cur):.5f}")
t_ref = rng.choice(["A", "B", "C", "D"], 5000, p=[0.4, 0.3, 0.2, 0.1])
t_cur = rng.choice(["A", "B", "C", "D"], 2000, p=[0.35, 0.3, 0.22, 0.13])
x2, df, p2 = chi2_noldan(t_ref, t_cur)
jad = np.array([[np.sum(t_ref == t) for t in "ABCD"],
[np.sum(t_cur == t) for t in "ABCD"]])
s = stats.chi2_contingency(jad, correction=False)
print(f" chi^2: noldan {x2:.4f} (df={df}, p={p2:.4f}) "
f"scipy {s.statistic:.4f} (p={s.pvalue:.4f})")
print("\n=== 2. Ssenariylar: bir ustun, to'rt o'lchov ===")
ssenariy = {
"drift yo'q": rng.lognormal(4.0, 0.5, 2000),
"o'rtacha +2%": rng.lognormal(4.0, 0.5, 2000) * 1.02,
"o'rtacha +10%": rng.lognormal(4.0, 0.5, 2000) * 1.10,
"o'rtacha +30%": rng.lognormal(4.0, 0.5, 2000) * 1.30,
"tarqoqlik x1.5": np.exp(4.0 + 0.75 * rng.normal(0, 1, 2000)),
}
print(f" {'ssenariy':<16} {'PSI':>7} {'KS D':>7} {'KS p':>10} "
f"{'W1/std':>7}")
for nom, c in ssenariy.items():
d, p = ks_noldan(ref, c)
print(f" {nom:<16} {psi(ref, c):>7.4f} {d:>7.4f} {p:>10.2e} "
f"{wasserstein_noldan(ref, c) / std:>7.4f}")
print("\n=== 3. Bin tanlash tuzog'i (1): bin soni va shovqin ===")
print(" drift YO'Q, current n=500; 200 takror o'rtachasi")
print(f" {'bin':>5} {'PSI o_rt':>9} {'PSI>0.1 ulushi':>15}")
for b in [5, 10, 20, 50, 100]:
qiymatlar = [psi(ref, rng.lognormal(4.0, 0.5, 500), bins=b)
for _ in range(200)]
qiymatlar = np.array(qiymatlar)
print(f" {b:>5} {qiymatlar.mean():>9.4f} "
f"{np.mean(qiymatlar > 0.1):>15.3f}")
print("\n=== 4. Bin tanlash tuzog'i (2): teng kenglik va bitta xato ===")
ichki = np.linspace(ref.min(), ref.max(), 11)[1:-1]
ulush = np.bincount(np.searchsorted(ichki, ref, side="right"),
minlength=10) / len(ref)
print(f" teng kenglik, 10 bin ulushlari: {np.round(ulush, 3).tolist()}")
ref_xato = np.append(ref, 1e6) # bitta xato yozuv
print(f" {'ssenariy':<16} {'kvantil':>8} {'teng':>8} {'teng+xato':>10}")
for nom in ["o'rtacha +10%", "o'rtacha +30%"]:
c = ssenariy[nom]
print(f" {nom:<16} {psi(ref, c):>8.4f} "
f"{psi(ref, c, usul='teng'):>8.4f} "
f"{psi(ref_xato, c, usul='teng'):>10.4f}")
print("\n=== 5. Kategoriya: yangi qiymat paydo bo'ldi ===")
t_yangi = t_cur.copy()
t_yangi[:100] = "E" # reference da yo'q
for eps in [1e-4, 1e-6]:
print(f" eps={eps:.0e}: PSI {psi_kat(t_ref, t_yangi, eps):.4f}")
x2, df, p2 = chi2_noldan(t_ref, t_yangi)
print(f" chi^2 {x2:.1f} (df={df}), p={p2:.2e}")
print(f" reference da yo'q toifa ulushi: {np.mean(t_yangi == 'E'):.3f}")
print(" ⭐ Yangi toifani alohida sanang - PSI qiymati eps ga bog'liq")
if __name__ == "__main__":
main()Natijaning muhim qismi:
=== 1. Noldan va scipy: bir xil javob ===
KS D: noldan 0.10840 scipy 0.10840
KS p: noldan 5.254e-15 scipy 4.452e-15
W1: noldan 7.23445 scipy 7.23445
chi^2: noldan 35.6664 (df=3, p=0.0000) scipy 35.6664 (p=0.0000)
=== 2. Ssenariylar: bir ustun, to'rt o'lchov ===
ssenariy PSI KS D KS p W1/std
drift yo'q 0.0037 0.0250 3.34e-01 0.0279
o'rtacha +2% 0.0101 0.0338 7.65e-02 0.0463
o'rtacha +10% 0.0427 0.0912 9.56e-11 0.2058
o'rtacha +30% 0.2521 0.2150 8.77e-58 0.5568
tarqoqlik x1.5 0.1837 0.1002 6.97e-13 0.4733
=== 3. Bin tanlash tuzog'i (1): bin soni va shovqin ===
drift YO'Q, current n=500; 200 takror o'rtachasi
bin PSI o_rt PSI>0.1 ulushi
5 0.0088 0.000
10 0.0195 0.000
20 0.0419 0.000
50 0.1110 0.695
100 0.2755 1.000
=== 4. Bin tanlash tuzog'i (2): teng kenglik va bitta xato ===
teng kenglik, 10 bin ulushlari: [0.186, 0.413, 0.234, 0.101, 0.039, 0.017, 0.007, 0.002, 0.001, 0.0]
ssenariy kvantil teng teng+xato
o'rtacha +10% 0.0427 0.0388 0.0001
o'rtacha +30% 0.2521 0.2432 0.0001
=== 5. Kategoriya: yangi qiymat paydo bo'ldi ===
eps=1e-04: PSI 0.3350
eps=1e-06: PSI 0.5659
chi^2 284.4 (df=4), p=2.53e-60
reference da yo'q toifa ulushi: 0.050
⭐ Yangi toifani alohida sanang - PSI qiymati eps ga bog'liqNatija tahlili.
1-bo'lim — noldan yozilgan funksiyalar scipy bilan mos: KS statistikasi D = 0.10840, Wasserstein 7.23445, chi-kvadrat 35.6664 — raqamma-raqam bir xil. KS p-qiymatida kichik farq bor (5.3e-15 va 4.5e-15): biz sof asimptotik Kolmogorov qatorini ishlatdik, scipy esa chekli namuna uchun kichik tuzatish qo'shadi. Bunday kichik p-qiymatlarda bu farq qarorga ta'sir qilmaydi.
2-bo'lim — bir ustunda to'rt o'lchov. "Drift yo'q" da hammasi kichik (PSI 0.0037, p 0.33). +2% siljishni hech bir o'lchov sezilarli demadi (p 0.077). +10% da KS p-qiymati juda kichik (9.6e-11), lekin PSI atigi 0.0427 — an'anaviy 0.1 chegarasidan ancha past. +30% da PSI 0.2521 — "katta siljish" chegarasidan o'tdi. E'tibor bering: "tarqoqlik x1.5" da o'rtacha deyarli o'zgarmagan, lekin PSI 0.1837 va W1/std 0.4733 — o'lchovlar faqat o'rtachani emas, taqsimot shaklini ham ko'radi.
3-bo'lim — birinchi bin tuzog'i. Drift yo'q, current da 500 qator. 10 binda PSI o'rtachasi 0.0195, 50 binda 0.1110 — 200 takrorning 69.5% ida 0.1 chegarasidan oshdi; 100 binda esa har safar (1.000). Ya'ni ko'p bin + kichik n = yolg'on drift. Bin sonini n ga moslang (odatda 10).
4-bo'lim — ikkinchi bin tuzog'i. Qiyshiq ustunda teng kenglikdagi 10 bin ulushlari juda notekis: oxirgi uch binda jami 0.3% ma'lumot. Shunga qaramay toza ma'lumotda natija kvantil binlarga yaqin. Lekin reference ga bitta xato yozuv (1e6) qo'shildi — va teng kenglikdagi PSI +30% siljishda ham 0.0001 ga tushdi: maksimum cho'zildi, butun ma'lumot bitta binga to'plandi, drift butunlay ko'rinmay qoldi. Kvantil chegaralar bitta chetlanishga chidamli.
5-bo'lim — reference da yo'q toifa (E, 5%). PSI eps ga bog'liq: 1e-4 da 0.3350, 1e-6 da 0.5659 — bir xil ma'lumot, ikki xil "drift kattaligi". Shuning uchun yangi toifa alohida, tushunarli metrika bilan sanaladi: "ko'rilmagan toifa ulushi 0.050".
Misol 2 — Katta namuna va ko'p testlash tuzoqlari
"""Ikki tuzoq: katta namunada p-qiymat va ko'p ustunda ko'p testlash."""
import numpy as np
from scipy import stats
def ks_p(ref_sorted, cur):
"""Tez KS (asimptotik p): ref oldindan saralangan."""
cur = np.sort(cur)
x = np.concatenate([ref_sorted, cur])
d = np.max(np.abs(np.searchsorted(ref_sorted, x, side="right") / len(ref_sorted)
- np.searchsorted(cur, x, side="right") / len(cur)))
en = np.sqrt(len(ref_sorted) * len(cur) / (len(ref_sorted) + len(cur)))
return float(stats.kstwobign.sf(en * d))
def bonferroni(p, alfa):
return p < alfa / len(p)
def bh(p, alfa):
"""Benjamini-Hochberg: FDR <= alfa."""
m = len(p)
tartib = np.argsort(p)
chegara = alfa * np.arange(1, m + 1) / m
otdi = p[tartib] <= chegara
rad = np.zeros(m, dtype=bool)
if otdi.any():
k = np.max(np.nonzero(otdi)[0])
rad[tartib[:k + 1]] = True
return rad
def main() -> None:
rng = np.random.default_rng(1)
print("=== 1. Bir xil KICHIK siljish (0.02 std), turli n ===")
d_nazariy = stats.norm.cdf(0.01) - stats.norm.cdf(-0.01)
print(f" haqiqiy (populyatsiya) D = {d_nazariy:.4f}, W1 = 0.0200")
print(f" {'n':>8} {'KS D':>7} {'W1':>7} {'KS p':>10} "
f"{'p<0.05':>7} {'D>=0.1':>7}")
for n in [500, 5000, 50_000, 500_000]:
ref = rng.normal(0, 1, n)
cur = rng.normal(0.02, 1, n)
r = stats.ks_2samp(ref, cur, method="asymp")
w = stats.wasserstein_distance(ref, cur)
print(f" {n:>8} {r.statistic:>7.4f} {w:>7.4f} {r.pvalue:>10.2e} "
f"{str(r.pvalue < 0.05):>7} {str(r.statistic >= 0.1):>7}")
print(" KATTA siljish (0.3 std), n=500 - solishtirish uchun:")
ref, cur = rng.normal(0, 1, 500), rng.normal(0.3, 1, 500)
r = stats.ks_2samp(ref, cur, method="asymp")
w = stats.wasserstein_distance(ref, cur)
print(f" {500:>8} {r.statistic:>7.4f} {w:>7.4f} {r.pvalue:>10.2e} "
f"{str(r.pvalue < 0.05):>7} {str(r.statistic >= 0.1):>7}")
print("\n=== 2. 40 ustun, 200 kun, HECH QAYERDA drift yo'q ===")
ustun, kun, n, alfa = 40, 200, 400, 0.05
ref = np.sort(rng.normal(0, 1, (ustun, 2000)), axis=1)
pq = np.empty((kun, ustun))
for t in range(kun):
cur = rng.normal(0, 1, (ustun, n))
pq[t] = [ks_p(ref[j], cur[j])
for j in range(ustun)]
usullar = {"tuzatishsiz": lambda p: p < alfa,
"Bonferroni": lambda p: bonferroni(p, alfa),
"BH (FDR)": lambda p: bh(p, alfa)}
print(f" {'usul':<12} {'signal/kun':>11} {'>=1 signal kunlar':>18}")
for nom, f in usullar.items():
rad = np.array([f(pq[t]) for t in range(kun)])
print(f" {nom:<12} {rad.sum(1).mean():>11.2f} "
f"{rad.any(1).mean():>18.3f}")
print(f" nazariya (tuzatishsiz): 1 - 0.95^40 = {1 - 0.95 ** 40:.3f}")
print("\n=== 3. 40 ustundan 4 tasida haqiqiy drift (0.25 std) ===")
haqiqiy = np.zeros(ustun, dtype=bool)
haqiqiy[:4] = True
for t in range(kun):
cur = rng.normal(0, 1, (ustun, n))
cur[haqiqiy] += 0.25
pq[t] = [ks_p(ref[j], cur[j])
for j in range(ustun)]
print(f" {'usul':<12} {'topildi (4 dan)':>16} {'yolg_on/kun':>12} "
f"{'yolg_on ulush':>14}")
for nom, f in usullar.items():
rad = np.array([f(pq[t]) for t in range(kun)])
top = rad[:, haqiqiy].sum(1).mean()
yol = rad[:, ~haqiqiy].sum(1).mean()
ulush = np.mean([rad[t, ~haqiqiy].sum() / max(rad[t].sum(), 1)
for t in range(kun)])
print(f" {nom:<12} {top:>16.2f} {yol:>12.2f} {ulush:>14.3f}")
print(" ⭐ Signal = statistik sezilarli VA amaliy katta (effekt chegarasi)")
if __name__ == "__main__":
main()Natijaning muhim qismi:
=== 1. Bir xil KICHIK siljish (0.02 std), turli n ===
haqiqiy (populyatsiya) D = 0.0080, W1 = 0.0200
n KS D W1 KS p p<0.05 D>=0.1
500 0.0700 0.1423 1.65e-01 False False
5000 0.0100 0.0154 9.62e-01 False False
50000 0.0129 0.0289 4.95e-04 True False
500000 0.0091 0.0218 2.00e-18 True False
KATTA siljish (0.3 std), n=500 - solishtirish uchun:
500 0.1440 0.2706 5.49e-05 True True
=== 2. 40 ustun, 200 kun, HECH QAYERDA drift yo'q ===
usul signal/kun >=1 signal kunlar
tuzatishsiz 1.74 0.810
Bonferroni 0.06 0.050
BH (FDR) 0.06 0.050
nazariya (tuzatishsiz): 1 - 0.95^40 = 0.871
=== 3. 40 ustundan 4 tasida haqiqiy drift (0.25 std) ===
usul topildi (4 dan) yolg_on/kun yolg_on ulush
tuzatishsiz 3.92 1.54 0.246
Bonferroni 2.88 0.02 0.004
BH (FDR) 3.42 0.17 0.035
⭐ Signal = statistik sezilarli VA amaliy katta (effekt chegarasi)Natija tahlili.
1-bo'lim — bir xil, arzimas siljish (0.02 std, populyatsiyadagi haqiqiy D = 0.0080). n = 500 va 5 000 da test "farq yo'q" dedi. n = 50 000 da p 4.95e-04, n = 500 000 da 2.00e-18 — "juda sezilarli drift". Lekin effekt o'lchami o'zgarmadi: D 0.0091, W1 0.0218 — haqiqiy qiymatga yaqinlashdi, xolos. Test "farq tasodif emas" deyapti va to'g'ri aytyapti — lekin bu farq amaliy jihatdan ahamiyatsiz. Solishtirish uchun haqiqiy katta siljish (0.3 std) atigi 500 qatorda ham ikkala mezondan o'tdi (D 0.1440). "Sezilarli VA katta" qoidasi ikkala holatni to'g'ri ajratadi.
2-bo'lim — 40 ustun, 200 kun, drift umuman yo'q. Tuzatishsiz testda kuniga o'rtacha 1.74 ta yolg'on signal va kunlarning 81% ida kamida bitta qizil ustun. Nazariy qiymat 0.871; biroz pastroq chiqqani asimptotik KS p-qiymatining kichik namunada biroz konservativ ekanidan. Bonferroni ham, BH ham yolg'on signalli kunlarni 5% ga tushirdi — to'liq nol gipotezada BH ham FWER ni nazorat qiladi.
3-bo'lim — 40 ustundan 4 tasida haqiqiy drift (0.25 std). Tuzatishsiz: 4 tadan 3.92 ta topildi, lekin kuniga 1.54 ta yolg'on — signallarning 24.6% i yolg'on. Bonferroni yolg'onni deyarli yo'q qildi (0.004), lekin haqiqiy driftlardan faqat 2.88 tasini topdi. BH o'rtada: 3.42 ta topildi, yolg'on ulush 3.5% — belgilangan 5% FDR dan past. Monitoring uchun bu odatda eng yaxshi murosa.
Misol 3 — Drift va model sifati: nima ko'rinadi, nima ko'rinmaydi
"""Drift va model sifati: kirish detektorlari nimani ko'radi, nimani ko'rmaydi."""
import numpy as np
from sklearn.ensemble import HistGradientBoostingClassifier
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import accuracy_score, roc_auc_score
from sklearn.model_selection import StratifiedKFold, cross_val_predict
from scipy import stats
USTUNLAR = ["x1", "x2", "x3", "x4"]
def haqiqiy_p(X, x2_koef=-1.0):
x1, x2, x3 = X[:, 0], X[:, 1], X[:, 2]
f = -0.3 + 1.6 * x1 - 0.5 * x1**2 + x2_koef * x2 + 0.3 * x3
return 1 / (1 + np.exp(-f))
def yarat(rng, n, siljish=None, x2_koef=-1.0, musbat=None):
if musbat is not None: # belgilar drifti: P(y) o'zgaradi,
X, y, p = yarat(rng, 8 * n) # P(X|y) esa o'sha-o'sha
i1 = rng.choice(np.flatnonzero(y == 1), int(musbat * n), replace=False)
i0 = rng.choice(np.flatnonzero(y == 0), n - len(i1), replace=False)
i = rng.permutation(np.r_[i1, i0])
return X[i], y[i], p[i]
X = rng.normal(0, 1, (n, 4))
if siljish:
j, qiymat = siljish
X[:, j] += qiymat
p = haqiqiy_p(X, x2_koef)
y = (rng.random(n) < p).astype(int)
return X, y, p
def psi(ref, cur, bins=10, eps=1e-4):
ichki = np.unique(np.quantile(ref, np.linspace(0, 1, bins + 1)[1:-1]))
k = len(ichki) + 1
p = np.bincount(np.searchsorted(ichki, ref, side="right"),
minlength=k) / len(ref)
q = np.bincount(np.searchsorted(ichki, cur, side="right"),
minlength=k) / len(cur)
p, q = np.clip(p, eps, None), np.clip(q, eps, None)
return float(np.sum((q - p) * np.log(q / p)))
def domen_auc(X_ref, X_cur):
"""Reference (0) va current (1) ni ajrata oladigan klassifikator AUC i."""
X = np.vstack([X_ref, X_cur])
d = np.r_[np.zeros(len(X_ref)), np.ones(len(X_cur))]
clf = HistGradientBoostingClassifier(max_iter=60, random_state=0)
s = cross_val_predict(clf, X, d, method="predict_proba",
cv=StratifiedKFold(3, shuffle=True,
random_state=0))[:, 1]
return roc_auc_score(d, s)
def main() -> None:
rng = np.random.default_rng(3)
X_tr, y_tr, _ = yarat(rng, 6000)
model = LogisticRegression(max_iter=1000).fit(X_tr, y_tr)
X_ref, y_ref, _ = yarat(rng, 3000)
p_ref = model.predict_proba(X_ref)[:, 1]
print("=== 1. Model va reference oyna ===")
print(f" koeffitsientlar: "
f"{dict(zip(USTUNLAR, np.round(model.coef_[0], 3).tolist()))}")
print(f" reference: P(y=1) {y_ref.mean():.3f}, AUC {roc_auc_score(y_ref, p_ref):.4f}, "
f"aniqlik {accuracy_score(y_ref, p_ref > 0.5):.4f}")
print(" x1, x2 - muhim; x4 - model uchun ahamiyatsiz")
ssenariylar = {
"A drift yo'q": dict(),
"B x4 +1.0 (muhim emas)": dict(siljish=(3, 1.0)),
"C x1 +1.5 (muhim)": dict(siljish=(0, 1.5)),
"D konsept: x2 belgisi": dict(x2_koef=+1.0),
"E belgilar: P(y)=0.7": dict(musbat=0.7),
}
natija = {}
for nom, kw in ssenariylar.items():
X, y, p_haq = yarat(rng, 3000, **kw)
p = model.predict_proba(X)[:, 1]
ks = [stats.ks_2samp(X_ref[:, j], X[:, j], method="asymp")
for j in range(4)]
signal = [USTUNLAR[j] for j in range(4)
if ks[j].pvalue < 0.05 / 4 and ks[j].statistic >= 0.1]
natija[nom] = {
"signal": ",".join(signal) if signal else "-",
"domen": domen_auc(X_ref, X),
"psi_p": psi(p_ref, p),
"taxmin": float(np.mean(np.maximum(p, 1 - p))),
"aniqlik": accuracy_score(y, p > 0.5),
"auc": roc_auc_score(y, p),
"ideal": roc_auc_score(y, p_haq),
}
print("\n=== 2. Kirish detektorlari ===")
print(f" {'ssenariy':<24} {'KS signal':>10} {'domen AUC':>10} "
f"{'bashorat PSI':>13}")
for nom, r in natija.items():
print(f" {nom:<24} {r['signal']:>10} {r['domen']:>10.3f} "
f"{r['psi_p']:>13.4f}")
print("\n=== 3. Haqiqiy sifat (belgilar kelgach) ===")
print(f" {'ssenariy':<24} {'taxmin':>7} {'aniqlik':>8} {'AUC':>7} "
f"{'ideal AUC':>10}")
for nom, r in natija.items():
print(f" {nom:<24} {r['taxmin']:>7.3f} {r['aniqlik']:>8.3f} "
f"{r['auc']:>7.3f} {r['ideal']:>10.3f}")
print("\n=== 4. Xulosa (natijadan hisoblangan) ===")
asos = natija["A drift yo'q"]["auc"]
for nom, r in natija.items():
korindi = r["signal"] != "-" or r["domen"] > 0.6
tushdi = (asos - r["auc"] > 0.02
or natija["A drift yo'q"]["aniqlik"] - r["aniqlik"] > 0.02)
xato = abs(r["taxmin"] - r["aniqlik"])
print(f" {nom:<24} drift ko'rindi: {str(korindi):<5} "
f"sifat tushdi: {str(tushdi):<5} |taxmin-haqiqiy| {xato:.3f}")
print(" ⭐ Kirish drifti != sifat tushishi; konsept drift - ko'rinmas")
if __name__ == "__main__":
main()Natijaning muhim qismi:
=== 1. Model va reference oyna ===
koeffitsientlar: {'x1': 1.298, 'x2': -1.01, 'x3': 0.294, 'x4': -0.015}
reference: P(y=1) 0.401, AUC 0.8313, aniqlik 0.7533
x1, x2 - muhim; x4 - model uchun ahamiyatsiz
=== 2. Kirish detektorlari ===
ssenariy KS signal domen AUC bashorat PSI
A drift yo'q - 0.508 0.0092
B x4 +1.0 (muhim emas) x4 0.732 0.0110
C x1 +1.5 (muhim) x1 0.847 1.2674
D konsept: x2 belgisi - 0.501 0.0089
E belgilar: P(y)=0.7 x1 0.582 0.1650
=== 3. Haqiqiy sifat (belgilar kelgach) ===
ssenariy taxmin aniqlik AUC ideal AUC
A drift yo'q 0.763 0.758 0.837 0.851
B x4 +1.0 (muhim emas) 0.755 0.755 0.829 0.838
C x1 +1.5 (muhim) 0.800 0.661 0.662 0.785
D konsept: x2 belgisi 0.758 0.579 0.594 0.842
E belgilar: P(y)=0.7 0.739 0.707 0.825 0.833
=== 4. Xulosa (natijadan hisoblangan) ===
A drift yo'q drift ko'rindi: False sifat tushdi: False |taxmin-haqiqiy| 0.005
B x4 +1.0 (muhim emas) drift ko'rindi: True sifat tushdi: False |taxmin-haqiqiy| 0.000
C x1 +1.5 (muhim) drift ko'rindi: True sifat tushdi: True |taxmin-haqiqiy| 0.139
D konsept: x2 belgisi drift ko'rindi: False sifat tushdi: True |taxmin-haqiqiy| 0.179
E belgilar: P(y)=0.7 drift ko'rindi: True sifat tushdi: True |taxmin-haqiqiy| 0.032
⭐ Kirish drifti != sifat tushishi; konsept drift - ko'rinmasNatija tahlili.
Haqiqiy bog'liqlik x1 bo'yicha nochiziqli (parabola: x1 ~ 1.6 atrofida xavf eng yuqori), model esa logistik regressiya — reference hududida (x1 ~ N(0, 1)) bu yaqinlashish yaxshi ishlaydi (AUC 0.8313). x4 ga model deyarli nol vazn bergan (-0.015).
- A — drift yo'q. Detektorlar jim (domen AUC
0.508), ishonch asosidagi taxmin (0.763) haqiqiy aniqlikka (0.758) juda yaqin — kalibrlangan model uchun g'oya ishlaydi. - B — ahamiyatsiz ustunda drift. KS
x4ni ushladi, domen AUC0.732— "katta drift". Lekin bashorat PSI0.0110(o'zgarmadi) va AUC0.829— A dagi0.837dan farq0.02chegarasidan kichik. Bu yolg'on xavotir; uni bashorat drifti to'g'ri ajratdi. - C — muhim ustunda drift.
x1ma'lumotining katta qismi parabola cho'qqisi atrofiga va undan keyingi hududga o'tdi — u yerda xavfx1bilan endi o'smaydi, balki kamayadi, chiziqli model esa buni bilmaydi. AUC0.662ga tushdi, garchi ideal (haqiqiy ehtimollar bilan) AUC0.785bo'lsa ham — ya'ni vazifa qiyinlashgani emas, model yomonlashgani. Bashorat PSI1.2674— juda katta. Diqqat: ishonch asosidagi taxmin0.800, haqiqiy aniqlik0.661— model yangi hududda ortiqcha ishonchli, taxmin aldanadi. - D — konsept drift.
x2ning ta'siri teskarisiga o'zgardi, X taqsimoti o'sha-o'sha. KS signal yo'q, domen AUC0.501, bashorat PSI0.0089— hamma kirish detektorlari jim. Taxmin0.758— "hammasi joyida". Haqiqiy aniqlik esa0.579, AUC0.594. Bu drift faqat belgilar kelgach ko'rinadi. - E — belgilar drifti. Ketuvchilar ulushi
0.40dan0.70ga oshdi. X ham o'zgardi (KSx1ni ushladi, bashorat PSI0.1650). AUC deyarli o'zgarmadi (0.825) — tartiblash sifati saqlanadi; lekin0.5chegarasidagi aniqlik0.758dan0.707ga tushdi, chunki model eski ulushga kalibrlangan.
4-bo'lim jadvali darsning asosiy fikrini raqam bilan beradi: "drift ko'rindi" va "sifat tushdi" ustunlari bir-biriga mos kelmaydi (B va D).
Misol 4 — Oqim ustida: Page-Hinkley va oynali test
"""Oqim ustida drift: Page-Hinkley noldan, oynali test bilan solishtirish."""
import numpy as np
from scipy import stats
class PageHinkley:
"""O'rtachaning OSHISHINI kuzatadi (masalan, xato darajasi)."""
def __init__(self, delta=0.01, lam=20.0, min_n=30):
self.delta, self.lam, self.min_n = delta, lam, min_n
self.n, self.orta, self.m, self.m_min = 0, 0.0, 0.0, 0.0
def yangila(self, x):
self.n += 1
self.orta += (x - self.orta) / self.n
self.m += x - self.orta - self.delta
self.m_min = min(self.m_min, self.m)
return self.n >= self.min_n and self.m - self.m_min > self.lam
def oqim(rng, turi, uzunlik=4000, ozgarish=2000, p0=0.20, p1=0.30):
"""Har qadamda model xato qildimi (1) yoki yo'q (0)."""
p = np.full(uzunlik, p0)
if turi == "to'satdan":
p[ozgarish:] = p1
elif turi == "asta-sekin":
p[ozgarish:] = np.minimum(
p1, p0 + (p1 - p0) * np.arange(uzunlik - ozgarish) / 1000)
elif turi == "mavsumiy":
p = p0 + 0.05 * np.sin(2 * np.pi * np.arange(uzunlik) / 1000)
return (rng.random(uzunlik) < p).astype(float)
def birinchi_signal(detektor, xlar):
for t, x in enumerate(xlar):
if detektor.yangila(x):
return t
return None
def oynali_signal(xlar, ref=1000, oyna=500, qadam=100, alfa=0.001):
"""Birinchi 1000 qadam - reference; har 100 qadamda oxirgi 500 ni z-test."""
p_ref = xlar[:ref].mean()
for t in range(ref + oyna, len(xlar) + 1, qadam):
p_cur = xlar[t - oyna:t].mean()
p_bir = (p_ref * ref + p_cur * oyna) / (ref + oyna)
se = np.sqrt(p_bir * (1 - p_bir) * (1 / ref + 1 / oyna))
if se > 0 and stats.norm.sf((p_cur - p_ref) / se) < alfa:
return t - 1
return None
def baho(signal_ol, turi, seedlar, ozgarish=2000):
"""(yolg'on signal ulushi, {seed: kechikish}) - faqat o'zgarishdan keyin."""
yolgon, kechikish = 0, {}
for s in seedlar:
t = signal_ol(oqim(np.random.default_rng(s), turi))
if turi in ("yo'q", "mavsumiy"):
yolgon += t is not None
elif t is not None and t < ozgarish:
yolgon += 1
elif t is not None:
kechikish[s] = t - ozgarish
return yolgon / len(seedlar), kechikish
def main() -> None:
seedlar = range(100)
print("=== 1. Bitta oqim: xato darajasi 0.20 -> 0.30 (t=2000) ===")
xlar = oqim(np.random.default_rng(7), "to'satdan")
t = birinchi_signal(PageHinkley(lam=40), xlar)
print(f" Page-Hinkley (lambda=40) signal: t={t}, kechikish {t - 2000}")
print(f" oynali z-test signal: t={oynali_signal(xlar)}")
for a in [1000, 1900, 2100, 2500]:
print(f" t={a}: oxirgi 200 qadamdagi xato "
f"{xlar[a - 200:a].mean():.3f}")
print("\n=== 2. Lambda: yolg'on signal va kechikish (100 oqim) ===")
print(f" {'detektor':<16} {'yolg_on':>8} {'to_satdan':>10} "
f"{'topildi':>8} {'asta-sekin':>11} {'mavsumiy':>9}")
detektorlar = [(f"PH lambda={lam}",
lambda x, lam=lam: birinchi_signal(PageHinkley(lam=lam), x))
for lam in [10, 20, 40, 60, 80]]
detektorlar.append(("oynali z-test", oynali_signal))
jadval = {}
for nom, f in detektorlar:
yolgon, _ = baho(f, "yo'q", seedlar)
_, k_tos = baho(f, "to'satdan", seedlar)
_, k_ast = baho(f, "asta-sekin", seedlar)
mavs, _ = baho(f, "mavsumiy", seedlar)
jadval[nom] = (yolgon, k_tos)
med = lambda k: np.median(list(k.values())) if k else float("nan")
print(f" {nom:<16} {yolgon:>8.2f} {med(k_tos):>10.0f} "
f"{len(k_tos) / len(seedlar):>8.2f} {med(k_ast):>11.0f} "
f"{mavs:>9.2f}")
print(" kechikish - mediana (qadam); topildi - o'zgarishdan KEYIN")
print(" birinchi signal bergan oqimlar ulushi")
print("\n=== 3. Qaror: yolg'on signal <= 0.05 bo'lganlar ichida ===")
nomzod = sorted((np.median(list(k.values())), nom)
for nom, (yo, k) in jadval.items() if yo <= 0.05 and k)
(m1, a), (m2, b) = nomzod[0], nomzod[1]
print(f" eng tez ikkitasi: {a} ({m1:.0f}), {b} ({m2:.0f})")
umumiy = sorted(set(jadval[a][1]) & set(jadval[b][1]))
d = np.array([jadval[b][1][s] - jadval[a][1][s] for s in umumiy])
se = d.std(ddof=1) / np.sqrt(len(d))
print(f" juftlashgan farq ({b} - {a}): {d.mean():+.1f} qadam, "
f"SE {se:.1f}, sezilarli: {abs(d.mean()) > 2 * se}")
if abs(d.mean()) > 2 * se:
print(f" tanlov: {a if d.mean() > 0 else b}")
else:
print(" tezlikda farq yo'q -> boshqa mezon: mavsumiy yolg'on signal")
print(" va xotira (PH: O(1), oyna: 1500 qiymat)")
print(" ⭐ Tezlik va yolg'on signal - bitta tugmaning ikki uchi")
if __name__ == "__main__":
main()Natijaning muhim qismi:
=== 1. Bitta oqim: xato darajasi 0.20 -> 0.30 (t=2000) ===
Page-Hinkley (lambda=40) signal: t=2376, kechikish 376
oynali z-test signal: t=2499
t=1000: oxirgi 200 qadamdagi xato 0.225
t=1900: oxirgi 200 qadamdagi xato 0.150
t=2100: oxirgi 200 qadamdagi xato 0.240
t=2500: oxirgi 200 qadamdagi xato 0.310
=== 2. Lambda: yolg'on signal va kechikish (100 oqim) ===
detektor yolg_on to_satdan topildi asta-sekin mavsumiy
PH lambda=10 0.99 76 0.16 344 1.00
PH lambda=20 0.39 150 0.81 604 0.79
PH lambda=40 0.01 412 1.00 932 0.02
PH lambda=60 0.00 697 1.00 1193 0.00
PH lambda=80 0.00 996 0.99 1518 0.00
oynali z-test 0.02 399 1.00 999 0.10
kechikish - mediana (qadam); topildi - o'zgarishdan KEYIN
birinchi signal bergan oqimlar ulushi
=== 3. Qaror: yolg'on signal <= 0.05 bo'lganlar ichida ===
eng tez ikkitasi: oynali z-test (399), PH lambda=40 (412)
juftlashgan farq (PH lambda=40 - oynali z-test): -2.5 qadam, SE 10.0, sezilarli: False
tezlikda farq yo'q -> boshqa mezon: mavsumiy yolg'on signal
va xotira (PH: O(1), oyna: 1500 qiymat)
⭐ Tezlik va yolg'on signal - bitta tugmaning ikki uchiNatija tahlili.
1-bo'lim — bitta oqimda xato darajasi t = 2000 da 0.20 dan 0.30 ga oshdi. Page-Hinkley (lambda = 40) t = 2376 da signal berdi — kechikish 376 qadam. Oynali z-test t = 2499 da. Oxirgi 200 qadam xato ulushiga qarang: o'zgarishdan oldin ham u 0.150 dan 0.225 gacha tebranadi — shuning uchun detektor bir necha yuz qadam "dalil to'plashi" kerak.
2-bo'lim — 100 oqimda murosa. lambda = 10 juda sezgir: drift bo'lmagan oqimlarning 99% ida yolg'on signal, to'satdan driftning ko'pida signal o'zgarishdan oldin chiqib ketgan (o'zgarishdan keyingi birinchi signal faqat 16% oqimda). lambda oshgan sari yolg'on signal kamayadi (0.39 → 0.01 → 0.00), kechikish esa oshadi (150 → 412 → 697 → 996 qadam). Asta-sekin driftda kechikish ancha katta (lambda = 40 da 932) — o'zgarish kichik qadamlar bilan yig'iladi. Mavsumiy oqimda (drift emas) sezgir detektorlar muntazam yolg'on signal beradi (lambda = 20 da 0.79).
3-bo'lim — qaror oldindan belgilangan mezon bilan: yolg'on signal <= 0.05 bo'lganlar ichida eng tezi. Oynali z-test (399) va PH lambda = 40 (412) — bir xil oqimlarda juftlashgan farq -2.5 qadam, SE 10.0 — sezilarli emas. Tezlik bir xil ekan, boshqa mezonlar hal qiladi: mavsumiy oqimda PH 0.02, oynali test 0.10 yolg'on signal berdi, PH esa O(1) xotira bilan ishlaydi. Bu natija faqat shu sozlamalar (oyna 500, qadam 100) uchun — ularni o'zgartirsangiz, taqqoslashni qayta o'tkazing.
5. To'g'ri va noto'g'ri tushunishlar
| Noto'g'ri fikr | To'g'risi |
|---|---|
| "p < 0.05 — demak drift bor va muhim" | Katta n da arzimas farq ham sezilarli; effekt o'lchami kerak |
| "Drift bor — qayta o'qitish kerak" | Ahamiyatsiz ustundagi drift sifatga ta'sir qilmaydi (B) |
| "Kirish detektorlari jim — model yaxshi" | Konsept drift X ni o'zgartirmaydi (D) |
| "PSI — bitta aniq raqam" | Bin soni, bin turi va eps ga bog'liq |
| "Har ustunni alohida tekshirish yetarli" | Ko'p testlash + korrelyatsiya o'zgarishi ko'rinmaydi |
| "Ishonch asosidagi taxmin sifatni o'lchaydi" | Faqat kalibrlangan va konsept drift yo'q holda |
| "Belgilar drifti AUC ni tushiradi" | AUC deyarli saqlanadi, chegara va kalibrovka buziladi |
| "Sezgir detektor — yaxshi detektor" | Sezgirlik = ko'p yolg'on signal; murosa kerak |
| "Mavsumiy o'zgarish ham drift" | Kutilgan o'zgarish; reference mavsumni qamrasin |
6. Keng tarqalgan xatolar va yechimlari
1. Faqat p-qiymat
drift = stats.ks_2samp(ref, cur).pvalue < 0.05 # ⚠️
r = stats.ks_2samp(ref, cur)
drift = (r.pvalue < 0.05 / m) and (r.statistic >= 0.1) # ✅2. Tuzatishsiz ko'p test
signallar = [c for c in ustunlar if p[c] < 0.05] # ⚠️
signallar = [c for c, r in zip(ustunlar, bh(p_massiv)) if r] # ✅3. Teng kenglikdagi binlar
chegaralar = np.linspace(ref.min(), ref.max(), 11) # ⚠️
chegaralar = np.quantile(ref, np.linspace(0, 1, 11)) # ✅4. Chegaralarni current dan olish
chegaralar = np.quantile(np.r_[ref, cur], q) # ⚠️
chegaralar = np.quantile(ref, q) # ✅ faqat reference5. Yangi toifani eps ga yashirish
psi_kat(ref, cur, eps=1e-4) # ⚠️ yolg'iz
korilmagan = np.mean(~np.isin(cur, np.unique(ref))) # ✅ alohida metrika6. Faqat kirish drifti
if max_psi < 0.1: print("model sog'lom") # ⚠️
# belgilar kelgach: haqiqiy AUC/aniqlik reference bilan # ✅7. Kalibrlanmagan oqim detektori
ph = PageHinkley(lam=10) # ⚠️ taxminiy
# lambda ni drift yo'q tarixiy oqimda yolg'on signal darajasi bo'yicha tanlang # ✅7. Integratsiya — bu bilim qayerda kerak bo'ladi
- 11.7, 11.9-darslar (o'tilgan): Noparametrik testlar (KS) va ko'p taqqoslash
- 14-qism, 18.9-dars (o'tilgan): Kalibrovka va metrika tanlash
- 27.3-dars (o'tilgan): Ma'lumot quvuridagi validatsiya — sxema buzilishi drift emas, xato
- 27.11-dars (o'tilgan): Monitoring — drift metrikalari shu tizimga yoziladi
- 27.13-dars: Qayta o'qitish triggeri va o'quv oynasi drift turiga qarab
- 27.14-dars: Amaliyot — oylik partiyalarda drift, kechikkan belgilar va trigger
8. Eng yaxshi amaliyotlar
Drift turini aniqlang:
P(X),P(y|X)yokiP(y).Signal = sezilarli VA katta; effekt chegarasini oldindan kelishing.
Ko'p ustunda BH yoki Bonferroni.
PSI uchun kvantil binlar, reference dan, n ga mos bin soni.
Muhim ustunlarga va bashorat driftiga ustuvorlik bering.
Domen klassifikatori bilan ko'p o'lchovli tekshiruv.
Sifatni belgilar bilan albatta o'lchang — konsept drift faqat shunda ko'rinadi.
Oqim detektorini drift yo'q tarixda kalibrlang; mavsumni hisobga oling.
9. Amaliy topshiriq
Vazifa 1: Bashorat qiling
1. # P(y|X) o'zgarsa, X taqsimoti o'zgaradimi?
2. # PSI chegaralari qaysi to'plamdan olinadi?
3. # drift yo'q, n=500, 100 bin - PSI qanday bo'ladi?
4. # bitta xato yozuv teng kenglikdagi PSI ga nima qiladi?
5. # 0.02 std siljish, n=500000 - KS p-qiymat?
6. # 40 ustun, alfa=0.05, drift yo'q - kamida bitta signal ehtimoli?
7. # BH nimani nazorat qiladi?
8. # domen AUC 0.5 nimani bildiradi?
9. # ahamiyatsiz ustunda drift - bashorat PSI?
10. # belgilar driftida AUC?
11. # konsept driftda ishonch asosidagi taxmin?
12. # Page-Hinkley lambda oshsa?Javoblar
- Yo'q — konsept drift X ni o'zgartirmasligi mumkin
- Faqat reference
- Shovqin tufayli katta (> 0.1) — yolg'on drift
- Hamma ma'lumot bitta binga tushadi, PSI ~ 0
- Juda kichik ("sezilarli"), effekt esa arzimas
1 - 0.95^40 ~ 0.87- FDR — signallar ichidagi yolg'onlar ulushini
- Ikki to'plamni ajratib bo'lmaydi — drift yo'q
- Deyarli o'zgarmaydi
- Deyarli o'zgarmaydi; aniqlik va kalibrovka buziladi
- "Hammasi joyida" deydi — aldanadi
- Yolg'on signal kamayadi, kechikish oshadi
Vazifa 2: Xatolarni tuzating
1. drift = stats.ks_2samp(ref, cur).pvalue < 0.05
2. chegaralar = np.linspace(ref.min(), ref.max(), 11)
3. signallar = [c for c in ustunlar if p[c] < 0.05] # 60 ustun
4. chegaralar = np.quantile(np.r_[ref, cur], q)
5. if domen_auc < 0.6 and max_psi < 0.1: print("model sog'lom")Javoblar
1. r = stats.ks_2samp(ref, cur); drift = r.pvalue < 0.05 / m and r.statistic >= 0.1
2. chegaralar = np.quantile(ref, np.linspace(0, 1, 11))
3. signallar = [c for c, rad in zip(ustunlar, bh(p_massiv)) if rad]
4. chegaralar = np.quantile(ref, q)
5. # kirish jim bo'lsa ham: belgilar kelgach haqiqiy sifatni reference bilan solishtiringVazifa 3: O'lchovlar
Modellang (1-misol asosida):
psini kategoriyali ustun uchun yozing vachi2_noldanbilan bir xil ma'lumotda solishtiring- Bin soni 10 va current n = 200, 1000, 5000 da drift yo'q PSI shovqin tagligini o'lchang
(bins - 1) * (1/n_ref + 1/n_cur)formulasi bilan solishtiring- W1 ni turli birlikdagi ikki ustunda hisoblang va std ga bo'lish nega kerakligini ko'rsating
Vazifa 4: Tuzatishlar
Modellang (2-misol asosida):
- Ustunlar sonini 10, 40, 100 qiling — tuzatishsiz yolg'on kunlar ulushi
- Haqiqiy drift kattaligini 0.1, 0.25, 0.5 std qiling — BH va Bonferroni quvvati
- "BH + D >= 0.1" birlashgan qoidani qo'shing
- Natijani jadval qilib, monitoring uchun qoida taklif qiling
Vazifa 5: Drift va sifat
Modellang (3-misol asosida):
- Logistik regressiya o'rniga
HistGradientBoosting— C ssenariysida sifat qancha tushadi? - Korrelyatsiya drifti:
x1vax2ni o'zaro bog'lang (marginallari o'zgarmasin) — KS va domen AUC - Domen klassifikatorining belgi ahamiyatini chiqarib, qaysi ustun siljiganini toping
- E ssenariysi uchun chegarani yangi ulushga moslab (belgilar kelgach) aniqlikni tiklang
Vazifa 6: Oqim
Modellang (4-misol asosida):
- Page-Hinkley ga
deltani 0.005, 0.01, 0.02 qilib, kechikishga ta'sirini o'lchang - Signaldan keyin detektorni qayta boshlab (reset), bitta oqimdagi signallar sonini sanang
- Mavsumiy oqim uchun reference ni "o'tgan davr" qilib, yolg'on signalni kamaytiring
- Oddiy ADWIN g'oyasini yozing: oynani ikki qismga bo'lib, o'rtachalar farqini Hoeffding chegarasi bilan solishtiring
Vazifa 7: O'ylash
Drift panelida 60 ta ustun. Har kuni 3-4 tasi qizil. Jamoa signallarga qaramay qo'ygan. Rahbar: "Panel ishlamayapti, uni o'chiramiz — oyiga bir marta qayta o'qitamiz, bo'ldi" dedi. Nima deysiz?
Javob
Qisqa javob: panelni o'chirish emas, qayta sozlash kerak. Hozirgi panel noto'g'ri savolga javob beryapti, lekin driftni kuzatish zaruratini bekor qilmaydi.
1. Nega har kuni qizil. 60 ta ustun, tuzatishsiz alfa = 0.05: drift bo'lmasa ham kuniga o'rtacha 3 ta yolg'on signal (2-misol: 40 ustunda 1.74, kunlarning 81% ida kamida bitta). Katta hajmli ma'lumotda esa har qanday arzimas o'zgarish "sezilarli" (1-bo'lim: p = 2e-18 da D = 0.009). Panel o'z vazifasini "to'g'ri" bajaryapti — faqat vazifa noto'g'ri qo'yilgan.
2. Qanday tuzatiladi.
# 1) ko'p testlash: BH (FDR 5%)
# 2) effekt chegarasi: D >= 0.1 yoki PSI >= 0.1 (biznes bilan kelishilgan)
# 3) ustuvorlik: belgi ahamiyati yuqori ustunlar + bashorat PSI
# 4) ko'p o'lchovli: haftada bir domen klassifikatori AUC
# 5) sifat: belgilar kelgach haqiqiy AUC - bu ASOSIY signal3-misolda aynan shu ko'rindi: ahamiyatsiz ustundagi drift (B) sifatga ta'sir qilmadi, bashorat PSI esa uni to'g'ri e'tiborsiz qoldirdi.
3. "Oyiga bir marta qayta o'qitamiz" nega yetarli emas.
- To'satdan drift (ma'lumot manbai buzildi, yangi versiya) oy oxirini kutmaydi — zarar shu kuniyoq boshlanadi.
- Konsept drift paytida qayta o'qitish yordam beradi, lekin faqat yangi belgilar yetarli to'plangandan keyin; qachon ekanini bilish uchun sifat monitoringi kerak.
- Qayta o'qitish ham xato qilishi mumkin (buzilgan ma'lumotda o'qitish). Drift va sifat monitoringi yangi modelni tekshirish uchun ham kerak (27.13 — darvoza).
Rahbarga javob: "Panel shovqin chiqaryapti, chunki 60 ta testni tuzatishsiz va effekt chegarasisiz ishlatyapmiz. Uni FDR nazorati, amaliy chegara va muhim ustunlarga ustuvorlik bilan qayta sozlaymiz — kutilgan yolg'on signal haftasiga bir martadan kam bo'ladi. Asosiy signal esa belgilar kelgach haqiqiy sifat bo'ladi. Muntazam qayta o'qitish yaxshi g'oya, lekin u monitoringning o'rnini bosmaydi."
Nimani mustahkamlaydi: 2.5, 2.6, 2.9-bo'limlar.
Xulosa
Bu darsda driftni aniqlash va talqin qilishni o'rgandik.
Eng muhim uch fikr:
Drift uch xil va ular turli detektorga ko'rinadi. Kovariat drift
P(X), konsept driftP(y|X), belgilar driftiP(y). 3-misolda ahamiyatsiz ustundagi drift barcha kirish detektorlarini yoqdi (domen AUC0.732), lekin sifatni o'zgartirmadi; muhim ustundagi drift AUC ni0.662ga tushirdi; konsept drift esa kirish detektorlarining birortasiga ko'rinmasdan (domen AUC0.501) AUC ni0.594ga tushirdi. Belgilarsiz ishonch taxmini aynan xavfli holatlarda aldandi (0.758taxmin,0.579haqiqiy). Shuning uchun monitoring ikki qavatli: tez kirish/bashorat drifti va belgilar kelgach haqiqiy sifat.Signal = sezilarli VA katta, ko'p testlash tuzatilgan holda. 2-misolda arzimas siljish (
D = 0.009) 500 000 qatordap = 2e-18berdi; 40 ta ustunni tuzatishsiz tekshirish drift bo'lmagan kunlarning81%ida qizil chiroq yoqdi. BH yolg'on signal ulushini3.5%ga tushirib, haqiqiy driftlarning ko'pini (3.42/4) saqladi. PSI esa bin tanlashiga sezgir: ko'p bin + kichik n yolg'on drift beradi, teng kenglik va bitta xato yozuv driftni butunlay yashiradi.Oqimda kechikish va yolg'on signal — bitta murosa. Page-Hinkley noldan bir necha qatorda yoziladi va O(1) xotira bilan ishlaydi.
lambdaoshsa, yolg'on signal0.99dan0.00gacha kamaydi, kechikish esa76dan996qadamgacha oshdi. Chegarani drift yo'q tarixda kalibrlab, oldindan belgilangan mezon bilan tanlaymiz; mavsumiy o'zgarish drift emas — reference uni qamrashi kerak.
Keyingi darsda Qayta o'qitish va A/B test: drift aniqlangandan keyin nima qilish kerak — qachon va qanday oynada qayta o'qitish, yangi modelni shadow rejimda sinash, darvoza qoidalari, onlayn A/B test va uning "peeking" tuzog'i, ko'p qo'lli banditlar va avtomatik rollback.
Izohlar (0)
Izoh yozish uchun kiring.
- Hozircha izoh yo'q. Birinchi bo'ling!