Backpropagation Through Time (BPTT) 이해하기
BPTT는 RNN에서 시간적 의존성을 따라 Gradient를 역전파하는 알고리즘이다. 이 글에서는 BPTT의 원리와 계산 방법을 설명한다.
RNN은 이전 시점의 Hidden State를 이용해 현재 Hidden State를 계산한다.
\[a_t=Ux_t+Vh_{t-1}+b\] \[h_t=\tanh(a_t)\]여기서 $U,V,b$는 학습 가능한 파라미터이며, 모든 시점에서 동일하게 사용된다.
BPTT(Backpropagation Through Time)는 RNN을 시간 방향으로 펼친 뒤, 각 시점에 걸쳐 Gradient를 역전파하는 과정이다.
1. RNN을 시간 방향으로 펼치기
세 시점에 걸친 RNN을 생각해 보자.
각 Hidden State는 다음 시점의 계산에 사용된다.
\[h_0\rightarrow h_1\rightarrow h_2\rightarrow h_3\]Language Modeling처럼 모든 시점에서 예측을 수행한다면 전체 Loss는 다음과 같이 정의할 수 있다.
\[L=L_1+L_2+L_3\]여기서 중요한 것은 $V$가 세 시점에 모두 등장한다는 점이다.
따라서 $V$에 대한 전체 Gradient를 구하려면 각 시점에서 $V$가 Loss에 미친 영향을 모두 합해야 한다.
2. 우리가 구하려는 Gradient
목표는 다음과 같다.
\[\frac{\partial L}{\partial V}\]하지만 $V$는 $a_1,a_2,a_3$를 계산하는 데 반복해서 사용된다.
따라서 각 시점에서의 기여를 합하면
\[\frac{\partial L}{\partial V} = \left.\frac{\partial L}{\partial V}\right|_{t=1} + \left.\frac{\partial L}{\partial V}\right|_{t=2} + \left.\frac{\partial L}{\partial V}\right|_{t=3}\]가 된다.
여기서 각 항은 해당 시점에서 사용된 $V$가 전체 Loss에 미치는 기여를 의미한다.
이를 계산하려면 먼저 각 $a_t$에 대한 전체 Loss의 Gradient가 필요하다.
\[\frac{\partial L}{\partial a_1}, \qquad \frac{\partial L}{\partial a_2}, \qquad \frac{\partial L}{\partial a_3}\]왜 $a_t$일까?
$V$가 직접 사용되는 연산이
\[a_t=Ux_t+Vh_{t-1}+b\]이기 때문이다.
따라서 $a_t$까지 역전파된 Gradient를 알면, Chain Rule을 이용해 해당 시점의 $V$에 대한 기여를 구할 수 있다.
이제 이 Gradient들을 마지막 시점부터 계산해 보자.
3. 마지막 시점부터 역전파하기
먼저 $a_3$에 대한 Gradient를 구한다.
\[\frac{\partial L}{\partial a_3}\]$a_3$는 $h_3$를 통해 $L_3$에 영향을 준다. 반면 이전 시점의 $L_1,L_2$에는 영향을 주지 않는다.
따라서 Chain Rule에 의해
\[\frac{\partial L}{\partial a_3} = \frac{\partial L_3}{\partial h_3} \frac{\partial h_3}{\partial a_3}\]이다.
$h_3=\tanh(a_3)$이므로
\[\frac{\partial h_3}{\partial a_3}=1-h_3^2\]이다. 위 식은 이해를 위해 Scalar RNN으로 쓴 것이며, 벡터 RNN에서는 원소별 미분을 적용한다.
결국
\[\frac{\partial L}{\partial a_3} = \frac{\partial L_3}{\partial h_3}(1-h_3^2)\]를 얻는다.
여기까지는 일반적인 Backpropagation과 다르지 않다.
이번에는 $a_2$를 살펴보자
$a_2$를 선택하는 이유는 현재 Loss와 미래 Loss에 동시에 영향을 주는 중간 시점이기 때문이다.
$a_2$는 $h_2$를 거쳐 두 경로로 Loss에 영향을 준다.
첫 번째는 현재 시점의 Loss로 이어지는 경로다.
\[a_2\rightarrow h_2\rightarrow L_2\]두 번째는 미래 시점의 Loss로 이어지는 경로다.
\[a_2\rightarrow h_2\rightarrow a_3\rightarrow h_3\rightarrow L_3\]따라서 두 경로의 Gradient를 합산해야 한다.
\[\begin{aligned} \frac{\partial L}{\partial a_2} ={}& \frac{\partial L_2}{\partial h_2} \frac{\partial h_2}{\partial a_2}\\ &+ \frac{\partial L_3}{\partial h_3} \frac{\partial h_3}{\partial a_3} \frac{\partial a_3}{\partial h_2} \frac{\partial h_2}{\partial a_2} \end{aligned}\]두 항에서 공통으로 등장하는 $\partial h_2/\partial a_2$를 묶으면
\[\frac{\partial L}{\partial a_2} = \left( \frac{\partial L_2}{\partial h_2} + \frac{\partial L_3}{\partial h_3} \frac{\partial h_3}{\partial a_3} \frac{\partial a_3}{\partial h_2} \right) \frac{\partial h_2}{\partial a_2}\]가 된다.
여기서 이미 계산한 결과를 활용할 수 있다.
\[\frac{\partial L_3}{\partial h_3} \frac{\partial h_3}{\partial a_3} = \frac{\partial L}{\partial a_3}\]또한 Scalar RNN에서는
\[\frac{\partial a_3}{\partial h_2}=v\]이고,
\[\frac{\partial h_2}{\partial a_2}=1-h_2^2\]이다.
따라서
\[\boxed{ \frac{\partial L}{\partial a_2} = \left( \frac{\partial L_2}{\partial h_2} + v\frac{\partial L}{\partial a_3} \right)(1-h_2^2) }\]를 얻는다.
이 식에서 중요한 점은 다음 시점의 Gradient를 재사용한다는 것이다.
4. Delta의 등장
앞에서 반복적으로 등장한 미분을 다음처럼 정의하자.
\[\delta_t=\frac{\partial L}{\partial a_t}\]그러면 방금 유도한 식을 간단하게 표현할 수 있다.
\[\delta_2= \left( \frac{\partial L_2}{\partial h_2} +v\delta_3 \right)(1-h_2^2)\]같은 방식으로 $t=1$에서도
\[\delta_1= \left( \frac{\partial L_1}{\partial h_1} +v\delta_2 \right)(1-h_1^2)\]가 된다.
이를 벡터 RNN으로 일반화하면 다음과 같다.
\[\boxed{ \delta_t= \left( \frac{\partial L_t}{\partial h_t} + V^\top\delta_{t+1} \right) \odot(1-h_t^2) }\]여기서 각 항의 의미는 다음과 같다.
- $\partial L_t/\partial h_t$: 현재 시점의 Loss가 전달하는 Gradient
- $V^\top\delta_{t+1}$: 미래 시점에서 전달되는 Gradient
- $1-h_t^2$: Tanh의 Derivative
$\delta_{t+1}$에는 이미 그 이후의 Loss에 대한 영향이 포함되어 있다.
따라서 역전파는 마지막 시점부터 시작해, 이전 시점으로 이동하며 계산한 Gradient를 재사용할 수 있다.
5. 공유 가중치의 Gradient 계산
이제 처음의 목표였던 $\partial L/\partial V$로 돌아가자.
각 시점에서는
\[a_t=Ux_t+Vh_{t-1}+b\]를 계산한다.
해당 시점에서 $V$에 대한 Gradient 기여는
\[\delta_t h_{t-1}^\top\]이다.
따라서 전체 Gradient는
\[\boxed{ \frac{\partial L}{\partial V} = \sum_{t=1}^{T}\delta_t h_{t-1}^\top }\]가 된다.
여기서 두 가지 계산이 이루어진다.
첫째, $\delta_t$를 역순으로 계산하며 미래 Loss의 영향을 반영한다.
둘째, 동일한 $V$를 사용한 모든 시점의 Gradient 기여를 합산한다.
이것이 BPTT의 핵심이다.
6. LSTM에도 적용할 수 있을까?
LSTM도 이전 State를 다음 시점으로 전달하고, 동일한 파라미터를 여러 시점에서 사용한다.
따라서 BPTT가 그대로 적용된다.
다만 Vanilla RNN은 Hidden State $h_t$를 중심으로 계산하지만, LSTM은 Hidden State $h_t$와 Cell State $c_t$를 함께 사용한다.
\[c_t=f_t\odot c_{t-1}+i_t\odot\tilde c_t\] \[h_t=o_t\odot\tanh(c_t)\]따라서 LSTM에서는 두 State를 통한 Gradient 경로와 Gate의 Gradient까지 계산해야 한다.
BPTT의 원리는 동일하지만, 각 시점의 계산 그래프가 더 복잡하다.
7. Vanishing Gradient
BPTT에서는 Gradient가 여러 시점을 지나며 Recurrent Weight와 Activation Derivative를 반복적으로 곱한다.
이 과정에서 Gradient가 작아지면 Vanishing Gradient, 커지면 Exploding Gradient 문제가 발생할 수 있다.
LSTM은 Cell State와 Gate를 사용해 장기적인 Gradient 전달을 개선하도록 설계되었다.
정리
BPTT를 이해하는 데 중요한 것은 세 가지다.
- 각 Hidden State는 현재 Loss와 미래 Loss에 모두 영향을 줄 수 있다.
- Chain Rule을 이용해 Gradient를 마지막 시점부터 역전파한다.
- 동일한 파라미터를 여러 시점에서 사용하므로 Gradient 기여를 합산한다.
Vanilla RNN과 LSTM 모두 같은 원리로 학습한다. 차이는 시간에 따라 역전파해야 하는 내부 계산 구조에 있다.