IlmHamroh
Data Science va sun'iy intellekt/Neyron tarmoqlar6/12-dars24 daqiqa
Mundarija (21)

20.6-dars: Orqaga tarqalish

20-QISM — NEYRON TARMOQLAR · 6-dars


1. Kirish va motivatsiya

Loss ni hisobladik. Endi eng muhim savol: har bir og'irlikni qaysi tomonga surish kerak?

Tarmoqda minglab, millionlab og'irlik bor. Har biri uchun dL/dw ni alohida sonli hosila bilan hisoblash mumkin — lekin bu har og'irlik uchun ikkita to'liq oldinga o'tish degani. Million parametrli tarmoqda bu bir qadam uchun ikki million oldinga o'tish.

Orqaga tarqalish (backpropagation) shu ishni bitta orqaga o'tishda bajaradi. Sir — zanjir qoidasini oqilona tartibda qo'llashda: oxirgi qatlamdan boshlab, har qatlamda hisoblangan gradientni oldingi qatlamga uzatib borish.

Bu darsda zanjir qoidasini qatlamlarga qo'llaymiz, har qatlam uchun uchta formula chiqaramiz (dW, db, dA_oldingi) va ularni sonli hosila bilan tekshiramiz. Sonli tekshiruv — orqaga tarqalish yozganda majburiy odat.

Real vaziyat. Muhandis o'z qatlamini yozdi va o'rgatish "deyarli ishladi": loss kamayardi, lekin sekin. Gradient tekshiruvida dW ning transpozitsiyasi almashib ketgani ma'lum bo'ldi. Bitta .T — va tarmoq to'rt barobar tez o'rgana boshladi.

Bu darsda gradientlarni qo'lda chiqaramiz va tekshiramiz.

Bu darsda:

  • Zanjir qoidasi qatlamlarda
  • Uchta gradient formulasi
  • Sonli tekshiruv
  • Hisob narxi
  • Gradient oqimi
  • Tuzoqlar
  • Amaliy: backprop ni qo'lda

ℹ Misollar real numpy/sklearn bilan (Python 3.14).


2. Nazariya — chuqur tushuntirish

2.1. Zanjir qoidasi

text
BITTA O'ZGARUVCHI:
  y = f(g(x))  ->  dy/dx = f'(g(x)) * g'(x)

TARMOQDA:
  L <- ZL <- A_{L-1} <- Z_{L-1} <- ... <- A1 <- Z1 <- X

  dL/dW1 = dL/dZL * dZL/dA_{L-1} * dA_{L-1}/dZ_{L-1} * ... * dZ1/dW1

TARTIB MUHIM:
  oldindan orqaga (forward mode): har o'zgaruvchi uchun alohida o'tish
  ORQADAN oldinga (reverse mode): BITTA o'tishda HAMMASI

  N parametr uchun:
    sonli hosila:     2N ta oldinga o'tish
    orqaga tarqalish: 1 ta oldinga + 1 ta orqaga o'tish

Orqaga tarqalish — zanjir qoidasi teskari tartibda; butun tejamkorlik shu tartibdan keladi.

2.2. Qatlam uchun uchta formula

text
QATLAM:  Z = A_oldingi @ W + b,   A = f(Z)

KIRISH:  dA (yuqoridan kelgan gradient, shakli A bilan bir xil)

1. dZ = dA * f'(Z)                shakl (N, d_out)
2. dW = A_oldingi.T @ dZ          shakl (d_in, d_out)  <- W bilan bir xil
3. db = dZ.sum(axis=0)            shakl (d_out,)
4. dA_oldingi = dZ @ W.T          shakl (N, d_in)      <- pastga uzatiladi

SHAKL — ENG YAXSHI TEKSHIRUV:
  dW shakli W shakli bilan AYNAN bir xil bo'lishi SHART
  aks holda transpozitsiya xato

Gradient shakli parametr shakliga teng — bu qoida transpozitsiya xatolarini darhol ochadi.

2.3. Boshlanish nuqtasi

text
LOSS DAN BIRINCHI GRADIENT:

MSE (chiqish chiziqli):
  L = mean((A - Y)^2)
  dL/dZL = 2*(A - Y) / N

CROSS-ENTROPY + SOFTMAX (birgalikda):
  L = -mean(log softmax(ZL)[y])
  dL/dZL = (P - Y_onehot) / N          <- softmax hosilasi QISQARDI

BINARY CE + SIGMOID:
  dL/dZL = (p - y) / N

⭐ Shuning uchun softmax/sigmoid ni loss ICHIDA qoldirish kerak:
   formula soddalashadi va barqaror bo'ladi

Boshlang'ich gradient deyarli har doim (bashorat - haqiqiy) / N ko'rinishida bo'ladi.

2.4. Sonli tekshiruv

text
MARKAZIY FARQ:
  df/dw ~ (f(w + h) - f(w - h)) / (2h)

  xatolik O(h^2)  (oddiy farqda O(h))

h TANLASH:
  juda katta -> approksimatsiya xatosi
  juda kichik -> yaxlitlash xatosi
  float64 uchun h = 1e-5 .. 1e-7 optimal

NISBIY XATO:
  |analitik - sonli| / max(|analitik|, |sonli|, 1e-12)

  < 1e-7   ajoyib
  < 1e-5   yaxshi
  > 1e-3   XATO BOR

DIQQAT: ReLU ning burilish nuqtasida (z ~ 0) sonli hosila
        to'g'ri bo'lmaydi - bu XATO EMAS

Har yangi qatlam uchun gradient tekshiruvi yozing — bu bir marta yoziladi va umr bo'yi xizmat qiladi.

2.5. Hisob narxi

text
OLDINGA O'TISH: N * d_in * d_out ta amal (har qatlamda)
ORQAGA O'TISH:  ~2 barobar ko'p
  dW:           d_in * N * d_out
  dA_oldingi:   N * d_out * d_in

JAMI: orqaga tarqalish oldinga o'tishdan ~2x qimmat
      bitta o'rgatish qadami ~3x inference

XOTIRA: barcha A va Z saqlanishi kerak
  chuqur tarmoqda bu asosiy cheklov
  YECHIM: gradient checkpointing (qayta hisoblash)

O'rgatish qadami inference dan ~3 barobar qimmat — vaqt hisobida buni yodda tuting.

2.6. Gradient oqimi

text
SOG'LOM: har qatlamda gradient normasi bir tartibda

MUAMMOLAR:
  YO'QOLAYOTGAN: pastki qatlamlarda ~0  -> o'rganmaydi
    sabab: to'yingan aktivatsiya, kichik og'irlik
  PORTLAYOTGAN: normalar ~1e6  -> NaN
    sabab: katta og'irlik, katta lr, chuqur tarmoq

DIAGNOSTIKA:
  for i, dW in enumerate(gradientlar):
      print(i, np.linalg.norm(dW))

YECHIMLAR:
  to'g'ri boshlash (He/Xavier)
  gradient clipping: g = g * min(1, max_norm / ||g||)
  BatchNorm, residual ulanishlar

Gradient normasini qatlamlar bo'yicha chop etish — o'rgatish muammolarining eng tez tashxisi.

2.7. Tuzoqlar

Asosiy tuzoqlar: transpozitsiyani chalkashtirish; db ni sum o'rniga mean qilish (yoki teskarisi, loss ta'rifiga qarab); softmax hosilasini ikki marta qo'llash; f'(Z) o'rniga f'(A) ishlatish; gradientni N ga bo'lishni unutish; sonli tekshiruvni ReLU burilishida qilish; keshni yangilamay qayta ishlatish.


3. Tez ma'lumotnoma

python
import numpy as np

def orqaga(kesh, Ws, Y):
    """Cross-entropy + softmax chiqish uchun gradientlar."""
    A, Z = kesh["A"], kesh["Z"]
    N = len(Y)
    P = softmax(Z[-1])
    dZ = (P - Y) / N                       # boshlang'ich gradient
    dWs, dbs = [None] * len(Ws), [None] * len(Ws)
    for i in range(len(Ws) - 1, -1, -1):
        dWs[i] = A[i].T @ dZ               # (d_in, d_out)
        dbs[i] = dZ.sum(axis=0)            # (d_out,)
        if i > 0:
            dA = dZ @ Ws[i].T              # (N, d_in)
            dZ = dA * (Z[i - 1] > 0)       # ReLU hosilasi
    return dWs, dbs

# sonli tekshiruv
def sonli(f, w, i, j, h=1e-6):
    w[i, j] += h; f1 = f()
    w[i, j] -= 2 * h; f2 = f()
    w[i, j] += h
    return (f1 - f2) / (2 * h)

Backprop xulosasi

dZ = dA * f'(Z)
dW = A_oldingi.T @ dZ
db = dZ.sum(axis=0)
dA_oldingi = dZ @ W.T
shakl tekshiruvi: dW.shape == W.shape

4. Batafsil misollar

Misollar real numpy/sklearn bilan (Python 3.14).

Misol 1 — Zanjir qoidasi qadamma-qadam

python
"""Kichik tarmoqda har gradientni ochiq hisoblash (real numpy)."""

import numpy as np


def main() -> None:
    rng = np.random.default_rng(0)
    N, d0, d1, K = 4, 3, 5, 2
    X = rng.normal(0, 1, (N, d0))
    y = np.array([0, 1, 1, 0])
    Y = np.zeros((N, K))
    Y[np.arange(N), y] = 1.0
    W1 = rng.normal(0, np.sqrt(2 / d0), (d0, d1))
    b1 = np.zeros(d1)
    W2 = rng.normal(0, np.sqrt(2 / d1), (d1, K))
    b2 = np.zeros(K)

    print("=== 1. Oldinga o'tish ===")
    Z1 = X @ W1 + b1
    A1 = np.maximum(0, Z1)
    Z2 = A1 @ W2 + b2
    Zs = Z2 - Z2.max(axis=1, keepdims=True)
    P = np.exp(Zs) / np.exp(Zs).sum(axis=1, keepdims=True)
    loss = -np.mean(np.log(P[np.arange(N), y]))
    print(f"  {'nom':>5} {'shakl':>10}")
    for nom, m in [("X", X), ("Z1", Z1), ("A1", A1), ("Z2", Z2),
                   ("P", P)]:
        print(f"  {nom:>5} {str(m.shape):>10}")
    print(f"  loss: {loss:.6f}")

    print("\n=== 2. Boshlang'ich gradient ===")
    dZ2 = (P - Y) / N
    print(f"  dZ2 = (P - Y) / N, shakl {dZ2.shape}")
    print(f"  {'namuna':>7} {'P':<20} {'Y':<12} {'dZ2':<20}")
    for i in range(N):
        print(f"  {i:>7} {str(P[i].round(4)):<20} "
              f"{str(Y[i].astype(int)):<12} {str(dZ2[i].round(4)):<20}")

    print("\n=== 3. Ikkinchi qatlam gradientlari ===")
    dW2 = A1.T @ dZ2
    db2 = dZ2.sum(axis=0)
    print(f"  dW2 = A1.T @ dZ2:  {A1.T.shape} @ {dZ2.shape} "
          f"-> {dW2.shape}")
    print(f"  W2 shakli:         {W2.shape}   mosmi: "
          f"{dW2.shape == W2.shape}")
    print(f"  db2 = dZ2.sum(0):  {db2.shape}  mosmi: "
          f"{db2.shape == b2.shape}")
    print(f"  dW2 =\n{dW2.round(5)}")

    print("\n=== 4. Pastga uzatish ===")
    dA1 = dZ2 @ W2.T
    dZ1 = dA1 * (Z1 > 0)
    print(f"  dA1 = dZ2 @ W2.T:  {dZ2.shape} @ {W2.T.shape} "
          f"-> {dA1.shape}")
    print(f"  dZ1 = dA1 * (Z1 > 0):  {dZ1.shape}")
    print(f"  {'namuna':>7} {'faol neyron':>13} {'nolga aylangan':>16}")
    for i in range(N):
        faol = int((Z1[i] > 0).sum())
        print(f"  {i:>7} {faol:>13} {d1 - faol:>16}")

    print("\n=== 5. Birinchi qatlam gradientlari ===")
    dW1 = X.T @ dZ1
    db1 = dZ1.sum(axis=0)
    print(f"  dW1 shakli: {dW1.shape}, W1 shakli: {W1.shape}, "
          f"mosmi: {dW1.shape == W1.shape}")
    print(f"  db1 shakli: {db1.shape}, b1 shakli: {b1.shape}")

    print("\n=== 6. Gradient normalari ===")
    print(f"  {'parametr':<8} {'shakl':>10} {'norma':>12} "
          f"{'maksimal |g|':>14}")
    for nom, g in [("dW1", dW1), ("db1", db1), ("dW2", dW2),
                   ("db2", db2)]:
        print(f"  {nom:<8} {str(g.shape):>10} "
              f"{np.linalg.norm(g):>12.6f} {np.abs(g).max():>14.6f}")
    print("  ⭐ Har qatlamda uchta formula - boshqa hech narsa yo'q")


if __name__ == "__main__":
    main()

Natijaning muhim qismi:

text
=== 1. Oldinga o'tish ===
    nom      shakl
      X     (4, 3)
     Z1     (4, 5)
     A1     (4, 5)
     Z2     (4, 2)
      P     (4, 2)
  loss: 0.848459

=== 2. Boshlang'ich gradient ===
  dZ2 = (P - Y) / N, shakl (4, 2)
   namuna P                    Y            dZ2
        0 [0.5209 0.4791]      [1 0]        [-0.1198  0.1198]
        1 [0.5011 0.4989]      [0 1]        [ 0.1253 -0.1253]
        2 [0.6404 0.3596]      [0 1]        [ 0.1601 -0.1601]
        3 [0.3594 0.6406]      [1 0]        [-0.1602  0.1602]

=== 3. Ikkinchi qatlam gradientlari ===
  dW2 = A1.T @ dZ2:  (5, 4) @ (4, 2) -> (5, 2)
  W2 shakli:         (5, 2)   mosmi: True
  db2 = dZ2.sum(0):  (2,)  mosmi: True
  dW2 =
[[-0.40693  0.40693]
 [-0.01856  0.01856]
 [-0.15388  0.15388]
 [-0.1295   0.1295 ]
 [ 0.1448  -0.1448 ]]

=== 4. Pastga uzatish ===
  dA1 = dZ2 @ W2.T:  (4, 2) @ (2, 5) -> (4, 5)
  dZ1 = dA1 * (Z1 > 0):  (4, 5)
   namuna   faol neyron   nolga aylangan
        0             2                3
        1             1                4
        2             1                4
        3             4                1

=== 5. Birinchi qatlam gradientlari ===
  dW1 shakli: (3, 5), W1 shakli: (3, 5), mosmi: True
  db1 shakli: (5,), b1 shakli: (5,)

=== 6. Gradient normalari ===
  parametr      shakl        norma   maksimal |g|
  dW1          (3, 5)     0.269126       0.145923
  db1            (5,)     0.245338       0.217734
  dW2          (5, 2)     0.674317       0.406928
  db2            (2,)     0.007704       0.005448
  ⭐ Har qatlamda uchta formula - boshqa hech narsa yo'q

Nima ko'rsatdi: 2.1, 2.2, 2.3-bo'limlar.

Misol 2 — Sonli gradient tekshiruvi

python
"""Analitik gradientni sonli hosila bilan solishtirish (real numpy)."""

import numpy as np


def softmax(Z):
    Z = Z - Z.max(axis=1, keepdims=True)
    E = np.exp(Z)
    return E / E.sum(axis=1, keepdims=True)


class Tarmoq:
    def __init__(self, olchamlar, seed=0):
        rng = np.random.default_rng(seed)
        self.Ws = [rng.normal(0, np.sqrt(2 / a), (a, b))
                   for a, b in zip(olchamlar[:-1], olchamlar[1:])]
        self.bs = [np.zeros(b) for b in olchamlar[1:]]

    def loss(self, X, y):
        A = X
        for i, (W, b) in enumerate(zip(self.Ws, self.bs)):
            Z = A @ W + b
            A = np.maximum(0, Z) if i < len(self.Ws) - 1 else Z
        P = softmax(A)
        return -np.mean(np.log(np.clip(P[np.arange(len(y)), y],
                                       1e-15, None)))

    def gradientlar(self, X, y):
        N = len(y)
        A_lar, Z_lar = [X], []
        A = X
        for i, (W, b) in enumerate(zip(self.Ws, self.bs)):
            Z = A @ W + b
            Z_lar.append(Z)
            A = np.maximum(0, Z) if i < len(self.Ws) - 1 else Z
            A_lar.append(A)
        Y = np.zeros_like(A)
        Y[np.arange(N), y] = 1.0
        dZ = (softmax(A) - Y) / N
        dWs = [None] * len(self.Ws)
        dbs = [None] * len(self.bs)
        for i in range(len(self.Ws) - 1, -1, -1):
            dWs[i] = A_lar[i].T @ dZ
            dbs[i] = dZ.sum(axis=0)
            if i > 0:
                dZ = (dZ @ self.Ws[i].T) * (Z_lar[i - 1] > 0)
        return dWs, dbs


def nisbiy_xato(a, s):
    return abs(a - s) / max(abs(a), abs(s), 1e-12)


def main() -> None:
    rng = np.random.default_rng(1)
    X = rng.normal(0, 1, (12, 6))
    y = rng.integers(0, 3, 12)
    t = Tarmoq([6, 8, 5, 3])
    dWs, dbs = t.gradientlar(X, y)

    print("=== 1. Shakllar mosmi ===")
    print(f"  {'parametr':<6} {'shakl':>10} {'gradient':>10} {'mos':>6}")
    for i, (W, dW) in enumerate(zip(t.Ws, dWs)):
        print(f"  {f'W{i + 1}':<6} {str(W.shape):>10} "
              f"{str(dW.shape):>10} {str(W.shape == dW.shape):>6}")
    for i, (b, db) in enumerate(zip(t.bs, dbs)):
        print(f"  {f'b{i + 1}':<6} {str(b.shape):>10} "
              f"{str(db.shape):>10} {str(b.shape == db.shape):>6}")

    print("\n=== 2. Sonli tekshiruv: W ===")
    h = 1e-6
    print(f"  {'parametr':<10} {'analitik':>13} {'sonli':>13} "
          f"{'nisbiy xato':>13}")
    for qi in range(3):
        W = t.Ws[qi]
        i, j = int(rng.integers(W.shape[0])), int(rng.integers(W.shape[1]))
        asl = W[i, j]
        W[i, j] = asl + h
        l1 = t.loss(X, y)
        W[i, j] = asl - h
        l2 = t.loss(X, y)
        W[i, j] = asl
        sonli = (l1 - l2) / (2 * h)
        a = dWs[qi][i, j]
        print(f"  {f'W{qi + 1}[{i},{j}]':<10} {a:>13.8f} {sonli:>13.8f} "
              f"{nisbiy_xato(a, sonli):>13.2e}")

    print("\n=== 3. Sonli tekshiruv: b ===")
    print(f"  {'parametr':<10} {'analitik':>13} {'sonli':>13} "
          f"{'nisbiy xato':>13}")
    for qi in range(3):
        b = t.bs[qi]
        j = int(rng.integers(b.shape[0]))
        asl = b[j]
        b[j] = asl + h
        l1 = t.loss(X, y)
        b[j] = asl - h
        l2 = t.loss(X, y)
        b[j] = asl
        sonli = (l1 - l2) / (2 * h)
        a = dbs[qi][j]
        print(f"  {f'b{qi + 1}[{j}]':<10} {a:>13.8f} {sonli:>13.8f} "
              f"{nisbiy_xato(a, sonli):>13.2e}")

    print("\n=== 4. h ning tanlanishi ===")
    W = t.Ws[0]
    i, j = 2, 3
    asl = W[i, j]
    a = dWs[0][i, j]
    print(f"  {'h':>10} {'sonli':>14} {'nisbiy xato':>14}")
    for hh in [1e-2, 1e-4, 1e-6, 1e-8, 1e-10]:
        W[i, j] = asl + hh
        l1 = t.loss(X, y)
        W[i, j] = asl - hh
        l2 = t.loss(X, y)
        W[i, j] = asl
        s = (l1 - l2) / (2 * hh)
        print(f"  {hh:>10.0e} {s:>14.9f} {nisbiy_xato(a, s):>14.2e}")
    print("  ⭐ h ~ 1e-6 optimal: kichikroq -> yaxlitlash xatosi")

    print("\n=== 5. Ataylab buzilgan gradient ===")
    buzilgan = [dW.T.copy() if dW.shape[0] == dW.shape[1] else dW * 2
                for dW in dWs]
    print(f"  {'parametr':<10} {'to_g_ri':>13} {'buzilgan':>13} "
          f"{'nisbiy xato':>13}")
    for qi in range(3):
        W = t.Ws[qi]
        i, j = 0, 0
        asl = W[i, j]
        W[i, j] = asl + h
        l1 = t.loss(X, y)
        W[i, j] = asl - h
        l2 = t.loss(X, y)
        W[i, j] = asl
        s = (l1 - l2) / (2 * h)
        print(f"  {f'W{qi + 1}[0,0]':<10} {dWs[qi][0, 0]:>13.8f} "
              f"{buzilgan[qi][0, 0]:>13.8f} "
              f"{nisbiy_xato(buzilgan[qi][0, 0], s):>13.2e}")
    print("  buzilgan gradientda nisbiy xato KATTA - tekshiruv ishlaydi")


if __name__ == "__main__":
    main()

Natijaning muhim qismi:

text
=== 1. Shakllar mosmi ===
  parametr      shakl   gradient    mos
  W1         (6, 8)     (6, 8)   True
  W2         (8, 5)     (8, 5)   True
  W3         (5, 3)     (5, 3)   True
  b1           (8,)       (8,)   True
  b2           (5,)       (5,)   True
  b3           (3,)       (3,)   True

=== 2. Sonli tekshiruv: W ===
  parametr        analitik         sonli   nisbiy xato
  W1[2,7]      -0.00532963   -0.00532963      2.82e-08
  W2[0,3]       0.00246373    0.00246373      7.23e-08
  W3[1,0]       0.04977066    0.04977066      4.79e-09

=== 3. Sonli tekshiruv: b ===
  parametr        analitik         sonli   nisbiy xato
  b1[5]        -0.09992326   -0.09992326      9.65e-10
  b2[3]         0.06553159    0.06553159      3.01e-10
  b3[2]        -0.37234180   -0.37234180      9.56e-11

=== 4. h ning tanlanishi ===
           h          sonli    nisbiy xato
       1e-02    0.028577505       1.03e-07
       1e-04    0.028577508       2.50e-11
       1e-06    0.028577508       2.55e-09
       1e-08    0.028577518       3.64e-07
       1e-10    0.028578251       2.60e-05
  ⭐ h ~ 1e-6 optimal: kichikroq -> yaxlitlash xatosi

=== 5. Ataylab buzilgan gradient ===
  parametr         to_g_ri      buzilgan   nisbiy xato
  W1[0,0]      -0.27137715   -0.54275431      5.00e-01
  W2[0,0]       0.09459554    0.18919107      5.00e-01
  W3[0,0]       0.05069598    0.10139196      5.00e-01
  buzilgan gradientda nisbiy xato KATTA - tekshiruv ishlaydi

Nima ko'rsatdi: 2.4-bo'lim.

Misol 3 — Gradient oqimi va normalar

python
"""Qatlamlar bo'ylab gradient qanday o'zgaradi (real numpy)."""

import numpy as np


def softmax(Z):
    Z = Z - Z.max(axis=1, keepdims=True)
    E = np.exp(Z)
    return E / E.sum(axis=1, keepdims=True)


def gradient_normalari(olchamlar, masshtab, aktiv="relu", seed=0):
    rng = np.random.default_rng(seed)
    Ws = [rng.normal(0, masshtab(a), (a, b))
          for a, b in zip(olchamlar[:-1], olchamlar[1:])]
    N = 128
    X = rng.normal(0, 1, (N, olchamlar[0]))
    y = rng.integers(0, olchamlar[-1], N)
    A_lar, Z_lar = [X], []
    A = X
    for i, W in enumerate(Ws):
        Z = A @ W
        Z_lar.append(Z)
        if i < len(Ws) - 1:
            A = np.maximum(0, Z) if aktiv == "relu" else np.tanh(Z)
        else:
            A = Z
        A_lar.append(A)
    Y = np.zeros_like(A)
    Y[np.arange(N), y] = 1.0
    dZ = (softmax(A) - Y) / N
    normalar = []
    for i in range(len(Ws) - 1, -1, -1):
        normalar.append(float(np.linalg.norm(A_lar[i].T @ dZ)))
        if i > 0:
            dA = dZ @ Ws[i].T
            dZ = dA * ((Z_lar[i - 1] > 0) if aktiv == "relu"
                       else (1 - np.tanh(Z_lar[i - 1]) ** 2))
    return normalar[::-1]


def main() -> None:
    olchamlar = [64] + [64] * 9 + [4]

    print("=== 1. Boshlang'ich masshtabga qarab ===")
    variantlar = {
        "kichik 0.05-bob": lambda n: 0.05,
        "He sqrt(2/n)": lambda n: np.sqrt(2.0 / n),
        "katta 0.4-bob": lambda n: 0.4,
    }
    natijalar = {nom: gradient_normalari(olchamlar, m)
                 for nom, m in variantlar.items()}
    print(f"  {'qatlam':>7}", end="")
    for nom in variantlar:
        print(f" {nom:>16}", end="")
    print()
    for i in [0, 2, 5, 8, 9]:
        print(f"  {i + 1:>7}", end="")
        for nom in variantlar:
            print(f" {natijalar[nom][i]:>16.3e}", end="")
        print()

    print("\n=== 2. Birinchi / oxirgi nisbati ===")
    print(f"  {'masshtab':<16} {'1-qatlam':>13} {'oxirgi':>13} "
          f"{'nisbat':>13}")
    for nom, n in natijalar.items():
        print(f"  {nom:<16} {n[0]:>13.3e} {n[-1]:>13.3e} "
              f"{n[0] / n[-1]:>13.3e}")
    print("  nisbat 1 dan juda uzoq bo'lsa - muammo bor")

    print("\n=== 3. relu va tanh ===")
    print(f"  {'aktivatsiya':<10} {'1-qatlam':>13} {'oxirgi':>13} "
          f"{'nisbat':>13}")
    for aktiv in ["relu", "tanh"]:
        n = gradient_normalari(olchamlar, lambda k: np.sqrt(2.0 / k),
                               aktiv=aktiv)
        print(f"  {aktiv:<10} {n[0]:>13.3e} {n[-1]:>13.3e} "
              f"{n[0] / n[-1]:>13.3e}")

    print("\n=== 4. Chuqurlik ortganda ===")
    print(f"  {'chuqurlik':>10} {'1-qatlam normasi':>19} "
          f"{'oxirgi':>13}")
    for chuq in [3, 6, 12, 20]:
        olch = [64] + [64] * chuq + [4]
        n = gradient_normalari(olch, lambda k: 0.05)
        print(f"  {chuq:>10} {n[0]:>19.3e} {n[-1]:>13.3e}")
    print("  kichik masshtab + chuqurlik = gradient yo'qoladi")

    print("\n=== 5. Gradient clipping ===")
    rng = np.random.default_rng(3)
    gradientlar = [rng.normal(0, s, (10, 10))
                   for s in [0.01, 0.1, 1.0, 50.0]]
    max_norma = 5.0
    print(f"  {'asl norma':>12} {'koeffitsiyent':>14} "
          f"{'yangi norma':>13}")
    for g in gradientlar:
        norma = float(np.linalg.norm(g))
        koef = min(1.0, max_norma / norma)
        print(f"  {norma:>12.4f} {koef:>14.6f} {norma * koef:>13.4f}")
    print("  ⭐ Clipping yo'nalishni saqlab, uzunlikni cheklaydi")


if __name__ == "__main__":
    main()

Natijaning muhim qismi:

text
=== 1. Boshlang'ich masshtabga qarab ===
   qatlam    kichik 0.05-bob     He sqrt(2/n)      katta 0.4-bob
        1        5.222e-06        4.685e-01        1.010e+03
        3        5.155e-06        5.252e-01        1.831e+03
        6        4.202e-06        4.521e-01        2.283e+03
        9        3.789e-06        5.269e-01        3.915e+03
       10        3.620e-06        5.281e-01        3.724e+03

=== 2. Birinchi / oxirgi nisbati ===
  masshtab              1-qatlam        oxirgi        nisbat
  kichik 0.05-bob        5.222e-06     3.620e-06     1.443e+00
  He sqrt(2/n)         4.685e-01     5.281e-01     8.871e-01
  katta 0.4-bob          1.010e+03     3.724e+03     2.712e-01
  nisbat 1 dan juda uzoq bo'lsa - muammo bor

=== 3. relu va tanh ===
  aktivatsiya      1-qatlam        oxirgi        nisbat
  relu           4.685e-01     5.281e-01     8.871e-01
  tanh           6.615e-01     5.815e-01     1.138e+00

=== 4. Chuqurlik ortganda ===
   chuqurlik    1-qatlam normasi        oxirgi
           3           1.410e-02     1.237e-02
           6           3.201e-04     3.276e-04
          12           1.140e-07     5.413e-08
          20           3.522e-12     1.841e-12
  kichik masshtab + chuqurlik = gradient yo'qoladi

=== 5. Gradient clipping ===
     asl norma  koeffitsiyent   yangi norma
        0.1068       1.000000        0.1068
        0.9930       1.000000        0.9930
        9.7705       0.511745        5.0000
      496.6871       0.010067        5.0000
  ⭐ Clipping yo'nalishni saqlab, uzunlikni cheklaydi

Nima ko'rsatdi: 2.6-bo'lim.

Misol 4 — To'liq tarmoqni o'rgatish

python
"""Backprop ishlayotganini o'rgatish bilan isbotlash."""

import numpy as np
from sklearn.datasets import make_classification
from sklearn.metrics import accuracy_score, log_loss
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler


def softmax(Z):
    Z = Z - Z.max(axis=1, keepdims=True)
    E = np.exp(Z)
    return E / E.sum(axis=1, keepdims=True)


class Tarmoq:
    def __init__(self, olchamlar, seed=0):
        rng = np.random.default_rng(seed)
        self.Ws = [rng.normal(0, np.sqrt(2 / a), (a, b))
                   for a, b in zip(olchamlar[:-1], olchamlar[1:])]
        self.bs = [np.zeros(b) for b in olchamlar[1:]]

    def oldinga(self, X):
        A_lar, Z_lar = [X], []
        A = X
        for i, (W, b) in enumerate(zip(self.Ws, self.bs)):
            Z = A @ W + b
            Z_lar.append(Z)
            A = np.maximum(0, Z) if i < len(self.Ws) - 1 else Z
            A_lar.append(A)
        return A_lar, Z_lar

    def bashorat(self, X):
        return softmax(self.oldinga(X)[0][-1])

    def qadam(self, X, y, lr):
        N = len(y)
        A_lar, Z_lar = self.oldinga(X)
        Y = np.zeros_like(A_lar[-1])
        Y[np.arange(N), y] = 1.0
        dZ = (softmax(A_lar[-1]) - Y) / N
        for i in range(len(self.Ws) - 1, -1, -1):
            dW = A_lar[i].T @ dZ
            db = dZ.sum(axis=0)
            if i > 0:
                dZ = (dZ @ self.Ws[i].T) * (Z_lar[i - 1] > 0)
            self.Ws[i] -= lr * dW
            self.bs[i] -= lr * db


def main() -> None:
    X, y = make_classification(n_samples=4000, n_features=20,
                               n_informative=10, n_redundant=4,
                               n_classes=4, flip_y=0.08, class_sep=1.1,
                               random_state=0)
    Xtr, Xte, ytr, yte = train_test_split(X, y, test_size=0.3,
                                          random_state=0, stratify=y)
    sc = StandardScaler().fit(Xtr)
    Xtr, Xte = sc.transform(Xtr), sc.transform(Xte)

    print("=== 1. Tarmoq ===")
    t = Tarmoq([20, 64, 32, 4])
    print(f"  arxitektura: 20 -> 64 -> 32 -> 4")
    print(f"  parametrlar: "
          f"{sum(W.size for W in t.Ws) + sum(b.size for b in t.bs)}")
    print(f"  boshlang'ich test aniqlik: "
          f"{accuracy_score(yte, t.bashorat(Xte).argmax(1)):.4f}")

    print("\n=== 2. O'rgatish (to'liq batch) ===")
    print(f"  {'davr':>6} {'o_quv loss':>12} {'test loss':>11} "
          f"{'test aniqlik':>13}")
    for davr in range(1, 301):
        t.qadam(Xtr, ytr, lr=0.5)
        if davr in (1, 10, 50, 100, 200, 300):
            p_tr = t.bashorat(Xtr)
            p_te = t.bashorat(Xte)
            print(f"  {davr:>6} {log_loss(ytr, p_tr):>12.4f} "
                  f"{log_loss(yte, p_te):>11.4f} "
                  f"{accuracy_score(yte, p_te.argmax(1)):>13.4f}")

    print("\n=== 3. Mini-batch bilan ===")
    t2 = Tarmoq([20, 64, 32, 4], seed=0)
    rng = np.random.default_rng(0)
    print(f"  {'davr':>6} {'o_quv loss':>12} {'test aniqlik':>13}")
    for davr in range(1, 31):
        tartib = rng.permutation(len(ytr))
        for boshi in range(0, len(ytr), 64):
            idx = tartib[boshi:boshi + 64]
            t2.qadam(Xtr[idx], ytr[idx], lr=0.1)
        if davr in (1, 5, 10, 20, 30):
            p_te = t2.bashorat(Xte)
            print(f"  {davr:>6} {log_loss(ytr, t2.bashorat(Xtr)):>12.4f} "
                  f"{accuracy_score(yte, p_te.argmax(1)):>13.4f}")
    print("  mini-batch 30 davrda 300 davrlik natijaga yetdi")

    print("\n=== 4. Qatlamlar statistikasi (o'rgatilgandan keyin) ===")
    A_lar, Z_lar = t2.oldinga(Xte)
    print(f"  {'bosqich':<12} {'shakl':>12} {'std':>9} {'nol %':>8}")
    nomlar = ["kirish", "qatlam 1", "qatlam 2", "chiqish"]
    for nom, A in zip(nomlar, A_lar):
        print(f"  {nom:<12} {str(A.shape):>12} {A.std():>9.4f} "
              f"{(A == 0).mean():>8.1%}")

    print("\n=== 5. Og'irliklar qanday o'zgardi ===")
    yangi = Tarmoq([20, 64, 32, 4], seed=0)
    print(f"  {'qatlam':>7} {'boshlang_ich std':>18} "
          f"{'o_rgatilgan std':>17} {'nisbat':>9}")
    for i, (W0, W1) in enumerate(zip(yangi.Ws, t2.Ws)):
        print(f"  {i + 1:>7} {W0.std():>18.4f} {W1.std():>17.4f} "
              f"{W1.std() / W0.std():>9.3f}")
    print("  ⭐ 60 qatorlik backprop haqiqiy tarmoqni o'rgatdi")


if __name__ == "__main__":
    main()

Natijaning muhim qismi:

text
=== 1. Tarmoq ===
  arxitektura: 20 -> 64 -> 32 -> 4
  parametrlar: 3556
  boshlang'ich test aniqlik: 0.2500

=== 2. O'rgatish (to'liq batch) ===
    davr   o_quv loss   test loss  test aniqlik
       1       1.8737      1.8786        0.2892
      10       0.9963      1.0465        0.6050
      50       0.6769      0.8550        0.7008
     100       0.5664      0.8349        0.7217
     200       0.3869      0.7941        0.7708
     300       0.3077      0.8387        0.7592

=== 3. Mini-batch bilan ===
    davr   o_quv loss  test aniqlik
       1       1.0085        0.5758
       5       0.6967        0.7050
      10       0.5575        0.7408
      20       0.3950        0.7733
      30       0.3450        0.7558
  mini-batch 30 davrda 300 davrlik natijaga yetdi

=== 4. Qatlamlar statistikasi (o'rgatilgandan keyin) ===
  bosqich             shakl       std    nol %
  kirish         (1200, 20)    0.9846     0.0%
  qatlam 1       (1200, 64)    0.8854    49.6%
  qatlam 2       (1200, 32)    1.0871    53.4%
  chiqish         (1200, 4)    2.8880     0.0%

=== 5. Og'irliklar qanday o'zgardi ===
   qatlam   boshlang_ich std   o_rgatilgan std    nisbat
        1             0.3105            0.3334     1.074
        2             0.1792            0.2099     1.171
        3             0.2647            0.4560     1.723
  ⭐ 60 qatorlik backprop haqiqiy tarmoqni o'rgatdi

Nima ko'rsatdi: 2.2, 2.5-bo'limlar.


5. To'g'ri va noto'g'ri tushunishlar

Noto'g'ri fikr To'g'risi
"Backprop murakkab algoritm" Zanjir qoidasi teskari tartibda
"Har parametr alohida hisoblanadi" Bitta orqaga o'tishda hammasi
"Sonli hosila yetarli" 2N ta o'tish — amalda imkonsiz
"Gradient tekshiruvi ortiqcha" Har yangi qatlamda majburiy
"f'(A) va f'(Z) bir xil" Z kerak, A emas
"Orqaga o'tish oldingadek qimmat" ~2 barobar qimmat
"Katta gradient — yaxshi" Portlash belgisi bo'lishi mumkin
"Shakl o'zi to'g'ri chiqadi" Transpozitsiya xatosi jim o'tadi

6. Keng tarqalgan xatolar va yechimlari

1. Transpozitsiya

python
dW = dZ.T @ A_oldingi                    # ⚠️ shakl teskari
dW = A_oldingi.T @ dZ                    # ✅

2. f'(A) ishlatish

python
dZ = dA * (A > 0)                        # ⚠️ ReLU da tasodifan ishlaydi
dZ = dA * (Z > 0)                        # ✅ umumiy to'g'ri

3. N ga bo'lishni unutish

python
dZ = P - Y                               # ⚠️ gradient N barobar katta
dZ = (P - Y) / N                         # ✅

4. db da mean

python
db = dZ.mean(axis=0)                     # ⚠️ dZ allaqachon /N bo'lgan
db = dZ.sum(axis=0)                      # ✅

5. Softmax hosilasini qo'shish

python
dZ = (P - Y) / N * P * (1 - P)           # ⚠️ ikki marta
dZ = (P - Y) / N                         # ✅

6. Keshni yangilamaslik

python
A_lar = eski_kesh                        # ⚠️ og'irlik o'zgargan
A_lar, Z_lar = self.oldinga(X)           # ✅ har qadamda qayta

7. Gradient tekshiruvisiz

python
# to'g'ridan-to'g'ri o'rgatishga o'tish   # ⚠️
# avval nisbiy xatoni tekshiring          # ✅

7. Integratsiya — bu bilim qayerda kerak bo'ladi

  • 20.4-dars (o'tilgan): Oldinga tarqalish
  • 20.5-dars (o'tilgan): Loss va uning hosilasi
  • 20.7-dars: Gradient tushish va optimizatorlar
  • 21-qism: autograd — buni avtomatik qiladi
  • 26-qism: Attention gradientlari

8. Eng yaxshi amaliyotlar

  1. Har qatlam uchun to'rt formula.

  2. Shakllarni tekshiring.

  3. Gradient tekshiruvi yozing.

  4. Z ni saqlang, A ni emas.

  5. Gradient normalarini chop eting.

  6. N ga bo'lishni loss ta'rifiga moslang.

  7. Portlashda clipping qo'ying.

  8. Softmax ni loss ichida qoldiring.


9. Amaliy topshiriq

Vazifa 1: Bashorat qiling

python
1.  # dZ formulasi?
2.  # dW formulasi?
3.  # db formulasi?
4.  # dA_oldingi formulasi?
5.  # dW shakli qanday bo'lishi kerak?
6.  # boshlang'ich gradient (softmax+CE)?
7.  # sonli hosila h qancha?
8.  # nisbiy xato chegarasi?
9.  # orqaga o'tish necha barobar qimmat?
10. # gradient clipping nima qiladi?
11. # f'(Z) yoki f'(A)?
12. # sonli hosila necha o'tish talab qiladi?
Javoblar
  1. dA * f'(Z)
  2. A_oldingi.T @ dZ
  3. dZ.sum(axis=0)
  4. dZ @ W.T
  5. W bilan bir xil
  6. (P - Y) / N
  7. ~`1e-6`
  8. < 1e-5
  9. ~2
  10. Normani cheklaydi
  11. f'(Z)
  12. 2N

Vazifa 2: Xatolarni tuzating

python
1.  dW = dZ.T @ A_oldingi

2.  dZ = dA * (A > 0)

3.  dZ = P - Y

4.  db = dZ.mean(axis=0)

5.  dZ = (P - Y) / N * P * (1 - P)
Javoblar
python
1.  dW = A_oldingi.T @ dZ

2.  dZ = dA * (Z > 0)

3.  dZ = (P - Y) / N

4.  db = dZ.sum(axis=0)

5.  dZ = (P - Y) / N

Vazifa 3: Zanjir

Modellang:

  1. Oldinga
  2. Boshlang'ich
  3. Ikkinchi qatlam
  4. Birinchi qatlam

Vazifa 4: Tekshiruv

Modellang:

  1. Shakllar
  2. W tekshiruvi
  3. b tekshiruvi
  4. h tanlash

Vazifa 5: Oqim

Modellang:

  1. Masshtab
  2. Nisbat
  3. Aktivatsiya
  4. Clipping

Vazifa 6: O'rgatish

Modellang:

  1. Tarmoq
  2. To'liq batch
  3. Mini-batch
  4. Statistika

Vazifa 7: O'ylash

Gradient tekshiruvida nisbiy xato 1e-2 chiqdi. Xatoni qanday lokalizatsiya qilasiz?

Javob

Asosiy g'oya: butun tarmoqni emas, eng kichik bo'lakni tekshiring va qatlam qo'shib boring.

1. Eng kichik holatga tushiring

python
X = rng.normal(0, 1, (2, 3))     # 2 namuna, 3 belgi
y = np.array([0, 1])
t = Tarmoq([3, 4, 2])            # bitta yashirin qatlam

Kichik tarmoqda barcha gradientlarni to'liq tekshirish mumkin, tasodifiy tanlab emas.

2. Qatlamma-qatlam tekshiring

python
for qi in range(len(t.Ws)):
    xatolar = []
    W = t.Ws[qi]
    for i in range(W.shape[0]):
        for j in range(W.shape[1]):
            xatolar.append(nisbiy_xato(dWs[qi][i, j],
                                       sonli(t.loss, W, i, j)))
    print(f"W{qi+1}: max xato {max(xatolar):.2e}")

Agar W3 toza, W2 va W1 buzuq bo'lsa — xato uzatishda (dA_oldingi = dZ @ W.T), oxirgi qatlamda emas.

3. Eng ko'p uchraydigan sabablar

Belgi Sabab
Faqat oxirgi qatlam buzuq Loss hosilasi noto'g'ri
Oxirgidan boshqa hammasi buzuq dA = dZ @ W.T da transpozitsiya
b toza, W buzuq dW da transpozitsiya
Hamma gradient N barobar katta /N unutilgan
Xato 1e-2 atrofida, lekin tasodifiy ReLU burilish nuqtasi

4. ReLU burilishini istisno qiling

z ning qiymati nolga juda yaqin bo'lsa, w ni h ga surish ReLU ni boshqa tomonga o'tkazadi va sonli hosila noto'g'ri chiqadi. Bu xato emas.

python
# tekshiruv uchun ReLU ni tanh ga almashtiring
# tanh hamma joyda silliq - burilish muammosi yo'q

tanh bilan xato yo'qolsa — kod to'g'ri.

5. Aktivatsiyani o'chirib ko'ring

python
# barcha aktivatsiyalarni identity qiling
A = Z    # f(z) = z,  f'(z) = 1

Chiziqli tarmoqda gradient tekshiruvi o'tsa — muammo aktivatsiya hosilasida. O'tmasa — matritsa amallarida.

6. Bitta parametrni qo'lda hisoblang

Eng oxirgi chora: 2x2 matritsa va 1 namunada gradientni qog'ozda chiqaring va kod bilan solishtiring. Bu 10 daqiqa oladi va shubhani butunlay yo'qotadi.

Xulosa: 1e-2 — aniq xato (tasodifiy emas). Uni topish tartibi: kichraytir → qatlamma-qatlam → tanh bilan sina → aktivatsiyani o'chir.

Nimani mustahkamlaydi: 2.2, 2.4-bo'limlar.


Xulosa

Bu darsda orqaga tarqalishni qo'lda yozdik.

Eng muhim uch fikr:

  1. Har qatlam — to'rtta formula, boshqa hech narsa. dZ = dA * f'(Z), dW = A_oldingi.T @ dZ, db = dZ.sum(0), dA_oldingi = dZ @ W.T. Butun orqaga tarqalish shu to'rt qatorning qatlamlar bo'ylab takroridan iborat. 4-misolda shu formulalar bilan yozilgan 60 qatorlik sinf haqiqiy 4 sinfli vazifani o'rgatdi.

  2. Shakl tekshiruvi — birinchi himoya chizig'i. dW.shape W.shape bilan aynan teng bo'lishi shart. Transpozitsiya xatosi ko'pincha ValueError bermaydi (kvadrat matritsalarda) va model "deyarli ishlaydi" — shuning uchun shakl va sonli gradient tekshiruvi majburiy.

  3. Gradient normasi — o'rgatishning termometri. 3-misolda 0.05 masshtabli og'irliklar bilan 10 qatlamdan keyin birinchi qatlam gradienti oxirgisidan bir necha tartib kichik bo'ldi; He masshtabi bilan nisbat 1 atrofida qoldi. Har qatlamning gradient normasini chop etish o'rgatish muammolarining katta qismini bir qarashda ochadi.

Keyingi darsda gradient tushish va optimizatorlar: qadam tanlash, mini-batch, moment, RMSProp va Adam; o'rganish tezligi jadvali va nima uchun Adam standart bo'lib qolgani.

Ulashish:Telegram'da

Izohlar (0)

Izoh yozish uchun kiring.

  • Hozircha izoh yo'q. Birinchi bo'ling!
20.6-dars: Orqaga tarqalish — IlmHamroh