핸즈온머신러닝&딥러닝

RNN과 CNN을 사용해 시퀀스 처리하기 2

threegopark 2021. 4. 30. 13:14
728x90

긴 시퀀스 다루기

 

긴 시퀀스로 RNN을 훈련하려면 많은 타임 스텝에 걸쳐 실행해야 하므로 펼친 RNN이 매우 깊은 네트워크가 된다.

보통 이렇게 깊은 네트워크가 형성되면 "그레이디언트 소실 & 폭주 문제"를 가지게 되고 훈련 시간이 매우 오래걸리거나 훈련이 불안정할 수 있다. 또한 긴 시퀀스를 다루는 중 입력의 첫 번째 부분을 조금씩 잊어버릴 수도 있다. 

 

 

불안정한 그레이디언트 문제 해결

 

심층 신경망에서 사용하였던 가중치 초기화, 빠른 옵티마이저, 드롭아웃 등을 RNN에 적용하여 해결할 수 있다. 그러나 기존 심층 신경망에서 사용하였던 ReLU같은 수렴하지 않는 활성화 함수는 큰 도움이 되지 않는다. (오히려 더 불안정하게 만들 수 있다.) 왜 수렴하지 않는 활성화 함수는 도움이 되지 않을까? 경사 하강법이 첫 번째 타임 스텝에서 출력을 조금 증가시키는 방향으로 가중치를 업데이트한다고 가정해보자. 동일한 가중치가 모든 타임 스텝에서 사용되기 때문에 두 번째 타임스텝의 출력도 조금 증가할 것이고 이런식으로 출력 (혹은 그레이디언트)이 폭주하게 되는 것이다. 물론 학습률을 작게 낮춰서 이를 어느정도 막을 수 있지만 tanh과 같이 수렴하는 활성화 함수를 사용함으로써 간단히 해결할 수 있다.

 

훈련이 불안정하다고 느껴지면 그레이디언트의 크기를 모니터링하고 그레이디언트 클리핑을 사용하여 해결할 수 있다.

 

배치 정규화는 심층 피드포워드 네트워크처럼 RNN과 효율적으로 사용할 수 없다. 그 이유는 RNN에서는 각 단계마다 서로 다른 통계치를 가지고, 매 단계마다 층에 별도의 배치 정규화 층을 적용해야 하는 것을 의미한다. (층마다 다른 배치 정규화 층을 적용하게 되면 모델이 매우 복잡해질 것이다.) 또한 시간에 대해 변하는 속성 때문에 통계치가 새롭게 갱신 될 것이고 기존의 배치 정규화의 정적인 속성은 이러한 문제를 해결하기 어렵다.

그래도 배치 정규화를 RNN 층에서 활용하겠다... 한다면 메모리 셀에 배치 정규화 층을 추가하여 타임 스텝마다 적용해야 한다. 하지만 입력이나 은닉 상태의 실제 스케일과 이동에 상관없이 동일한 파라미터를 가진 배치 정규화가 타임 스텝마다 사용될 것이고 이는 큰 활약을 하지 못한다. (논문에 의해 그렇다고 한다.)

 

그렇다면 RNN과 잘 맞는 정규화는 없는 것인가!

다행스럽게도 RNN과 잘 맞는 다른 종류의 정규화가 존재한다. 바로 "층 정규화 (Layer normalization)" 이다.

이는 배치 정규화와 매우 비슷하지만 배치 차원에 대해 정규화하는 대신 특성 차원에 대해 정규화한다. (즉, 동일한 층의 뉴런간 정규화를 진행하는 것이다.)

층 정규화는 일반적으로 입력과 은닉 상태의 선형 조합 직후에 사용되며 배치 정규화와 마찬가지로 입력마다 하나의 스케일과 이동 파라미터를 학습한다.

이의 장점은 샘플에 독립적으로 타임 스텝마다 동적으로 필요한 통계를 계산할 수 있다는 것이다. 

 

www.slideshare.net/ssuser06e0c5/normalization-72539464'

 

Normalization 방법

오사카 대학 Nishida Geio군이 Normalization 관련기술 을 정리한 자료입니다. Normalization이 왜 필요한지부터 시작해서 Batch, Weight, Layer Normalization별로 수식에 대한 설명과 함께 마지막으로 3방법의 비교

www.slideshare.net

 

그렇다면 사용자 정의 함수를 활용하여 간단한 메모리 셀 안에 층 정규화를 구현해보겠다.

 

(+ keras.layers.RNN을 제외한 모든 순환 층과 케라스 제공 셀에서는 드롭아웃 매개변수와 recurrent_dropout 매개변수를 지원하므로 간단하게 드롭아웃을 추가할 수 있다.)

 

 

 

단기 기억 문제 해결하기

 

RNN을 거치면서 데이터가 변환되므로 일부 정보는 매 훈련 스텝 후 사라진다. 어느 정도 시간이 지나면 모델의 상태는 사실상 첫 번째 입력을 거의 기억하지 못하는 상태가 된다. 이러한 문제점들을 해결하기 위해 LSTM, GRU, 핍홀 연결 등 다양한 방식의 모델이 고안되었는데 간단히 짚고 넘어가자.

 

 

LSTM 셀

 

장단기 메모리 (Long short-term memory) 인 LSTM은 기본 셀과 매우 비슷하게 생겼지만 훈련이 더 빠르게 수렴하고 데이터를 장기간 보존할 수 있도록 해준다. 구현을 위해 간단하게 SimpleRNN --> LSTM 으로 바꿔주기만 하면 된다.

 

https://excelsior-cjh.tistory.com/185

 

  • h : 단기 상태
  • c : 장기 상태
  • g : 현재 입력 x(t)와 이전의 단기 상태 h(t-1)를 분석, 가장 중요한 부분을 장기 상태 c에 저장, 나머지는 버림
  • f, i, o : 게이트 제어기, 로지스틱 활성화 함수를 사용(출력 범위 : 0~1), 원소별 곱셈 연산으로 주입 및 출력되며 0을 출력하면 게이트를 닫고 1을 출력하면 게이트를 연다.
  • f : 삭제 게이트, 장기 상태 c 의 어느 부분이 삭제되어야 하는지 제어
  • i : 입력 게이트, g의 어떤 부분이 장기 상태 c에 저장되어야 하는지 제어
  • o : 출력 게이트, 장기 상태의 어느 부분을 읽어서 이 타임 스텝의 h(t)와 y(t)로 출력해야 하는지 제어

LSTM의 기능은 간단하다. 

 

1. 장기 상태에 저장할 것

2. 버릴 것

3. 읽어들일 것

을 학습하는 셀이다.

 

LSTM은 중요한 입력을 인식하고, 장기 상태에 저장하며, 필요한 기간 동안 이를 보존하고, 필요시 추출한다. 이러한  특성으로 인해 시계열, 긴 텍스트, 오디오 녹음 등에서 장기 패턴을 잡아내는 데 놀라운 성과를 낸다.

 

 

핍홀 연결

 

일반 LSTM 셀에서 게이트 제어기는 입력 x(t)와 이전 단기 상태 h(t-1) 만 바라볼 수 있다. 만약 게이트 제어기에서 장기 상태인 c에 관련된 내용도 조금 읽을 수 있다면 성능이 더 좋아질 수도 있을 것이다. 이러한 아이디어를 적용한 것이 바로 핍홀 연결이다.

핍홀 연결은 이전 장기 상태 c(t-1)이 입력 게이트와 삭제 게이트의 제어기 f와 i에 입력으로 추가된다. 그리고 현재의 장기 상태 c(t)는 출력 게이트의 제어기 o에 입력으로 추가된다. 

 

https://excelsior-cjh.tistory.com/185

 

 

 

GRU 셀

 

게이트 순환 유닛(Gated recurrent unit) 셀은 LSTM의 간소화된 버전이고 유사하게 작동되는 것 처럼 보인다.

 

https://excelsior-cjh.tistory.com/185

 

LSTM에서는 단기 h와 장기 c가 존재했었다. GRU 에서는 이 둘이 합쳐져 h가 되었다. (하나의 벡터)

하나의 게이트 z가 삭제 게이트와 입력 게이트를 모두 제어한다. (게이트 제어기가 1을 출력 --> 삭제 게이트 open, 입력 게이트 close  // 게이트 제어기가 0을 출력 --> 삭제 게이트 close, 입력 게이트 open) 

출력 게이트가 없다. 즉, 전체 상태 벡터가 매 타임 스텝마다 출력된다. 그러나 이전 상태의 어느 부분이 주 층인 g에 노출될지 제어하는 새로운 게이트 제어기 r이 존재한다.

 

 

1D 합성곱 층을 사용해 시퀀스 처리하기

 

위에서 다룬 LSTM과 GRU는 아주 좋은 성능을 보여주지만 100타임 스텝 이상의 시퀀스에서 장기 패턴을 학습하는 데 어려움이 존재한다. 이 문제를 해결하기 위한 한 가지 좋은 방법은 바로 1D 합성곱 층을 사용하여 입력 시퀀스를 짧게 줄이는 것이다.

앞서 공부한 컴퓨터 비전 파트에서 2D 합성곱 층이 이미지에 대해 몇 개의 매우 작은 커널(or 필터)가 슬라이딩하여 커널마다 하나씩 2D 특성 맵을 만든다는 것을 알았다. 비슷하게 1D 합성곱 층이 몇 개의 커널을 시퀀스 위를 슬라이딩하며 커널마다 1D의 특성 맵을 출력하게 만들 수도 있다. 각 커널은 매우 짧은 하나의 순차 패턴을 감지하도록 학습되며 10개의 커널을 사용하면 이 층의 출력은 모두 길이가 같은 10개의 1차원 시퀀스로 구성된다. (이 출력할ㄹ 하나의 10차원 시퀀스로 볼 수도 있다.)

 

이 말은 순환 층과 1D 합성곱 층을 섞어서 신경망을 구성할 수 있다는 의미이다.

 

스트라이드 1, same 패딩으로 1D 합성곱 층을 사용하면 출력 시퀀스의 길이는 입력 시퀀스와 같다. 하지만 Valid 패딩과 1보다 큰 스트라이드를 사용하면 출력 시퀀스는 입력 시퀀스보다 짧아진다. 따라서 경우에 맞게 긴 시퀀스를 짧게 변환시킬 수 있게되는 것이다. 

 

 

WAVENET

 

이 네트워크는 층마다 팽창 비율(각 뉴런의 입력이 떨어져 있는 간격)을 두 배로 늘리는 1D 합성곱 층을 쌓는다. 첫 번째 합성곱 층이 한 번에 2개의 타임 스텝만 바라본다. 다음 층은 4개의 타임 스텝을 보고, 다음은 8개의 타임 스텝을 본다. 이런 식으로 하위 층은 단기 패턴을 학습하고 상위 층은 장기 패턴을 학습한다. 팽창 비율을 2배로 늘린 덕분에 긴 시퀀스를 아주 잘 처리할 수 있게 된 것이다.

 

https://paperswithcode.com/method/wavenet

논문을 확인해보면 실제로 팽창 비율이 각각 1, 2, 4, 8, --- , 256, 512 인 합성곱 층 10개를 쌓았다. 그다음 팽창 비율이 동일한 층 10개를 따로 그룹지어 쌓았다. 이런 팽창 비율을 가진 합성곱 층 10개가 1024 크기의 커널 한 개로 이루어진 매우 효율적인 합성곱 층처럼 작동하는 것을 확인할 수 있다.