12-bo‘lim
XOR - birinchi ishlaydigan tarmoq
Uch bo'lim oldin imkonsiz bo'lgan masalani yechamiz va yashirin qatlam nima qilishini ko'ramiz.
Ushbu bo‘lim mundarijasi
3-bo'limda bitta neyron XOR ni hech qachon yecha olmasligini ko'rdik. Endi ikkinchi qatlam qo'shamiz va o'sha masalani yechamiz.
Masala #
x₁ | x₂ | XOR |
|---|---|---|
| 0 | 0 | 0 |
| 0 | 1 | 1 |
| 1 | 0 | 1 |
| 1 | 1 | 0 |
"Faqat bittasi bo'lsa - ha". Bu chiziqli ajralmaydigan masala.
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.olchamlar = olchamlar
self.qatlamlar = len(olchamlar) - 1
self.W, self.b = [], []
for i in range(self.qatlamlar):
chegara = np.sqrt(2.0 / olchamlar[i])
self.W.append(gen.normal(0, chegara, (olchamlar[i], olchamlar[i + 1])))
self.b.append(np.zeros(olchamlar[i + 1]))
def oldinga(self, X):
A = X
kesh = {"A0": X}
for i in range(self.qatlamlar):
Z = A @ self.W[i] + self.b[i]
A = sigmoid(Z) if i == self.qatlamlar - 1 else np.tanh(Z)
kesh[f"Z{i+1}"] = Z
kesh[f"A{i+1}"] = A
return A, kesh
def orqaga(self, y, kesh):
n = len(y)
dW = [None] * self.qatlamlar
db = [None] * self.qatlamlar
dZ = (kesh[f"A{self.qatlamlar}"] - y) / n
for i in range(self.qatlamlar - 1, -1, -1):
dW[i] = kesh[f"A{i}"].T @ dZ
db[i] = dZ.sum(axis=0)
if i > 0:
dZ = (dZ @ self.W[i].T) * (1.0 - kesh[f"A{i}"] ** 2)
return dW, db
def yangilash(self, dW, db, tezlik):
for i in range(self.qatlamlar):
self.W[i] -= tezlik * dW[i]
self.b[i] -= tezlik * db[i]
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)))
X_xor = np.array([[0.0, 0.0], [0.0, 1.0], [1.0, 0.0], [1.0, 1.0]])
y_xor = np.array([[0.0], [1.0], [1.0], [0.0]])
tanhYuqoridagi klassda yashirin qatlam np.tanh ishlatadi va
uning hosilasi 1 - A².
Sabab: XOR juda kichik masala - atigi to'rtta namuna va ikki-uchta yashirin neyron. ReLU bilan o'lik neyronlar (4-bo'lim) ehtimoli bu yerda juda yuqori: bitta neyron o'lsa, tarmoqning yarmi yo'qoladi.
tanh hech qachon butunlay o'lmaydi - uning hosilasi faqat
juda katta |z| da nolga yaqinlashadi.
| Masala | Tavsiya |
|---|---|
| Kichik, kam neyronli | tanh |
| Katta, ko'p qatlamli | ReLU |
Bu amaliy qoida: kichik tarmoqlarda tanh ko'pincha
barqarorroq ishlaydi.
Yashirin qatlamsiz - ishlamaydi #
t = Tarmoq([2, 1], urug=0)
for _ in range(20000):
_, kesh = t.oldinga(X_xor)
dW, db = t.orqaga(y_xor, kesh)
t.yangilash(dW, db, 0.5)
bashorat, _ = t.oldinga(X_xor)
print("yo'qotish:", round(t.yoqotish(X_xor, y_xor), 6))
print("bashoratlar:", np.round(bashorat.ravel(), 4))
print("kerak: ", y_xor.ravel())
print("aniqlik:", float(((bashorat > 0.5).astype(float) == y_xor).mean()))
yo'qotish: 0.693147
bashoratlar: [0.5 0.5 0.5 0.5]
kerak: [0. 1. 1. 0.]
aniqlik: 0.5
Yigirma ming davr - va model hech nima o'rganmadi.
Yo'qotish aynan ln(2), bashorat esa har namunada 0.5.
Yo'qotish 0.693147 da qotib qoldi. Bu 6-bo'limdagi
tasodifiy model mo'ljali.
Model "bilmayman" deyishni o'rgandi - chunki bu uning uchun eng yaxshi mumkin bo'lgan javob.
Sabab 3-bo'limda isbotlangan: bitta chiziq XOR ni
ajratmaydi. Tarmoq mumkin bo'lgan barcha chiziqlarni sinab
ko'rdi va eng kam zararli variantni tanladi - hammaga
0.5 deyish.
Bu muhim tashxis naqshi:
| Belgi | Sabab |
|---|---|
Yo'qotish ln(2) da qotib qoldi | Model quvvati yetmayapti |
| Yo'qotish sekin, lekin tushyapti | Tezlik kichik yoki davrlar kam |
| Yo'qotish sakrayapti | Tezlik katta |
Birinchi holatda tezlikni yoki davrlar sonini oshirish hech nima bermaydi. Modelni kattalashtirish kerak.
Ikkita yashirin neyron - birinchi urinish #
t = Tarmoq([2, 2, 1], urug=3)
for davr in range(20001):
_, kesh = t.oldinga(X_xor)
dW, db = t.orqaga(y_xor, kesh)
t.yangilash(dW, db, 0.5)
if davr % 5000 == 0:
print(f"davr {davr:>5}: yo'qotish = {t.yoqotish(X_xor, y_xor):.6f}")
bashorat, _ = t.oldinga(X_xor)
print("bashoratlar:", np.round(bashorat.ravel(), 4))
print("aniqlik:", float(((bashorat > 0.5).astype(float) == y_xor).mean()))
davr 0: yo'qotish = 0.695803
davr 5000: yo'qotish = 0.347119
davr 10000: yo'qotish = 0.346837
davr 15000: yo'qotish = 0.346747
davr 20000: yo'qotish = 0.346702
bashoratlar: [1.000e-04 9.999e-01 4.999e-01 5.001e-01]
aniqlik: 0.5
Kutilmagan natija: tarmoq yechmadi.
Lekin diqqat qiling - bu birinchi holatdan boshqacha muvaffaqiyatsizlik:
| Model | Yo'qotish | Bashoratlar |
|---|---|---|
[2, 1] | 0.6931 = ln(2) | Hammasi 0.5 |
[2, 2, 1], urug' 3 | 0.3467 | Ikkitasi aniq, ikkitasi 0.5 |
Ikkinchi model ikkita namunani to'g'ri o'rgandi
(0.0001 va 0.9999), qolgan ikkitasida esa taslim bo'ldi.
Ya'ni u yechimga qisman yaqinlashdi, keyin qotib qoldi.
Bu model quvvati muammosi emas - ikkita yashirin neyron XOR uchun nazariy jihatdan yetarli.
Muammo - boshlang'ich og'irliklar. Gradient tushish yomon mahalliy minimumga tushdi va undan chiqa olmadi.
Buni tasdiqlash oson: aynan shu arxitekturani boshqa urug' bilan sinang.
t = Tarmoq([2, 2, 1], urug=0)
for davr in range(20001):
_, kesh = t.oldinga(X_xor)
dW, db = t.orqaga(y_xor, kesh)
t.yangilash(dW, db, 0.5)
if davr % 5000 == 0:
print(f"davr {davr:>5}: yo'qotish = {t.yoqotish(X_xor, y_xor):.6f}")
bashorat, _ = t.oldinga(X_xor)
print("bashoratlar:", np.round(bashorat.ravel(), 4))
print("aniqlik:", float(((bashorat > 0.5).astype(float) == y_xor).mean()))
davr 0: yo'qotish = 0.700105
davr 5000: yo'qotish = 0.001282
davr 10000: yo'qotish = 0.000624
davr 15000: yo'qotish = 0.000412
davr 20000: yo'qotish = 0.000307
bashoratlar: [2.000e-04 9.996e-01 9.996e-01 2.000e-04]
aniqlik: 1.0
Bir xil arxitektura, bir xil ma'lumot, bir xil o'qitish tezligi - faqat urug' boshqa. Va natija butunlay boshqa.
Yashirin qatlam nimani o'rgandi #
Ishlagan tarmoqni (urug=0) ochib ko'ramiz.
t = Tarmoq([2, 2, 1], urug=0)
for _ in range(20000):
_, kesh = t.oldinga(X_xor)
dW, db = t.orqaga(y_xor, kesh)
t.yangilash(dW, db, 0.5)
_, kesh = t.oldinga(X_xor)
H = kesh["A1"]
print("kirish -> yashirin qatlam:")
for x, h, y in zip(X_xor, H, y_xor):
print(f" ({x[0]:.0f}, {x[1]:.0f}) -> ({h[0]:>7.4f}, {h[1]:>7.4f}) XOR = {y[0]:.0f}")
kirish -> yashirin qatlam:
(0, 0) -> ( 0.9671, -0.9639) XOR = 0
(0, 1) -> ( 1.0000, 0.9737) XOR = 1
(1, 0) -> (-0.9757, -1.0000) XOR = 1
(1, 1) -> ( 0.9742, -0.9723) XOR = 0
Endi tekshiramiz: yangi fazoda nuqtalar chiziq bilan ajraladimi?
t = Tarmoq([2, 2, 1], urug=0)
for _ in range(20000):
_, kesh = t.oldinga(X_xor)
dW, db = t.orqaga(y_xor, kesh)
t.yangilash(dW, db, 0.5)
_, kesh = t.oldinga(X_xor)
H = kesh["A1"]
# chiqish qatlami - shunchaki bitta chiziq
z = H @ t.W[1] + t.b[1]
print("chiqish qatlamining og'irliklari:", np.round(t.W[1].ravel(), 4))
print("z qiymatlari:", np.round(z.ravel(), 4))
print("z ishoralari:", (z > 0).astype(int).ravel())
print("kerakli javob:", y_xor.astype(int).ravel())
print("mos keladi:", bool(((z > 0).astype(float) == y_xor).all()))
chiqish qatlamining og'irliklari: [-8.5087 8.5168]
z qiymatlari: [-8.4152 7.8073 7.8085 -8.5465]
z ishoralari: [0 1 1 0]
kerakli javob: [0 1 1 0]
mos keladi: True
Chiqish qatlami - oddiy chiziqli neyron. U hech qanday sehr qilmadi. Butun ish yashirin qatlamda bajarildi: u nuqtalarni chiziq ajratadigan holatga ko'chirdi.
Har yashirin qatlam ma'lumotni yangi fazoga ko'chiradi, u yerda masala biroz osonroq bo'ladi. Oxirgi qatlam esa oddiy chiziq chizadi.
| Soha | Yashirin qatlamlar nima o'rganadi |
|---|---|
| Rasm | Chiziqlar → shakllar → obyekt qismlari → obyekt |
| Matn | Harflar → so'zlar → iboralar → ma'no |
| Ovoz | Chastotalar → tovushlar → bo'g'inlar → so'zlar |
"Chuqur o'qitish" nomi shundan: ko'p bosqichli o'zgartirish.
Va shu sababli oxirgi qatlamdan oldingi qiymatlar (embeddings) juda qimmatli - ular masalani osonlashtirgan tayyor ifodalar.
Nechta neyron kerak #
for yashirin in [1, 2, 3, 4, 8]:
yaxshi = 0
for urug in range(10):
t = Tarmoq([2, yashirin, 1], urug=urug)
for _ in range(8000):
_, kesh = t.oldinga(X_xor)
dW, db = t.orqaga(y_xor, kesh)
t.yangilash(dW, db, 0.5)
bashorat, _ = t.oldinga(X_xor)
if ((bashorat > 0.5).astype(float) == y_xor).all():
yaxshi += 1
print(f"{yashirin} yashirin neyron: 10 urug'dan {yaxshi} tasida yechildi")
1 yashirin neyron: 10 urug'dan 0 tasida yechildi
2 yashirin neyron: 10 urug'dan 5 tasida yechildi
3 yashirin neyron: 10 urug'dan 9 tasida yechildi
4 yashirin neyron: 10 urug'dan 10 tasida yechildi
8 yashirin neyron: 10 urug'dan 10 tasida yechildi
Natijalar aniq: ikkita neyron o'nta urug'dan sakkiztasida ishladi, uchtasi esa hammasida.
Sabab: ikkita neyron bilan yechim bor, lekin yo'l tor. Boshlang'ich og'irliklar noqulay bo'lsa, gradient tushish yomon mahalliy minimumga tushadi va chiqa olmaydi.
Uchinchi neyron zaxira yo'l beradi.
Bu amaliy qoida: nazariy minimumdan biroz kattaroq tarmoq oling. Ortiqcha quvvat o'qitishni osonlashtiradi.
Diqqat: bu "qanchalik katta bo'lsa, shuncha yaxshi" degani emas. Haddan ortiq katta model boshqa muammoni keltiradi - haddan ortiq moslashuv (16-bo'lim).
Va bu natija urug'ga bog'liqligining yaxshi misoli: bitta tajriba "ikkita neyron yetarli" degan noto'g'ri xulosaga olib kelishi mumkin edi.
O'qitish tezligining ta'siri #
for tezlik in [0.01, 0.1, 0.5, 2.0, 10.0]:
t = Tarmoq([2, 3, 1], urug=3)
for _ in range(5000):
_, kesh = t.oldinga(X_xor)
dW, db = t.orqaga(y_xor, kesh)
t.yangilash(dW, db, tezlik)
L = t.yoqotish(X_xor, y_xor)
bashorat, _ = t.oldinga(X_xor)
aniq = float(((bashorat > 0.5).astype(float) == y_xor).mean())
print(f"tezlik = {tezlik:<6} yo'qotish = {L:>10.6f} aniqlik = {aniq:.2f}")
tezlik = 0.01 yo'qotish = 0.125842 aniqlik = 1.00
tezlik = 0.1 yo'qotish = 0.005474 aniqlik = 1.00
tezlik = 0.5 yo'qotish = 0.001015 aniqlik = 1.00
tezlik = 2.0 yo'qotish = 0.000250 aniqlik = 1.00
tezlik = 10.0 yo'qotish = 3.721487 aniqlik = 0.75
Natija ikki tomonlama: 0.1 dan 2.0 gacha hammasi
ishladi, 10.0 esa buzdi - yo'qotish 3.72 ga
ko'tarildi va aniqlik 0.75 ga tushdi.
Ya'ni to'rtta namunali eng oddiy masalada ham juda katta tezlik zarar keltiradi. Haqiqiy ma'lumotda chegara ancha pastroq bo'ladi.
Chiziqli faollashtirish bilan - yana ishlamaydi #
class ChiziqliTarmoq(Tarmoq):
def oldinga(self, X):
A = X
kesh = {"A0": X}
for i in range(self.qatlamlar):
Z = A @ self.W[i] + self.b[i]
A = sigmoid(Z) if i == self.qatlamlar - 1 else Z # faollashtirish YO'Q
kesh[f"Z{i+1}"] = Z
kesh[f"A{i+1}"] = A
return A, kesh
def orqaga(self, y, kesh):
n = len(y)
dW = [None] * self.qatlamlar
db = [None] * self.qatlamlar
dZ = (kesh[f"A{self.qatlamlar}"] - y) / n
for i in range(self.qatlamlar - 1, -1, -1):
dW[i] = kesh[f"A{i}"].T @ dZ
db[i] = dZ.sum(axis=0)
if i > 0:
dZ = dZ @ self.W[i].T # hosila = 1
return dW, db
t = ChiziqliTarmoq([2, 8, 8, 1], urug=3)
for _ in range(20000):
_, kesh = t.oldinga(X_xor)
dW, db = t.orqaga(y_xor, kesh)
t.yangilash(dW, db, 0.5)
bashorat, _ = t.oldinga(X_xor)
print("uchta qatlam, chiziqsizliksiz:")
print(" yo'qotish:", round(t.yoqotish(X_xor, y_xor), 6))
print(" bashoratlar:", np.round(bashorat.ravel(), 4))
print(" aniqlik:", float(((bashorat > 0.5).astype(float) == y_xor).mean()))
uchta qatlam, chiziqsizliksiz:
yo'qotish: 0.693147
bashoratlar: [0.5 0.5 0.5 0.5]
aniqlik: 0.5
Uchta qatlam, o'n oltita yashirin neyron - va natija yashirin qatlamsiz model bilan aynan bir xil.
Bu 4-bo'limdagi da'voning eng aniq isboti: chiziqsizliksiz chuqurlik behuda.
- XOR jadvalini yozing va nima uchun chiziqli ajralmasligini tushuntiring.
[2, 1]tarmoqni 20000 davr o'qiting - natija nima?- Yo'qotish nima uchun aynan
ln(2)da qotib qolganini ayting. [2, 2, 1]tarmoqni o'qiting va aniqlikni tekshiring.- Yashirin qatlam chiqishlarini chop eting.
- Yangi fazoda nuqtalar chiziq bilan ajralishini tasdiqlang.
- 1, 2, 3, 4 neyron bilan 10 urug'da sinang.
- Nima uchun uchta neyron ikkitadan ishonchliroq ekanini ayting.
- Beshta turli tezlikda natijani solishtiring.
- Faollashtirishni olib tashlab, chuqurlikning behudaligini ko'rsating.
Xulosa #
- XOR - chiziqli ajralmaydigan masala; bitta neyron yecha olmaydi.
- Yashirin qatlamsiz model
ln(2)da qotib qoladi. - Yo'qotish mo'ljalda qotsa - model quvvati yetmayapti.
- Bunday holatda tezlik yoki davrlarni oshirish foyda bermaydi.
- Ikkita yashirin neyron XOR ni yechadi.
- Yashirin qatlam masalani yechmaydi - uni yechiladigan qiladi.
- Chiqish qatlami baribir oddiy chiziq chizadi.
- Nazariy minimumdan biroz kattaroq tarmoq oling.
- Natija urug'ga bog'liq - bir necha urug' bilan sinang.
- Chiziqsizliksiz uch qatlam ham bitta qatlamga teng.
Keyingi bo'limda og'irliklarni to'g'ri boshlashni o'rganamiz - Xavier va He usullari.
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.