Recurrent Neural Network(RNN)은 순서가 있는 데이터(시계열 데이터)에서 의미를 찾아내기 위해 고안된 모델로 기울기 소멸 문제(vanishing gradient problem)가 존재합니다. RNN 단점을 보완한 모델로 Long Short Term Memory(LSTM)과 Gated Recurrent Unit(GRU)가 있습니다. LSTM은 은닉층의 각 노드(혹은 뉴런)에 망각 게이트(forget gate), 입력 게이트(input gate), 출력 게이트(output gate)를 추가하여 cell state의 정보 흐름을 제어합니다. LSTM은 복잡한 구조 때문에 많은 파라미터가 필요하여 데이터가 충분하지 않은 경우 오버피팅 문제가 발생합니다. 본 내용은 LSTM보다 구조가 더 간단한 게이트 메커니즘이 적용된 RNN 프레임워크의 한 종류인 GRU에 대한 글입니다.
▶ Key point of GRU
- LSTM의 forget gate와 input gate를 통합하여 하나의 update gate를 생성하였습니다.
- LSTM의 cell state $C_t$와 hidden state $h_t$를 통합하였습니다.
위 2가지 사실을 통해 GRU는 LSTM에 보다 파라미터(parameter) 수가 적기 때문에 연산 비용이 적게 들고 구조도 더 간단함을 알 수 있습니다. 성능 면에서도 LSTM과 비슷한 결과를 가진다는 장점이 존재합니다.
0. Long Short Term Memory(LSTM) v.s. Gated Recurrent Unit(GRU)
Gated Recurrent Unit(GRU)의 구조에 대해 자세하게 살펴보기 전 Long Short Term Memory(LSTM)과의 비교를 통해 GRU에 대해 간략하게 살펴보고자 합니다.
LSTM | GRU |
Forget Gate Input Gate Output Gate |
Reset Gate Update Gate |
Output gate를 통해서 cell state $C_t$의 일부분만 출력 |
Output gate가 없기 때문에 전체 cell state를 그대로 출력 |
Input gate와 output gate가 서로 독립 |
Input gate와 output gate가 통합되어 update gate를 통해 조절됨 |
More parameters |
Fewer Parameters |
▶ LSTM과 GRU의 차이점
- LSTM의 cell state $C_t$와 hidden state $h_t$가 GRU에서는 하나의 벡터 $h_t$로 합쳐집니다.
- LSTM의 output gate를 제거하고 reset gate를 대신 사용합니다.
- LSTM의 forget gate와 input gate를 update gate로 통합합니다.
- LSTM에서 forget gate와 input gate는 서로 독립이었으나 GRU에서는 forget한 만큼 input하는 방식으로 제어합니다. 이는 gate controlloer $z_t$에 의해서 조절됩니다.
GRU의 전체적인 구조에 대해서 [Section 1: GRU]에서 자세하게 살펴보겠습니다.
1. Gated Recurrent Unit(GRU)
GRU는 정보의 흐름을 제어하는 게이트(gate) 매커니즘으로 reset gate와 update gate가 존재합니다.
1.1 Reset gate
" 이전의 정보를 얼마나 잊어야 하는가?"
$$r_t = \sigma(W_r h_{t-1} + W_r x_t)$$
- input: 과거의 정보 $h_{t-1}$, 현재 정보 $x_t$
- output: 과거의 정보를 얼마나 반영할지에 대한 비중 $r_t \in (0,1)$
- $\sigma$: sigmoid function
GRU는 reset gate를 통해서 과거의 정보 $h_{t-1}$를 어느 정도 반영할지 혹은 망각할지를 결정하는 게이트입니다.
- $r_t=1$인 경우, 과거의 정보 $h_{t-1}$를 그대로 반영합니다.
- $r_t=0$인 경우, 과거의 정보 $h_{t-1}$를 반영하지 않습니다("초기화").
- $r_t=1, r_t=0$인 경우는 Vnailla RNN과 같습니다.
1.2 Candidate Hidden State
$$\tilde{h}_t = \text{tanh}(W x_{t} + W r_t h_{t-1})$$
- input: 과거의 정보 $h_{t-1}$, 현재 정보 $x_t$
- output: candidate hidden state $\tilde{h}_t$
- 현시점의 정보에 대한 후보군을 계산한 값입니다.
과거 정보 $h_{t-1}$를 그대로 이용하지 않고 reset gate $r_t$를 이용하여 candidate hidden state를 구하였습니다. 즉, 새로운 정보 $x_t$와 이전 과거의 정보의 일부분($r_t h_{t-1}$)만을 사용하였습니다. 아직 update gate를 반영하지 않았기 때문에 'candidate' hidden state라고 불립니다.
1.3 Update Gate
"과거 정보 $h_{t-1}$와 현재 새로 업데이트된 정보 $\tilde{h_t}$의 비중을 어떻게 조절할 것인가?"
$$z_t = \sigma(W_z h_{t-1} + W_z x_t)$$
- input: 과거의 정보 $h_{t-1}$, 현재 정보 $x_t$
- output: 과거와 현재 정보의 최산화 비중을 결정하는 역할 $z_t \in (0,1)$
- $\sigma$: sigmoid function
이전의 정보 $h_{t-1}$는 얼마나 통과시킬지에 대한 비중 $(1-z_t)$, 새로 업데이트된 정보 $\tilde{h}_t$를 얼마나 통과시킬지에 대한 비중 $z_t$를 계산하는 과정입니다.
1.4 Hidden state $h_t$
$$h_t = (1-z_t)\tilde{h}_{t} + z_th_{t-1}$$
Update gate 결과 $1-z_t$와 후보군 결과 $\tilde{h}_t$와의 곱과 $z_t$와 과거 정보 $h_{t-1}$을 결합하여 현 시점의 산출값(은닉층) $h_t$를 계산합니다.
- $z_t=0$이면, 정보를 모두 candidate state $\tilde{h}_t$(새로운 정보)로 대체합니다.
- $z_t=1$이면, old state $h_{t-1}$(이전 정보)를 그대로 사용합니다.
※ 참고
- reset gate는 short-term dependency를 의미합니다.
- update gate는 long-term dependency를 의미합니다.
2. GRU 구현코드
GRU 구현코드는 이론에 대한 이해를 돕고자 참고자료로 넣었습니다.
2.1 GRU cell 구현코드
class GRUCell(nn.Module):
def __init__(self, input_size, hidden_size, bias=True):
super(GRUCell, self).__init__()
self.input_size = input_size
self.hidden_size = hidden_size
self.bias = bias
self.x2h = nn.Linear(input_size, 3*hidden_size, bias=bias)
self.h2h = nn.Linear(hidden_size, 3*hidden_size, bias=bias)
self.reset_parameters()
def reset_parameters(self):
std = 1.0/math.sqrt(self.hidden_size)
for w in self.parameters():
w.data.uniform_(-std,std)
def forward(self,x, hidden):
x = x.view(-1, x.size(1))
gate_x = self.x2h(x)
gate_h = self.h2h(hidden)
gate_x = gate_x.squeeze()
gate_h = gate_h.squeeze()
i_r, i_i, i_n = gate_x.chunk(3,1)
h_r, h_i, h_n = gate_h.chunk(3,1)
resetgate = F.sigmoid(i_r + h_r)
inputgate = F.sigmoid(i_i + h_i)
newgate = F.tanh(i_n + (resetgate*h_n))
hy = newgate + inputgate*(hidden - newgate)
return hy ## 새로운 hidden state로 update
2.2 GRU model 구현코드
class GRUModel(nn.Module):
def __init__(self, input_dim, hidden_dim, layer_dim, output_dim, bias=True):
super(GRUModel, self).__init__()
self.hidden_dim = hidden_dim
self.layer_dim = layer_dim
self.gru_cell = GRUCell(input_dim, hidden_dim, layer_dim)
self.fc = nn.Linear(hidden_dim, output_dim)
def forward(self,x):
if torch.cuda.is_available():
h0 = Variable(torch.zeros(self.layer_dim, x.size(0), self.hidden_dim).cuda())
else:
h0 = Variable(torch.zeros(self.layer_dim, x.size(0), self.hidden_dim))
outs = []
hn = h0[0,:,:]
for seq in range(x.size(1)):
hn = self.gru_cell(x[:,seq,:],hn)
outs.append(hn)
out = outs[-1].squeeze()
out = self.fc(out)
return out
'Time Series Analaysis > Time Series Analysis' 카테고리의 다른 글
Linear Gaussian State Space Model (0) | 2024.06.10 |
---|---|
Convolutional LSTM network(ConvLSTM) (0) | 2024.01.29 |
Long Short Term Memory(LSTM) (0) | 2024.01.22 |
Recurrent Neural Network(RNN) (0) | 2024.01.22 |