RNN과 기울기 소실, 폭발

RNN이 시계열을 어떻게 처리하고, 왜 기울기 소실과 폭발에 시달리는지 정리한다.

RNN과 recurrence#

RNN은 시계열 데이터를 처리하기 위한 모델이다. 시점 t의 hidden state는 이전 시점의 hidden state와 현재 입력으로 정해진다.

recurrence(재귀)의 의미는 이전의 RNN 출력 을 다음 입력으로 다시 쓴다는 것이다. 그리고 모든 시점에서 항상 같은 weight를 쓴다. 입력에 곱하는 와 이전 hidden에 곱하는 가 시점이 바뀌어도 그대로 공유된다.

구체적으로 vanilla RNN의 cell은 이렇게 생겼다.

기울기가 소실되거나 폭발하는 이유#

학습할 때는 손실에서 거꾸로 gradient를 흘려보내야 하는데, 에서 로 가는 미분은

가 된다. 시점 t에서 먼 과거까지 gradient를 전달하려면 이 항을 시점 수만큼 반복해서 곱해야 한다.

여기서 두 가지 문제가 보인다.

첫째, 는 거의 항상 1보다 작은 값이다. 1보다 작은 값을 t-1번 곱하면 0으로 수렴한다. 이게 기울기 소실(vanishing gradient)이다.

둘째, 는 모든 시점에서 동일한 행렬이라 같은 행렬을 계속 곱하는 꼴이 된다. 동일한 행렬의 반복 곱셈에서는 그 행렬의 가장 큰 특이값(singular value)이 기준이 된다. 특이값이 1보다 크면 값이 발산하고(기울기 폭발, exploding gradient), 1보다 작으면 0으로 수렴한다(기울기 소실).

Eigendecomposition으로 다시 보기#

같은 행렬을 반복해 곱하는 부분을 eigendecomposition으로 보면 더 정확하게 따질 수 있다. 위 식은 첫 hidden state에 를 반복적으로 곱하는 것과 유사하다.

이때 가 eigendecomposition을 통해 로 분해될 수 있다고 가정하면,

가 된다. 행렬 는 eigenvalue의 대각 행렬이므로, 은 각 eigenvalue의 지수승을 모은 것이다.

따라서 가중치 행렬 를 eigendecomposition 했을 때,

  • eigenvalue의 절댓값이 1보다 크면 → 기울기 폭발(exploding gradient)
  • eigenvalue의 절댓값이 1보다 작으면 → 기울기 소실(vanishing gradient)

앞의 특이값 직관과 같은 이야기를 지수승의 관점에서 더 분명하게 보는 것이다. 동일한 행렬을 번 곱할 때 증폭과 감쇠를 지배하는 것은 eigenvalue이고, 1을 기준으로 지수승이 발산하거나 0으로 수렴한다. 참고로 역전파에서 local gradient를 구할 때는 전치행렬이 곱해진다(일 때 ).

Exploding은 gradient clipping으로 완화한다#

기울기 폭발은 gradient clipping 기법으로 어느 정도 해결할 수 있다. gradient의 크기가 정해둔 threshold를 넘기면 그 이하로 잘라내는 방식이다. 다만 이것은 폭발을 막을 뿐이고, 소실은 이걸로 해결되지 않는다. 소실까지 다루기 위해 Cell state라는 우회로를 두는 구조가 LSTM이다(ResNet에서 LSTM까지).

시퀀스 길이가 곧 깊이다#

불편한 진실은, RNN에서 입력 시퀀스의 길이가 사실상 네트워크의 depth처럼 작용한다는 것이다. x의 길이가 길어질수록 같은 cell을 그만큼 깊게 통과하는 셈이라, 깊은 네트워크가 겪는 기울기 문제를 시퀀스가 길어질 때 똑같이 겪는다.

정리#

  • RNN은 로 이전 출력을 다음 입력에 다시 쓰며, 모든 시점에서 weight를 공유한다.
  • 를 반복해 곱하므로, 이라 소실되고 동일 행렬 의 특이값에 따라 폭발하거나 소실한다.
  • 로 보면, eigenvalue의 절댓값이 1보다 크면 폭발, 작으면 소실한다.
  • 폭발은 gradient clipping으로 완화하지만 소실은 막지 못한다. 소실까지 다루려면 LSTM 같은 구조가 필요하다.
  • 시퀀스 길이가 곧 depth라서 길어질수록 기울기 문제가 심해진다.

댓글