12-bo‘lim

XOR - birinchi ishlaydigan tarmoq

Uch bo'lim oldin imkonsiz bo'lgan masalani yechamiz va yashirin qatlam nima qilishini ko'ramiz.

🕑 13 daqiqa o‘qish 📄 976 so‘z 👁 1 marta ko‘rilgan
Ushbu bo‘lim mundarijasi
  1. Masala
  2. Yashirin qatlamsiz - ishlamaydi
  3. Ikkita yashirin neyron - birinchi urinish
  4. Yashirin qatlam nimani o'rgandi
  5. Nechta neyron kerak
  6. O'qitish tezligining ta'siri
  7. Chiziqli faollashtirish bilan - yana ishlamaydi
  8. Xulosa

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
000
011
101
110

"Faqat bittasi bo'lsa - ha". Bu chiziqli ajralmaydigan masala.

Python
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]])
Bu bo'limda ReLU emas, tanh

Yuqoridagi 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.

MasalaTavsiya
Kichik, kam neyronlitanh
Katta, ko'p qatlamliReLU

Bu amaliy qoida: kichik tarmoqlarda tanh ko'pincha barqarorroq ishlaydi.

Yashirin qatlamsiz - ishlamaydi #

Python
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()))
Natija
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.

Bu "sekin o'qish" emas - bu chegara

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:

BelgiSabab
Yo'qotish ln(2) da qotib qoldiModel quvvati yetmayapti
Yo'qotish sekin, lekin tushyaptiTezlik kichik yoki davrlar kam
Yo'qotish sakrayaptiTezlik katta

Birinchi holatda tezlikni yoki davrlar sonini oshirish hech nima bermaydi. Modelni kattalashtirish kerak.

Ikkita yashirin neyron - birinchi urinish #

Python
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()))
Natija
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:

ModelYo'qotishBashoratlar
[2, 1]0.6931 = ln(2)Hammasi 0.5
[2, 2, 1], urug' 30.3467Ikkitasi 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.

Bir xil arxitektura, boshqa natija

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.

Python
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()))
Natija
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 nima qiladi Asl fazo (x₁, x₂) yashil va oq diagonalda bitta chiziq ajrata olmaydi yashirin qatlam Yangi fazo (h₁, h₂) nuqtalar ko'chdi endi chiziq ajratadi Yashirin qatlam masalani yechmaydi - u masalani yechiladigan qilib qo'yadi. Chiqish qatlami esa yana o'sha oddiy chiziqni chizadi.
Chuqur o'qitishning butun g'oyasi shu bitta rasmda

Yashirin qatlam nimani o'rgandi #

Ishlagan tarmoqni (urug=0) ochib ko'ramiz.

Python
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}")
Natija
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?

Python
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()))
Natija
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.

Bu chuqur o'qitishning butun mohiyati

Har yashirin qatlam ma'lumotni yangi fazoga ko'chiradi, u yerda masala biroz osonroq bo'ladi. Oxirgi qatlam esa oddiy chiziq chizadi.

SohaYashirin qatlamlar nima o'rganadi
RasmChiziqlar → shakllar → obyekt qismlari → obyekt
MatnHarflar → so'zlar → iboralar → ma'no
OvozChastotalar → 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 #

Python
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")
Natija
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
Ikkita neyron nazariy minimum, amaliy emas

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 #

Python
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}")
Natija
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 #

Python
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()))
Natija
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.

Amaliy topshiriq
  1. XOR jadvalini yozing va nima uchun chiziqli ajralmasligini tushuntiring.
  2. [2, 1] tarmoqni 20000 davr o'qiting - natija nima?
  3. Yo'qotish nima uchun aynan ln(2) da qotib qolganini ayting.
  4. [2, 2, 1] tarmoqni o'qiting va aniqlikni tekshiring.
  5. Yashirin qatlam chiqishlarini chop eting.
  6. Yangi fazoda nuqtalar chiziq bilan ajralishini tasdiqlang.
  7. 1, 2, 3, 4 neyron bilan 10 urug'da sinang.
  8. Nima uchun uchta neyron ikkitadan ishonchliroq ekanini ayting.
  9. Beshta turli tezlikda natijani solishtiring.
  10. 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.

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.