Sıradaki

Yoğun katman geri geçişi

Zorluk: Zor

sinir ağları

Bir yoğun katman (dense layer), NN örneklik (sample) bir yığın (batch) X∈RN×dinX \in \mathbb{R}^{N \times d_{\text{in}}} için çıktıyı şöyle hesaplar:

Z=XW+bZ = XW + \mathbf{b}

Burada:

  • W∈Rdin×doutW \in \mathbb{R}^{d_{\text{in}} \times d_{\text{out}}}: ağırlık matrisi (weight matrix).
  • b∈Rdout\mathbf{b} \in \mathbb{R}^{d_{\text{out}}}: sapma vektörü (bias). Her satıra eklenir.

Geri geçişte (backward pass) layer, kaybın (loss) kendi çıktısına göre gradyanını (gradient) bir sonraki layer’dan alır:

dZ=∂L∂ZdZ = \frac{\partial L}{\partial Z}

dZdZ‘nin şekli (shape) (N,dout)(N, d_{\text{out}})‘dur.

dense_backward(X, W, dZ) fonksiyonunu yaz. Zincir kuralıyla (chain rule), LL‘nin layer’ın girdisine, ağırlıklarına ve sapmasına göre gradyanlarını hesapla:

dX=∂L∂X,dW=∂L∂W,db=∂L∂bdX = \frac{\partial L}{\partial X}, \qquad dW = \frac{\partial L}{\partial W}, \qquad d\mathbf{b} = \frac{\partial L}{\partial \mathbf{b}}

Her gradyanın shape’i, türevi alınan şeyin shape’iyle aynıdır. Sapmanın değeri gradyanlara girmediği için fonksiyon b\mathbf{b}‘yi almaz.

(dX, dW, db) demetini (tuple) döndür.

Örnekler

Girdi: X = [[1.0, 2.0], [3.0, 4.0], [5.0, 6.0]], W = [[1.0, 0.0, -1.0, 2.0], [0.5, 1.0, 0.0, -2.0]], dZ = [[1.0, 0.0, 0.0, 1.0], [0.0, 1.0, 0.0, -1.0], [2.0, 0.0, 1.0, 0.0]]
Çıktı: (array([[ 3. , -1.5], [-2. , 3. ], [ 1. , 1. ]]), array([[11., 3., 5., -2.], [14., 4., 6., -2.]]), array([3., 1., 1., 0.]))

Girdi: X = [[2.0, -1.0, 0.5]], W = [[1.0, 0.0], [0.0, 1.0], [1.0, -1.0]], dZ = [[1.0, -2.0]]
Çıktı: (array([[ 1., -2., 3.]]), array([[ 2. , -4. ], [-1. , 2. ], [ 0.5, -1. ]]), array([ 1., -2.]))

Girdi: X = [[-1.0, 2.0], [0.5, -3.0]], W = [[1.5, -0.5], [2.0, 1.0]], dZ = [[-1.0, 0.5], [2.0, -2.0]]
Çıktı: (array([[-1.75, -1.5 ], [ 4. , 2. ]]), array([[ 2. , -1.5], [-8. , 7. ]]), array([ 1. , -1.5]))

İpucu 1

Shape’ler hangi matris çarpımının geçerli olduğunu söyler. dXdX‘in shape’i XX‘in shape’iyle, yani (N,din)(N, d_{\text{in}}) ile aynıdır.

Elinde iki matris var:

  • dZdZ: (N,dout)(N, d_{\text{out}}) shape’inde.
  • WW: (din,dout)(d_{\text{in}}, d_{\text{out}}) shape’inde.

Bu ikisinden (N,din)(N, d_{\text{in}}) çıkaran bir matris çarpımı için birinin devriği (transpose) gerekir. Aynı bulmacayı dWdW için de çöz: elinde XX ve dZdZ var.

İpucu 2

WkjW_{kj} yalnızca ZZ‘nin jj. sütununu etkiler. Bunu ZZ‘nin tanımında görürsün:

Zij=∑kXikWkj+bjZ_{ij} = \sum_k X_{ik} W_{kj} + b_j

Her ii satırında XikX_{ik} bu etkiyi ölçekler. Bu yüzden:

dWkj=∑iXik dZijdW_{kj} = \sum_i X_{ik}\, dZ_{ij}

Bu toplam bir matris çarpımının tanımıdır.

İpucu 3

dbjdb_j, dZdZ‘nin jj. sütunundaki değerlerin toplamıdır. Çünkü bjb_j, ZZ‘nin jj. sütununun her satırına eklenir ve yerel türevi her yerde 11‘dir. Hangi eksen (axis) boyunca toplayacağını (d_out,) shape’i söyler.

Gereksinimler

  • X (n, d_in), W (d_in, d_out) ve dZ (n, d_out) shape’inde array’lerdir.
  • Dönüş değeri, sırasıyla (n, d_in), (d_in, d_out) ve (d_out,) shape’inde üç NumPy array’inden oluşan bir tuple’dır: (dX, dW, db). Liste değil.
  • db, (d_out,) shape’inde bir vektördür. (n,) ya da (1, d_out) değil.
  • dZ zaten kaybın gradyanıdır. Sonuçları n’ye bölme ya da başka bir sayıyla ölçekleme.
  • X’i, W’yi ve dZ’yi değiştirme. Fonksiyon bittiğinde çağıran kodun array’leri aynı kalmalı.
  • Örnekler üzerinde döngü kullanma.

Geri geçişte her layer iki iş yapar.

  • Kendi parametrelerinin gradyanını hesaplar.
  • Gradyanı bir önceki layer’a iletir.

İkisi de zincir kuralından çıkar. Tek bir örnekle başlayalım: x=(2,−1)\mathbf{x} = (2, -1) ve dz=(3,4,5)d\mathbf{z} = (3, 4, 5) olsun.

  1. WkjW_{kj} ağırlığı çıktının jj. elemanına xkx_k ile çarpılarak katılır. Bu yüzden dWkj=xk⋅dzjdW_{kj} = x_k \cdot dz_j olur.
  2. dWdW, x\mathbf{x} ile dzd\mathbf{z} vektörlerinin dış çarpımıdır (outer product): ilk satır (6,8,10)(6, 8, 10), ikinci satır (−3,−4,−5)(-3, -4, -5).
  3. bjb_j doğrudan çıktının jj. elemanına eklenir. Bu yüzden db=dzd\mathbf{b} = d\mathbf{z} çıkar.

Bir batch’te her örnek kendi dış çarpımını getirir. WW ile b\mathbf{b} bütün örnekler için ortak olduğundan hepsi toplanır:

dW=X⊤dZ,db=∑idZi,dX=dZ W⊤dW = X^\top dZ, \qquad d\mathbf{b} = \sum_{i} dZ_i, \qquad dX = dZ\, W^\top

dXdX ise girdinin her elemanının çıktıyı WW üzerinden etkilemesinden gelir:

dxk=∑jdzjWkjdx_k = \sum_j dz_j W_{kj}

Shape’lere bakmak yanlışları ucuza yakalar. İki kontrol var:

  • dZ W⊤dZ\, W^\top için (N,dout)(dout,din)(N, d_{\text{out}})(d_{\text{out}}, d_{\text{in}}), yani (N,din)(N, d_{\text{in}}) çıkar.
  • X⊤dZX^\top dZ için (din,N)(N,dout)(d_{\text{in}}, N)(N, d_{\text{out}}), yani (din,dout)(d_{\text{in}}, d_{\text{out}}) çıkar.

Kayıp bir ortalamaysa 1/N1/N çarpanı dZdZ‘nin içinde zaten vardır. Gradyanları ayrıca NN‘ye bölmezsin.

Daha fazlası için Yoğun katmanın geri geçişi ve gradyan kontrolü dersine bak.

Üç gradyan da birer matris çarpımı ya da toplamdır. Her birinin shape’i, türevi alınan şeyin shape’iyle uyuşur.

import numpy as npdef dense_backward(X: np.ndarray, W: np.ndarray, dZ: np.ndarray) -> tuple[np.ndarray, np.ndarray, np.ndarray]:    dX = dZ @ W.T    dW = X.T @ dZ    db = dZ.sum(axis=0)    return dX, dW, db

Sık yapılan hata, sapma gradyanını yanlış axis boyunca toplamaktır:

  • dZ.sum(axis=1), (d_out,) yerine (n,) shape’inde bir vektör verir.
  • n ile d_out eşitse shape bile uyuşur. Hata yalnızca değerlerde görünür.

Sapma her satıra eklenir. Gradyanını satırlar boyunca, yani axis=0 üzerinden toplarsın.

Bu problemde bir sorun mu var?

solution.py
import numpy as npdef dense_backward(X: np.ndarray, W: np.ndarray, dZ: np.ndarray) -> tuple[np.ndarray, np.ndarray, np.ndarray]:    """Yoğun katmanın girdi, ağırlık ve sapma gradyanlarını (dX, dW, db) döndürür."""    # Kodunu buraya yaz.    return np.zeros_like(X), np.zeros_like(W), np.zeros(W.shape[1])

İçindekiler

Geri bildirim

Tür