19-bo‘lim
Tarmoqni baholash
Aniqlik yetarli emas - chalkashlik matritsasi, precision, recall va chegarani tanlash.
Ushbu bo‘lim mundarijasi
"Model 95% aniqlik beradi" - bu jumla ko'pincha hech nimani anglatmaydi. Bu bo'lim nima uchun shundayligi va uning o'rniga nima qilish kerakligi haqida.
Aniqlikning chegarasi #
import numpy as np
def chalkashlik(haqiqiy, bashorat):
"""Ikkilik tasniflash uchun chalkashlik matritsasi."""
tp = int(((bashorat == 1) & (haqiqiy == 1)).sum())
tn = int(((bashorat == 0) & (haqiqiy == 0)).sum())
fp = int(((bashorat == 1) & (haqiqiy == 0)).sum())
fn = int(((bashorat == 0) & (haqiqiy == 1)).sum())
return {"TP": tp, "TN": tn, "FP": fp, "FN": fn}
def olchovlar(haqiqiy, bashorat):
m = chalkashlik(haqiqiy, bashorat)
tp, tn, fp, fn = m["TP"], m["TN"], m["FP"], m["FN"]
aniqlik = (tp + tn) / max(1, tp + tn + fp + fn)
precision = tp / max(1, tp + fp)
recall = tp / max(1, tp + fn)
f1 = 2 * precision * recall / max(1e-12, precision + recall)
return {"aniqlik": aniqlik, "precision": precision,
"recall": recall, "f1": f1}
def kasallik_malumoti(n=1000, urug=0):
"""1% kasal - kuchli nomutanosib to'plam."""
gen = np.random.default_rng(urug)
kasal = gen.uniform(0, 1, n) < 0.01
belgi = gen.normal(0, 1, n) + kasal * 2.5
return belgi.reshape(-1, 1), kasal.astype(float).reshape(-1, 1)
def kop_sinfli_matritsa(haqiqiy, bashorat, sinflar):
m = np.zeros((sinflar, sinflar), dtype=int)
for h, b in zip(haqiqiy, bashorat):
m[int(h), int(b)] += 1
return m
def sinf_bo_yicha(M):
sinflar = M.shape[0]
natija = []
for k in range(sinflar):
tp = M[k, k]
fp = M[:, k].sum() - tp
fn = M[k, :].sum() - tp
p = tp / max(1, tp + fp)
r = tp / max(1, tp + fn)
f1 = 2 * p * r / max(1e-12, p + r)
natija.append((k, int(tp), int(fp), int(fn), p, r, f1))
return natija
X, y = kasallik_malumoti(1000)
print("kasallar soni:", int(y.sum()), "/", len(y))
dangasa = np.zeros_like(y) # "hech kim kasal emas"
m = olchovlar(y, dangasa)
print("'hech kim kasal emas' modeli:")
for nom, qiymat in m.items():
print(f" {nom:<10} {qiymat:.4f}")
kasallar soni: 11 / 1000
'hech kim kasal emas' modeli:
aniqlik 0.9890
precision 0.0000
recall 0.0000
f1 0.0000
Model hech kimni tekshirmaydi, hamma sog'lom deydi - va 99.2% aniqlik oladi.
Sakkiz kasal odamning hech biri topilmadi.
Bu nomutanosib ma'lumotdagi asosiy tuzoq. Aniqlik faqat sinflar taxminan teng bo'lganda ma'noli.
Amaliy misollar:
| Soha | Musbat sinf ulushi | Aniqlik foydalimi |
|---|---|---|
| Kamdan-kam kasallik | 0.1% | Yo'q |
| Firibgarlik | 0.5% | Yo'q |
| Spam | 30-50% | Ha |
| Rasm tasniflash (teng sinflar) | 10% × 10 | Ha |
Birinchi ikkitasida aniqlik o'rniga recall va precision ishlatiladi.
Chalkashlik matritsasi #
haqiqiy = np.array([1, 1, 1, 1, 0, 0, 0, 0, 0, 0]).reshape(-1, 1)
bashorat = np.array([1, 1, 0, 0, 1, 0, 0, 0, 0, 0]).reshape(-1, 1)
m = chalkashlik(haqiqiy, bashorat)
print("chalkashlik matritsasi:", m)
o = olchovlar(haqiqiy, bashorat)
for nom, qiymat in o.items():
print(f"{nom:<10} {qiymat:.4f}")
chalkashlik matritsasi: {'TP': 2, 'TN': 5, 'FP': 1, 'FN': 2}
aniqlik 0.7000
precision 0.6667
recall 0.5000
f1 0.5714
Qo'lda tekshiramiz: model uch marta "musbat" dedi, ulardan
ikkitasi to'g'ri → precision = 2/3 = 0.667. To'rtta haqiqiy
musbatdan ikkitasi topildi → recall = 2/4 = 0.5.
Precision va recall - almashuv #
def sigmoid(z):
return 1.0 / (1.0 + np.exp(-z))
X, y = kasallik_malumoti(2000, urug=1)
ehtimollik = sigmoid(2.0 * X - 3.0) # oddiy model o'rniga
print("chegara TP FP FN precision recall F1")
for chegara in [0.1, 0.2, 0.3, 0.5, 0.7, 0.9]:
bashorat = (ehtimollik > chegara).astype(float)
m = chalkashlik(y, bashorat)
o = olchovlar(y, bashorat)
print(f"{chegara:>6} {m['TP']:>3} {m['FP']:>3} {m['FN']:>3} "
f"{o['precision']:>8.4f} {o['recall']:>6.4f} {o['f1']:>6.4f}")
chegara TP FP FN precision recall F1
0.1 16 677 2 0.0231 0.8889 0.0450
0.2 15 404 3 0.0358 0.8333 0.0686
0.3 15 271 3 0.0524 0.8333 0.0987
0.5 13 123 5 0.0956 0.7222 0.1688
0.7 13 49 5 0.2097 0.7222 0.3250
0.9 7 11 11 0.3889 0.3889 0.3889
Bir xil model, bir xil og'irliklar - lekin oltita butunlay boshqa xatti-harakat.
| Chegara | Xatti-harakat |
|---|---|
0.1 | Barcha kasallarni topdi, lekin 81 ta yolg'on signal |
0.5 | Muvozanatli |
0.9 | Yolg'on signal yo'q, lekin 13 ta kasal o'tkazib yuborildi |
Chegara modelning qismi emas - u qaror qabul qilish siyosati. Va uni masalaga qarab tanlaysiz:
| Vaziyat | Chegara | Sabab |
|---|---|---|
| Skrining testi | Past (0.1-0.3) | Hech kimni o'tkazib yubormaslik |
| Yakuniy tashxis | Yuqori (0.8+) | Yolg'on tashvish bermaslik |
| Spam | Yuqori | Muhim xatni yo'qotmaslik |
| Firibgarlik ogohlantirishi | Past | Tekshirish arzon |
Muhim: chegarani validatsiya to'plamida tanlang, test to'plamida emas (16-bo'lim).
F1 nima uchun garmonik o'rtacha #
holatlar = [(1.0, 0.0), (0.9, 0.1), (0.5, 0.5), (0.7, 0.6)]
print("precision recall oddiy o'rtacha F1")
for p, r in holatlar:
oddiy = (p + r) / 2
f1 = 0.0 if p + r == 0 else 2 * p * r / (p + r)
print(f"{p:>9.2f} {r:>6.2f} {oddiy:>13.3f} {f1:>6.3f}")
precision recall oddiy o'rtacha F1
1.00 0.00 0.500 0.000
0.90 0.10 0.500 0.180
0.50 0.50 0.500 0.500
0.70 0.60 0.650 0.646
Birinchi uchta qatorda oddiy o'rtacha bir xil (0.5),
F1 esa butunlay boshqa.
precision = 1.0, recall = 0.0 - bu model bitta namunani
"musbat" deb belgilagan va u to'g'ri chiqqan, qolgan
hammasini o'tkazib yuborgan.
Oddiy o'rtacha 0.5 deb baholaydi. F1 esa 0.0 -
va bu ancha halolroq.
F1 ikkala o'lchov ham yuqori bo'lgandagina yuqori bo'ladi. Bitta o'lchov nolga yaqin bo'lsa, F1 ham nolga intiladi.
Diqqat: F1 ham universal emas. U TN ni umuman hisobga
olmaydi. Agar to'g'ri rad etish ham muhim bo'lsa, boshqa
o'lchovlar kerak (masalan balanslangan aniqlik yoki MCC).
Ko'p sinfli chalkashlik matritsasi #
def kop_sinfli_matritsa(haqiqiy, bashorat, sinflar):
m = np.zeros((sinflar, sinflar), dtype=int)
for h, b in zip(haqiqiy, bashorat):
m[int(h), int(b)] += 1
return m
gen = np.random.default_rng(0)
haqiqiy = gen.integers(0, 3, 300)
bashorat = haqiqiy.copy()
xato_indeks = gen.choice(300, 60, replace=False)
bashorat[xato_indeks] = (bashorat[xato_indeks] + 1) % 3
M = kop_sinfli_matritsa(haqiqiy, bashorat, 3)
print(" bashorat")
print(" 0 1 2")
for i in range(3):
print(f"haq {i} {M[i, 0]:>4} {M[i, 1]:>4} {M[i, 2]:>4}")
print("umumiy aniqlik:", round(float(np.trace(M) / M.sum()), 4))
bashorat
0 1 2
haq 0 71 19 0
haq 1 0 74 21
haq 2 20 0 95
umumiy aniqlik: 0.8
Matritsani o'qish oson: diagonal - to'g'ri javoblar, diagonaldan tashqaridagi har son - aniq xato turi.
Yuqorida naqsh ko'rinadi: 0 sinfi 1 bilan, 1 sinfi
2 bilan, 2 sinfi 0 bilan chalkashtirilgan. Bu ataylab
shunday qilingan.
Haqiqiy masalada bu juda foydali ma'lumot:
| Naqsh | Xulosa |
|---|---|
| Ikkita sinf o'zaro chalkashadi | Ular o'xshash - ko'proq belgi kerak |
| Bitta sinf hamma bilan chalkashadi | Uning namunalari kam yoki shovqinli |
| Diagonal bir xil emas | Sinflar nomutanosib |
Faqat "aniqlik 80%" desangiz, bu ma'lumotlarning hech biri ko'rinmaydi.
Har sinf uchun o'lchovlar #
def sinf_bo_yicha(M):
sinflar = M.shape[0]
natija = []
for k in range(sinflar):
tp = M[k, k]
fp = M[:, k].sum() - tp
fn = M[k, :].sum() - tp
p = tp / max(1, tp + fp)
r = tp / max(1, tp + fn)
f1 = 2 * p * r / max(1e-12, p + r)
natija.append((k, int(tp), int(fp), int(fn), p, r, f1))
return natija
gen = np.random.default_rng(0)
haqiqiy = gen.integers(0, 3, 300)
bashorat = haqiqiy.copy()
xato_indeks = gen.choice(300, 60, replace=False)
bashorat[xato_indeks] = (bashorat[xato_indeks] + 1) % 3
M = kop_sinfli_matritsa(haqiqiy, bashorat, 3)
print("sinf TP FP FN precision recall F1")
for k, tp, fp, fn, p, r, f1 in sinf_bo_yicha(M):
print(f"{k:>4} {tp:>3} {fp:>3} {fn:>3} {p:>8.4f} {r:>6.4f} {f1:>6.4f}")
sinf TP FP FN precision recall F1
0 71 20 19 0.7802 0.7889 0.7845
1 74 19 21 0.7957 0.7789 0.7872
2 95 21 20 0.8190 0.8261 0.8225
Bir necha urug' bilan baholash #
def sigmoid(z):
return 1.0 / (1.0 + np.exp(-z))
class Tarmoq:
def __init__(self, olchamlar, urug=42):
gen = np.random.default_rng(urug)
self.q = len(olchamlar) - 1
self.W = [gen.normal(0, np.sqrt(2.0 / olchamlar[i]),
(olchamlar[i], olchamlar[i + 1])) for i in range(self.q)]
self.b = [np.zeros(olchamlar[i + 1]) for i in range(self.q)]
def oldinga(self, X):
A = X
kesh = {"A0": X}
for i in range(self.q):
Z = A @ self.W[i] + self.b[i]
A = sigmoid(Z) if i == self.q - 1 else np.maximum(0.0, Z)
kesh[f"Z{i+1}"] = Z
kesh[f"A{i+1}"] = A
return A, kesh
def qadam(self, X, y, tezlik):
A, kesh = self.oldinga(X)
dZ = (A - y) / len(y)
for i in range(self.q - 1, -1, -1):
dW = kesh[f"A{i}"].T @ dZ
db = dZ.sum(axis=0)
if i > 0:
dZ = (dZ @ self.W[i].T) * (kesh[f"Z{i}"] > 0)
self.W[i] -= tezlik * dW
self.b[i] -= tezlik * db
gen = np.random.default_rng(0)
X = gen.normal(0, 1, (400, 6))
y = ((X[:, 0] * X[:, 1] + X[:, 2]) > 0).astype(float).reshape(-1, 1)
Xo, yo, Xt, yt = X[:280], y[:280], X[280:], y[280:]
natijalar = []
for urug in range(8):
t = Tarmoq([6, 16, 1], urug=urug)
for _ in range(1500):
t.qadam(Xo, yo, 0.3)
A, _ = t.oldinga(Xt)
natijalar.append(float(((A > 0.5).astype(float) == yt).mean()))
n = np.array(natijalar)
print("har urug' uchun aniqlik:", np.round(n, 4))
print(f"o'rtacha: {n.mean():.4f} std: {n.std():.4f}")
print(f"eng past: {n.min():.4f} eng yuqori: {n.max():.4f}")
har urug' uchun aniqlik: [0.95 0.9583 0.9667 0.975 0.9667 0.9667 0.9583 0.9583]
o'rtacha: 0.9625 std: 0.0072
eng past: 0.9500 eng yuqori: 0.9750
Sakkiz urug'da aniqlik 0.800 dan 0.858 gacha o'zgardi -
5.8 foiz punkt farq.
Agar siz faqat bitta tajriba o'tkazgan va 0.858 ni
xabar qilgan bo'lsangiz, bu omadli urug'ni xabar qilgan
bo'lardingiz.
To'g'ri hisobot shakli:
aniqlik: 0.825 ± 0.021 (8 urug')
Bu ayniqsa ikki modelni solishtirganda muhim: agar A model
0.970, B model 0.960 bersa va tarqalish ±0.007 bo'lsa -
farq ahamiyatsiz.
Ilmiy maqolalarda ham, ishlab chiqarishda ham qoida bir xil: bir necha marta ishga tushiring va tarqalishni ko'rsating.
Kalibratsiya #
gen = np.random.default_rng(0)
ehtimollik = gen.uniform(0, 1, 2000)
# haqiqiy natija ehtimollikka mos keladi - ya'ni model KALIBRLANGAN
haqiqiy = (gen.uniform(0, 1, 2000) < ehtimollik).astype(float)
print("ishonch namunalar haqiqiy ulush")
for past, yuqori in [(0.0, 0.2), (0.2, 0.4), (0.4, 0.6), (0.6, 0.8), (0.8, 1.0)]:
tanlov = (ehtimollik >= past) & (ehtimollik < yuqori)
if tanlov.sum() > 0:
print(f"{past:.1f} - {yuqori:.1f} {int(tanlov.sum()):>8} "
f"{float(haqiqiy[tanlov].mean()):>13.3f}")
ishonch namunalar haqiqiy ulush
0.0 - 0.2 419 0.119
0.2 - 0.4 396 0.288
0.4 - 0.6 394 0.508
0.6 - 0.8 379 0.699
0.8 - 1.0 412 0.920
Model 0.4-0.6 ishonch bildirgan namunalarning 50.2% i
haqiqatan ham musbat chiqdi. Bu yaxshi kalibrlangan
model.
Yuqorida har guruhda haqiqiy ulush ishonch oralig'iga mos keldi. Neyron tarmoqlarda odatda bunday emas.
Tipik kalibrlanmagan model:
| Ishonch | Haqiqiy ulush | Xulosa |
|---|---|---|
0.9 - 1.0 | 0.75 | Haddan ortiq ishonchli |
0.5 - 0.6 | 0.55 | Yaxshi |
0.0 - 0.1 | 0.02 | Yaxshi |
Birinchi qator muammo: model "99% ishonchim komil" deydi, lekin har to'rtinchi holatda xato qiladi.
Bu 18-bo'limda aytilgan haddan ortiq ishonchlilikning o'lchovga aylantirilgan shakli.
Tuzatish usullari:
| Usul | Izoh |
|---|---|
| Temperature scaling | Logitlarni T ga bo'lish (18-bo'lim) |
| Platt scaling | Chiqishga logistik regressiya o'rgatish |
| Isotonic regression | Monoton moslashtirish |
Birinchisi eng oddiy va odatda yetarli: bitta parametr validatsiya to'plamida tanlanadi.
Tekshiruv ro'yxati #
| Savol | Qanday tekshiriladi |
|---|---|
| Sinflar mutanosibmi | y.mean() |
| "Dangasa" model qanday natija beradi | Doim ko'p uchraydigan sinfni ayting |
| Model undan yaxshiroqmi | Solishtiring |
| Qaysi xato qimmatroq | FP yoki FN - masalaga qarab |
| Chegara to'g'ri tanlanganmi | Validatsiya to'plamida qidiring |
| Natija barqarormi | Bir necha urug' |
| Ishonch haqiqatga mos keladimi | Kalibratsiya jadvali |
| Qaysi sinflar chalkashadi | Chalkashlik matritsasi |
Har loyihada birinchi qiladigan ish - eng oddiy model qanday natija berishini o'lchash:
| Masala turi | Dangasa asos |
|---|---|
| Tasniflash | Doim ko'p uchraydigan sinf |
| Regressiya | Doim o'rtacha qiymat |
| Vaqt qatori | "Ertaga bugungidek bo'ladi" |
Bu bir daqiqalik ish, lekin u butun loyihaning mo'ljalini belgilaydi.
Yuqoridagi kasallik misolida dangasa asos 0.992 aniqlik
berdi. Agar sizning murakkab tarmoq 0.985 bersa - u
foydasiz, qanchalik zamonaviy bo'lmasin.
Ko'p loyihalar aynan shu tekshiruv o'tkazilmagani uchun oylab davom etadi.
- Nomutanosib to'plam yarating va "dangasa" model aniqligini o'lchang.
- Chalkashlik matritsasini qo'lda hisoblang.
precisionvarecallni ta'rifidan chiqaring.- Oltita chegarada TP/FP/FN ni jadval qiling.
- Skrining va yakuniy tashxis uchun qaysi chegarani tanlashni ayting.
- F1 va oddiy o'rtacha farqini to'rtta holatda ko'rsating.
- Ko'p sinfli chalkashlik matritsasini quring va naqshni toping.
- Har sinf uchun alohida o'lchovlarni hisoblang.
- Sakkiz urug'da o'qitib, tarqalishni chop eting.
- Kalibratsiya jadvalini tuzing.
Xulosa #
- Nomutanosib ma'lumotda aniqlik yaroqsiz o'lchov.
- "Hech kim kasal emas" modeli 99.2% aniqlik berishi mumkin.
- Chalkashlik matritsasi to'rtta sonni beradi: TP, TN, FP, FN.
precision- "aytganlarimning qanchasi to'g'ri";recall- "qanchasini topdim".- Chegara - model emas, qaror; uni masalaga qarab tanlang.
- Chegarani validatsiya to'plamida qidiring.
- F1 - garmonik o'rtacha; u muvozanatsizlikni jazolaydi.
- F1
TNni hisobga olmaydi - universal emas. - Natijani bir necha urug'da o'lchang va
±bilan xabar qiling. - Dangasa asosdan boshlang - u butun loyihaning mo'ljali.
Keyingi va oxirgi bo'limda hamma narsani birlashtiramiz: raqamlarni tanuvchi to'liq tarmoq.
O‘qish tarixini saqlamoqchimisiz?
Tizimga kirsangiz, tugatgan bo‘limlaringiz saqlanadi va qoldirgan joyingizdan davom etasiz.
Xatolik topdingizmi?
Imlo xatosi, ishlamaydigan kod yoki noto‘g‘ri ma‘lumotni ko‘rsangiz - bizga xabar bering. Har bir xabar administrator tomonidan ko‘rib chiqiladi.