Mundarija (22)
- 1. Kirish va motivatsiya
- 2. Nazariya — chuqur tushuntirish
- 2.1. CRISP-DM: olti bosqich va amaldagi ko'rinishi
- 2.2. Biznes savolidan DS vazifasiga
- 2.3. Muvaffaqiyat mezoni: biznes metrikasi va model metrikasi
- 2.4. Bazaviylar va "model kerakmi?"
- 2.5. Loyiha xatarlari
- 2.6. Loyiha rejasi: tez prototip → baholash → ishlab chiqarish
- 2.7. Loyiha hujjati (problem statement) shabloni
- 2.8. Tuzoqlar
- 3. Tez ma'lumotnoma
- 4. Batafsil misollar
- Misol 1 — Biznes savolidan DS vazifasiga: tahlil birligi, xarajat matritsasi va qiymat chegaralari
- Misol 2 — Model metrikasi va biznes metrikasi: AUC va tejalgan zarar
- Misol 3 — Bazaviylar va "model kerakmi?": qoidalar, ekspert ball-kartasi va LogReg
- Misol 4 — Loyiha xatarlari: belgi ta'rifi (30 yoki 60 kun) va ma'lumot mavjudligi
- 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
29.1-dars: Data Science loyihasining hayot sikli
29-QISM — LOYIHALAR VA KARYERA · 1-dars
1. Kirish va motivatsiya
28-qism oxirida aytganimizdek, bu — kursning yakuniy qismi. 28 qism davomida biz alohida vositalarni o'rgandik: ma'lumotni yig'ish va tozalash, EDA, statistika va gipoteza testlari, klassik ML va chuqur o'rganish, baholash dizayni, MLOps va maxsus mavzular. Endi savol boshqacha: bu bo'laklardan qanday qilib foydali loyiha yig'iladi? Amalda eng qiyin qism ko'pincha model emas. Qiyinchilik savolni to'g'ri qo'yishda, muvaffaqiyatni to'g'ri o'lchashda va loyiha qayerda sinishi mumkinligini oldindan ko'rishda.
Real vaziyat. Tijorat bankining Data Science jamoasi kredit karta mijozlari "to'lovni kechiktiradimi" degan modelni qurdi. Validatsiyada AUC 0.86 chiqdi va hisobotda "yuqori sifatli model" deb yozildi. Model har oy 3 ming xavfli mijoz ro'yxatini chiqara boshladi. Lekin qo'ng'iroq markazi oyiga faqat bir necha yuz mijozga qo'ng'iroq qila oladi. Operatorlar ro'yxat boshidagilarga qo'ng'iroq qildi va ularning ko'pchiligi "ha, unutib qo'yibman, ertaga to'layman" deb javob berdi. Bu mijozlar qo'ng'iroqsiz ham to'lar edi. Model "30 kunlik kechikish" belgisida o'qitilgan edi, bankni esa haqiqiy zarar keltiradigan uzoq kechikishlar qiziqtirardi. Uch oydan keyin rahbariyat savol berdi: "bu model bizga qancha pul tejadi?" Jamoa javob bera olmadi: bunday o'lchov loyihaning boshida rejalashtirilmagan edi.
Bu to'rt darsda (29.1-29.4) aynan shu loyihani boshidan to'g'ri qilamiz. Vazifa: bankda kredit karta mijozlari orasidan kelgusi 60 kunda jiddiy kechikishga (60+ kun) tushadiganlarni oldindan aniqlash va cheklangan qo'ng'iroq markazi resursini ular orasida taqsimlash. Loyiha to'rt qismga bo'lingan:
- 29.1 (bu dars) — biznes savoli, muvaffaqiyat mezoni, bazaviylar, xatarlar, reja va loyiha hujjati.
- 29.2 — ma'lumot: bir nechta jadval, SQL bilan belgilar, vaqt nuqtasi va sizish, sifat, vaqt bo'yicha bo'lish.
- 29.3 — modellashtirish: bazaviylardan HistGB gacha, oldindan yozilgan baholash rejasi, kalibrlash, sig'im bilan siyosat, test.
- 29.4 — natijani texnik bo'lmagan auditoriyaga yetkazish: piramida, noaniqlik, avtomatik hisobot, pilot rejasi.
Hamma darsda bitta sintetik ma'lumot generatori (urug' 29) ishlatiladi: 6000 mijoz, 24 oy (2024-01 dan 2025-12 gacha), to'rtta jadval — mijozlar, hisobvaraqlar, to'lovlar va tranzaksiyalar.
Bu darsda loyiha hayot siklini ko'ramiz: biznes savolini DS vazifasiga aylantirish, muvaffaqiyatni pul bilan o'lchash, "model kerakmi?" savoliga raqam bilan javob berish va xatarlarni loyiha boshida o'lchash.
Bu darsda:
- CRISP-DM va uning amaldagi ko'rinishi
- Biznes savolidan DS vazifasiga: qaror kim tomonidan, qachon, qanday qabul qilinadi
- Biznes metrikasi va model metrikasi: xarajat matritsasi, top-K, "tejalgan zarar"
- Bazaviylar va "model kerakmi?" savoli
- Loyiha xatarlari: ma'lumot mavjudligi, sizish, belgi ta'rifi, belgi yetilishi, qaror halqasi
- Loyiha rejasi: tez prototip, baholash, ishlab chiqarish
- Loyiha hujjati (problem statement) shabloni
ℹ Misollar real numpy/pandas/sklearn bilan (Python 3.14). Ma'lumot sintetik, lekin real portfelga o'xshash qurilgan: yashirin "moliyaviy qiyinchilik" va "unutuvchanlik" omillari bor, makroiqtisodiy yuklama 2025-yil ikkinchi yarmida oshadi. Biznes parametrlari faraziy: qo'ng'iroq DPD60 holatlarining
30%ini oldini oladi, bitta qo'ng'iroq narxi 20 ming so'm, DPD60 ga tushgan hisobning kutilgan zarari balansning45%i. Real bankda bu raqamlar pilot va moliya bo'limi bilan birga aniqlanadi 29.4-bob.
2. Nazariya — chuqur tushuntirish
2.1. CRISP-DM: olti bosqich va amaldagi ko'rinishi
CRISP-DM (Cross-Industry Standard Process for Data Mining) — DS loyihasining eng ko'p tilga olinadigan jarayon modeli. U 1990-yillar oxirida sanoat konsorsiumi tomonidan yozilgan va hozir ham loyihani bosqichlarga ajratish uchun qulay "xarita" bo'lib qolmoqda.
+--------------------------+
| 1. BIZNESNI TUSHUNISH |<----------------------------+
+--------------------------+ |
| ^ |
v | |
+--------------------------+ |
| 2. MA'LUMOTNI TUSHUNISH | |
+--------------------------+ |
| |
v |
+--------------------------+ +--------------------+ |
| 3. MA'LUMOT TAYYORLASH |<--->| 4. MODELLASHTIRISH | |
+--------------------------+ +--------------------+ |
| |
v |
+--------------+ |
| 5. BAHOLASH |------+ yo'q
+--------------+
| ha (biznes mezoni bajarildi)
v
+--------------+
| 6. JORIY |
| ETISH |
+--------------+| Bosqich | Asosiy savol | Natija (artefakt) | Kursdagi joyi |
|---|---|---|---|
| 1. Biznesni tushunish | Qaysi qaror yaxshilanadi? Qanday o'lchaymiz? | loyiha hujjati, muvaffaqiyat mezoni | 29.1, 8.2 |
| 2. Ma'lumotni tushunish | Qanday ma'lumot bor, qachon paydo bo'ladi? | ma'lumot inventari, EDA, sifat hisoboti | 7, 8, 29.2 |
| 3. Ma'lumot tayyorlash | Tahlil birligi, belgilar, bo'lish | belgilar jadvali, ma'lumot kartasi | 6, 17, 29.2 |
| 4. Modellashtirish | Qaysi yondashuv yetarli? | bazaviylar, nomzodlar, tanlov | 12-26, 29.3 |
| 5. Baholash | Biznes mezoni bajarildimi? | baholash hisoboti, model kartasi | 18, 29.3 |
| 6. Joriy etish | Qanday ishlatiladi va kuzatiladi? | xizmat, monitoring, pilot | 27, 29.4 |
Amaldagi ko'rinishi. Rasmdagi strelkalar loyiha chiziqli emasligini ko'rsatadi, va amalda bu yanada kuchliroq seziladi:
- Orqaga qaytishlar odatiy. Modellashtirishda belgining noto'g'ri ta'riflangani ma'lum bo'ladi va 1-bosqichga qaytiladi. Baholashda sizish topiladi va 3-bosqich qayta qilinadi.
- Vaqtning katta qismi 1-3-bosqichlarga ketadi. Buning aniq ulushi loyihaga bog'liq. Lekin model tanlash odatda eng qisqa bosqichlardan biri bo'ladi.
- 6-bosqich — oxiri emas. 27-qismda ko'rganimizdek, joriy etilgan model monitoring, drift va qayta o'qitish bilan yashaydi. Qaror halqasi esa 2.5-bob keyingi ma'lumotni o'zgartiradi.
- Rasmda yo'q, lekin muhim bosqich — "to'xtatish". Har bosqich oxirida "davom etamizmi?" degan qaror bo'lishi kerak. Loyihani vaqtida to'xtatish ham muvaffaqiyat hisoblanadi: resurs tejaladi.
Boshqa jarayon modellari ham bor. Masalan, TDSP va turli "ML lifecycle" sxemalari MLOps bosqichlarini batafsilroq ajratadi. Lekin ularning barchasi bitta skeletga tayanadi: savol → ma'lumot → model → baholash → joriy etish → kuzatish, va bu bosqichlar orasida qaytishlar bor.
2.2. Biznes savolidan DS vazifasiga
Buyurtmachi DS jamoasiga odatda savol bilan emas, istak bilan keladi: "kechikishlarni kamaytirish uchun model kerak". Bu hali vazifa emas. Uni vazifaga aylantirish uchun qaror haqida beshta savolga javob berish kerak:
1. QAROR NIMA? qaysi mijozga qo'ng'iroq qilish (eslatma + restrukturizatsiya taklifi)
2. KIM QABUL QILADI? qo'ng'iroq markazi rahbari; operatorlar ro'yxat bo'yicha ishlaydi
3. QACHON? har oy oxirida (hisobvaraq chiqqanda), keyingi oy davomida qo'ng'iroq
4. QANDAY CHEKLOV? oyiga K = 200 qo'ng'iroq (kuniga ~10 ta, ~20 ish kuni)
5. NIMA O'ZGARADI? qo'ng'iroq qilingan xavfli mijozlarning bir qismi 60+ kunga tushmaydi
-> bank zarari kamayadi; qo'ng'iroq narxi -> xarajat
| tarjima
v
TAHLIL BIRLIGI: (mijoz, as-of oy) - oy oxiridagi holat
NOMZODLAR: hozir kechikmagan (oxirgi hisobvaraq to'langan) faol mijozlar
AS-OF (vaqt nuqtasi): t-oy oxiri; belgilar FAQAT shu paytgacha ma'lum ma'lumotdan
GORIZONT: keyingi 60 kun (t va t+1 hisobvaraqlari)
BELGI (label): y60 = ikkala hisobvaraq bo'yicha minimal to'lov qilinmagan (60+ kun)
MODEL CHIQISHI: p = P(y60 = 1 | t gacha ma'lumot) - kalibrlangan ehtimol
SIYOSAT: har oy p * zarar bo'yicha top-200 ga qo'ng'iroq
BIZNES METRIKASI: tejalgan zarar - qo'ng'iroq xarajati (mln so'm/oy)Ikki xil qo'yilishni solishtiring:
| Yomon qo'yilish | Yaxshi qo'yilish |
|---|---|
| "Kechikadiganlarni bashorat qiluvchi model" | "Har oy 200 ta qo'ng'iroqni kutilgan tejam eng katta mijozlarga taqsimlash" |
| Belgi: "kechikdi" (qaysi? qachon?) | Belgi: as-of dan keyingi 60 kunda 60+ kun kechikish |
| Metrika: AUC | Metrika: oylik tejam (mln so'm); yordamchi: precision@200, AUC, kalibrlash |
| Natija: ro'yxat | Natija: siyosat + kutilgan tejam + noaniqlik + pilot rejasi |
Oltin savol: "model mukammal bo'lsa, kim nima qiladi va bu qancha turadi?" Agar bunga javob bo'lmasa, loyiha hali boshlanmagan. 1-misolda bu savolga raqam bilan javob beramiz: "kelajakni biladigan" (oracle) siyosat oyiga taxminan 78.6 mln so'm tejaydi. Bu — har qanday modelning yuqori chegarasi.
2.3. Muvaffaqiyat mezoni: biznes metrikasi va model metrikasi
Metrikalar zinapoyasi:
BIZNES METRIKASI tejalgan zarar - xarajat (mln so'm/oy) <- rahbariyat shu bilan qaror qiladi
^
QAROR METRIKASI tejam@K, precision@K, recall@K <- siyosat sifatini o'lchaydi
^
MODEL METRIKASI AUC, log-loss, Brier, kalibrlash <- model ichki sifati
+
TO'SIQ (guardrail) shikoyatlar, qayta qo'ng'iroqlar, segmentlar bo'yicha adolat (29.11)Xarajat matritsasi. Qaror ikki xil: qo'ng'iroq qilish yoki qilmaslik. Holat ham ikki xil: mijoz DPD60 ga tushadi yoki tushmaydi. Qiymatni "qo'ng'iroq qilinmasa" holatiga nisbatan o'lchaymiz:
qo'ng'iroq qilindi qo'ng'iroq qilinmadi
mijoz DPD60 ga tushadi (TP/FN) +0.30 * zarar - 0.02 0 (zarar to'liq qoladi)
mijoz tushmaydi (FP/TN) -0.02 0
zarar = 0.45 * balans (faraziy LGD), 0.30 - qo'ng'iroq samarasi (faraziy), 0.02 = 20 ming so'm
tejam(S) = sum_{i in S} ( 0.30 * zarar_i * y60_i - 0.02 )
qo'ng'iroq foydali, agar p_i * 0.30 * zarar_i > 0.02 (p_i kalibrlangan bo'lishi SHART)Nega AUC biznes metrikasi bilan mos kelmasligi mumkin:
- AUC butun tartibni o'lchaydi, qaror esa faqat top-K ga bog'liq. Top-K dan tashqaridagi tartib yaxshilansa, AUC oshadi, tejam esa o'zgarmaydi (2-misol, 3-bo'lim).
- AUC zarar hajmini bilmaydi. 10 mln balansli mijozni topish 1 mln balansli mijozni topishdan 10 barobar qimmatroq, AUC uchun esa ular teng.
- AUC kalibrlashni bilmaydi. Ehtimolni 10 ga ko'paytirsak ham AUC o'zgarmaydi. "p * zarar > narx" qarori esa butunlay o'zgaradi 29.3-bob.
- AUC sig'imni bilmaydi. K = 50 va K = 2000 da modellar orasidagi farq turlicha (3-misol, 4-bo'lim).
Bu model metrikasi keraksiz degani emas. AUC va log-loss modelni tez tanlash va diagnostika uchun qulay. Lekin loyiha muvaffaqiyati biznes metrikasi bilan e'lon qilinadi, va bu metrika loyiha boshlanishidan oldin yoziladi.
2.4. Bazaviylar va "model kerakmi?"
QIYMAT ZINAPOYASI (oyiga tejam, har loyihada o'lchanadi):
0. hech narsa qilmaslik 0 <- har doim variant!
1. tasodifiy K ta qo'ng'iroq ~0 <- reyting qiymati shu bilan o'lchanadi
2. oddiy qoida (util eng yuqori) ? <- bitta ustun, 5 daqiqa
3. ekspert ball-kartasi ? <- bo'lim tajribasi
4. oddiy model (LogReg) ?
5. murakkab model (HistGB, tarmoq) ?
6. oracle (kelajakni biladi) yuqori chegara - erishib bo'lmaydi
MODEL QIYMATI = model - ENG YAXSHI QOIDA (tasodifiydan emas!)
MODEL XARAJATI = ishlab chiqish + ma'lumot quvuri + monitoring + qayta o'qitish + xatar"Model kerak emas" degan signallar:
- oddiy qoida eng yaxshi modeldan sezilarli yomon emas (juftlashgan farq 2*SE ichida);
- model foydasi yillik xarajatdan kichik;
- qaror kam uchraydi yoki arzon, xato esa qimmat va tushuntirishni talab qiladi (regulyatsiya);
- oracle ham kichik qiymat beradi, ya'ni muammo reytingda emas (masalan, sig'im juda kichik yoki harakat samarasiz).
Qoida bilan boshlashning afzalligi: u darhol ishlaydi, tushunarli va keyinchalik modelni o'lchash uchun "nazorat" bo'lib xizmat qiladi. 3-misolda kutilmagan natija olamiz: 3 oylik validatsiyada bitta ustundan iborat qoida modeldan sezilarli yomon emas.
2.5. Loyiha xatarlari
| Xatar | Savol | Qanday o'lchanadi / ushlanadi |
|---|---|---|
| Ma'lumot mavjudligi | Kerakli jadvallar bormi, kirish huquqi, qancha tarix? | inventar; o'quv tarixi uzunligi bo'yicha egri chiziq (4-misol) |
| Sizish (leakage) | Belgi as-of dan keyingi ma'lumotni ko'rmaydimi? | vaqt nuqtasi auditi, "snapshot" testi (29.2) |
| Belgi ta'rifi | "Kechikish" nima: 30 kunmi, 60 kunmi? | har ta'rifda o'qitib, biznes metrikasi bilan solishtirish (4-misol) |
| Belgi yetilishi | Belgi qachon ma'lum bo'ladi? | as-of t uchun belgi t + gorizont da; o'quv oylari shunga moslanadi |
| Qaror halqasi | Qo'ng'iroqlar boshlangach, belgilar o'zgaradimi? | qo'ng'iroq qilinmaydigan nazorat guruhi (holdout), 29.4 |
| Drift | Dunyo o'zgaradimi? | vaqt bo'yicha bo'lish, monitoring (27.11, 27.12) |
| Qabul qilish | Operatorlar ro'yxatga ishonadimi? | pilot, tushuntirish, sabab kodlari |
| Etika va regulyatsiya | Qaror adolatlimi, taqiqlangan belgi yo'qmi? | segmentlar bo'yicha tahlil 29.3-bob, 29.11 |
Belgi ta'rifi — loyiha qarori, texnik tafsilot emas. "30+ kun kechikish" belgisi "60+" dan besh barobar ko'p uchraydi. Shuning uchun model o'qitishga qulayroqdek ko'rinadi. Lekin 4-misolda ko'ramizki, y30 = 1 bo'lganlarning 81% i o'zi to'laydi: ular unutuvchan, lekin to'lashga qodir mijozlar. y30 da o'qitilgan model ro'yxatni aynan shular bilan to'ldiradi va y60 da o'qitilgan modeldan oyiga taxminan 7 mln so'm kam tejaydi. Bu farq 7 oyning har birida takrorlandi.
Qaror halqasi (feedback loop). Model joriy etilgach, eng xavfli mijozlarga qo'ng'iroq qilinadi va ularning bir qismi DPD60 ga tushmaydi. Keyingi o'qitishda model "bunday belgili mijozlar kechikmaydi" degan noto'g'ri saboqni olishi mumkin. Yechim — loyiha boshidanoq kichik tasodifiy nazorat guruhini (qo'ng'iroq qilinmaydigan) rejalashtirish. Bu guruh keyin haqiqiy samarani o'lchashga ham xizmat qiladi (27.13, 29.4).
2.6. Loyiha rejasi: tez prototip → baholash → ishlab chiqarish
BOSQICH 0. RAMKA (1-2 hafta)
loyiha hujjati, biznes metrikasi, xarajat parametrlari (faraziy -> moliya tasdiqlaydi)
ma'lumot inventari, kirish huquqlari, oracle va qoida bazaviysi
DARVOZA: oracle qiymati loyiha xarajatidan sezilarli katta? yo'q -> TO'XTATISH
BOSQICH 1. TEZ PROTOTIP (2-4 hafta)
bitta jadval, sizishsiz belgilar, oddiy qoida va LogReg, bitta vaqt bo'yicha val
DARVOZA: model eng yaxshi qoidadan yaxshiroqmi yoki hech bo'lmaganda istiqbollimi?
BOSQICH 2. TO'LIQ BAHOLASH (3-6 hafta)
sifat tekshiruvlari, vaqt bo'yicha CV, nomzodlar, kalibrlash, siyosat, segmentlar
oldindan yozilgan baholash rejasi -> test BIR MARTA -> model kartasi
DARVOZA: test da biznes mezoni bajarildimi?
BOSQICH 3. PILOT (dastlabki taxmin 2-3 oy; 29.4 da namuna hajmi bilan tekshiriladi)
mijozlarning bir qismida A/B: model ro'yxati vs qoida ro'yxati vs qo'ng'iroqsiz
DARVOZA: haqiqiy samara (0.30 faraziyi) tasdiqlandimi?
BOSQICH 4. ISHLAB CHIQARISH
quvur, xizmat, monitoring, qayta o'qitish qoidasi, doimiy nazorat guruhi (27-qism)Muddatlar — namuna. Ular jamoa va ma'lumot holatiga bog'liq. Muhimi muddatda emas, darvozalarda: har bosqich oxirida oldindan kelishilgan mezon bo'yicha "davom etish, qayta ko'rib chiqish yoki to'xtatish" qarori qabul qilinadi.
2.7. Loyiha hujjati (problem statement) shabloni
Loyiha hujjati — 1-2 sahifali matn. Uni buyurtmachi, DS jamoasi va foydalanuvchi (qo'ng'iroq markazi) loyiha boshida birga imzolaydi. Keyinroq "biz buni kelishmagan edik" degan bahslarning ko'pchiligini u oldini oladi.
# Loyiha hujjati: kredit karta kechikishini oldindan aniqlash
**Egasi:** chakana kredit risk bo'limi · **DS jamoasi:** 2 kishi · **Versiya:** 0.3 (namuna)
## 1. Biznes muammosi
60+ kunlik kechikishga tushgan kredit karta hisoblari bank uchun asosiy zarar manbai.
Qo'ng'iroq markazi oyiga ~200 profilaktik qo'ng'iroq qila oladi, lekin hozir ro'yxat
qo'lda tuziladi (limitdan foydalanish ulushi bo'yicha).
## 2. Qaror va foydalanuvchi
- Qaror: har oy oxirida qaysi 200 mijozga qo'ng'iroq qilish.
- Foydalanuvchi: qo'ng'iroq markazi rahbari; ro'yxat CRM ga tushadi.
- Harakat: eslatma + to'lov jadvalini qayta ko'rib chiqish taklifi.
## 3. DS vazifasi
- Tahlil birligi: (mijoz, as-of oy); nomzodlar - hozir kechikmagan faol mijozlar.
- Belgi: as-of dan keyingi 60 kunda 60+ kun kechikish (y60).
- Chiqish: kalibrlangan ehtimol p; siyosat: p * kutilgan zarar bo'yicha top-K.
## 4. Muvaffaqiyat mezoni
- Asosiy: oylik tejam = 0.30 * sum(zarar * y60) - 0.02 * K (mln so'm), test oylarida.
- Talab: joriy qoidadan juftlashgan farq > 2*SE.
- Yordamchi: precision@200, recall@200, AUC, kalibrlash (bashorat / haqiqiy ulush).
- To'siqlar: segmentlar (hudud, yangi mijozlar) bo'yicha keskin farq yo'q; shikoyatlar oshmaydi.
## 5. Bazaviylar
Hech narsa; tasodifiy 200; qoida "util eng yuqori"; ekspert ball-kartasi.
## 6. Ma'lumot
mijozlar, hisobvaraqlar, to'lovlar, tranzaksiyalar (2024-01 dan boshlab), omborga kirish bor.
As-of qoidasi: faqat sanasi <= as-of bo'lgan yozuvlar.
## 7. Farazlar va xatarlar
- Faraziy: qo'ng'iroq samarasi 30%, qo'ng'iroq narxi 20 ming so'm, zarar = 45% balans
-> pilotda va moliya bo'limida tasdiqlanadi.
- Belgi yetilishi 2 oy; qaror halqasi -> 5% nazorat guruhi doimiy.
- Drift (makroiqtisodiy) -> oylik monitoring.
## 8. Reja va darvozalar
Ramka (2 hafta) -> prototip (3 hafta) -> baholash (4 hafta) -> pilot (muddat - namuna hajmi hisobidan) -> ishlab chiqarish.
To'xtatish mezoni: prototipda model qoidadan ustun emas va oracle qiymati past.
## 9. Doiradan tashqari
Kredit limitini o'zgartirish, undiruv (collection) strategiyasi, yangi mijozlarni skoring.2.8. Tuzoqlar
Asosiy tuzoqlar: savol o'rniga istak bilan boshlash ("bizga model kerak"); qarorni, uning egasini va sig'imni aniqlamaslik; muvaffaqiyatni faqat AUC bilan e'lon qilish; zarar hajmini e'tiborsiz qoldirib, faqat ehtimol bo'yicha tartiblash; oddiy qoida bazaviysini o'lchamaslik (model tasodifiydan yaxshi — bu hali qiymat emas); oracle chegarasini hisoblamaslik; belgini qulaylik uchun tanlash (ko'p musbat bo'lsin deb 30 kun); belgi yetilishini hisobga olmay oxirgi oylarni o'quvga qo'shish; qaror halqasi va nazorat guruhini rejalashtirmaslik; faraziy parametrlarni (samara, narx) fakt sifatida taqdim etish; darvozalarsiz reja — loyihani to'xtatib bo'lmaydi.
3. Tez ma'lumotnoma
import numpy as np
# qo'ng'iroq qiymati (xarajat matritsasi): qo'ng'iroq qilinsa, qo'ng'iroqsizga nisbatan
qiymat = SAMARA * zarar * y60 - NARX # TP: +0.30*z-0.02, FP: -0.02
# har oy top-K siyosatining tejami (biznes metrikasi)
def tejam_k(qiymat, ball, guruhlar, k=200):
return sum(qiymat[g[np.argsort(-ball[g])[:k]]].sum() for g in guruhlar) / len(guruhlar)
# qiymat zinapoyasi
tejam_k(qiymat, rng.random(n), guruhlar) # tasodifiy ~0
tejam_k(qiymat, df.util.to_numpy(), guruhlar) # oddiy qoida
tejam_k(qiymat, p * zarar, guruhlar) # model: kutilgan zarar bo'yicha
tejam_k(qiymat, y60 * zarar, guruhlar) # oracle - yuqori chegara
# qaror qoidasi: eng yaxshisidan sezilarli yomon bo'lmagan eng sodda
farq = baho[nom] - baho[eng]
sezilarli_yomon = farq < -2 * se # se - juftlashgan bootstrap yoki oylar bo'yichaLoyiha boshidagi savollar ro'yxati
| Savol | Bizning loyihada |
|---|---|
| Qaror nima va kim qabul qiladi? | 200 ta qo'ng'iroq; qo'ng'iroq markazi rahbari |
| Qachon va qanday chastotada? | har oy oxirida (as-of) |
| Sig'im? | oyiga K = 200 |
| Belgi va gorizont? | 60 kun ichida 60+ kun kechikish |
| Biznes metrikasi? | tejalgan zarar - qo'ng'iroq xarajati |
| Bazaviy? | qoida "util eng yuqori" |
| Yuqori chegara? | oracle: oyiga ~78.6 mln so'm (val) |
| Faraziy parametrlar? | samara 0.30, narx 0.02, zarar 0.45 * balans |
| Nazorat guruhi? | ha, doimiy |
Hayot sikli xulosasi
istak -> qaror (kim, qachon, sig'im) -> DS vazifasi (birlik, as-of, gorizont, belgi)
metrikalar: biznes > qaror (top-K) > model (AUC) + to'siqlar
qiymat zinapoyasi: hech narsa, tasodifiy, qoida, model, oracle
xatarlar: mavjudlik, sizish, belgi ta'rifi va yetilishi, qaror halqasi, drift
reja: ramka -> prototip -> baholash -> pilot -> ishlab chiqarish (darvozalar bilan)4. Batafsil misollar
Misollar real numpy/pandas/sklearn bilan (Python 3.14). Har misol mustaqil ishlaydi, shuning uchun loyiha generatori (
yarat_bank) va belgilar jadvali (belgilar_jadvali) har birida takrorlanadi. Bu kod 29.2-29.4 darslarida ham aynan bir xil. Belgilar qanday qurilgani va nega aynan shunday ekani 29.2 da SQL bilan batafsil ko'riladi. Bu darsda ular tayyor vosita sifatida ishlatiladi.
Misol 1 — Biznes savolidan DS vazifasiga: tahlil birligi, xarajat matritsasi va qiymat chegaralari
"""Biznes savolidan DS vazifasiga: tahlil birligi, xarajat matritsasi, qiymat chegaralari."""
import numpy as np
import pandas as pd
HUDUDLAR = ["Toshkent", "Samarqand", "Farg'ona", "Andijon", "Buxoro", "Qashqadaryo"]
OYLAR = 24 # 1 = 2024-01, ..., 24 = 2025-12
BELGILAR = ["yosh", "daromad", "limit", "avto_tolov", "maosh_loyihasi", "staj",
"util", "util_3", "util_ozg", "kechikish_6", "tolov_nisbati_3",
"tolov_kuni_3", "naqd_soni_3", "naqd_summa_3", "xarid_soni_3"]
K, SAMARA, NARX = 200, 0.30, 0.02 # oyiga qo'ng'iroq, oldini olish, mln so'm
def sigmoid(z):
return 1 / (1 + np.exp(-z))
def oy_nomi(m):
return f"{2024 + (m - 1) // 12}-{(m - 1) % 12 + 1:02d}"
def yarat_bank(seed=29, n=6000):
"""Kredit karta portfeli: mijozlar, hisobvaraqlar, tolovlar, tranzaksiyalar."""
rng = np.random.default_rng(seed)
ids = np.arange(1, n + 1)
yosh = rng.integers(21, 66, n)
maosh = rng.random(n) < 0.45
daromad = np.round(np.exp(rng.normal(np.log(6.0), 0.45, n)), 2) # mln so'm
ochilgan = np.where(rng.random(n) < 0.7, rng.integers(-40, 1, n),
rng.integers(1, 20, n))
xavf = (rng.normal(0, 1, n) - 0.5 * maosh - 0.8 * np.log(daromad / 6)
+ 0.015 * (35 - yosh)) # yashirin: moliyaviy xavf
unut = rng.normal(0, 1, n) + 0.03 * (35 - yosh) # yashirin: unutuvchanlik
avto = (rng.random(n) < sigmoid(-0.2 - 0.9 * unut)).astype(int)
limit = np.round(daromad * rng.uniform(1.5, 3.5, n), 1)
hudud = rng.choice(HUDUDLAR, n, p=[0.34, 0.15, 0.14, 0.13, 0.12, 0.12])
mj = pd.DataFrame({"mijoz_id": ids, "yosh": yosh, "hudud": hudud,
"daromad": daromad, "limit": limit, "avto_tolov": avto,
"maosh_loyihasi": maosh.astype(int), "ochilgan_oy": ochilgan})
s, oldingi, ketma = rng.normal(0, 1, n), np.zeros(n, int), np.zeros(n, int)
hv, tl, tr = [], [], []
for m in range(1, OYLAR + 1):
faol = (ochilgan <= m) & (ketma < 3) # 3 ketma-ket kechikish - defolt
s = 0.75 * s + 0.66 * rng.normal(0, 1, n)
d = 0.8 * xavf + 0.7 * s + 0.15 * (m % 12 == 1) + 0.08 * max(m - 18, 0)
util = np.clip(sigmoid(-0.4 + 0.55 * d + rng.normal(0, 0.5, n)), 0.01, 1)
balans = np.round(util * limit, 3)
min_t = np.round(np.maximum(0.05 * balans, 0.05), 3)
for turi, lam in [("xarid", np.exp(1.2 + 0.15 * np.log(daromad / 6) - 0.1 * d)),
("naqd", np.exp(-1.6 + 0.6 * d)),
("onlayn", np.exp(0.6 - 0.02 * (yosh - 35)))]:
i = np.repeat(np.arange(n), rng.poisson(lam) * faol)
summa = (rng.uniform(0.2, 1.5, i.size) * (1 + 0.3 * d[i].clip(0))
if turi == "naqd" else np.exp(rng.normal(np.log(0.25), 0.8, i.size)))
tr.append(pd.DataFrame({"mijoz_id": ids[i], "oy": m,
"kun": rng.integers(1, 29, i.size), "turi": turi,
"summa": np.round(summa, 3)}))
q = rng.random(n) < sigmoid(-3.7 + 1.3 * d + 1.2 * (util > 0.85)
+ 3.8 * (oldingi == 2)) # qiyinchilik
u = ~q & (rng.random(n) < sigmoid(-3.4 + 1.1 * unut - 1.8 * avto)
* (oldingi != 1)) # unutish
miss = (q | u) & faol
ulush = sigmoid(0.3 - 1.2 * d + rng.normal(0, 0.5, n))
summa = np.where(miss, min_t * rng.uniform(0, 0.6, n),
min_t + (balans - min_t).clip(0) * ulush)
tolaydi = faol & (~miss | (rng.random(n) < 0.4))
kun = np.clip(np.round(6 + 14 * sigmoid(unut + 0.5 * d)
+ rng.normal(0, 3, n)), 1, 25)
hv.append(pd.DataFrame({"mijoz_id": ids[faol], "oy": m,
"balans": balans[faol], "min_tolov": min_t[faol]}))
if m < OYLAR: # m-oy hisobvarag'i (m+1)-oyda to'lanadi
tl.append(pd.DataFrame({"mijoz_id": ids[tolaydi], "hisob_oy": m,
"tolov_oy": m + 1,
"tolov_kuni": kun[tolaydi].astype(int),
"summa": np.round(summa[tolaydi], 3)}))
ketma = np.where(faol, np.where(miss, ketma + 1, 0), ketma)
oldingi = np.where(miss, np.where(q, 2, 1), 0)
return {"mijozlar": mj, "hisobvaraqlar": pd.concat(hv, ignore_index=True),
"tolovlar": pd.concat(tl, ignore_index=True),
"tranzaksiyalar": pd.concat(tr, ignore_index=True)}
def orta(a):
"""Qator bo'yicha NaN siz o'rtacha (bo'sh qatorda NaN, ogohlantirishsiz)."""
k = (~np.isnan(a)).sum(axis=1)
return np.where(k > 0, np.nansum(a, axis=1) / np.maximum(k, 1), np.nan)
def belgilar_jadvali(bank, oylar=range(7, 23)):
"""Har as-of oy t (oy oxiri): faqat t gacha ma'lum belgilar + t, t+1 natijasi."""
mj = bank["mijozlar"]
ids, kol = mj["mijoz_id"].to_numpy(), np.arange(1, OYLAR + 1)
def mat(df, qiymat, ustun="oy"):
return (df.pivot(index="mijoz_id", columns=ustun, values=qiymat)
.reindex(index=ids, columns=kol).to_numpy(dtype=float))
hv, tl = bank["hisobvaraqlar"], bank["tolovlar"]
bal, mint = mat(hv, "balans"), mat(hv, "min_tolov")
tol, kun = mat(tl, "summa", "hisob_oy"), mat(tl, "tolov_kuni", "hisob_oy")
bor = ~np.isnan(bal)
miss = bor & ~(np.nan_to_num(tol) >= mint - 1e-9) # minimal to'lov qilinmagan
g = (bank["tranzaksiyalar"].groupby(["turi", "mijoz_id", "oy"])["summa"]
.agg(["size", "sum"]))
def tr_mat(turi, ust):
return (g.loc[turi, ust].unstack("oy").reindex(index=ids, columns=kol)
.fillna(0).to_numpy())
naqd_n, naqd_s = tr_mat("naqd", "size"), tr_mat("naqd", "sum")
xarid_n = tr_mat("xarid", "size")
util = bal / mj["limit"].to_numpy()[:, None]
och = mj["ochilgan_oy"].to_numpy()
qismlar = []
for t in oylar:
c = t - 1 # t-oy ustuni (0 dan)
ok = (och <= t - 1) & bor[:, c] & bor[:, c - 1] & ~miss[:, c - 1]
u3 = util[:, c - 3]
f = mj.drop(columns="ochilgan_oy").assign(
oy=t, staj=t - och, util=util[:, c], util_3=orta(util[:, c - 2:c + 1]),
util_ozg=util[:, c] - np.where(np.isnan(u3), util[:, c], u3),
kechikish_6=miss[:, max(c - 6, 0):c].sum(axis=1),
tolov_nisbati_3=orta(np.nan_to_num(tol[:, c - 3:c]) / bal[:, c - 3:c]),
tolov_kuni_3=orta(kun[:, c - 3:c]),
naqd_soni_3=naqd_n[:, c - 2:c + 1].sum(axis=1),
naqd_summa_3=naqd_s[:, c - 2:c + 1].sum(axis=1),
xarid_soni_3=xarid_n[:, c - 2:c + 1].sum(axis=1),
balans=bal[:, c], y30=(miss[:, c] | miss[:, c + 1]).astype(int),
y60=(miss[:, c] & miss[:, c + 1]).astype(int))
qismlar.append(f[ok])
df = pd.concat(qismlar, ignore_index=True)
df["tolov_kuni_3"] = df["tolov_kuni_3"].fillna(25.0)
df["tolov_nisbati_3"] = df["tolov_nisbati_3"].fillna(0.0)
df["zarar"] = 0.45 * df["balans"] # DPD60 bo'lsa kutilgan zarar
df["qiymat"] = SAMARA * df["zarar"] * df["y60"] - NARX # qo'ng'iroq qiymati
return df
def top_k_qiymat(df, ball, k=K):
"""Har as-of oyda ball bo'yicha top-k ga qo'ng'iroq; oylik tejam (mln so'm)."""
natija = []
for _, g in df.assign(_b=ball).groupby("oy"):
natija.append(g.nlargest(k, "_b")["qiymat"].sum())
return np.array(natija)
def main() -> None:
bank = yarat_bank()
print("=== 1. Manba jadvallari (xom ma'lumot) ===")
for nom in ["mijozlar", "hisobvaraqlar", "tolovlar", "tranzaksiyalar"]:
print(f" {nom:<15} {len(bank[nom]):>8} qator")
print(f" davr: {oy_nomi(1)} - {oy_nomi(OYLAR)}")
df = belgilar_jadvali(bank)
oylik = df.groupby("oy").agg(n=("y60", "size"), y30=("y30", "mean"),
y60=("y60", "mean"), dpd60=("y60", "sum"))
print("\n=== 2. Tahlil birligi: (mijoz, as-of oy) ===")
print(f" qatorlar: {len(df)}, as-of oylar: {oy_nomi(df.oy.min())} - "
f"{oy_nomi(df.oy.max())} ({df.oy.nunique()} oy)")
print(f" oyiga nomzod mijozlar (hozir kechikmagan): {oylik.n.min()} - "
f"{oylik.n.max()}")
print(f" 60 kun ichida 30+ kun kechikish (y30): {df.y30.mean():.3f}")
print(f" 60 kun ichida 60+ kun kechikish (y60): {df.y60.mean():.3f}")
print(f" oyiga DPD60 holatlari: o'rtacha {oylik.dpd60.mean():.1f} "
f"(min {oylik.dpd60.min()}, max {oylik.dpd60.max()})")
print(f" qo'ng'iroq sig'imi: oyiga K = {K} ta = nomzodlarning "
f"{K / oylik.n.mean():.1%} i")
print("\n=== 3. Bitta qo'ng'iroqning qiymati (xarajat matritsasi, mln so'm) ===")
z60 = df.loc[df.y60 == 1, "zarar"]
print(f" DPD60 bo'lgan mijozning kutilgan zarari: o'rtacha {z60.mean():.2f}, "
f"mediana {z60.median():.2f}")
print(" qo'ng'iroq qilinsa (qo'ng'iroqsiz holatga nisbatan):")
print(f" mijoz DPD60 ga tushadigan bo'lsa (TP): +{SAMARA} * zarar - {NARX}")
print(f" mijoz DPD60 ga tushmaydigan bo'lsa (FP): -{NARX}")
print(" qo'ng'iroq qilinmasa: 0 (FN da zarar to'liq qoladi, TN - hech narsa)")
ulush = z60.sort_values(ascending=False)
top20 = ulush.iloc[: int(0.2 * len(ulush))].sum() / ulush.sum()
print(f" zarar konsentratsiyasi: eng katta 20% DPD60 zararning {top20:.1%} i")
p_chegara = NARX / (SAMARA * z60.median())
print(f" zararsizlik chegarasi (mediana zararda): p > {p_chegara:.3f}; "
f"bazaviy ulush {df.y60.mean():.3f}")
print("\n=== 4. Qiymat chegaralari: val oylari 16-18, oyiga tejam (mln so'm) ===")
va = df[df.oy.between(16, 18)].copy()
rng = np.random.default_rng(0)
tasodifiy = np.mean([top_k_qiymat(va, rng.random(len(va))).mean()
for _ in range(50)])
chegaralar = {
"hech kimga qo'ng'iroq yo'q": 0.0,
"tasodifiy 200 ta (50 marta)": tasodifiy,
"qoida: util eng yuqori": top_k_qiymat(va, va.util).mean(),
"oracle (kelajakni biladi)": top_k_qiymat(va, va.y60 * va.zarar).mean(),
}
for nom, v in chegaralar.items():
print(f" {nom:<30} {v:>+8.2f}")
toliq = (SAMARA * va.zarar * va.y60).groupby(va.oy).sum().mean()
print(f" oldini olish mumkin bo'lgan jami zarar (hammaga qo'ng'iroq): {toliq:.1f}")
print("\n=== 5. Xulosa (natijadan hisoblangan) ===")
qoida = chegaralar["qoida: util eng yuqori"]
oracle = chegaralar["oracle (kelajakni biladi)"]
if abs(tasodifiy) < 0.05 * qoida:
print(f" tasodifiy qo'ng'iroq deyarli nol ({tasodifiy:+.2f}) -> reyting shart")
print(f" oddiy qoida oracle qiymatining {qoida / oracle:.0%} ini beradi")
print(f" model uchun o'yin maydoni: oyiga ko'pi bilan {oracle - qoida:.1f} mln so'm "
f"(amalda ancha kam)")
if oylik.dpd60.mean() < K:
print(" sig'im DPD60 holatlaridan katta -> muammo reytingda, sig'imda emas")
if __name__ == "__main__":
main()Natijaning muhim qismi:
=== 1. Manba jadvallari (xom ma'lumot) ===
mijozlar 6000 qator
hisobvaraqlar 111058 qator
tolovlar 100780 qator
tranzaksiyalar 586885 qator
davr: 2024-01 - 2025-12
=== 2. Tahlil birligi: (mijoz, as-of oy) ===
qatorlar: 68532, as-of oylar: 2024-07 - 2025-10 (16 oy)
oyiga nomzod mijozlar (hozir kechikmagan): 4064 - 4475
60 kun ichida 30+ kun kechikish (y30): 0.109
60 kun ichida 60+ kun kechikish (y60): 0.020
oyiga DPD60 holatlari: o'rtacha 87.6 (min 66, max 116)
qo'ng'iroq sig'imi: oyiga K = 200 ta = nomzodlarning 4.7% i
=== 3. Bitta qo'ng'iroqning qiymati (xarajat matritsasi, mln so'm) ===
DPD60 bo'lgan mijozning kutilgan zarari: o'rtacha 3.64, mediana 3.12
qo'ng'iroq qilinsa (qo'ng'iroqsiz holatga nisbatan):
mijoz DPD60 ga tushadigan bo'lsa (TP): +0.3 * zarar - 0.02
mijoz DPD60 ga tushmaydigan bo'lsa (FP): -0.02
qo'ng'iroq qilinmasa: 0 (FN da zarar to'liq qoladi, TN - hech narsa)
zarar konsentratsiyasi: eng katta 20% DPD60 zararning 38.9% i
zararsizlik chegarasi (mediana zararda): p > 0.021; bazaviy ulush 0.020
=== 4. Qiymat chegaralari: val oylari 16-18, oyiga tejam (mln so'm) ===
hech kimga qo'ng'iroq yo'q +0.00
tasodifiy 200 ta (50 marta) +0.01
qoida: util eng yuqori +24.26
oracle (kelajakni biladi) +78.57
oldini olish mumkin bo'lgan jami zarar (hammaga qo'ng'iroq): 82.6
=== 5. Xulosa (natijadan hisoblangan) ===
tasodifiy qo'ng'iroq deyarli nol (+0.01) -> reyting shart
oddiy qoida oracle qiymatining 31% ini beradi
model uchun o'yin maydoni: oyiga ko'pi bilan 54.3 mln so'm (amalda ancha kam)
sig'im DPD60 holatlaridan katta -> muammo reytingda, sig'imda emasNatija tahlili.
1-bo'lim — xom ma'lumot: to'rtta jadval va 24 oy. Tranzaksiyalar eng katta jadval (586885 qator). Mijozlar jadvali eng kichigi (6000), lekin undagi belgilar (daromad, limit, avto to'lov) har as-of oyda qayta ishlatiladi.
2-bo'lim — tahlil birligi. Model mijozni emas, (mijoz, as-of oy) juftini baholaydi: har oy oxirida "hozir kechikmagan" mijozlar nomzod bo'ladi, oyiga 4064 dan 4475 gacha. Jami 68532 qator, 16 ta as-of oy (2024-07 dan 2025-10 gacha). Birinchi 6 oy belgilar tarixi uchun ketadi, oxirgi 2 oy esa belgi yetilishi uchun: 2025-11 va 2025-12 dagi as-of lar uchun natija hali ma'lum emas. Ikki ta'rif keskin farq qiladi: 60 kun ichida 30+ kunlik kechikish 10.9%, 60+ kunlik esa 2.0%. Oyiga o'rtacha 87.6 ta DPD60 holati bor, sig'im esa 200 qo'ng'iroq. Demak sig'im muammo emas: mukammal model hamma DPD60 larga qo'ng'iroq qila olardi. Muammo — ularni 4 mingdan ortiq nomzod ichidan topish, ya'ni reyting.
3-bo'lim — bitta qo'ng'iroqning qiymati. TP qo'ng'iroq +0.3 * zarar - 0.02 beradi, FP esa -0.02. DPD60 ga tushadiganlarning o'rtacha kutilgan zarari 3.64 mln so'm, mediana 3.12. Taqsimot o'ngga qiyshiq: eng katta 20% holat jami zararning 38.9% ini beradi. Shuning uchun qaysi DPD60 ni ushlash ham muhim, faqat nechtasini ushlash emas. Zararsizlik chegarasi p > 0.021 va bazaviy ulushga (0.020) juda yaqin: tasodifan tanlangan mijozga qo'ng'iroq o'rtacha deyarli nol qiymat beradi. Bu 4-bo'limda tasdiqlanadi.
4-bo'lim — qiymat zinapoyasi val oylarida (16-18). Qo'ng'iroq qilmaslik 0. Tasodifiy 200 ta qo'ng'iroq (50 marta o'rtachasi) +0.01, ya'ni deyarli nol. Bitta ustunli qoida ("limitdan foydalanish ulushi eng yuqori 200") +24.26. Oracle +78.57 mln so'm/oy tejaydi. Hammaga qo'ng'iroq qilinsa (qo'ng'iroq narxisiz), oldini olish mumkin bo'lgan jami zarar 82.6, lekin 4 ming qo'ng'iroq sig'imdan 20 barobar katta.
5-bo'lim — xulosalar raqamdan chiqarildi. Oddiy qoida oracle qiymatining 31% ini beradi, demak modelning o'yin maydoni oyiga ko'pi bilan 54.3 mln so'm. Amalda model oracle ga yaqinlasha olmaydi: kelajakdagi qiyinchilik to'liq kuzatilmaydi. Shuning uchun haqiqiy savol "model qoidadan qancha qo'shadi?", va bu 3-misolda o'lchanadi. Loyiha hujjatiga ikki raqam yoziladi: yuqori chegara (78.6) va joriy qoida (24.3). Qolgan hamma natija shu ikkisi orasida baholanadi.
Misol 2 — Model metrikasi va biznes metrikasi: AUC va tejalgan zarar
"""Model metrikasi va biznes metrikasi: AUC va tejalgan zarar bir xil narsani o'lchamaydi."""
import numpy as np
import pandas as pd
from sklearn.ensemble import HistGradientBoostingClassifier
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import roc_auc_score
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import StandardScaler
HUDUDLAR = ["Toshkent", "Samarqand", "Farg'ona", "Andijon", "Buxoro", "Qashqadaryo"]
OYLAR = 24 # 1 = 2024-01, ..., 24 = 2025-12
BELGILAR = ["yosh", "daromad", "limit", "avto_tolov", "maosh_loyihasi", "staj",
"util", "util_3", "util_ozg", "kechikish_6", "tolov_nisbati_3",
"tolov_kuni_3", "naqd_soni_3", "naqd_summa_3", "xarid_soni_3"]
K, SAMARA, NARX = 200, 0.30, 0.02 # oyiga qo'ng'iroq, oldini olish, mln so'm
def sigmoid(z):
return 1 / (1 + np.exp(-z))
def oy_nomi(m):
return f"{2024 + (m - 1) // 12}-{(m - 1) % 12 + 1:02d}"
def yarat_bank(seed=29, n=6000):
"""Kredit karta portfeli: mijozlar, hisobvaraqlar, tolovlar, tranzaksiyalar."""
rng = np.random.default_rng(seed)
ids = np.arange(1, n + 1)
yosh = rng.integers(21, 66, n)
maosh = rng.random(n) < 0.45
daromad = np.round(np.exp(rng.normal(np.log(6.0), 0.45, n)), 2) # mln so'm
ochilgan = np.where(rng.random(n) < 0.7, rng.integers(-40, 1, n),
rng.integers(1, 20, n))
xavf = (rng.normal(0, 1, n) - 0.5 * maosh - 0.8 * np.log(daromad / 6)
+ 0.015 * (35 - yosh)) # yashirin: moliyaviy xavf
unut = rng.normal(0, 1, n) + 0.03 * (35 - yosh) # yashirin: unutuvchanlik
avto = (rng.random(n) < sigmoid(-0.2 - 0.9 * unut)).astype(int)
limit = np.round(daromad * rng.uniform(1.5, 3.5, n), 1)
hudud = rng.choice(HUDUDLAR, n, p=[0.34, 0.15, 0.14, 0.13, 0.12, 0.12])
mj = pd.DataFrame({"mijoz_id": ids, "yosh": yosh, "hudud": hudud,
"daromad": daromad, "limit": limit, "avto_tolov": avto,
"maosh_loyihasi": maosh.astype(int), "ochilgan_oy": ochilgan})
s, oldingi, ketma = rng.normal(0, 1, n), np.zeros(n, int), np.zeros(n, int)
hv, tl, tr = [], [], []
for m in range(1, OYLAR + 1):
faol = (ochilgan <= m) & (ketma < 3) # 3 ketma-ket kechikish - defolt
s = 0.75 * s + 0.66 * rng.normal(0, 1, n)
d = 0.8 * xavf + 0.7 * s + 0.15 * (m % 12 == 1) + 0.08 * max(m - 18, 0)
util = np.clip(sigmoid(-0.4 + 0.55 * d + rng.normal(0, 0.5, n)), 0.01, 1)
balans = np.round(util * limit, 3)
min_t = np.round(np.maximum(0.05 * balans, 0.05), 3)
for turi, lam in [("xarid", np.exp(1.2 + 0.15 * np.log(daromad / 6) - 0.1 * d)),
("naqd", np.exp(-1.6 + 0.6 * d)),
("onlayn", np.exp(0.6 - 0.02 * (yosh - 35)))]:
i = np.repeat(np.arange(n), rng.poisson(lam) * faol)
summa = (rng.uniform(0.2, 1.5, i.size) * (1 + 0.3 * d[i].clip(0))
if turi == "naqd" else np.exp(rng.normal(np.log(0.25), 0.8, i.size)))
tr.append(pd.DataFrame({"mijoz_id": ids[i], "oy": m,
"kun": rng.integers(1, 29, i.size), "turi": turi,
"summa": np.round(summa, 3)}))
q = rng.random(n) < sigmoid(-3.7 + 1.3 * d + 1.2 * (util > 0.85)
+ 3.8 * (oldingi == 2)) # qiyinchilik
u = ~q & (rng.random(n) < sigmoid(-3.4 + 1.1 * unut - 1.8 * avto)
* (oldingi != 1)) # unutish
miss = (q | u) & faol
ulush = sigmoid(0.3 - 1.2 * d + rng.normal(0, 0.5, n))
summa = np.where(miss, min_t * rng.uniform(0, 0.6, n),
min_t + (balans - min_t).clip(0) * ulush)
tolaydi = faol & (~miss | (rng.random(n) < 0.4))
kun = np.clip(np.round(6 + 14 * sigmoid(unut + 0.5 * d)
+ rng.normal(0, 3, n)), 1, 25)
hv.append(pd.DataFrame({"mijoz_id": ids[faol], "oy": m,
"balans": balans[faol], "min_tolov": min_t[faol]}))
if m < OYLAR: # m-oy hisobvarag'i (m+1)-oyda to'lanadi
tl.append(pd.DataFrame({"mijoz_id": ids[tolaydi], "hisob_oy": m,
"tolov_oy": m + 1,
"tolov_kuni": kun[tolaydi].astype(int),
"summa": np.round(summa[tolaydi], 3)}))
ketma = np.where(faol, np.where(miss, ketma + 1, 0), ketma)
oldingi = np.where(miss, np.where(q, 2, 1), 0)
return {"mijozlar": mj, "hisobvaraqlar": pd.concat(hv, ignore_index=True),
"tolovlar": pd.concat(tl, ignore_index=True),
"tranzaksiyalar": pd.concat(tr, ignore_index=True)}
def orta(a):
"""Qator bo'yicha NaN siz o'rtacha (bo'sh qatorda NaN, ogohlantirishsiz)."""
k = (~np.isnan(a)).sum(axis=1)
return np.where(k > 0, np.nansum(a, axis=1) / np.maximum(k, 1), np.nan)
def belgilar_jadvali(bank, oylar=range(7, 23)):
"""Har as-of oy t (oy oxiri): faqat t gacha ma'lum belgilar + t, t+1 natijasi."""
mj = bank["mijozlar"]
ids, kol = mj["mijoz_id"].to_numpy(), np.arange(1, OYLAR + 1)
def mat(df, qiymat, ustun="oy"):
return (df.pivot(index="mijoz_id", columns=ustun, values=qiymat)
.reindex(index=ids, columns=kol).to_numpy(dtype=float))
hv, tl = bank["hisobvaraqlar"], bank["tolovlar"]
bal, mint = mat(hv, "balans"), mat(hv, "min_tolov")
tol, kun = mat(tl, "summa", "hisob_oy"), mat(tl, "tolov_kuni", "hisob_oy")
bor = ~np.isnan(bal)
miss = bor & ~(np.nan_to_num(tol) >= mint - 1e-9) # minimal to'lov qilinmagan
g = (bank["tranzaksiyalar"].groupby(["turi", "mijoz_id", "oy"])["summa"]
.agg(["size", "sum"]))
def tr_mat(turi, ust):
return (g.loc[turi, ust].unstack("oy").reindex(index=ids, columns=kol)
.fillna(0).to_numpy())
naqd_n, naqd_s = tr_mat("naqd", "size"), tr_mat("naqd", "sum")
xarid_n = tr_mat("xarid", "size")
util = bal / mj["limit"].to_numpy()[:, None]
och = mj["ochilgan_oy"].to_numpy()
qismlar = []
for t in oylar:
c = t - 1 # t-oy ustuni (0 dan)
ok = (och <= t - 1) & bor[:, c] & bor[:, c - 1] & ~miss[:, c - 1]
u3 = util[:, c - 3]
f = mj.drop(columns="ochilgan_oy").assign(
oy=t, staj=t - och, util=util[:, c], util_3=orta(util[:, c - 2:c + 1]),
util_ozg=util[:, c] - np.where(np.isnan(u3), util[:, c], u3),
kechikish_6=miss[:, max(c - 6, 0):c].sum(axis=1),
tolov_nisbati_3=orta(np.nan_to_num(tol[:, c - 3:c]) / bal[:, c - 3:c]),
tolov_kuni_3=orta(kun[:, c - 3:c]),
naqd_soni_3=naqd_n[:, c - 2:c + 1].sum(axis=1),
naqd_summa_3=naqd_s[:, c - 2:c + 1].sum(axis=1),
xarid_soni_3=xarid_n[:, c - 2:c + 1].sum(axis=1),
balans=bal[:, c], y30=(miss[:, c] | miss[:, c + 1]).astype(int),
y60=(miss[:, c] & miss[:, c + 1]).astype(int))
qismlar.append(f[ok])
df = pd.concat(qismlar, ignore_index=True)
df["tolov_kuni_3"] = df["tolov_kuni_3"].fillna(25.0)
df["tolov_nisbati_3"] = df["tolov_nisbati_3"].fillna(0.0)
df["zarar"] = 0.45 * df["balans"] # DPD60 bo'lsa kutilgan zarar
df["qiymat"] = SAMARA * df["zarar"] * df["y60"] - NARX # qo'ng'iroq qiymati
return df
def oy_guruhlari(df):
oy = df["oy"].to_numpy()
return [np.flatnonzero(oy == t) for t in np.unique(oy)]
def tejam_k(qiymat, ball, guruhlar, k=K):
"""Har oyda ball bo'yicha top-k; oylik o'rtacha tejam (mln so'm)."""
jami = 0.0
for g in guruhlar:
jami += qiymat[g[np.argsort(-ball[g], kind="stable")[:k]]].sum()
return jami / len(guruhlar)
def bootstrap(y, qiymat, ballar, guruhlar, takror=300, seed=1):
"""Oy ichida qayta tanlash: har ball uchun AUC va tejam namunalari (juftlashgan)."""
rng = np.random.default_rng(seed)
auc = {k: [] for k in ballar}
tej = {k: [] for k in ballar}
for _ in range(takror):
gb = [rng.choice(g, len(g)) for g in guruhlar]
idx = np.concatenate(gb)
yangi = []
bosh = 0
for g in gb:
yangi.append(np.arange(bosh, bosh + len(g)))
bosh += len(g)
for k, b in ballar.items():
auc[k].append(roc_auc_score(y[idx], b[idx]))
tej[k].append(tejam_k(qiymat[idx], b[idx], yangi))
return ({k: np.array(v) for k, v in auc.items()},
{k: np.array(v) for k, v in tej.items()})
def main() -> None:
df = belgilar_jadvali(yarat_bank())
tr, va = df[df.oy <= 14], df[df.oy.between(16, 18)].reset_index(drop=True)
guruhlar = oy_guruhlari(va)
y, qiymat, zarar = va["y60"].to_numpy(), va["qiymat"].to_numpy(), va["zarar"].to_numpy()
lr = make_pipeline(StandardScaler(), LogisticRegression(max_iter=2000))
p = lr.fit(tr[BELGILAR], tr["y60"]).predict_proba(va[BELGILAR])[:, 1]
print("=== 1. LogReg (y60), o'quv: as-of 7-14, val: 16-18 ===")
print(f" AUC {roc_auc_score(y, p):.4f}, tejam (top-{K}, p bo'yicha) "
f"{tejam_k(qiymat, p, guruhlar):.1f} mln so'm/oy")
tp = fp = fn = 0
for g in guruhlar:
top = g[np.argsort(-p[g], kind="stable")[:K]]
tp += y[top].sum()
fp += K - y[top].sum()
fn += y[g].sum() - y[top].sum()
m = len(guruhlar)
print(f" oyiga: TP {tp / m:.1f}, FP {fp / m:.1f}, FN {fn / m:.1f}; "
f"precision@{K} {tp / (tp + fp):.3f}, recall@{K} {tp / (tp + fn):.3f}")
print("\n=== 2. Bir xil model, ikki reyting: p va p * zarar ===")
pz = p * zarar
ballar = {"p": p, "p*zarar": pz}
for k, b in ballar.items():
print(f" {k:<8} AUC {roc_auc_score(y, b):.4f} tejam "
f"{tejam_k(qiymat, b, guruhlar):6.1f}")
print("\n=== 3. AUC oshadi, tejam o'zgarmaydi: faqat top-K dan tashqari tartib ===")
yangi = p.copy()
for g in guruhlar:
tartib = g[np.argsort(-p[g], kind="stable")]
top, qolgan = tartib[:K], tartib[K:]
kuchaytir = p[qolgan] * (1 + 4 * y[qolgan]) # musbatlarni yuqoriga
daraja = np.argsort(np.argsort(kuchaytir)) / len(qolgan)
yangi[qolgan] = p[top].min() * 0.999 * daraja
ballar["p (tashqari tuzatilgan)"] = yangi
for k in ["p", "p (tashqari tuzatilgan)"]:
print(f" {k:<24} AUC {roc_auc_score(y, ballar[k]):.4f} tejam "
f"{tejam_k(qiymat, ballar[k], guruhlar):6.1f}")
print("\n=== 4. Juftlashgan bootstrap (oy ichida, 300 takror): AUC va tejam farqi ===")
hgb = HistGradientBoostingClassifier(max_iter=200, learning_rate=0.05,
max_leaf_nodes=15, min_samples_leaf=100,
random_state=0)
ph = hgb.fit(tr[BELGILAR], tr["y60"]).predict_proba(va[BELGILAR])[:, 1]
ballar = {"p": p, "p*zarar": pz, "HGB p*zarar": ph * zarar}
auc_b, tej_b = bootstrap(y, qiymat, ballar, guruhlar)
xulosa = {}
for a, b in [("p*zarar", "p"), ("HGB p*zarar", "p*zarar")]:
da, dt = auc_b[a] - auc_b[b], tej_b[a] - tej_b[b]
ta = roc_auc_score(y, ballar[a]) - roc_auc_score(y, ballar[b])
tt = tejam_k(qiymat, ballar[a], guruhlar) - tejam_k(qiymat, ballar[b], guruhlar)
print(f" {a} - {b}:")
print(f" AUC farqi {ta:+.4f} (SE {da.std(ddof=1):.4f}) "
f"sezilarli: {abs(ta) > 2 * da.std(ddof=1)}")
print(f" tejam farqi {tt:+7.2f} (SE {dt.std(ddof=1):.2f}) "
f"sezilarli: {abs(tt) > 2 * dt.std(ddof=1)}")
xulosa[a] = (ta, abs(ta) > 2 * da.std(ddof=1), tt, abs(tt) > 2 * dt.std(ddof=1))
print("\n=== 5. Xulosa (natijadan hisoblangan) ===")
for nom, (ta, sa, tt, st) in xulosa.items():
if sa and not st:
print(f" {nom}: AUC farqi sezilarli ({ta:+.4f}), tejam farqi yo'q "
f"({tt:+.2f}) -> AUC farqi qiymatga o'tmadi")
a_p = roc_auc_score(y, p)
a_y = roc_auc_score(y, yangi)
if abs(tejam_k(qiymat, yangi, guruhlar) - tejam_k(qiymat, p, guruhlar)) < 1e-9:
print(f" top-K tashqarisi: AUC {a_y - a_p:+.4f}, tejam aynan o'zgarmadi")
print(" qaror top-K ga tayansa, metrika ham top-K qiymatini o'lchasin")
if __name__ == "__main__":
main()Natijaning muhim qismi:
=== 1. LogReg (y60), o'quv: as-of 7-14, val: 16-18 ===
AUC 0.8482, tejam (top-200, p bo'yicha) 26.3 mln so'm/oy
oyiga: TP 24.0, FP 176.0, FN 52.7; precision@200 0.120, recall@200 0.313
=== 2. Bir xil model, ikki reyting: p va p * zarar ===
p AUC 0.8482 tejam 26.3
p*zarar AUC 0.8314 tejam 28.8
=== 3. AUC oshadi, tejam o'zgarmaydi: faqat top-K dan tashqari tartib ===
p AUC 0.8482 tejam 26.3
p (tashqari tuzatilgan) AUC 0.9536 tejam 26.3
=== 4. Juftlashgan bootstrap (oy ichida, 300 takror): AUC va tejam farqi ===
p*zarar - p:
AUC farqi -0.0169 (SE 0.0046) sezilarli: True
tejam farqi +2.53 (SE 2.58) sezilarli: False
HGB p*zarar - p*zarar:
AUC farqi -0.0127 (SE 0.0039) sezilarli: True
tejam farqi +0.18 (SE 1.94) sezilarli: False
=== 5. Xulosa (natijadan hisoblangan) ===
p*zarar: AUC farqi sezilarli (-0.0169), tejam farqi yo'q (+2.53) -> AUC farqi qiymatga o'tmadi
HGB p*zarar: AUC farqi sezilarli (-0.0127), tejam farqi yo'q (+0.18) -> AUC farqi qiymatga o'tmadi
top-K tashqarisi: AUC +0.1054, tejam aynan o'zgarmadi
qaror top-K ga tayansa, metrika ham top-K qiymatini o'lchasinNatija tahlili.
1-bo'lim — oddiy LogReg (y60 da, as-of 7-14 da o'qitilgan). Val da AUC 0.8482. Ko'pchilik hisobotlar shu raqam bilan tugaydi. Qarorga tarjima qilsak, boshqacha rasm chiqadi. Ehtimol bo'yicha top-200 ga qo'ng'iroq oyiga 26.3 mln so'm tejaydi. 200 qo'ng'iroqdan o'rtacha faqat 24.0 tasi haqiqiy DPD60 (precision@200 0.120), va DPD60 larning 31.3% i ushlanadi (recall). "AUC 0.85" rahbarga yuqori raqamdek ko'rinadi. Lekin qo'ng'iroq markazi uchun uning ma'nosi: "har 8-9 qo'ng'iroqdan bittasi foydali". Bu raqam ham to'g'ri, ham kutilganlarni real darajaga tushiradi. Muvaffaqiyat mezoni aynan shunday tilda yozilishi kerak.
2-bo'lim — bir xil model, ikki reyting. p bo'yicha tartiblaganda AUC 0.8482, p * zarar bo'yicha esa 0.8314, ya'ni AUC pasaydi. Chunki kichik balansli, lekin ehtimoli yuqori mijozlar pastga tushdi, AUC uchun esa ular ham muhim. Tejam esa 26.3 dan 28.8 ga oshdi. Nazariy jihatdan bu kutilgan natija: qo'ng'iroqning kutilgan qiymati p * 0.30 * zarar - 0.02, shuning uchun p * zarar bo'yicha tartib — kalibrlangan p uchun optimal tartib.
3-bo'lim — AUC oshadi, tejam o'zgarmaydi. Faqat top-200 dan tashqaridagi mijozlar tartibini "tuzatdik": musbatlarni yuqoriroqqa, lekin chegaradan pastga surdik. AUC 0.8482 dan 0.9536 ga sakradi, tejam esa aynan 26.3 bo'lib qoldi, chunki qo'ng'iroq qilinadigan ro'yxat o'zgarmadi. Bu sun'iy tajriba, lekin amalda xuddi shu hol uchraydi: yangi belgi past xavfli mijozlarni yaxshiroq ajratadi, AUC o'sadi, biznes esa hech narsani sezmaydi.
4-bo'lim — juftlashgan bootstrap (oy ichida qayta tanlash, 300 takror). Ikkala taqqoslashda ham rasm bir xil:
p * zararvap: AUC farqi-0.0169(SE0.0046), sezilarli. Tejam farqi+2.53(SE2.58), sezilarli emas.- HistGB va LogReg (ikkalasi
p * zararbilan): AUC farqi-0.0127(SE0.0039), sezilarli. Tejam farqi+0.18(SE1.94), sezilarli emas.
Halol xulosa: AUC bo'yicha "sezilarli" farqlar biznes qiymatida sezilarli farqqa aylanmadi. p * zarar yo'nalishi nazariya bilan mos, lekin 3 oylik val uni isbotlash uchun yetarli emas. Buni 29.3 da ko'proq oy bilan tekshiramiz. Qaror qanday metrika bilan qabul qilinsa, taqqoslash ham shu metrika bilan va uning noaniqligi bilan qilinishi kerak. AUC farqining SE si tejam farqinikidan ancha kichik. Buning sababi: AUC barcha qatorlardan, tejam esa faqat top-200 dan hisoblanadi. Biznes metrikasi shovqinliroq, shuning uchun uni baholash uchun ko'proq ma'lumot (oylar) kerak. Bu loyiha rejasiga ta'sir qiladi: 29.3 da baholash 7 oy bo'yicha qilinadi.
Misol 3 — Bazaviylar va "model kerakmi?": qoidalar, ekspert ball-kartasi va LogReg
"""Bazaviylar va "model kerakmi?": qoidalar, ekspert ball-kartasi va LogReg."""
import numpy as np
import pandas as pd
from sklearn.linear_model import LogisticRegression
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import StandardScaler
HUDUDLAR = ["Toshkent", "Samarqand", "Farg'ona", "Andijon", "Buxoro", "Qashqadaryo"]
OYLAR = 24 # 1 = 2024-01, ..., 24 = 2025-12
BELGILAR = ["yosh", "daromad", "limit", "avto_tolov", "maosh_loyihasi", "staj",
"util", "util_3", "util_ozg", "kechikish_6", "tolov_nisbati_3",
"tolov_kuni_3", "naqd_soni_3", "naqd_summa_3", "xarid_soni_3"]
K, SAMARA, NARX = 200, 0.30, 0.02 # oyiga qo'ng'iroq, oldini olish, mln so'm
def sigmoid(z):
return 1 / (1 + np.exp(-z))
def oy_nomi(m):
return f"{2024 + (m - 1) // 12}-{(m - 1) % 12 + 1:02d}"
def yarat_bank(seed=29, n=6000):
"""Kredit karta portfeli: mijozlar, hisobvaraqlar, tolovlar, tranzaksiyalar."""
rng = np.random.default_rng(seed)
ids = np.arange(1, n + 1)
yosh = rng.integers(21, 66, n)
maosh = rng.random(n) < 0.45
daromad = np.round(np.exp(rng.normal(np.log(6.0), 0.45, n)), 2) # mln so'm
ochilgan = np.where(rng.random(n) < 0.7, rng.integers(-40, 1, n),
rng.integers(1, 20, n))
xavf = (rng.normal(0, 1, n) - 0.5 * maosh - 0.8 * np.log(daromad / 6)
+ 0.015 * (35 - yosh)) # yashirin: moliyaviy xavf
unut = rng.normal(0, 1, n) + 0.03 * (35 - yosh) # yashirin: unutuvchanlik
avto = (rng.random(n) < sigmoid(-0.2 - 0.9 * unut)).astype(int)
limit = np.round(daromad * rng.uniform(1.5, 3.5, n), 1)
hudud = rng.choice(HUDUDLAR, n, p=[0.34, 0.15, 0.14, 0.13, 0.12, 0.12])
mj = pd.DataFrame({"mijoz_id": ids, "yosh": yosh, "hudud": hudud,
"daromad": daromad, "limit": limit, "avto_tolov": avto,
"maosh_loyihasi": maosh.astype(int), "ochilgan_oy": ochilgan})
s, oldingi, ketma = rng.normal(0, 1, n), np.zeros(n, int), np.zeros(n, int)
hv, tl, tr = [], [], []
for m in range(1, OYLAR + 1):
faol = (ochilgan <= m) & (ketma < 3) # 3 ketma-ket kechikish - defolt
s = 0.75 * s + 0.66 * rng.normal(0, 1, n)
d = 0.8 * xavf + 0.7 * s + 0.15 * (m % 12 == 1) + 0.08 * max(m - 18, 0)
util = np.clip(sigmoid(-0.4 + 0.55 * d + rng.normal(0, 0.5, n)), 0.01, 1)
balans = np.round(util * limit, 3)
min_t = np.round(np.maximum(0.05 * balans, 0.05), 3)
for turi, lam in [("xarid", np.exp(1.2 + 0.15 * np.log(daromad / 6) - 0.1 * d)),
("naqd", np.exp(-1.6 + 0.6 * d)),
("onlayn", np.exp(0.6 - 0.02 * (yosh - 35)))]:
i = np.repeat(np.arange(n), rng.poisson(lam) * faol)
summa = (rng.uniform(0.2, 1.5, i.size) * (1 + 0.3 * d[i].clip(0))
if turi == "naqd" else np.exp(rng.normal(np.log(0.25), 0.8, i.size)))
tr.append(pd.DataFrame({"mijoz_id": ids[i], "oy": m,
"kun": rng.integers(1, 29, i.size), "turi": turi,
"summa": np.round(summa, 3)}))
q = rng.random(n) < sigmoid(-3.7 + 1.3 * d + 1.2 * (util > 0.85)
+ 3.8 * (oldingi == 2)) # qiyinchilik
u = ~q & (rng.random(n) < sigmoid(-3.4 + 1.1 * unut - 1.8 * avto)
* (oldingi != 1)) # unutish
miss = (q | u) & faol
ulush = sigmoid(0.3 - 1.2 * d + rng.normal(0, 0.5, n))
summa = np.where(miss, min_t * rng.uniform(0, 0.6, n),
min_t + (balans - min_t).clip(0) * ulush)
tolaydi = faol & (~miss | (rng.random(n) < 0.4))
kun = np.clip(np.round(6 + 14 * sigmoid(unut + 0.5 * d)
+ rng.normal(0, 3, n)), 1, 25)
hv.append(pd.DataFrame({"mijoz_id": ids[faol], "oy": m,
"balans": balans[faol], "min_tolov": min_t[faol]}))
if m < OYLAR: # m-oy hisobvarag'i (m+1)-oyda to'lanadi
tl.append(pd.DataFrame({"mijoz_id": ids[tolaydi], "hisob_oy": m,
"tolov_oy": m + 1,
"tolov_kuni": kun[tolaydi].astype(int),
"summa": np.round(summa[tolaydi], 3)}))
ketma = np.where(faol, np.where(miss, ketma + 1, 0), ketma)
oldingi = np.where(miss, np.where(q, 2, 1), 0)
return {"mijozlar": mj, "hisobvaraqlar": pd.concat(hv, ignore_index=True),
"tolovlar": pd.concat(tl, ignore_index=True),
"tranzaksiyalar": pd.concat(tr, ignore_index=True)}
def orta(a):
"""Qator bo'yicha NaN siz o'rtacha (bo'sh qatorda NaN, ogohlantirishsiz)."""
k = (~np.isnan(a)).sum(axis=1)
return np.where(k > 0, np.nansum(a, axis=1) / np.maximum(k, 1), np.nan)
def belgilar_jadvali(bank, oylar=range(7, 23)):
"""Har as-of oy t (oy oxiri): faqat t gacha ma'lum belgilar + t, t+1 natijasi."""
mj = bank["mijozlar"]
ids, kol = mj["mijoz_id"].to_numpy(), np.arange(1, OYLAR + 1)
def mat(df, qiymat, ustun="oy"):
return (df.pivot(index="mijoz_id", columns=ustun, values=qiymat)
.reindex(index=ids, columns=kol).to_numpy(dtype=float))
hv, tl = bank["hisobvaraqlar"], bank["tolovlar"]
bal, mint = mat(hv, "balans"), mat(hv, "min_tolov")
tol, kun = mat(tl, "summa", "hisob_oy"), mat(tl, "tolov_kuni", "hisob_oy")
bor = ~np.isnan(bal)
miss = bor & ~(np.nan_to_num(tol) >= mint - 1e-9) # minimal to'lov qilinmagan
g = (bank["tranzaksiyalar"].groupby(["turi", "mijoz_id", "oy"])["summa"]
.agg(["size", "sum"]))
def tr_mat(turi, ust):
return (g.loc[turi, ust].unstack("oy").reindex(index=ids, columns=kol)
.fillna(0).to_numpy())
naqd_n, naqd_s = tr_mat("naqd", "size"), tr_mat("naqd", "sum")
xarid_n = tr_mat("xarid", "size")
util = bal / mj["limit"].to_numpy()[:, None]
och = mj["ochilgan_oy"].to_numpy()
qismlar = []
for t in oylar:
c = t - 1 # t-oy ustuni (0 dan)
ok = (och <= t - 1) & bor[:, c] & bor[:, c - 1] & ~miss[:, c - 1]
u3 = util[:, c - 3]
f = mj.drop(columns="ochilgan_oy").assign(
oy=t, staj=t - och, util=util[:, c], util_3=orta(util[:, c - 2:c + 1]),
util_ozg=util[:, c] - np.where(np.isnan(u3), util[:, c], u3),
kechikish_6=miss[:, max(c - 6, 0):c].sum(axis=1),
tolov_nisbati_3=orta(np.nan_to_num(tol[:, c - 3:c]) / bal[:, c - 3:c]),
tolov_kuni_3=orta(kun[:, c - 3:c]),
naqd_soni_3=naqd_n[:, c - 2:c + 1].sum(axis=1),
naqd_summa_3=naqd_s[:, c - 2:c + 1].sum(axis=1),
xarid_soni_3=xarid_n[:, c - 2:c + 1].sum(axis=1),
balans=bal[:, c], y30=(miss[:, c] | miss[:, c + 1]).astype(int),
y60=(miss[:, c] & miss[:, c + 1]).astype(int))
qismlar.append(f[ok])
df = pd.concat(qismlar, ignore_index=True)
df["tolov_kuni_3"] = df["tolov_kuni_3"].fillna(25.0)
df["tolov_nisbati_3"] = df["tolov_nisbati_3"].fillna(0.0)
df["zarar"] = 0.45 * df["balans"] # DPD60 bo'lsa kutilgan zarar
df["qiymat"] = SAMARA * df["zarar"] * df["y60"] - NARX # qo'ng'iroq qiymati
return df
def oy_guruhlari(df):
oy = df["oy"].to_numpy()
return [np.flatnonzero(oy == t) for t in np.unique(oy)]
def tejam_k(qiymat, ball, guruhlar, k=K):
jami = 0.0
for g in guruhlar:
jami += qiymat[g[np.argsort(-ball[g], kind="stable")[:k]]].sum()
return jami / len(guruhlar)
def ekspert_ball(df):
"""Kredit bo'limi tajribasidan: oddiy ball-karta, teng ballda katta balans oldin."""
ball = (2 * (df["util"] > 0.8) + 1 * (df["naqd_soni_3"] >= 2)
+ 2 * (df["tolov_nisbati_3"] < 0.15) + 1 * (df["util_ozg"] > 0.15))
return (ball + df["zarar"] / 100).to_numpy()
def main() -> None:
df = belgilar_jadvali(yarat_bank())
tr, va = df[df.oy <= 14], df[df.oy.between(16, 18)].reset_index(drop=True)
guruhlar = oy_guruhlari(va)
qiymat = va["qiymat"].to_numpy()
lr = make_pipeline(StandardScaler(), LogisticRegression(max_iter=2000))
p = lr.fit(tr[BELGILAR], tr["y60"]).predict_proba(va[BELGILAR])[:, 1]
rng = np.random.default_rng(0)
nomzodlar = { # soddalik tartibida
"0 hech kim": None,
"1 tasodifiy": rng.random(len(va)),
"2 qoida: util": va["util"].to_numpy(),
"3 qoida: util*zarar": (va["util"] * va["zarar"]).to_numpy(),
"4 ekspert ball-karta": ekspert_ball(va),
"5 LogReg p*zarar": p * va["zarar"].to_numpy(),
}
print("=== 1. Val oylari 16-18: oylik tejam (mln so'm) ===")
baho = {}
for nom, b in nomzodlar.items():
baho[nom] = 0.0 if b is None else tejam_k(qiymat, b, guruhlar)
print(f" {nom:<22} {baho[nom]:>7.2f}")
print("\n=== 2. Juftlashgan bootstrap (oy ichida, 300): eng yaxshisidan farq ===")
eng = max(baho, key=baho.get)
bs = {nom: [] for nom in nomzodlar}
rng = np.random.default_rng(1)
for _ in range(300):
gb = [rng.choice(g, len(g)) for g in guruhlar]
idx = np.concatenate(gb)
chegara = np.cumsum([0] + [len(g) for g in gb])
yangi = [np.arange(chegara[i], chegara[i + 1]) for i in range(len(gb))]
for nom, b in nomzodlar.items():
bs[nom].append(0.0 if b is None else tejam_k(qiymat[idx], b[idx], yangi))
tanlov = None
print(f" eng yaxshisi: {eng}")
print(f" {'nomzod':<22} {'farq':>7} {'SE':>6} {'sezilarli yomon':>16}")
for nom in nomzodlar:
d = np.array(bs[nom]) - np.array(bs[eng])
farq, se = baho[nom] - baho[eng], d.std(ddof=1)
yomon = farq < -2 * se
if not yomon and tanlov is None:
tanlov = nom
print(f" {nom:<22} {farq:>+7.2f} {se:>6.2f} {str(yomon):>16}")
print(f" qaror qoidasi -> eng sodda munosib: {tanlov}")
print("\n=== 3. \"Model kerakmi?\" - pul bilan ===")
qoidalar = [n for n in nomzodlar if n[0] in "234"]
eng_qoida = max(qoidalar, key=baho.get)
d = np.array(bs["5 LogReg p*zarar"]) - np.array(bs[eng_qoida])
farq, se = baho["5 LogReg p*zarar"] - baho[eng_qoida], d.std(ddof=1)
lo, hi = 12 * (farq - 2 * se), 12 * (farq + 2 * se)
print(f" eng yaxshi qoida: {eng_qoida}")
print(f" model - qoida: oyiga {farq:+.2f} (SE {se:.2f}) -> yiliga {12 * farq:+.0f} "
f"mln so'm, +-2*SE: [{lo:+.0f}, {hi:+.0f}]")
print(f" qoida model qiymatining {baho[eng_qoida] / baho['5 LogReg p*zarar']:.0%} "
f"ini beradi")
print("\n=== 4. Sig'im o'zgarsa: qoida va model farqi (oyiga, mln so'm) ===")
print(f" {'K':>5} {'qoida':>8} {'model':>8} {'farq':>7} {'model/qoida':>12}")
for k in [50, 200, 800, 2000]:
a = tejam_k(qiymat, nomzodlar[eng_qoida], guruhlar, k)
b = tejam_k(qiymat, nomzodlar["5 LogReg p*zarar"], guruhlar, k)
print(f" {k:>5} {a:>8.2f} {b:>8.2f} {b - a:>+7.2f} {b / a:>12.2f}")
print("\n=== 5. Xulosa (natijadan hisoblangan) ===")
if tanlov == "5 LogReg p*zarar":
print(" eng sodda munosib - model: qoidalar sezilarli yomon")
else:
print(f" eng sodda munosib - {tanlov}: model sezilarli ustun emas")
if lo <= 0:
print(" 3 oylik val da model foydasi noaniq (interval 0 ni qamraydi):")
print(" prototip qarori - qoida bazaviy va zaxira sifatida saqlanadi, model")
print(" ko'proq oyda (vaqt bo'yicha CV) tekshiriladi")
else:
print(" model foydasi 2*SE dan katta - loyiha keyingi bosqichga o'tadi")
if __name__ == "__main__":
main()Natijaning muhim qismi:
=== 1. Val oylari 16-18: oylik tejam (mln so'm) ===
0 hech kim 0.00
1 tasodifiy -0.32
2 qoida: util 24.26
3 qoida: util*zarar 23.04
4 ekspert ball-karta 19.36
5 LogReg p*zarar 28.85
=== 2. Juftlashgan bootstrap (oy ichida, 300): eng yaxshisidan farq ===
eng yaxshisi: 5 LogReg p*zarar
nomzod farq SE sezilarli yomon
0 hech kim -28.85 4.10 True
1 tasodifiy -29.17 4.09 True
2 qoida: util -4.59 2.93 False
3 qoida: util*zarar -5.81 2.98 False
4 ekspert ball-karta -9.49 3.09 True
5 LogReg p*zarar +0.00 0.00 False
qaror qoidasi -> eng sodda munosib: 2 qoida: util
=== 3. "Model kerakmi?" - pul bilan ===
eng yaxshi qoida: 2 qoida: util
model - qoida: oyiga +4.59 (SE 2.93) -> yiliga +55 mln so'm, +-2*SE: [-15, +125]
qoida model qiymatining 84% ini beradi
=== 4. Sig'im o'zgarsa: qoida va model farqi (oyiga, mln so'm) ===
K qoida model farq model/qoida
50 9.68 14.29 +4.61 1.48
200 24.26 28.85 +4.59 1.19
800 38.98 48.44 +9.46 1.24
2000 34.09 38.32 +4.24 1.12
=== 5. Xulosa (natijadan hisoblangan) ===
eng sodda munosib - 2 qoida: util: model sezilarli ustun emas
3 oylik val da model foydasi noaniq (interval 0 ni qamraydi):
prototip qarori - qoida bazaviy va zaxira sifatida saqlanadi, model
ko'proq oyda (vaqt bo'yicha CV) tekshiriladiNatija tahlili.
1-bo'lim — oltita nomzod soddalik tartibida. Tasodifiy tanlov bu safar -0.32 — nolga yaqin, ammo manfiy: 1-misoldagi "deyarli nol" tasdiqlandi. Bitta ustunli qoida 24.26, util * zarar qoidasi 23.04. Ekspert ball-kartasi 19.36 va u oddiy qoidadan ham yomon. Sababi: ballar diskret, bir xil ballli yuzlab mijoz orasida tartib faqat balansga qoladi. Bundan tashqari ekspert tanlagan chegaralar (util > 0.8, tolov_nisbati_3 < 0.15) ma'lumotda tekshirilmagan. LogReg p * zarar eng yaxshisi: 28.85.
2-bo'lim — qaror qoidasi. Eng yaxshisidan farqlar juftlashgan bootstrap bilan baholandi. Qoida "util" -4.59 (SE 2.93) farq qildi. Bu 2*SE = 5.86 dan kichik, ya'ni sezilarli yomon emas. "util * zarar" ham shunday: -5.81, SE 2.98. Ekspert kartasi esa sezilarli yomon: -9.49, SE 3.09. Qoida bo'yicha "eng sodda munosib" — bitta ustunli qoida. Bu kutilmagan va noqulay natija: 1-misoldan keyin biz model qurishga tayyor edik. Lekin qoida aniq: 3 oy ma'lumotda modelning afzalligi shovqindan ajralmaydi.
3-bo'lim — pul bilan. Model qoidadan oyiga +4.59 ko'p tejaydi, yiliga taxminan +55 mln so'm. Lekin +-2*SE oralig'i [-15, +125]: nolni qamraydi. Qoida model qiymatining 84% ini beradi. Bu raqamlarni loyiha xarajati bilan solishtirish kerak: ishlab chiqish, ma'lumot quvuri, monitoring va qayta o'qitish (27-qism). Agar yillik xarajat oraliqning pastki qismidan katta bo'lsa, "model kerak emas" javobi to'liq mumkin.
4-bo'lim — sig'im o'zgarsa. K = 50 da model qoidadan 1.48 barobar ko'p tejaydi: resurs qanchalik tanqis bo'lsa, reyting sifati shunchalik qimmat. K = 800 da farq eng katta (+9.46 mln so'm). K = 2000 da ikkalasining tejami K = 800 dagidan kam (34.09 va 38.32): ortiqcha qo'ng'iroqlar zararsizlik chegarasidan past mijozlarga ketib, pul yo'qotadi. Bundan ikki amaliy saboq chiqadi. Birinchidan, "model kerakmi?" savoliga javob sig'imga bog'liq. Ikkinchidan, sig'imni oshirish har doim ham foydali emas, va optimal K ni 29.3-bob ham hisoblash kerak.
5-bo'lim — prototip bosqichi qarori raqamlardan chiqdi. Qoida bazaviy va zaxira sifatida saqlanadi (u darhol ishlatilishi mumkin). Model loyihasi to'xtatilmaydi, lekin 2.6 dagi darvoza "model ustunligini ko'proq oyda isbotlash" deb yoziladi. 29.3 da vaqt bo'yicha CV 7 oyda aynan shu savolga javob beradi.
Misol 4 — Loyiha xatarlari: belgi ta'rifi (30 yoki 60 kun) va ma'lumot mavjudligi
"""Loyiha xatarlari: belgi ta'rifi (30 yoki 60 kun) va ma'lumot mavjudligi."""
import numpy as np
import pandas as pd
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import roc_auc_score
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import StandardScaler
HUDUDLAR = ["Toshkent", "Samarqand", "Farg'ona", "Andijon", "Buxoro", "Qashqadaryo"]
OYLAR = 24 # 1 = 2024-01, ..., 24 = 2025-12
BELGILAR = ["yosh", "daromad", "limit", "avto_tolov", "maosh_loyihasi", "staj",
"util", "util_3", "util_ozg", "kechikish_6", "tolov_nisbati_3",
"tolov_kuni_3", "naqd_soni_3", "naqd_summa_3", "xarid_soni_3"]
K, SAMARA, NARX = 200, 0.30, 0.02 # oyiga qo'ng'iroq, oldini olish, mln so'm
def sigmoid(z):
return 1 / (1 + np.exp(-z))
def oy_nomi(m):
return f"{2024 + (m - 1) // 12}-{(m - 1) % 12 + 1:02d}"
def yarat_bank(seed=29, n=6000):
"""Kredit karta portfeli: mijozlar, hisobvaraqlar, tolovlar, tranzaksiyalar."""
rng = np.random.default_rng(seed)
ids = np.arange(1, n + 1)
yosh = rng.integers(21, 66, n)
maosh = rng.random(n) < 0.45
daromad = np.round(np.exp(rng.normal(np.log(6.0), 0.45, n)), 2) # mln so'm
ochilgan = np.where(rng.random(n) < 0.7, rng.integers(-40, 1, n),
rng.integers(1, 20, n))
xavf = (rng.normal(0, 1, n) - 0.5 * maosh - 0.8 * np.log(daromad / 6)
+ 0.015 * (35 - yosh)) # yashirin: moliyaviy xavf
unut = rng.normal(0, 1, n) + 0.03 * (35 - yosh) # yashirin: unutuvchanlik
avto = (rng.random(n) < sigmoid(-0.2 - 0.9 * unut)).astype(int)
limit = np.round(daromad * rng.uniform(1.5, 3.5, n), 1)
hudud = rng.choice(HUDUDLAR, n, p=[0.34, 0.15, 0.14, 0.13, 0.12, 0.12])
mj = pd.DataFrame({"mijoz_id": ids, "yosh": yosh, "hudud": hudud,
"daromad": daromad, "limit": limit, "avto_tolov": avto,
"maosh_loyihasi": maosh.astype(int), "ochilgan_oy": ochilgan})
s, oldingi, ketma = rng.normal(0, 1, n), np.zeros(n, int), np.zeros(n, int)
hv, tl, tr = [], [], []
for m in range(1, OYLAR + 1):
faol = (ochilgan <= m) & (ketma < 3) # 3 ketma-ket kechikish - defolt
s = 0.75 * s + 0.66 * rng.normal(0, 1, n)
d = 0.8 * xavf + 0.7 * s + 0.15 * (m % 12 == 1) + 0.08 * max(m - 18, 0)
util = np.clip(sigmoid(-0.4 + 0.55 * d + rng.normal(0, 0.5, n)), 0.01, 1)
balans = np.round(util * limit, 3)
min_t = np.round(np.maximum(0.05 * balans, 0.05), 3)
for turi, lam in [("xarid", np.exp(1.2 + 0.15 * np.log(daromad / 6) - 0.1 * d)),
("naqd", np.exp(-1.6 + 0.6 * d)),
("onlayn", np.exp(0.6 - 0.02 * (yosh - 35)))]:
i = np.repeat(np.arange(n), rng.poisson(lam) * faol)
summa = (rng.uniform(0.2, 1.5, i.size) * (1 + 0.3 * d[i].clip(0))
if turi == "naqd" else np.exp(rng.normal(np.log(0.25), 0.8, i.size)))
tr.append(pd.DataFrame({"mijoz_id": ids[i], "oy": m,
"kun": rng.integers(1, 29, i.size), "turi": turi,
"summa": np.round(summa, 3)}))
q = rng.random(n) < sigmoid(-3.7 + 1.3 * d + 1.2 * (util > 0.85)
+ 3.8 * (oldingi == 2)) # qiyinchilik
u = ~q & (rng.random(n) < sigmoid(-3.4 + 1.1 * unut - 1.8 * avto)
* (oldingi != 1)) # unutish
miss = (q | u) & faol
ulush = sigmoid(0.3 - 1.2 * d + rng.normal(0, 0.5, n))
summa = np.where(miss, min_t * rng.uniform(0, 0.6, n),
min_t + (balans - min_t).clip(0) * ulush)
tolaydi = faol & (~miss | (rng.random(n) < 0.4))
kun = np.clip(np.round(6 + 14 * sigmoid(unut + 0.5 * d)
+ rng.normal(0, 3, n)), 1, 25)
hv.append(pd.DataFrame({"mijoz_id": ids[faol], "oy": m,
"balans": balans[faol], "min_tolov": min_t[faol]}))
if m < OYLAR: # m-oy hisobvarag'i (m+1)-oyda to'lanadi
tl.append(pd.DataFrame({"mijoz_id": ids[tolaydi], "hisob_oy": m,
"tolov_oy": m + 1,
"tolov_kuni": kun[tolaydi].astype(int),
"summa": np.round(summa[tolaydi], 3)}))
ketma = np.where(faol, np.where(miss, ketma + 1, 0), ketma)
oldingi = np.where(miss, np.where(q, 2, 1), 0)
return {"mijozlar": mj, "hisobvaraqlar": pd.concat(hv, ignore_index=True),
"tolovlar": pd.concat(tl, ignore_index=True),
"tranzaksiyalar": pd.concat(tr, ignore_index=True)}
def orta(a):
"""Qator bo'yicha NaN siz o'rtacha (bo'sh qatorda NaN, ogohlantirishsiz)."""
k = (~np.isnan(a)).sum(axis=1)
return np.where(k > 0, np.nansum(a, axis=1) / np.maximum(k, 1), np.nan)
def belgilar_jadvali(bank, oylar=range(7, 23)):
"""Har as-of oy t (oy oxiri): faqat t gacha ma'lum belgilar + t, t+1 natijasi."""
mj = bank["mijozlar"]
ids, kol = mj["mijoz_id"].to_numpy(), np.arange(1, OYLAR + 1)
def mat(df, qiymat, ustun="oy"):
return (df.pivot(index="mijoz_id", columns=ustun, values=qiymat)
.reindex(index=ids, columns=kol).to_numpy(dtype=float))
hv, tl = bank["hisobvaraqlar"], bank["tolovlar"]
bal, mint = mat(hv, "balans"), mat(hv, "min_tolov")
tol, kun = mat(tl, "summa", "hisob_oy"), mat(tl, "tolov_kuni", "hisob_oy")
bor = ~np.isnan(bal)
miss = bor & ~(np.nan_to_num(tol) >= mint - 1e-9) # minimal to'lov qilinmagan
g = (bank["tranzaksiyalar"].groupby(["turi", "mijoz_id", "oy"])["summa"]
.agg(["size", "sum"]))
def tr_mat(turi, ust):
return (g.loc[turi, ust].unstack("oy").reindex(index=ids, columns=kol)
.fillna(0).to_numpy())
naqd_n, naqd_s = tr_mat("naqd", "size"), tr_mat("naqd", "sum")
xarid_n = tr_mat("xarid", "size")
util = bal / mj["limit"].to_numpy()[:, None]
och = mj["ochilgan_oy"].to_numpy()
qismlar = []
for t in oylar:
c = t - 1 # t-oy ustuni (0 dan)
ok = (och <= t - 1) & bor[:, c] & bor[:, c - 1] & ~miss[:, c - 1]
u3 = util[:, c - 3]
f = mj.drop(columns="ochilgan_oy").assign(
oy=t, staj=t - och, util=util[:, c], util_3=orta(util[:, c - 2:c + 1]),
util_ozg=util[:, c] - np.where(np.isnan(u3), util[:, c], u3),
kechikish_6=miss[:, max(c - 6, 0):c].sum(axis=1),
tolov_nisbati_3=orta(np.nan_to_num(tol[:, c - 3:c]) / bal[:, c - 3:c]),
tolov_kuni_3=orta(kun[:, c - 3:c]),
naqd_soni_3=naqd_n[:, c - 2:c + 1].sum(axis=1),
naqd_summa_3=naqd_s[:, c - 2:c + 1].sum(axis=1),
xarid_soni_3=xarid_n[:, c - 2:c + 1].sum(axis=1),
balans=bal[:, c], y30=(miss[:, c] | miss[:, c + 1]).astype(int),
y60=(miss[:, c] & miss[:, c + 1]).astype(int))
qismlar.append(f[ok])
df = pd.concat(qismlar, ignore_index=True)
df["tolov_kuni_3"] = df["tolov_kuni_3"].fillna(25.0)
df["tolov_nisbati_3"] = df["tolov_nisbati_3"].fillna(0.0)
df["zarar"] = 0.45 * df["balans"] # DPD60 bo'lsa kutilgan zarar
df["qiymat"] = SAMARA * df["zarar"] * df["y60"] - NARX # qo'ng'iroq qiymati
return df
def oy_guruhlari(df):
oy = df["oy"].to_numpy()
return [np.flatnonzero(oy == t) for t in np.unique(oy)]
def top_k(ball, guruhlar, k=K):
return np.concatenate([g[np.argsort(-ball[g], kind="stable")[:k]] for g in guruhlar])
def lr_ball(tr, va, belgi):
m = make_pipeline(StandardScaler(), LogisticRegression(max_iter=2000))
return m.fit(tr[BELGILAR], tr[belgi]).predict_proba(va[BELGILAR])[:, 1]
def main() -> None:
df = belgilar_jadvali(yarat_bank())
print("=== 1. Ikki ta'rif (as-of 7-22, barcha qatorlar) ===")
print(f" y30 (60 kun ichida 30+ kun kechikish): {df.y30.mean():.3f}")
print(f" y60 (60 kun ichida 60+ kun kechikish): {df.y60.mean():.3f}")
print(f" y60 = 1 bo'lganlar ichida y30 = 1: {df.loc[df.y60 == 1, 'y30'].mean():.3f}")
ozi = ((df.y30 == 1) & (df.y60 == 0)).sum() / df.y30.sum()
print(f" y30 = 1 lar ichida o'zi to'lagan (y60 = 0): {ozi:.1%}")
print(f" oldingi 6 oyda kechikkan (kechikish_6 > 0) ulushi: "
f"y60 da {(df.loc[df.y60 == 1, 'kechikish_6'] > 0).mean():.3f}, "
f"faqat y30 da {(df.loc[(df.y30 == 1) & (df.y60 == 0), 'kechikish_6'] > 0).mean():.3f}")
print(f" avto_tolov ulushi: y60 da {df.loc[df.y60 == 1, 'avto_tolov'].mean():.3f}, "
f"faqat y30 da {df.loc[(df.y30 == 1) & (df.y60 == 0), 'avto_tolov'].mean():.3f}")
print("\n=== 2. Qaysi belgida o'qitamiz? o'quv 7-14, val 16-18 ===")
tr, va = df[df.oy <= 14], df[df.oy.between(16, 18)].reset_index(drop=True)
guruhlar = oy_guruhlari(va)
qiymat, zarar = va["qiymat"].to_numpy(), va["zarar"].to_numpy()
ballar = {b: lr_ball(tr, va, b) for b in ["y30", "y60"]}
print(f" {'model':<10} {'AUC y30':>8} {'AUC y60':>8} {'tejam/oy':>9} "
f"{'y60':>6} {'faqat y30':>10} {'toza':>6}")
tejamlar = {}
for b, p in ballar.items():
top = top_k(p * zarar, guruhlar)
tejamlar[b] = qiymat[top].sum() / len(guruhlar)
y60, y30 = va["y60"].to_numpy()[top], va["y30"].to_numpy()[top]
m = len(guruhlar)
print(f" {b + ' da':<10} {roc_auc_score(va.y30, p):>8.4f} "
f"{roc_auc_score(va.y60, p):>8.4f} {tejamlar[b]:>9.2f} "
f"{y60.sum() / m:>6.1f} {((y30 == 1) & (y60 == 0)).sum() / m:>10.1f} "
f"{(y30 == 0).sum() / m:>6.1f}")
print(" ko'proq dalil: har oy t = 12-18 uchun o'quv - as-of <= t-2 (7 juft):")
farqlar = []
for t in range(12, 19):
o, s = df[df.oy <= t - 2], df[df.oy == t]
q, z = s["qiymat"].to_numpy(), s["zarar"].to_numpy()
tt = {b: q[np.argsort(-lr_ball(o, s, b) * z, kind="stable")[:K]].sum()
for b in ["y30", "y60"]}
farqlar.append(tt["y60"] - tt["y30"])
farqlar = np.array(farqlar)
farq, se = farqlar.mean(), farqlar.std(ddof=1) / np.sqrt(len(farqlar))
print(f" oylik farqlar (y60 - y30): {np.round(farqlar, 1).tolist()}")
print(f" o'rtacha {farq:+.2f} mln so'm/oy (SE {se:.2f}), sezilarli: "
f"{abs(farq) > 2 * se}, y60 yaxshi oylar: {(farqlar > 0).sum()}/{len(farqlar)}")
print("\n=== 3. Ma'lumot mavjudligi: belgi qachon yetiladi? ===")
print(" as-of t uchun y30/y60 (t va t+1 hisobvaraqlari) (t+2)-oy oxirida ma'lum")
for nom, ufq in [("y60 (60 kun)", 2), ("y90 (90 kun, faraziy)", 3)]:
oxirgi = 16 - ufq
print(f" {nom:<22} 16-oy oxirida o'qitish uchun oxirgi as-of: {oxirgi} "
f"-> {oxirgi - 6} oy tarix")
sarlavha = "o'quv oylari"
print(f" {sarlavha:<14} {'qator':>6} {'y60 soni':>9} {'AUC':>7} {'tejam/oy':>9}")
natija = {}
for bosh in [13, 11, 7]:
t2 = df[df.oy.between(bosh, 14)]
p = lr_ball(t2, va, "y60")
natija[bosh] = qiymat[top_k(p * zarar, guruhlar)].sum() / len(guruhlar)
print(f" {f'{bosh}-14':<14} {len(t2):>6} {t2.y60.sum():>9} "
f"{roc_auc_score(va.y60, p):>7.4f} {natija[bosh]:>9.2f}")
print("\n=== 4. Xulosa (natijadan hisoblangan) ===")
if farq > 2 * se:
print(f" y30 da o'qitilgan model oyiga {farq:.1f} mln so'm kam tejaydi: u o'zi "
f"to'laydigan 'unutuvchan'larni ham tanlaydi")
print(f" y30 lar ichida {ozi:.0%} i o'zi to'laydi -> ta'rif qaror bilan mos bo'lishi shart")
qiymatlar = np.array(list(natija.values()))
if qiymatlar.max() - qiymatlar.min() < 0.1 * qiymatlar.mean():
print(f" 2, 4 va 8 oylik o'quv yaqin ({qiymatlar.min():.2f}-{qiymatlar.max():.2f})"
f" -> bu vazifada tarix hajmi asosiy cheklov emas")
if __name__ == "__main__":
main()Natijaning muhim qismi:
=== 1. Ikki ta'rif (as-of 7-22, barcha qatorlar) ===
y30 (60 kun ichida 30+ kun kechikish): 0.109
y60 (60 kun ichida 60+ kun kechikish): 0.020
y60 = 1 bo'lganlar ichida y30 = 1: 1.000
y30 = 1 lar ichida o'zi to'lagan (y60 = 0): 81.3%
oldingi 6 oyda kechikkan (kechikish_6 > 0) ulushi: y60 da 0.223, faqat y30 da 0.323
avto_tolov ulushi: y60 da 0.481, faqat y30 da 0.258
=== 2. Qaysi belgida o'qitamiz? o'quv 7-14, val 16-18 ===
model AUC y30 AUC y60 tejam/oy y60 faqat y30 toza
y30 da 0.7302 0.7369 22.41 16.3 47.3 136.3
y60 da 0.6561 0.8482 28.85 20.7 35.0 144.3
ko'proq dalil: har oy t = 12-18 uchun o'quv - as-of <= t-2 (7 juft):
oylik farqlar (y60 - y30): [2.8, 6.3, 9.4, 8.5, 7.4, 6.8, 7.9]
o'rtacha +7.00 mln so'm/oy (SE 0.81), sezilarli: True, y60 yaxshi oylar: 7/7
=== 3. Ma'lumot mavjudligi: belgi qachon yetiladi? ===
as-of t uchun y30/y60 (t va t+1 hisobvaraqlari) (t+2)-oy oxirida ma'lum
y60 (60 kun) 16-oy oxirida o'qitish uchun oxirgi as-of: 14 -> 8 oy tarix
y90 (90 kun, faraziy) 16-oy oxirida o'qitish uchun oxirgi as-of: 13 -> 7 oy tarix
o'quv oylari qator y60 soni AUC tejam/oy
13-14 8488 192 0.8434 28.56
11-14 16918 373 0.8405 30.47
7-14 33358 697 0.8482 28.85
=== 4. Xulosa (natijadan hisoblangan) ===
y30 da o'qitilgan model oyiga 7.0 mln so'm kam tejaydi: u o'zi to'laydigan 'unutuvchan'larni ham tanlaydi
y30 lar ichida 81% i o'zi to'laydi -> ta'rif qaror bilan mos bo'lishi shart
2, 4 va 8 oylik o'quv yaqin (28.56-30.47) -> bu vazifada tarix hajmi asosiy cheklov emasNatija tahlili.
1-bo'lim — ikki ta'rif. y60 — y30 ning qism to'plami: DPD60 larning 1.000 qismida y30 ham 1, chunki 60 kunlik kechikish avval 30 kunlik bo'ladi. Lekin teskarisi to'g'ri emas: y30 larning 81.3% i ikkinchi hisobvaraqni o'zi to'lagan. Ular boshqa turdagi mijozlar. Faqat y30 bo'lganlarning 32.3% i oldin ham kechikkan (DPD60 larda 22.3%), avto to'lov esa ularda kamroq (0.258 va 0.481). Bu "unutuvchan" mijozlar portreti: vaqti-vaqti bilan kechikadi, lekin to'laydi. Qiziq tomoni: kechikish_6 belgisi DPD60 dan ko'ra ko'proq aynan ularni ko'rsatadi.
2-bo'lim — qaysi belgida o'qitish kerak? y30 da o'qitilgan model o'z belgisida yaxshiroq (AUC y30 0.7302 va 0.6561). Lekin biznes uchun muhim bo'lgan y60 ni yomon ajratadi (0.7369 va 0.8482). Top-200 tarkibi hammasini ko'rsatadi. y30-model ro'yxatida oyiga 16.3 DPD60 va 47.3 "o'zi to'laydigan" mijoz bor, y60-modelda esa 20.7 va 35.0. Tejam: 22.41 va 28.85. 3 oylik val da bu farq bitta son, shuning uchun 7 ta oyning har birida juftlashtirib tekshirdik (har oy t uchun o'quv — as-of <= t-2). y60 yetti oyning yettisida yaxshiroq, o'rtacha +7.00 mln so'm/oy (SE 0.81). Belgi ta'rifi bu loyihadagi eng katta "bepul" yaxshilanish bo'ldi. Hech qanday model murakkabligi bunday farq bermadi (2-misolda HistGB +0.18).
3-bo'lim — ma'lumot mavjudligi. As-of t uchun belgi t+2-oy oxirida ma'lum bo'ladi. Shuning uchun 16-oy oxirida model qayta o'qitilsa, eng oxirgi belgili as-of 14 bo'ladi. Faraziy 90 kunlik ta'rifda bu 13 bo'lardi: har qo'shimcha gorizont oyi modelni bir oy "eskiroq" qiladi. O'quv tarixi uzunligi bo'yicha egri chiziq: 2 oy (8488 qator, 192 musbat) 28.56, 4 oy 30.47, 8 oy 28.85. Farqlar kichik va tartibsiz, demak bu vazifada tarix hajmi cheklov emas. Bu foydali xabar. Yangi mahsulotda, masalan, faqat 3 oylik tarix bo'lganda ham loyihani boshlash mumkin edi. Bir ogohlantirish bor: bu xulosa faqat shu ma'lumot uchun o'lchandi. Kamyob hodisalarda (musbatlar yuztadan kam) tarix hajmi hal qiluvchi bo'lishi mumkin.
4-bo'lim — xulosa loyiha hujjatiga yoziladi: belgi y60, chunki qaror (qo'ng'iroq) zararni kamaytirish uchun. Belgi yetilishi 2 oy. Tarix hajmi cheklov emas.
5. To'g'ri va noto'g'ri tushunishlar
| Noto'g'ri fikr | To'g'risi |
|---|---|
| "Loyiha model tanlashdan boshlanadi" | Qaror, uning egasi, sig'im va metrikadan boshlanadi |
| "AUC yuqori — loyiha muvaffaqiyatli" | Muvaffaqiyat — biznes metrikasi (tejam), top-K va zarar bilan |
| "AUC oshsa, tejam ham oshadi" | Top-K dan tashqaridagi yaxshilanish qiymat bermaydi (2-misol: AUC +0.105, tejam 0) |
| "Ehtimol bo'yicha tartiblash yetarli" | Kutilgan qiymat bo'yicha: p * zarar |
| "Model tasodifiydan yaxshi — demak foydali" | Model qiymati = model - eng yaxshi qoida |
| "Qoida — ibtidoiy, model har doim yutadi" | 3-misolda 3 oy val da qoida sezilarli yomon emas |
| "Ko'p musbat bo'lsin deb 30 kunlik belgi olamiz" | Belgi qaror bilan mos bo'lsin: y30 oyiga 7 mln kam tejadi |
| "Sig'imni oshirish har doim foydali" | K = 2000 da tejam K = 800 dagidan kam |
| "Faraziy parametrlar — tafsilot" | Ular natijani belgilaydi; pilotda tekshiriladi |
| "Joriy etish — loyihaning oxiri" | Monitoring, qaror halqasi va nazorat guruhi bilan davom etadi |
6. Keng tarqalgan xatolar va yechimlari
1. Istak bilan boshlash
vazifa = "kechikishni bashorat qiluvchi model" # ⚠️
vazifa = {"qaror": "oyiga 200 qo'ng'iroq", "belgi": "y60", "metrika": "tejam/oy"} # ✅2. Faqat AUC
print(roc_auc_score(y, p)) # ⚠️
print(tejam_k(qiymat, p * zarar, guruhlar), roc_auc_score(y, p)) # ✅ ikkalasi3. Zararsiz tartib
ro_yxat = df.nlargest(200, "p") # ⚠️
ro_yxat = df.assign(k=df.p * df.zarar).nlargest(200, "k") # ✅4. Bazaviysiz model
print("model tasodifiydan 30 mln ko'p tejaydi") # ⚠️
print("model eng yaxshi qoidadan +4.6 (SE 2.9) ko'p tejaydi") # ✅5. Qulay belgi
y = df.y30 # musbatlar ko'p, o'qitish oson # ⚠️
y = df.y60 # qaror maqsadi: zararli kechikish # ✅6. Belgi yetilishini unutish
oquv = df[df.oy <= joriy_oy] # ⚠️ belgi hali yo'q
oquv = df[df.oy <= joriy_oy - 2] # ✅ 60 kunlik gorizont7. Nazorat guruhisiz joriy etish
qongiroq = top_k(hammasi) # ⚠️ samara o'lchanmaydi
nazorat = rng.random(n) < 0.05; qongiroq = top_k(hammasi[~nazorat]) # ✅7. Integratsiya — bu bilim qayerda kerak bo'ladi
- 8.2-dars (o'tilgan): Savol berish — EDA ham savoldan boshlanadi
- 14.9, 14.10-darslar (o'tilgan): Nomutanosib sinflar va kalibrlash — p * zarar qarori uchun
- 17.8-dars (o'tilgan): Leakage — 29.2 da as-of bilan o'lchanadi
- 18.9, 18.10-darslar (o'tilgan): Metrika tanlash va modellarni juftlashgan taqqoslash
- 27-qism (o'tilgan): Joriy etish, monitoring, qaror halqasi va A/B test
- 28.11-dars (o'tilgan): Sababiy xulosa — qo'ng'iroq samarasi 0.30-bob aslida sababiy savol
- 29.2-29.4-darslar: Shu loyihaning ma'lumot, model va taqdimot qismlari
- 29.10-dars: Intervyudagi case study — aynan shu 2.2 dagi savollar bilan boshlanadi
8. Eng yaxshi amaliyotlar
Loyiha qarordan boshlanadi: kim, qachon, qanday cheklov bilan, nima o'zgaradi.
Biznes metrikasi loyiha boshida yoziladi; model metrikasi — yordamchi.
Qiymat zinapoyasini o'lchang: hech narsa, tasodifiy, qoida, model, oracle.
Model qiymati — eng yaxshi qoidaga nisbatan, noaniqligi bilan.
Belgi ta'rifini qaror bilan moslang va bir necha ta'rifni biznes metrikasi bilan solishtiring.
Belgi yetilishi, sizish va qaror halqasini reja bosqichida hisobga oling.
Faraziy parametrlarni aniq belgilang va pilotda tekshirishni rejalashtiring.
Har bosqich oxirida darvoza: davom etish, qayta ko'rish yoki to'xtatish.
9. Amaliy topshiriq
Vazifa 1: Bashorat qiling
1. # oyiga 87.6 DPD60, sig'im 200 - sig'im cheklovmi?
2. # zararsizlik chegarasi 0.021, bazaviy ulush 0.020 - tasodifiy qo'ng'iroq qiymati?
3. # p o'rniga p * zarar bo'yicha tartib - AUC va tejam qayoqqa?
4. # top-K dan tashqaridagi tartibni yaxshilasak - AUC va tejam?
5. # 3 oy val da qoida "util" modeldan -4.59 (SE 2.93) - qaysi tanlanadi?
6. # K = 50 va K = 800 - model/qoida nisbati qayerda katta?
7. # K = 2000 - tejam K = 800 dan ko'pmi?
8. # y30 lar ichida o'zi to'laganlar ulushi taxminan?
9. # y30 da o'qitilgan model - AUC y30 bo'yicha yaxshimi, tejam bo'yicha-chi?
10. # 90 kunlik belgi - o'quv uchun oxirgi as-of qanday o'zgaradi?Javoblar
- Yo'q — sig'im DPD60 sonidan katta; muammo reytingda (1-misol)
- Deyarli nol (+0.01 va -0.32)
- AUC pasayadi (0.8482 → 0.8314), tejam oshadi (26.3 → 28.8), lekin 3 oyda sezilarli emas
- AUC oshadi 0.9536-bob, tejam aynan o'zgarmaydi
- Qoida — eng sodda munosib (farq 2*SE = 5.86 dan kichik)
- K = 50 da (1.48 barobar)
- Yo'q — 34.09 va 38.32 (K = 800 da 38.98 va 48.44)
- ~81%
- AUC y30 bo'yicha yaxshi 0.7302-bob, tejam bo'yicha yomon (oyiga ~7 mln kam)
- Bir oy oldinga suriladi (14 → 13) — model bir oy eskiroq
Vazifa 2: Xatolarni tuzating
1. hisobot = f"Model AUC {auc:.2f} - loyiha muvaffaqiyatli"
2. royxat = df[df.oy == t].nlargest(200, "p")
3. foyda = tejam_k(qiymat, p * zarar, g) - tejam_k(qiymat, rng.random(n), g)
print(f"model foydasi {foyda:.1f}")
4. oquv = df[df.oy <= t] # t - joriy as-of oy
5. y = df["y30"] # "kechikish" = 30 kun, ko'p musbatJavoblar
1. hisobot = f"tejam {tejam:.1f} mln/oy; qoidadan farq {farq:+.1f} (SE {se:.1f}); AUC {auc:.3f}"
2. royxat = df[df.oy == t].assign(k=lambda d: d.p * d.zarar).nlargest(200, "k")
3. foyda = tejam_k(qiymat, p * zarar, g) - tejam_k(qiymat, df.util.to_numpy(), g)
# + juftlashgan SE; tasodifiy emas, eng yaxshi qoida bilan
4. oquv = df[df.oy <= t - 2] # belgi 2 oyda yetiladi
5. y = df["y60"] # qaror maqsadi bilan mos; y30 ni biznes metrikasida solishtiringVazifa 3: Qiymat zinapoyasi
Modellang (1- va 3-misollar asosida):
- Qo'ng'iroq samarasi
0.30o'rniga0.15va0.45bo'lsin — oracle, qoida va model tejami qanday o'zgaradi? Qaysi qaror o'zgaradi? - Qo'ng'iroq narxi
0.02o'rniga0.05— tasodifiy qo'ng'iroq va K = 800 dagi tejam? - Optimal K ni toping: K = 50, 100, ..., 2000 uchun model va qoida tejami egri chizig'i
- Ekspert ball-kartasining chegaralarini ma'lumotda tanlang (masalan, util kvantillari) — u qoidadan o'tadimi?
Vazifa 4: Metrikalar
Modellang (2-misol asosida):
pni 10 ga ko'paytiring (kalibrlashni buzing) — AUC, top-K tejam va "p * 0.30 * zarar > 0.02" siyosatining tejami qanday o'zgaradi?- Precision@200 va tejam@200 ni oylar bo'yicha chizing — ular bir xil tartibdami?
- Log-loss va Brier ni ham hisoblang — qaysi model metrikasi tejam bilan eng yaxshi mos keladi?
- Top-K dan ichidagi tartibni o'zgartiring (top-200 ichida aralashtiring) — tejam o'zgaradimi? Nega?
Vazifa 5: Belgi ta'rifi
Modellang (4-misol asosida):
- "Kamida bitta kechikish (y30) va balansi 5 mln dan katta" degan gibrid belgi bilan o'qiting — tejam?
- y30 modeli va y60 modeli ehtimollarini o'rtachalang — natija?
- y30 dagi "o'zi to'laganlar" uchun alohida model quring — ularga qo'ng'iroq o'rniga SMS eslatma yuborilsa (narx 0.001), qo'shimcha qiymat qancha bo'lardi? (faraz: SMS kechikishni 20% kamaytiradi va kechikish to'lovi bankka zarar emas)
Vazifa 6: Loyiha hujjati
Boshqa vazifa uchun 2.7 dagi shablonni to'ldiring: telekom kompaniyasi mijoz ketishini kamaytirish uchun oyiga 500 ta chegirma taklifi bera oladi. Qaror, belgi, gorizont, biznes metrikasi (xarajat matritsasi bilan), bazaviylar, xatarlar va darvozalarni yozing. Qaysi raqamlar faraziy ekanini belgilang.
Vazifa 7: O'ylash
Kredit risk bo'limi boshlig'i: "Bizga murakkab model kerak emas. Hozir ham operatorlar limitdan foydalanish ulushi eng yuqori mijozlarga qo'ng'iroq qilyapti va bu yaxshi ishlayapti. Siz 3 oy ishlab AUC 0.85 li model qildingiz, lekin o'zingiz aytyapsizki, u qoidadan sezilarli yaxshi emas. Loyihani yopamiz."
Javob
Qisqa javob: Boshliq qisman haq: 3 oylik val da model qoidadan sezilarli yaxshi emas. Lekin loyiha faqat "model" emas edi, va ikki topilma qoidaning o'zini ham yaxshilaydi.
1. U nimada haq. 3-misolda qoida "util" modeldan -4.59 (SE 2.93) farq qildi, bu sezilarli emas. Qoida model qiymatining 84% ini beradi. Model xarajati (quvur, monitoring, qayta o'qitish) haqiqiy va uni e'tiborsiz qoldirib bo'lmaydi.
2. Loyiha allaqachon nima berdi.
# 1) qiymat chegarasi: oracle oyiga ~78.6 mln - joriy qoida uning 31% ini beradi
# 2) belgi ta'rifi: 30 kunlik ro'yxat oyiga ~7 mln kam tejaydi (7/7 oy) -
# agar operatorlar "30 kun kechikkanlar"ga ham qo'ng'iroq qilsa, bu to'g'ridan-to'g'ri foyda
# 3) sig'im: K = 2000 da tejam K = 800 dagidan kam - "ko'proq qo'ng'iroq" har doim yaxshi emas
# 4) tasodifiy qo'ng'iroq ~0 - reyting shart; qoida buni allaqachon qilyapti3. Nega hozir yopish erta. Farq noaniq, lekin yo'nalish modelga qarab: oyiga +4.59, yiliga ~+55 mln so'm, oraliq [-15, +125]. Bu savolni 3 oy emas, ko'proq oy bilan tekshirish arzon: yangi ma'lumot kerak emas, faqat vaqt bo'yicha CV kerak. K = 50 da model 1.48 barobar ustun: agar sig'im qisqarsa, model ahamiyati ortadi.
4. Taklif.
- Qoida hozirdan ishlashda davom etadi — u bazaviy va zaxira.
- 2 hafta: 7 oylik vaqt bo'yicha CV. Mezon: model - qoida > 2*SE bo'lsa, davom etamiz, aks holda loyiha yopiladi va hisobotda "qoida yetarli" deb yoziladi.
- Mezon oldindan, hozir yoziladi, natija kelgach o'zgartirilmaydi.
Boshliqqa javob: "Siz qisman haqsiz: 3 oy ma'lumotda model qoidadan ishonchli yaxshi emas va biz buni yashirmaymiz. Lekin qoidaning o'zi oracle imkoniyatining uchdan birini beradi, va belgini to'g'ri tanlash allaqachon oyiga ~7 mln farq qiladi. Menga ikki hafta bering: mavjud ma'lumotning 7 oyida bitta oldindan yozilgan mezon bilan tekshiraman. Agar model qoidadan ishonchli yaxshi bo'lmasa, loyihani o'zim yopishni taklif qilaman."
Nimani mustahkamlaydi: 2.2, 2.3, 2.4, 2.5-bo'limlar.
Xulosa
Bu darsda DS loyihasining hayot siklini bank loyihasi misolida boshidan ko'rdik: istakni qarorga, qarorni DS vazifasiga va muvaffaqiyatni pulga aylantirdik.
Eng muhim uch fikr:
Loyiha qarordan boshlanadi, muvaffaqiyat — biznes metrikasi bilan. 1-misolda tahlil birligi (mijoz, as-of oy), sig'im (oyiga 200 qo'ng'iroq) va xarajat matritsasi aniqlandi. Oracle oyiga
78.57mln so'm tejashi mumkin, oddiy qoida24.26, tasodifiy tanlov esa deyarli nol. 2-misolda AUC va tejam ikki marta turlicha gapirdi.p * zarartartibi AUC ni0.8482dan0.8314ga tushirdi, tejamni esa26.3dan28.8ga oshirdi. Top-K dan tashqaridagi o'zgarish AUC ni0.9536ga ko'tardi, tejam esa aynan o'zgarmadi. AUC farqlari "sezilarli" chiqdi, tejam farqlari esa yo'q.Model qiymati — eng yaxshi qoidaga nisbatan, noaniqligi bilan. 3-misolda 3 oylik val da bitta ustunli qoida modeldan sezilarli yomon emas (
-4.59, SE2.93): model yiliga~+55mln so'm qo'shadi, lekin oraliq[-15, +125]. Prototip qarori halol yozildi: qoida zaxira sifatida qoladi, model ko'proq oyda tekshiriladi. Sig'im javobni o'zgartiradi: K = 50 da model1.48barobar ustun, K = 2000 da esa ortiqcha qo'ng'iroqlar tejamni kamaytiradi.Xatarlar loyiha boshida o'lchanadi. 4-misolda belgi ta'rifi eng katta farqni berdi: y30 larning
81.3%i o'zi to'laydi, y30 da o'qitilgan model oyiga7.00mln so'm (SE0.81) kam tejaydi va y60 yetti oyning yettisida yutdi. Belgi yetilishi o'quv oylarini 2 oyga cheklaydi, tarix hajmi esa bu vazifada cheklov emas. Qaror halqasi uchun nazorat guruhi, faraziy parametrlar uchun esa pilot reja loyiha hujjatiga yozildi.
Keyingi darsda To'liq loyiha: muammo va ma'lumot: loyihaning birinchi amaliy qismi. To'rtta jadvalni sqlite ga yig'ib, belgilarni SQL bilan quramiz. Vaqt nuqtasi (as-of) qoidasini va sizishni "snapshot" testi bilan o'lchab ushlaymiz. Qarorga ta'sir qiladigan EDA, sifat tekshiruvlari, oylar bo'yicha train/val/test bo'lish va ma'lumot kartasi (datasheet) ham shu darsda.
Izohlar (0)
Izoh yozish uchun kiring.
- Hozircha izoh yo'q. Birinchi bo'ling!