본문 바로가기

책/밑바닥부터 시작하는 딥러닝 2

6장 게이트가 추가된 RNN (1)

Simple RNN은 시간적으로 멀리 떨어진 long term 의존 관계를 잘 학습할 수 없다는 단점이 있다. 그래서 'gate'라는 구조가 추가된 LSTM이나 GRU가 주로 쓰인다.

 

RNN의 문제점

장기 의존 관계를 학습하기 어려운 원인은 BPTT(시간 방향의 역전파)에서 기울기 소실 혹은 폭발이 일어난다.

 

기울기 소실과 폭발의 원인

hidden state의 역전파인 빨간색 선에만 주목해서 보면 tanh, +, MatMul 연산을 통과한다는 것을 알 수 있다. + 노드는 미분 값을 그대로 흘려보내기 때문에 문제가 되지 않는다. tanh의 미분 그래프는 다음과 같다.

 

보다시피 값이 1.0 이하이고 x가 0에서 멀어질수록 작아진다. 즉 역전파에서 기울기가 tanh 노드를 지날 때마다 값이 계속 작아진다는 뜻이다. 다음 MatMul 연산이 역전파에 미치는 영향을 알아보자.

 

이 행렬 곱은 매번 같은 $W_h$가 사용된다는 것이 문제이다. $W_h$ 계속 곱해지기 때문에 값이 지수적으로 증감하여 기울기 소실 혹은 폭발을 야기한다.

 

기울기 폭발 대책

전통적으로 기울기 클리핑이라는 기법이 있다. 이는 기울기의 L2 norm 값이 미리 정한 threshold 값을 초과하면 기울기에 (L2-norm / threshold) 곱해주어 값을 조정해주는 방식이다. 코드로 구현해보면 다음과 같다.

 

dW1 = np.random.rand(3, 3) * 10
dW2 = np.random.rand(3, 3) * 10
grads = [dW1, dW2]
max_norm = 0.5

def clip_grads(grads, max_norm):
    total_norm = 0
    for grad in grads:
        total_norm += np.sum(grad**2)
    total_norm = np.sqrt(total_norm)

    rate = max_norm / (total_norm + 1e-6)
    if rate < 1:
        for grad in grads:
            grad *= rate

print('before:', dW1.flatten())
clip_grads(grads, max_norm)
print('after:', dW1.flatten())

before: [8.74166839 1.06246335 8.26763222 1.68303006 6.38974287 9.15756209
 9.87744257 4.72825515 8.11084133]
after: [0.14669253 0.01782903 0.13873781 0.02824266 0.10722525 0.15367158
 0.16575178 0.07934409 0.13610673]

이제 기울기 소실의 대책으로 LSTM을 알아보자

 

기울기 소실과 LSTM

LSTM의 인터페이스를 RNN과 비교해보자

 

LSTM 계층에는 $c$라는 경로가 있는 것이 차이점이고 $c$를 기억 셀(memory cell)이라고 부른다. 혹은 단순히 셀이라고도 부른다. 셀의 특징은 데이터를 LSTM 계층 내에서만 주고 받는 다는 것이다. hidden state처럼 다른 계층으로 출력되지 않는다.

 

LSTM 계층 조립하기

LSTM에는 기억 셀 $c_t$가 있고, 이 $c_t$에는 과거로부터 시각 t까지에 필요한 모든 정보가 저장되어 있다. 그리고 이를 바탕으로 외부 계층에 은닉 상태 $h_t$를 출력한다. 이때 출력하는 $h_t$는 다음 그림과 같이 $c_t$을 tanh 함수로 변환한 값이다.

 

그림에서 알 수 있듯이 현재의 $c_t$는 3개의 입력($c_{t-1}$, $h_{t-1}$, $x_t$)으로부터 '어떤 계산'을 수행하여 구할 수 있다. 어떤 계산에 대해 자세히 알아보기 전에 게이트의 역할을 간단히 알아보자

 

게이트는 데이터의 흐름을 제어한다.

 

그림처럼 게이트의 열림 상태는 0.0~1.0 사이의 실수로 나타낸다. 값의 범위를 보아하니 시그모이드 함수를 사용할 것임을 짐작할 수 있고 게이트의 열림의 정도 또한 데이터로부터 자동으로 학습한다.

 

output gate

output 게이트의 열림 상태는 다음 수식으로 계산된다.

 

e 6.1

output 게이트가 추가된 그림은 다음과 같다.

 

$tanh(c_t)$가 출력되기 전에 $o$와 아다마르 곱(원소별 곱)을 수행한 뒤 $h_t$로 출력한다.

e 6.2

forget gate

forget 게이트는 $c_{t-1}$에서 불필요한 기억을 잊게 해준다. forget 게이트가 추가된 그림은 다음과 같다.

 

e 6.3

$o$를 계산하는 수식과 동일한 계산이지만 차이점은 각자의 가중치를 가지고 있는 것이다. 마찬가지로 $c_{t-1}$와 아다마르 곱을 수행한다.

 

새로운 기억 셀

forget 게이트로 기억을 삭제하기만 하면 안되니 새로 기억해야 할 정보를 셀에 추가해야 한다. 이것이 추가된 그림은 다음과 같다.

 

e 6.4

input gate

마지막으로 위의 그림에 input 게이트를 추가해보자

 

e 6.5

 

LSTM의 기울기 흐름

LSTM이 기울기 소실의 대책이 될 수 있는 것은 셀의 역전파에만 주목하면 알 수 있다. 셀은 역전파시 +, ⨀ 노드만 지나게 된다. + 노드는 값을 그대로 전달하기 때문에 기울기 변화가 일어나지 않는다.

노드는 원소별 곱이 이뤄지는데 매 시각 새로운 게이트 값을 이용해 원소별 곱을 계산하기 때문에 곱셈의 효과가 누적되지 않아. 기울기 소실이 일어나기 어려운 것이다. 

 

추가 GRU

GRU는 은닉 상태만 사용한다.

 

GRU의 계산 그래프

  • 그림에서 시그모이드 노드가 2개 있기 때문에 우리는 GRU에 게이트가 2개가 있음을 알 수 있다. r은 reset gate이고 z는 update gate이다.
  • reset은 과거의 은닉 상태를 얼마나 무시할지 정한다. 만약 $r$이 0이면 새로운 은닉 상태 $\tilde{h}$는 입력 $x_t$만으로 결정된다. tanh 노드를 보면 입력으로 $x_t$와 $r$⨀$h_{t-1}$이 사용된다.
  • update는 은닉 상태를 갱신하는 게이트이다. LSTM에서 forget과 input 두 가지 역할을 혼자 담당하는 것이다. forget의 역할은 아래 [식 c.4]에서 $(1-z){\odot}h_{t-1}$ 부분이고 input의 역할은 $z {\odot} \tilde{h} $이다.

이처럼 GRU는 LSTM을 더 단순하게 만든 아키텍처로써 계산 비용과 매개변수 수를 줄일 수 있다.

GRU 내부에서 수행하는 계산은 다음과 같다.

e c.1
e c.2
e c.3
e c.4

 

' > 밑바닥부터 시작하는 딥러닝 2' 카테고리의 다른 글

7장 RNN을 사용한 문장 생성 (1)  (0) 2023.11.01
6장 게이트가 추가된 RNN (2)  (0) 2023.10.31
5장 RNN (2)  (0) 2023.09.27
5장 RNN (1)  (0) 2023.09.26
4장 word2vec 속도 개선  (0) 2023.09.26