14-bo‘lim
Paketli o'qitish
To'liq paket, mini-paket va SGD - shovqin, tezlik va xotira o'rtasidagi almashuv.
Ushbu bo‘lim mundarijasi
Hozirgacha har qadamda butun ma'lumot ishlatildi. Katta to'plamda bu imkonsiz - va kerak ham emas.
Uch xil yondashuv #
| Usul | Bir qadamda | Qadamlar soni | Shovqin |
|---|---|---|---|
| To'liq paket | Hamma namuna | Kam | Yo'q |
| Mini-paket | 32-256 namuna | O'rtacha | O'rtacha |
| SGD | Bitta namuna | Juda ko'p | Katta |
Sinov muhiti #
import numpy as np
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
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 malumot(n=1000, urug=0):
gen = np.random.default_rng(urug)
X = gen.normal(0, 1, (n, 8))
qoida = (X[:, 0] * X[:, 1] + X[:, 2] - 0.5 * X[:, 3]) > 0
y = qoida.astype(float).reshape(-1, 1)
return X, y
def paketlar(X, y, hajm, gen):
n = len(X)
tartib = gen.permutation(n)
for boshi in range(0, n, hajm):
idx = tartib[boshi:boshi + hajm]
yield X[idx], y[idx]
def oqit(hajm, davrlar, tezlik, urug=1):
X, y = malumot(1000)
t = Tarmoq([8, 16, 16, 1], urug=urug)
gen = np.random.default_rng(0)
qadamlar = 0
for _ in range(davrlar):
for xb, yb in paketlar(X, y, hajm, gen):
t.qadam(xb, yb, tezlik)
qadamlar += 1
return t, qadamlar, X, y
def oqit_qadamlar(hajm, jami_qadam, tezlik, urug=1):
X, y = malumot(1000)
t = Tarmoq([8, 16, 16, 1], urug=urug)
gen = np.random.default_rng(0)
bajarilgan = 0
while bajarilgan < jami_qadam:
for xb, yb in paketlar(X, y, hajm, gen):
t.qadam(xb, yb, tezlik)
bajarilgan += 1
if bajarilgan >= jami_qadam:
break
return t, X, y
X, y = malumot(1000)
print("X:", X.shape, " y:", y.shape)
print("sinflar muvozanati:", round(float(y.mean()), 4))
X: (1000, 8) y: (1000, 1)
sinflar muvozanati: 0.485
Paketlarga bo'lish #
def paketlar(X, y, hajm, gen):
n = len(X)
tartib = gen.permutation(n)
for boshi in range(0, n, hajm):
idx = tartib[boshi:boshi + hajm]
yield X[idx], y[idx]
X, y = malumot(1000)
gen = np.random.default_rng(0)
hajmlar = [len(xb) for xb, _ in paketlar(X, y, 32, gen)]
print("paketlar soni:", len(hajmlar))
print("birinchi 5 ta paket hajmi:", hajmlar[:5])
print("oxirgi paket hajmi:", hajmlar[-1])
print("jami namunalar:", sum(hajmlar))
paketlar soni: 32
birinchi 5 ta paket hajmi: [32, 32, 32, 32, 32]
oxirgi paket hajmi: 8
jami namunalar: 1000
gen.permutation(n) - har davrda ma'lumotni qayta
aralashtirish.
Usiz nima bo'ladi: paketlar har davrda bir xil bo'ladi va model ularning tartibini ham "o'rganib" oladi.
Yomonroq holat: ma'lumot saralangan bo'lsa (avval barcha
0, keyin barcha 1), aralashtirmasdan har paket bitta
sinfdan iborat bo'ladi. Model navbat bilan "hammasi 0"
va "hammasi 1" deyishni o'rganadi va hech qachon
yaqinlashmaydi.
Bu klassik xato: ma'lumot fayldan tartib bilan o'qilgan va aralashtirilmagan.
| Vaziyat | Aralashtirish |
|---|---|
| Tasniflash, regressiya | Har davrda |
| Vaqt qatorlari | Yo'q - tartib ma'noli |
| Tekshiruv to'plami | Kerak emas |
X, y = malumot(200)
# ataylab saralangan ma'lumot
tartib = np.argsort(y.ravel())
X_saralangan, y_saralangan = X[tartib], y[tartib]
gen = np.random.default_rng(0)
print("aralashtirmasdan - har paketdagi 1 lar ulushi:")
for i, boshi in enumerate(range(0, 200, 50)):
ulush = float(y_saralangan[boshi:boshi + 50].mean())
print(f" paket {i}: {ulush:.2f}")
print("aralashtirib:")
for i, (xb, yb) in enumerate(paketlar(X_saralangan, y_saralangan, 50, gen)):
print(f" paket {i}: {float(yb.mean()):.2f}")
aralashtirmasdan - har paketdagi 1 lar ulushi:
paket 0: 0.00
paket 1: 0.00
paket 2: 0.90
paket 3: 1.00
aralashtirib:
paket 0: 0.58
paket 1: 0.48
paket 2: 0.44
paket 3: 0.40
Aralashtirmasdan birinchi ikki paketda bitta ham musbat namuna yo'q.
Uch usulni solishtirish #
def oqit(hajm, davrlar, tezlik, urug=1):
X, y = malumot(1000)
t = Tarmoq([8, 16, 16, 1], urug=urug)
gen = np.random.default_rng(0)
qadamlar = 0
for _ in range(davrlar):
for xb, yb in paketlar(X, y, hajm, gen):
t.qadam(xb, yb, tezlik)
qadamlar += 1
return t, qadamlar, X, y
for nom, hajm, davrlar in [("to'liq paket", 1000, 200),
("mini-paket 32", 32, 200),
("SGD (1)", 1, 20)]:
t, qadamlar, X, y = oqit(hajm, davrlar, 0.1)
print(f"{nom:<16} qadamlar: {qadamlar:>6} "
f"yo'qotish: {t.yoqotish(X, y):.4f} aniqlik: {t.aniqlik(X, y):.4f}")
to'liq paket qadamlar: 200 yo'qotish: 0.2400 aniqlik: 0.9150
mini-paket 32 qadamlar: 6400 yo'qotish: 0.0020 aniqlik: 1.0000
SGD (1) qadamlar: 20000 yo'qotish: 0.0850 aniqlik: 0.9620
Uchala model ham 200 davr (SGD faqat 20) ishladi, lekin qadamlar soni butunlay boshqa.
To'liq paketda 200 davr = atigi 200 qadam. Bu juda kam: model deyarli hech nimani o'rganmadi.
Mini-paketda esa 200 davr = 6400 qadam, chunki har davrda 32 ta paket bor.
Bu tez-tez chalkashtiriladi:
| Atama | Ma'nosi |
|---|---|
| Davr (epoch) | Butun ma'lumot bir marta ko'rildi |
| Qadam (iteration) | Og'irliklar bir marta yangilandi |
| Bog'lanish | qadamlar = davrlar × (n / paket_hajmi) |
Ya'ni paket hajmini kichraytirish - ko'proq yangilanish degani. Shuning uchun mini-paket to'liq paketdan tezroq o'qiydi, garchi har qadam kamroq ma'lumot ko'rsa ham.
Adolatli taqqoslash: bir xil qadamlar soni #
def oqit_qadamlar(hajm, jami_qadam, tezlik, urug=1):
X, y = malumot(1000)
t = Tarmoq([8, 16, 16, 1], urug=urug)
gen = np.random.default_rng(0)
bajarilgan = 0
while bajarilgan < jami_qadam:
for xb, yb in paketlar(X, y, hajm, gen):
t.qadam(xb, yb, tezlik)
bajarilgan += 1
if bajarilgan >= jami_qadam:
break
return t, X, y
for nom, hajm in [("to'liq paket", 1000), ("mini-paket 128", 128),
("mini-paket 32", 32), ("SGD (1)", 1)]:
t, X, y = oqit_qadamlar(hajm, 3000, 0.1)
print(f"{nom:<17} 3000 qadam -> yo'qotish: {t.yoqotish(X, y):.4f} "
f"aniqlik: {t.aniqlik(X, y):.4f}")
to'liq paket 3000 qadam -> yo'qotish: 0.0107 aniqlik: 1.0000
mini-paket 128 3000 qadam -> yo'qotish: 0.0105 aniqlik: 1.0000
mini-paket 32 3000 qadam -> yo'qotish: 0.0116 aniqlik: 0.9990
SGD (1) 3000 qadam -> yo'qotish: 0.1488 aniqlik: 0.9370
Endi manzara boshqacha: bir xil qadamlar sonida to'liq paket va mini-paket yaqin natija berdi, SGD esa sezilarli yomonroq.
Ikkala taqqoslash birga to'liq rasmni beradi:
| O'lchov | To'liq paket | Mini-paket | SGD |
|---|---|---|---|
| Bir qadamning narxi | Juda qimmat | O'rtacha | Arzon |
| Bir qadamning sifati | Eng yaxshi | Yaxshi | Shovqinli |
| Davrdagi qadamlar | 1 | n/hajm | n |
| Xotira | Butun to'plam | Paket | Bitta namuna |
| Apparat samaradorligi | Yaxshi | Eng yaxshi | Juda yomon |
Oxirgi qator ko'pincha hal qiluvchi: zamonaviy protsessor va videokartalar matritsa amallariga mo'ljallangan. Bitta namunani qayta ishlash ularning quvvatini deyarli ishlatmaydi.
Shu sababli amalda 32 dan 512 gacha paket standart. "Sof SGD" bugun deyarli ishlatilmaydi - lekin uning nomi mini-paket usuliga ham qo'llanadi (chalkashlik shundan).
Paket hajmi va o'qitish tezligi bog'liq #
for hajm, tezlik in [(32, 0.03), (32, 0.1), (32, 0.3),
(256, 0.03), (256, 0.1), (256, 0.3)]:
t, X, y = oqit_qadamlar(hajm, 2000, tezlik)
print(f"hajm {hajm:>4}, tezlik {tezlik:<5} -> "
f"yo'qotish: {t.yoqotish(X, y):.4f} aniqlik: {t.aniqlik(X, y):.4f}")
hajm 32, tezlik 0.03 -> yo'qotish: 0.0699 aniqlik: 0.9770
hajm 32, tezlik 0.1 -> yo'qotish: 0.0221 aniqlik: 0.9970
hajm 32, tezlik 0.3 -> yo'qotish: 0.0039 aniqlik: 1.0000
hajm 256, tezlik 0.03 -> yo'qotish: 0.0687 aniqlik: 0.9760
hajm 256, tezlik 0.1 -> yo'qotish: 0.0204 aniqlik: 0.9980
hajm 256, tezlik 0.3 -> yo'qotish: 0.0025 aniqlik: 1.0000
Katta paket aniqroq gradient beradi, ya'ni undan kattaroq qadam tashlash xavfsiz.
Keng tarqalgan qoida (linear scaling rule): paket hajmini
k barobar oshirsangiz, tezlikni ham k barobar oshiring.
| Paket | Tezlik |
|---|---|
| 32 | 0.01 |
| 64 | 0.02 |
| 128 | 0.04 |
| 256 | 0.08 |
Bu qoida cheksiz ishlamaydi: juda katta paketda (8192+)
u buziladi va "warmup" - tezlikni asta oshirish - kerak
bo'ladi.
Yuqoridagi jadvalda ham bu ko'rinadi: 256 paket 0.03
tezlikda 32 paketdan yomonroq ishladi, 0.3 tezlikda
esa ular tenglashdi.
Yo'qotishning tebranishi #
X, y = malumot(1000)
t = Tarmoq([8, 16, 16, 1], urug=1)
gen = np.random.default_rng(0)
paket_yoqotishlari = []
for _ in range(3):
for xb, yb in paketlar(X, y, 32, gen):
paket_yoqotishlari.append(t.yoqotish(xb, yb))
t.qadam(xb, yb, 0.1)
toliq = t.yoqotish(X, y)
p = np.array(paket_yoqotishlari)
print("paket yo'qotishlari - eng kichik:", round(float(p.min()), 4))
print("paket yo'qotishlari - eng katta: ", round(float(p.max()), 4))
print("tarqalish (std):", round(float(p.std()), 4))
print("to'liq to'plamda:", round(toliq, 4))
paket yo'qotishlari - eng kichik: 0.3075
paket yo'qotishlari - eng katta: 0.7794
tarqalish (std): 0.1111
to'liq to'plamda: 0.3759
Yuqorida paketlar bo'yicha yo'qotish 0.38 dan 0.78 gacha
tebrandi. Bu nosozlik emas: har paket boshqa namunalardan
iborat va ularning qiyinligi har xil.
Shu sababli o'qitishni kuzatganda:
| Nimani chizish | Izoh |
|---|---|
| Har paket yo'qotishi | Juda shovqinli - foydasiz |
| Davr bo'yicha o'rtacha | Yaxshi |
| To'liq to'plamda yo'qotish | Eng aniq, lekin qimmat |
| Tekshiruv to'plamida | Eng muhimi (16-bo'lim) |
Amalda: har davr oxirida bir marta o'lchang. Har qadamda o'lchash vaqtni behuda sarflaydi va grafikni o'qib bo'lmaydigan qiladi.
Xotira #
import sys
X, y = malumot(1000)
t = Tarmoq([8, 64, 64, 1], urug=0)
for hajm in [1, 32, 1000]:
xb = X[:hajm]
_, kesh = t.oldinga(xb)
baytlar = sum(v.nbytes for v in kesh.values())
print(f"paket {hajm:>4}: keshdagi oraliq qiymatlar {baytlar:>8} bayt")
paket 1: keshdagi oraliq qiymatlar 2128 bayt
paket 32: keshdagi oraliq qiymatlar 68096 bayt
paket 1000: keshdagi oraliq qiymatlar 2128000 bayt
Kesh hajmi paketga to'g'ri proporsional. Kichik tarmoqda bu ahamiyatsiz, lekin katta modelda aynan shu xotira chegarasini belgilaydi.
paketlargeneratorini yozing va oxirgi paket hajmini tekshiring.- Saralangan ma'lumotda aralashtirmasdan paketlar tarkibini ko'ring.
- Aralashtirib, farqni ko'rsating.
- Uch usulni bir xil davrlar sonida solishtiring.
- Ularni bir xil qadamlar sonida qayta solishtiring.
- Davr va qadam farqini o'z so'zlaringiz bilan tushuntiring.
- Ikki paket hajmi va uch tezlikda jadval tuzing.
- Paket kattalashganda tezlik nima uchun oshishi kerakligini ayting.
- Paket yo'qotishlarining tarqalishini hisoblang.
- Kesh hajmining paketga bog'liqligini o'lchang.
Xulosa #
- Davr - ma'lumot bir marta ko'rildi; qadam - og'irliklar bir marta yangilandi.
qadamlar = davrlar × (n / paket_hajmi).- Har davrda ma'lumotni aralashtiring; vaqt qatorlari - istisno.
- Aralashtirmaslik saralangan ma'lumotda o'qitishni butunlay buzadi.
- Bir xil davrda mini-paket to'liq paketdan ancha oldinda.
- Bir xil qadamda ular yaqinlashadi, SGD esa ortda qoladi.
- Mini-paket apparat uchun eng samarali - 32 dan 512 gacha.
- Paket kattalashsa - tezlikni ham oshiring.
- Paket yo'qotishi sakraydi - davr bo'yicha o'lchang.
- Kesh hajmi paketga proporsional - xotira chegarasi shundan.
Keyingi bo'limda optimizatorlarni ko'ramiz: momentum, RMSProp va Adam.
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.