[NLP] Recurrent Neural Network, 순환신경망 개념
RNN 공부 기록
Vanilla RNN (Simple RNN)
입력과 출력을 시퀀스 단위로 처리하는 시퀀스 모델
특징 : 은닉층의 노드에서 활성화 함수를 통해 나온 결과값을 출력층 방향으로 보내면서 다시 은닉층 노드의 다음 계산의 입력으로 보낸다
- x : 입력층의 입력벡터
- y : 출력층의 출력벡터
- RNN 셀 : 은닉층에서 활성화 함수를 통해 결과를 내보내는 역할을 하는 노드, 이전의 값을 기억하려고 하는 일종의 메모리 역할 수행
- 은닉층의 RNN 셀: 각각의 time step에서 바로 이전 time step에서의 은닉층의 메모리 셀에서 나온 값을 자신의 입력으로 사용
- hidden state : 메모리 셀이 출력층 방향 또는 다음 시점인 t+1의 자신에게 보내는 값
RNN 종류
one-to-many : 하나의 이미지 입력에 대해서 사진의 제목을 출력하는 이미지 캡셔닝 작업에 사용가능
many-to-one : 단어 시퀀스에 대해 하나의 출력. sentiment classification, spam detection 등에 사용
many-to-many : 사용자가 문장을 입력하면 대답 문장을 출력하는 챗봇, 번역기, 태깅작업 등
\(h_t\) : 현재 시점 t에서의 은닉상태값
\(W_x\) : 입력층을 위한 가중치
\(W_h\) : 이전시점 t-1의 은닉상태값 \(h_{t-1}\)을 위한 가중치 값
은닉층 : \(h_t=tanh(W_xx_t+W_hh_{t-1}+b)\)
출력층 : \(y_t=f(W_yh_t+b)\)
→ f는 non-linear activation function
\(W_x, W_h, W_y\) : 하나의 층에서는 모든 시점에서 값을 동일하게 공유
각 은닉층에서의 가중치는 서로 다름.
Keras로 RNN 구현
from tensorflow.keras.layers import SimpleRNN
model.add(SimpleRNN(hidden_units))
# 추가 인자를 사용할 때
model.add(SimpleRNN(hidden_units, input_shape=(timesteps, input_dim)))
# 다른 표기
model.add(SimpleRNN(hidden_units, input_length=M, input_dim=N))
- hidden_units = 은닉 상태의 크기 정의. 메모리 셀이 다음 시점의 메모리 셀과 출력층으로 보내는 값의 크기(output_dim)와도 동일.
- timesteps = 입력 시퀀스의 길이(input_length)라고 표현하기도 함. 시점의 수
- input_dim = 입력의 크기
RNN 층은 (batch_size, timesteps, input_dim) 크기의 3D 텐서를 입력으로 받는다.
batch_size = 한번에 학습하는 데이터 개수
return_sequences =True : 메모리 셀이 모든 time step에 대해 은닉 상태값을 출력
→ 다음층에 RNN 은닉층이 하나 더 있는 경우/ many-to-many 문제 가능
return_sequences = False : 마지막 time step 메모리 셀의 은닉 상태값만 출력(디폴트)
→ many-to-one 문제 가능
Bidirectional Recurrent Neural Network
현재 시점의 출력층에서 하나의 출력값을 예측하기 위해 2개의 메모리 셀 사용
1번째 메모리 셀(주황색): forward states를 전달받아 현재의 은닉 상태 계산
2번째 메모리 셀(초록색): backward states를 전달받아 현재의 은닉 상태 계산
LSTM
Vanilla RNN의 한계
단점
- 비교적 짧은 sequence에만 효과
- time step이 길어질수록 앞의 정보가 뒤로 충분히 전달되지 못하는 현상 발생
- 그림에서 남색 x1 정보량이 시점이 지날수록 손실되어 가는 것 확인 가능
바닐라 RNN 내부
LSTM (Long Short-Term Memory)
은닉층의 메모리 셀에 입력 게이트, 망각 게이트, 출력 게이트를 추가하여 불필요한 기억은 지우고 기억해야 할 것 정한다
긴 시퀀스의 입력을 처리하는데 탁월한 성능
- \(W_{xi}, W_{xg},W_{xf}, W_{xo}\) : \(x_t\)와 함께 각 게이트에서 사용되는 4개의 가중치
- \(W_{hi}, W_{hg},W_{hf},W_{ho}\) : \(h_{t-1}\)과 함께 각 게이트에서 사용되는 4개의 가중치
- \(b_i,b_g,b_f, b_o\) : 각 게이트에서 사용되는 4개의 bias
입력 게이트
\(i_t=\sigma(W_{xi}x_t+W_{hi}h_{t-1}+b_i)\)
\(g_t=tanh(W_{xg}x_t+W_{hg}h_{t-1}+b_g)\)
2가지 값을 가지고 이번에 선택된 기억할 정보의 양을 정한다.
삭제 게이트
기억을 삭제하기 위한 게이트
\(f_t=\sigma(W_{xf}x_t+W_{hf}h_{t-1}+b_f)\)
- 현재 시점 t의 x값과 이전 시점 t-1의 은닉 상태가 시그모이드 함수 통과
- 0에 가까울 수록 정보가 많이 삭제된 것이고 1에 가까울수록 정보를 온전히 기억한 것
cell state
\(C_t=f_t \circ C_{t-1}+i_t \circ g_t\)
- 입력게이트에서 구한 2개 값(i,g)에 대해서 entrywise prodcuct 진행 → 선택된 기억
- 입력게이트에서 선택된 기억을 삭제 게이트의 결과값과 더함.
- 삭제 게이트는 이전 시점의 입력을 얼마나 반영할지 결정
- 입력 게이트는 현재 시점의 입력을 얼마나 반영할지 결정
출력게이트, hidden state
\(o_t=\sigma(W_{xo}x_t+W_{ho}h_{t-1}+b_o)\)
\(h_t=o_t \circ tanh(c_t)\)
현재시점 t의 은닉상태 결정에 사용
GRU (Gated Recurrent Unit)
성능은 LSTM과 유사하면서 복잡했던 LSTM 구조 단순화
\(r_t=\sigma(W_{xr}x_t+W_{hr}h_{t-1}+b_r)\)
\(z_t=\sigma(W_{xz}x_t+W_{hz}h_{t-1}+b_z)\)
\(g_t=tanh(W_{hg}(r_t \circ h_{t-1}+W_{xg}x_t+b_g)\)
\(h_t=(1-z_t) \circ g_t +z_t \circ h_{t-1}\)
데이터 양이 적을 때는 매개변수의 양이 적은 GRU이 좀 더 낫고, 데이터 양이 많으면 LSTM이 더 낫다고 알려진다.