8.2 행렬 미분

실습 · 연습문제 2개

2층 MLP의 gradient를 손으로 구현하기 mlp-backward

입력 batch X∈RN×D\mathbf{X} \in \mathbb{R}^{N \times D}(행이 샘플)에 대한 2층 MLP와 손실은 다음과 같다.

A=XW1⊤+1b1⊤,H=ReLU(A),Z=HW2⊤+1b2⊤,L=−1N∑nlog⁡softmax⁡(zn)yn.\mathbf{A} = \mathbf{X}\mathbf{W}_1^\top + \mathbf{1}\mathbf{b}_1^\top, \quad \mathbf{H} = \mathrm{ReLU}(\mathbf{A}), \quad \mathbf{Z} = \mathbf{H}\mathbf{W}_2^\top + \mathbf{1}\mathbf{b}_2^\top, \quad L = -\frac{1}{N}\sum_{n} \log \operatorname{softmax}(\mathbf{z}_n)_{y_n}.

가중치 shape은 PyTorch의 nn.Linear와 같은 (출력 × 입력)이다.

인자 shape
X (N, D)
y (N,) 정수 레이블
W1, b1 (H, D), (H,)
W2, b2 (K, H), (K,)

함수 mlp_grads(X, y, W1, b1, W2, b2)를 완성해 {"W1": ..., "b1": ..., "W2": ..., "b2": ...} 딕셔너리를 반환한다. 각 gradient의 shape은 해당 파라미터와 같아야 한다.

본문의 유도를 따라 출력 쪽에서 입력 쪽으로 계산한다.

  1. GZ=(P−Y)/N\mathbf{G}_Z = (\mathbf{P} - \mathbf{Y})/N — softmax cross-entropy의 gradient
  2. ∇W2=GZ⊤H\nabla \mathbf{W}_2 = \mathbf{G}_Z^\top \mathbf{H}, ∇b2=GZ⊤1\nabla \mathbf{b}_2 = \mathbf{G}_Z^\top \mathbf{1}
  3. GH=GZW2\mathbf{G}_H = \mathbf{G}_Z \mathbf{W}_2, GA=GH⊙1[A>0]\mathbf{G}_A = \mathbf{G}_H \odot \mathbb{1}[\mathbf{A} > 0]
  4. ∇W1=GA⊤X\nabla \mathbf{W}_1 = \mathbf{G}_A^\top \mathbf{X}, ∇b1=GA⊤1\nabla \mathbf{b}_1 = \mathbf{G}_A^\top \mathbf{1}

테스트는 유한차분 gradient와 상대오차로 비교하고, shape과 몇 가지 성질(모든 은닉 뉴런이 꺼지면 첫 층의 gradient가 0)을 확인한다.

힌트: 각 단계에서 shape을 주석으로 적어 보면 전치를 어디에 붙일지 정해진다. GZ⊤H\mathbf{G}_Z^\top\mathbf{H}는 (K,N)(N,H)=(K,H)(K, N)(N, H) = (K, H)다.

softmax cross-entropy와 그 gradient softmax-ce-grad

batch의 logit 행렬 Z∈RN×K\mathbf{Z} \in \mathbb{R}^{N \times K}와 정답 클래스 y(길이 NN의 정수 배열)가 주어진다. nn번째 샘플의 확률은 pn=softmax⁡(zn)\mathbf{p}_n = \operatorname{softmax}(\mathbf{z}_n)이고, 손실은 batch 평균 cross-entropy

L=−1N∑n=1Nlog⁡pn,ynL = -\frac{1}{N}\sum_{n=1}^{N} \log p_{n, y_n}

이다. 함수 softmax_ce(Z, y)를 완성해 (L, dZ)를 반환한다. dZ는 ∂L/∂Z\partial L / \partial \mathbf{Z}로 shape이 Z와 같은 (N, K)다.

요구 사항:

  • 본문에서 유도한 ∂L/∂zn=(pn−yn)/N\partial L / \partial \mathbf{z}_n = (\mathbf{p}_n - \mathbf{y}_n)/N을 쓴다. 여기서 yn\mathbf{y}_n은 one-hot 벡터다.
  • logit이 1000처럼 커도 nan이나 inf가 나오지 않아야 한다. 행마다 최댓값을 빼고 계산하면 softmax 값은 그대로이고 overflow를 피할 수 있다.

테스트는 유한차분과 비교하고, 큰 logit에서의 안정성, 모든 logit이 같을 때 손실이 log⁡K\log K인지, dZ의 각 행의 합이 0인지 확인한다.

힌트: log⁡pn,k=zn,k−log⁡∑jezn,j\log p_{n,k} = z_{n,k} - \log\sum_j e^{z_{n,j}}로 계산하면 log⁡(0)\log(0)을 피할 수 있다.