16-bo‘lim
Haddan ortiq moslashuv
Model yodlab olganini qanday aniqlash va unga qarshi to'rtta chora.
Ushbu bo‘lim mundarijasi
Model o'qitish to'plamida mukammal, yangi ma'lumotda esa yomon ishlashi mumkin. Bu haddan ortiq moslashuv (overfitting) - amaliy mashinali o'qitishning asosiy muammosi.
Ma'lumotni bo'lish #
import numpy as np
def sigmoid(z):
return 1.0 / (1.0 + np.exp(-z))
class Tarmoq:
def __init__(self, olchamlar, urug=42, l2=0.0, dropout=0.0):
gen = np.random.default_rng(urug)
self.q = len(olchamlar) - 1
self.l2 = l2
self.dropout = dropout
self.gen = np.random.default_rng(urug + 1000)
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, oqitish=False):
A = X
kesh = {"A0": X}
for i in range(self.q):
Z = A @ self.W[i] + self.b[i]
if i == self.q - 1:
A = sigmoid(Z)
else:
A = np.maximum(0.0, Z)
if oqitish and self.dropout > 0:
maska = (self.gen.uniform(0, 1, A.shape) > self.dropout)
A = A * maska / (1.0 - self.dropout)
kesh[f"M{i+1}"] = maska
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, oqitish=True)
n = len(y)
dZ = (A - y) / n
for i in range(self.q - 1, -1, -1):
dW = kesh[f"A{i}"].T @ dZ + self.l2 * self.W[i]
db = dZ.sum(axis=0)
if i > 0:
dA = dZ @ self.W[i].T
if self.dropout > 0 and f"M{i}" in kesh:
dA = dA * kesh[f"M{i}"] / (1.0 - self.dropout)
dZ = dA * (kesh[f"Z{i}"] > 0)
self.W[i] -= tezlik * dW
self.b[i] -= tezlik * db
def yoqotish(self, X, y, eps=1e-12):
A, _ = self.oldinga(X)
A = np.clip(A, eps, 1 - eps)
return float(-np.mean(y * np.log(A) + (1 - y) * np.log(1 - A)))
def aniqlik(self, X, y):
A, _ = self.oldinga(X)
return float(((A > 0.5).astype(float) == y).mean())
def shovqinli_malumot(n=200, belgilar=20, urug=0):
"""Faqat dastlabki 2 belgi ma'noli, qolganlari - sof shovqin."""
gen = np.random.default_rng(urug)
X = gen.normal(0, 1, (n, belgilar))
y = ((X[:, 0] + X[:, 1]) > 0).astype(float).reshape(-1, 1)
# 10% yorliqni ataylab buzamiz
buzuq = gen.choice(n, size=n // 10, replace=False)
y[buzuq] = 1.0 - y[buzuq]
return X, y
def boling(X, y, ulush=0.7, urug=0):
gen = np.random.default_rng(urug)
tartib = gen.permutation(len(X))
chegara = int(len(X) * ulush)
o, t = tartib[:chegara], tartib[chegara:]
return X[o], y[o], X[t], y[t]
X, y = shovqinli_malumot(300)
Xo, yo, Xt, yt = boling(X, y)
print("o'qitish:", Xo.shape, " tekshiruv:", Xt.shape)
print("o'qitishda 1 lar ulushi:", round(float(yo.mean()), 3))
print("tekshiruvda 1 lar ulushi:", round(float(yt.mean()), 3))
o'qitish: (210, 20) tekshiruv: (90, 20)
o'qitishda 1 lar ulushi: 0.452
tekshiruvda 1 lar ulushi: 0.544
Tekshiruv to'plami modelning hech qachon ko'rmagan ma'lumoti bo'lishi kerak.
Buni buzishning uch usuli bor va uchalasi ham keng tarqalgan:
| Xato | Nima bo'ladi |
|---|---|
| Normalizatsiyani butun to'plamda hisoblash | Tekshiruv statistikasi o'qitishga "sizib" o'tadi |
| Giperparametrni tekshiruvda tanlash | Tekshiruv aslida o'qitish to'plamiga aylanadi |
| Bir necha marta tekshirib, eng yaxshisini tanlash | Xuddi shunday |
Uchinchisi eng nozik: siz 20 xil model sinasangiz va eng yaxshi tekshiruv natijasini tanlasangiz - o'sha natija haddan ortiq optimistik bo'ladi.
To'g'ri yechim - uchta to'plam:
| To'plam | Vazifasi |
|---|---|
| O'qitish (60-80%) | Og'irliklarni topish |
| Validatsiya (10-20%) | Giperparametrlarni tanlash |
| Test (10-20%) | Faqat bir marta, oxirida |
Test to'plamini oxirigacha umuman ochmang.
Haddan ortiq moslashuvni ko'rish #
X, y = shovqinli_malumot(300)
Xo, yo, Xt, yt = boling(X, y)
t = Tarmoq([20, 64, 64, 1], urug=1)
print("davr o'qitish tekshiruv")
for davr in range(1, 3001):
t.qadam(Xo, yo, 0.1)
if davr in (100, 300, 600, 1000, 2000, 3000):
print(f"{davr:>5} {t.yoqotish(Xo, yo):.4f} / {t.aniqlik(Xo, yo):.3f} "
f"{t.yoqotish(Xt, yt):.4f} / {t.aniqlik(Xt, yt):.3f}")
davr o'qitish tekshiruv
100 0.2350 / 0.933 0.4800 / 0.778
300 0.0448 / 1.000 0.6287 / 0.778
600 0.0110 / 1.000 0.8405 / 0.778
1000 0.0044 / 1.000 0.9841 / 0.767
2000 0.0015 / 1.000 1.1556 / 0.767
3000 0.0008 / 1.000 1.2481 / 0.767
Yuqoridagi jadvalda hamma narsa ko'rinadi:
| Davr | O'qitish yo'q. / aniqlik | Tekshiruv yo'q. / aniqlik |
|---|---|---|
| 100 | 0.235 / 0.933 | 0.480 / 0.778 |
| 300 | 0.045 / 1.000 | 0.629 / 0.778 |
| 600 | 0.011 / 1.000 | 0.841 / 0.778 |
| 3000 | 0.0008 / 1.000 | 1.248 / 0.767 |
O'qitish yo'qotishi 0.235 dan 0.0008 ga tushdi - ya'ni
300 barobar yaxshilandi. Tekshiruv yo'qotishi esa
0.480 dan 1.248 ga ko'tarildi.
Ya'ni 100-davrdan keyingi butun o'qitish - zarar.
Ma'lumotning 10% yorlig'i ataylab buzilgan edi. 300-davrda o'qitish aniqligi 100% - ya'ni model o'sha noto'g'ri yorliqlarni ham yodlab oldi. Bu yangi ma'lumotga umuman yordam bermaydi.
Uchta belgi:
| Belgi | Xulosa |
|---|---|
| O'qitish yo'qotishi tushyapti, tekshiruv o'syapti | Haddan ortiq moslashuv |
| Ikkalasi ham yuqori | Yetarli moslashmagan - model kichik |
| Ikkalasi ham past va yaqin | Yaxshi |
1-chora: erta to'xtatish #
X, y = shovqinli_malumot(300)
Xo, yo, Xt, yt = boling(X, y)
t = Tarmoq([20, 64, 64, 1], urug=1)
eng_yaxshi, eng_yaxshi_davr, sabr = float("inf"), 0, 0
for davr in range(1, 3001):
t.qadam(Xo, yo, 0.1)
tekshiruv = t.yoqotish(Xt, yt)
if tekshiruv < eng_yaxshi - 1e-4:
eng_yaxshi, eng_yaxshi_davr, sabr = tekshiruv, davr, 0
else:
sabr += 1
if sabr >= 200:
print(f"{davr}-davrda to'xtatildi (200 davr yaxshilanish yo'q)")
break
print(f"eng yaxshi davr: {eng_yaxshi_davr}, tekshiruv yo'qotishi: {eng_yaxshi:.4f}")
292-davrda to'xtatildi (200 davr yaxshilanish yo'q)
eng yaxshi davr: 92, tekshiruv yo'qotishi: 0.4798
Eng yaxshi natija 92-davrda bo'lgan - ya'ni 3000 davr o'qitishning 97% i behuda edi.
U hech qanday qo'shimcha hisob talab qilmaydi va deyarli har doim yordam beradi.
| Parametr | Ma'nosi |
|---|---|
sabr (patience) | Necha davr yaxshilanishsiz kutiladi |
| Odatiy qiymat | 10-50 davr (bu misolda 200) |
| Eng yaxshi og'irliklarni saqlash | Majburiy - aks holda foydasi yo'q |
Oxirgi qator muhim: to'xtaganda modelning og'irliklari eng yaxshi holatda emas, u yerdan 200 davr o'tgan.
To'g'ri amaliyot: eng yaxshi natija ko'rsatilganda og'irliklarning nusxasini saqlang va oxirida shuni qaytaring.
2-chora: L2 regularizatsiya #
L_yangi = L + (lambda/2) · Σ w²
dW_yangi = dW + lambda · W
X, y = shovqinli_malumot(300)
Xo, yo, Xt, yt = boling(X, y)
for l2 in [0.0, 0.001, 0.01, 0.05, 0.2]:
t = Tarmoq([20, 64, 64, 1], urug=1, l2=l2)
for _ in range(3000):
t.qadam(Xo, yo, 0.1)
ogirlik_normasi = float(np.sqrt(sum(float((W ** 2).sum()) for W in t.W)))
print(f"l2 = {l2:<6} o'qitish: {t.aniqlik(Xo, yo):.3f} "
f"tekshiruv: {t.aniqlik(Xt, yt):.3f} ‖W‖ = {ogirlik_normasi:>7.3f}")
l2 = 0.0 o'qitish: 1.000 tekshiruv: 0.767 ‖W‖ = 17.505
l2 = 0.001 o'qitish: 1.000 tekshiruv: 0.756 ‖W‖ = 13.642
l2 = 0.01 o'qitish: 1.000 tekshiruv: 0.789 ‖W‖ = 5.146
l2 = 0.05 o'qitish: 0.952 tekshiruv: 0.833 ‖W‖ = 3.137
l2 = 0.2 o'qitish: 0.548 tekshiruv: 0.456 ‖W‖ = 0.000
Yo'qotishga og'irliklar kvadratining yig'indisi qo'shiladi. Natijada model kichik og'irliklarni afzal ko'radi.
Nima uchun bu yordam beradi: katta og'irlik - bu "bu belgi juda muhim" degan qat'iy qaror. Shovqinli ma'lumotda bunday qat'iylik xato bo'lib chiqadi.
Jadvalda bog'liqlik aniq:
lambda | ‖W‖ | O'qitish | Tekshiruv |
|---|---|---|---|
0 | 17.5 | 1.000 | 0.767 |
0.05 | 3.1 | 0.952 | 0.833 |
0.2 | 0.000 | 0.548 | 0.456 |
0.05 da tekshiruv aniqligi eng yuqori - o'qitish
aniqligi tushgan bo'lsa ham.
Oxirgi qator - haddan ortiq regularizatsiya: og'irliklar
normasi aynan nolga tushdi. Model butunlay o'chdi va
aniqlik 0.456 - tasodifiy taxmindan ham yomonroq.
Ya'ni lambda ham giperparametr va u validatsiya
to'plamida tanlanadi.
3-chora: dropout #
X, y = shovqinli_malumot(300)
Xo, yo, Xt, yt = boling(X, y)
for p in [0.0, 0.2, 0.5, 0.8]:
t = Tarmoq([20, 64, 64, 1], urug=1, dropout=p)
for _ in range(3000):
t.qadam(Xo, yo, 0.1)
print(f"dropout = {p:<5} o'qitish: {t.aniqlik(Xo, yo):.3f} "
f"tekshiruv: {t.aniqlik(Xt, yt):.3f}")
dropout = 0.0 o'qitish: 1.000 tekshiruv: 0.767
dropout = 0.2 o'qitish: 1.000 tekshiruv: 0.822
dropout = 0.5 o'qitish: 1.000 tekshiruv: 0.844
dropout = 0.8 o'qitish: 0.919 tekshiruv: 0.822
Dropout 0.5 da tekshiruv aniqligi 0.767 dan 0.844
ga ko'tarildi - eng yaxshi natija. Diqqat qiling: o'qitish
aniqligi baribir 1.000 bo'lib qoldi, chunki dropout
bashorat rejimida o'chiriladi.
Dropout har qadamda neyronlarning bir qismini tasodifiy o'chiradi. Bu modelni bitta neyronga tayanmaslikka majbur qiladi.
Ikkita muhim tafsilot bor va ikkalasini ham unutish oson:
Birinchi: bashorat qilishda dropout o'chirilishi
kerak. Kodda bu oqitish=True bayrog'i bilan hal qilingan.
Agar uni unutsangiz, model har chaqiruvda boshqa javob beradi - va aniqlik tushadi.
Ikkinchi: 1 / (1 - p) ga bo'lish (inverted dropout).
Usiz o'qitishdagi va bashoratdagi chiqishlar turli
masshtabda bo'ladi.
p | Faol neyronlar | Tuzatuvchi koeffitsient |
|---|---|---|
0.2 | 80% | 1 / 0.8 = 1.25 |
0.5 | 50% | 1 / 0.5 = 2.0 |
Gradient tekshiruvi (11-bo'lim) dropout bilan ishlamaydi -
uni sinashdan oldin dropout=0 qo'ying.
X, y = shovqinli_malumot(300)
Xo, yo, Xt, yt = boling(X, y)
t = Tarmoq([20, 32, 1], urug=1, dropout=0.5)
for _ in range(500):
t.qadam(Xo, yo, 0.1)
a1, _ = t.oldinga(Xt[:3], oqitish=False)
a2, _ = t.oldinga(Xt[:3], oqitish=False)
b1, _ = t.oldinga(Xt[:3], oqitish=True)
b2, _ = t.oldinga(Xt[:3], oqitish=True)
print("bashorat rejimi barqarormi:", bool(np.allclose(a1, a2)))
print("o'qitish rejimi barqarormi:", bool(np.allclose(b1, b2)))
bashorat rejimi barqarormi: True
o'qitish rejimi barqarormi: False
4-chora: ko'proq ma'lumot #
for n in [100, 300, 1000, 3000]:
X, y = shovqinli_malumot(n)
Xo, yo, Xt, yt = boling(X, y)
t = Tarmoq([20, 64, 64, 1], urug=1)
for _ in range(2000):
t.qadam(Xo, yo, 0.1)
print(f"n = {n:>5} o'qitish: {t.aniqlik(Xo, yo):.3f} "
f"tekshiruv: {t.aniqlik(Xt, yt):.3f} farq: "
f"{t.aniqlik(Xo, yo) - t.aniqlik(Xt, yt):+.3f}")
n = 100 o'qitish: 1.000 tekshiruv: 0.567 farq: +0.433
n = 300 o'qitish: 1.000 tekshiruv: 0.767 farq: +0.233
n = 1000 o'qitish: 1.000 tekshiruv: 0.763 farq: +0.237
n = 3000 o'qitish: 0.989 tekshiruv: 0.781 farq: +0.207
Farq +0.433 dan +0.207 ga tushdi - ikki barobardan
ko'proq yaxshilanish.
Lekin diqqat qiling: farq yo'qolmadi. 64 neyronli ikki
qatlamli model 3000 namunani ham 2000 davrda deyarli to'liq
yodlab oldi (0.989).
Ya'ni ko'proq ma'lumot yordam beradi, lekin u yolg'iz yetarli emas - regularizatsiya yoki erta to'xtatish bilan birga ishlatiladi.
Amaliy tartib:
| Chora | Narxi | Samarasi |
|---|---|---|
| Ko'proq ma'lumot | Qimmat | Eng yuqori |
| Ma'lumotni kengaytirish (augmentation) | Arzon | Yuqori |
| Erta to'xtatish | Bepul | O'rtacha |
| L2 / dropout | Bepul | O'rtacha |
| Modelni kichraytirish | Bepul | O'rtacha |
Diqqat: n = 3000 da ham tekshiruv aniqligi 0.781 -
va u 0.9 dan oshmaydi. Sabab: ma'lumotning 10%
yorlig'i ataylab buzilgan. Hech qanday model bu
shovqindan nariga o'ta olmaydi.
Bu muhim tushuncha: har ma'lumot to'plamining nazariy chegarasi bor. Uni ortda qoldirish - haddan ortiq moslashuv belgisi, yaxshi model belgisi emas.
Model hajmi #
X, y = shovqinli_malumot(300)
Xo, yo, Xt, yt = boling(X, y)
for olchamlar in [[20, 4, 1], [20, 16, 1], [20, 64, 64, 1], [20, 128, 128, 128, 1]]:
t = Tarmoq(olchamlar, urug=1)
parametrlar = sum(W.size for W in t.W) + sum(b.size for b in t.b)
for _ in range(2000):
t.qadam(Xo, yo, 0.1)
print(f"{str(olchamlar):<24} parametrlar: {parametrlar:>6} "
f"o'qitish: {t.aniqlik(Xo, yo):.3f} tekshiruv: {t.aniqlik(Xt, yt):.3f}")
[20, 4, 1] parametrlar: 89 o'qitish: 0.948 tekshiruv: 0.711
[20, 16, 1] parametrlar: 353 o'qitish: 1.000 tekshiruv: 0.778
[20, 64, 64, 1] parametrlar: 5569 o'qitish: 1.000 tekshiruv: 0.767
[20, 128, 128, 128, 1] parametrlar: 35841 o'qitish: 1.000 tekshiruv: 0.711
Eng katta va eng kichik modellar bir xil yomon natija
berdi (0.711), o'rtadagi [20, 16, 1] esa eng yaxshisini
(0.778).
Bu klassik U shakli: juda kichik model yetarli o'rganmaydi, juda katta model yodlab oladi. Optimal hajm o'rtada yotadi va uni validatsiya to'plamida qidirasiz.
- Ma'lumotni o'qitish va tekshiruvga bo'ling.
- Nima uchun uchta to'plam kerakligini tushuntiring.
- 3000 davr o'qitib, ikki yo'qotishning ajralishini kuzating.
- Eng yaxshi tekshiruv davrini toping.
- Erta to'xtatishni
sabrbilan yozing. - Beshta
l2qiymatida og'irlik normasini va aniqlikni solishtiring. - Haddan ortiq regularizatsiya nima ekanini ko'rsating.
- To'rtta
dropoutqiymatida natijani solishtiring. - Dropout bashorat rejimida barqaror ekanini tekshiring.
- Ma'lumot hajmini oshirib, farqning kamayishini o'lchang.
Xulosa #
- Haddan ortiq moslashuv: o'qitish yaxshilanadi, tekshiruv yomonlashadi.
- Tekshiruv to'plami muqaddas - unda giperparametr tanlamang.
- To'g'risi - uchta to'plam: o'qitish, validatsiya, test.
- Model shovqinli yorliqlarni ham yodlab oladi.
- Erta to'xtatish - bepul va deyarli har doim yordam beradi.
- Eng yaxshi og'irliklarning nusxasini saqlang.
- L2 kichik og'irliklarni afzal ko'radi;
lambdajuda katta bo'lsa - model o'qimaydi. - Dropout faqat o'qitishda;
1/(1-p)ga bo'lishni unutmang. - Ko'proq ma'lumot - eng ishonchli chora.
- Har to'plamning nazariy chegarasi bor - undan oshish yodlash belgisi.
Keyingi bo'limda ma'lumotni tayyorlashni ko'ramiz: normalizatsiya va masshtablash.
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.