Post

[Paper]LSTM(순환신경망)

이전에 Vanilla RNN 포스트 다음으로 RNN의 장기 의존성 구조 파악의 단점을 보완한 LSTM에 대해 포스팅을 해보려고 합니다.

Ⅰ. LSTM(Long Short Term Memory)

LSTM은 “Long Short-Term Memory”이라는 논문으로 1997년에 Hochreiter와 Jürgen Schmidhuber에 의해 개발되었습니다. 전통적인 RNN 모델에서는 장시간 오차역전파를 통해 학습하는 것은 어렵습니다. 해당 논문은 이러한 문제를 해결하기 위해 LSTM을 제안하였고, LSTM은 특수 유닛 내에서 일정한 오류 흐름을 유지함으로서 데이터에서 장기 의존성을 학습하고 기억할 수 있는 새로운 유형의 RNN 아키텍쳐를 제시하였고, 이는 심층 학습 분야에서 중요한 발전을 이루게 되었습니다.

ⅰ. Motivation and Problem Definition

LSTM comparison

이전에 사용되던 기본적인 순환 신경망(RNN) 구조는 sequece 데이터를 처리하는 데 강한 기능을 가지고 있지만, 긴 시간에 따라 정보를 전달하고 기억하는 데 어려움이 있었습니다. 특히 긴 시퀀스 데이터를 처리하거나 장기적인 의존 관계를 학습하는 데 한계인 “긴 기간의 의존성\(_{long-term dependencies}\)“가 존재했었습니다. 즉, 크기가 큰 문맥을 받으면 필요한 정보를 얻기 위한 시간격차는 굉장히 커지게 되는데 이 격차가 늘어날수록 RNN은 학습하는 정보를 계속 이어나가기 힘들한다는 뜻인데 흔히 말해서 길 글에 대한 앞뒤 사정을 파악할 필요가 있다는 것입니다. 이는 주로 기울기의 문제인 “그래디언트 소실 및 폭발”로 인한것으로 제한적인 성능을 보였습니다. LSTM은 이러한 문제를 극복하기 위한 새로운 RNN 아키텍처를 제안함으로서 장기적인 의존성을 효과적으로 학습하고 저장하며 기울기 문제를 완화하고 장기 패턴을 인코딩하는 능력을 향상 시킬 수 있었습니다.

ⅱ. Model Definition

LSTM cell

Memory Cell

\[C_t = f_t *C_{t-1} + i_t * \tilde{C_t}\]

LSTM은 앞서 말한 문제를 극복하기 위해 메모리 셀(memory cell) 개념을 도입했습니다. 메모리 셀은 그림에서 수평으로 그어진 윗 선으로 RNN의 단계마다 이전 시간 단계에서의 정보를 저장하고, 필요한 경우 이를 활용하여 현재 단계의 계산에 반영합니다. 이는 시간적인 상태 정보를 유지하면서 장기적인 의존 관계를 학습하는 데 도움이 되었습니다.

이러한 Memory cell은 cell state를 뭔가를 더하거나 없애며 업데이트하는 게이트 매커니즘을 사용하여 원하는 정보를 식별 및 기억할 수 있으며 필요한 시점에 사용할 수 있습니다.

Gate Mechanism

LSTM의 핵심 개념은 게이트(gate)라고 불리는 요소입니다. 다음과 같은 3개의 게이트로 입력 게이트, 망각 게이트, 출력 게이트들이 LSTM에서 사용되며, 각 게이트는 시그모이드 함수와 행렬 연산을 통해 정보의 흐름을 제어합니다. 이 게이트들은 현재 입력과 이전 단계의 출력을 기반으로 어떤 정보를 업데이트하고, 어떤 정보를 유지 또는 삭제할지 결정합니다.

LSTM은 이러한 게이트 메커니즘을 통해 장기 의존 관계를 학습할 수 있으며, 긴 시퀀스에서도 효과적으로 정보를 전달할 수 있습니다. 이로 인해 LSTM은 다양한 자연어 처리 작업에 적합하며, 특히 기계 번역, 문장 생성, 감성 분석 등에서 뛰어난 성능을 보입니다.

LSTM은 딥러닝 분야에서 중요한 발전을 이루었으며, 이후 다양한 변형과 확장이 이루어져 LSTM 기반의 신경망 구조가 널리 사용되고 있습니다. LSTM의 성공은 순차적인 데이터 처리와 장기적인 패턴 학습에 대한 이해를 개선하고, 딥러닝 모델의 발전에 기여한 중요한 기술 중 하나로 평가됩니다.

  • Forget gate
    \[f_t = \sigma(W_f \cdot [h_{t-1}, x_t] + b_f)\]

    cell gate로부터 어떤 정보를 버릴 것인지를 정하는 것으로, sigmoid layer에 의해 결정된다. sigmoid 함수를 통해 0과 1사이의 값을 $C_{t-1}$으로 보내 정보의 보존량을 정한다. 즉, 이전 상태의 정보의 비율을 결정합니다.

  • Input gate
    \[i_t = \sigma(W_i \cdot [h_{t-1}, x_t] + b_i)\] \[\tilde{C_t} = tanh(W_C \cdot [h_{t-1}, x_t]) + b_C\]

    이제 이전 cell gate로부터 망각량을 정했으면 새로운 데이터를 받아들일 준비를 해야한다. sigmoid layer를 통해 어떤 값을 업데이트할 지(즉, 중요도) 정하고, tanh layer가 새로운 후보 값들인 vector를 생성하여 기존 cell state에 어떤 정보를 업데이트할지 결정하는 마스크 역할을 수행합니다.

    이렇게 망각 게이트와 입력 게이트를 지나 앞서 말한 Memory Cell의 cell state가 업데이트 된다.

  • Output gate

Input gate를 마치고 업데이트된 cell state를 기반으로 과거의 정보를 포함한 시계열 데이터의 연속성을 보장하는 이전 단계의 은닉 데이터와 새로운 정보를 조합하여 다음 시각에 전달할 은닉 상태를 결정합니다. 여기서 시그모이드 활성화 함수를 사용하여 다음 은닉 상태로 전달할 정보의 중요성을 결정합니다.

직관적으로는 ‘수도꼭지’의 역할로 이러한 동적 조절은 cell state에서 중요한 정보를 유지 및 필터링하고 불필요한 정보를 걸러내는 데 도움을 줍니다. 그 결과, LSTM 네트워크는 시간에 걸쳐 중요한 정보와 의존성을 보존하고 노이즈를 줄이는 데 효과적입니다.

\[o_t = \sigma(W_o[h_{t-1}, x_t] + b_o)\] \[h_t = o_t * tanh(C_t)\]

해당 결정은 현재 상황에 따라 중요한 정보를 보존하고 전달하며 동시에 불필요한 정보를 걸러내는 데 도움이 됩니다. 즉, 출력 게이트는 정보 전달의 결정권을 가지고 있어 셀 상태의 중요한 정보를 보존하고 미래에 영향을 미칠 수 있는 정보를 다음 층으로 전달하는데 중요한 역할을 합니다.

마지막으로 출력층을 통해 $\hat{y_t}$를 생성하는데, 해당 작업은 전통적인 RNN 방식과 동일합니다.

\[\hat{y_t} = softmax(W_y \cdot h_t + b_y)\]

이렇게 위의 단계들을 거치면서 LSTM 네트워크는 시간에 걸친 데이터에 대한 중요한 정보와 패턴을 효과적으로 학습하고 추론하는 데 중추적인 역할을 수행합니다.

이러한 지금까지의 LSTM모델은 완전 기초적인 모델로 peephole connection(엿보기 구명) 등을 추가한 ‘Peephole LSTM’, 합성곱 레이어를 LSTM의 고정 구조에 결합한 “Convolutional LSTM(ConvLSTM)”, 두 방향(정방향 및 역방향) 모두에서 발생하는 정보와 의존성을 고려하는 “Bidirectional LSTM (BiLSTM)”, 기계 번역 및 이미지 캡셔닝과 같은 문제 분야에서 다양한 컨텍스트 정보를 사용하는 데 효과적인 “LSTM을 사용한 Attention Mechanism” 모델 등 다양하게 존재한다.

이외에도 많은 RNN 변형들이 연구 및 개발되고 있으며, 이들은 특정 분야의 문제나 데이터 특성에 맞게 효과적인 결과를 내기 위해 고안되었습니다. 다양한 모델들은 종종 원래의 RNN 구조의 수정이나 기존 네트크와의 스태킹을 통해 새로운 솔루션을 제공하는데 초점을 맞추고 있습니다.

ⅲ. LSTM 구현

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
def call(self, inputs, states):
    prev_hidden_state, prev_cell_state = states
    
    # x.shape = (batch_size, input_size) => (input_size, batch_size)
    x = tf.transpose(inputs)

    # 이전 hidden state와 input x를 가지고 선형변환을 진행하는 gate식.
    gates = tf.matmul(self.W, x) + tf.matmul(self.U, prev_hidden_state) + self.b

    # tf.matmul(self.W, x) 계산식 
    # => (hidden_size * 4, input_size) x (input_size, batch_size) 
    # tf.matmul(self.U, prev_hidden_state) 계산식
    # => (hidden_size * 4, hiddne_size) x (hidden_size, batch_size)
    # gates.shape = (hidden_size, batch_size)

    i, f, o, c_tilde = tf.split(gates, num_or_size_splits=4, axis=0)

    i = self.sigmoid(i) # input gate
    f = self.sigmoid(f) # forget gate
    o = self.sigmoid(o) # output gate
    c_tilde = self.tanh(c_tilde) # 모델의 단순화를 위해 b_c 편향 제거.

    cell_state = f * prev_cell_state + i * c_tilde
    hidden_state = o * self.tanh(cell_state)

    return hidden_state, cell_state

ⅳ. GRU(Gated Recurrent Unit)

GRU(Gated Recurrent Unit)는 순환 신경망(RNN)의 변형 중 하나로, 긴 시퀀스를 처리하는 데 있어 장기 의존성 문제를 해결하기 위해 고안되었습니다. GRU는 LSTM(Long Short-Term Memory)과 유사한 원리로 작동하지만, 구조가 더 단순하고 계산 복잡성이 적습니다. GRU의 핵심 구성 요소는 아래와 같이 수식은 구조적으로 동일하지만, 실질적으로 서로 다른 역할을 하는 두 가지 게이트입니다.

asd 출처 : https://it-ist.tistory.com/27

  • Updata Gate

    \[z_t = \sigma(W_z \cdot [h_{t-1}, x_t])\]

    업데이트 게이트는 LSTM의 입력 게이트와 망각 게이트의 역할을 합친 것으로, 셀 상태에서 얼마나 많은 과거 정보를 유지할 것인지와 새로운 입력 정보를 얼마나 받아들일지를 계산합니다. 즉, 이전 은닉상태와 입력 데이터를 얼마나 통합할지 결정합니다.

  • Reset Gate

    \[r_t = \sigma(W_r \cdot [h_{t-1}, x_t])\]

    리셋 게이트는 이전 시간 단계의 은닉 상태와의 관계를 얼마나 유지할지 결정합니다. 그 결과, 현재 시간 단계의 입력 정보와의 상호 작용이 결정됩니다.즉, 이전 은닉 상태의 얼마나 많은 부분이 후보 은닉 상태 계산에 영향을 미칠 지를 결정합니다.

  • Candidate hidden state

    \[\tilde{h_t} = tanh(W \cdot [r_t * h_{t-1}, x_t])\]

    후보 은닉 상태($\tilde{h_t}$)는 현재 시간 단계의 새로운 정보를 통합하는 잠재적인 은닉 상태를 나타냅니다. 아직 네트워크에서 전달되지 않은 잠재적 상태로 이전 정보를 얼마나 잊어버릴 것인지와 새로운 입력 정보를 얼마나 수용할 것인지에 대한 Trade-off를 조절합니다.

  • Final hidden state

    \[h_t = (1-z_t)*h_{t-1} + z_t*\tilde{h_t}\]

    최종 은닉 상태($h_t$)는 네트워크가 결정하여 다음 스텝으로 전달하는 실제 은닉 상태(출력)입니다. 업데이트 게이트를 사용하여 이전 은닉 상태와 시간에 걸친 데이터의 중요한 정보와 패턴을 보존하기 위해 후보 은닉 상태 사이의 관계를 조절하고 최종적으로 출력되는 은닉 상태를 결정합니다.

GRU는 이 두 게이트를 사용하여 셀 상태의 정보 전달을 제어하고, 후보 은닉 상태를 통해 현재 시간 단계의 정보를 통합하는 잠재적인 상태를 표현하며 최종 은닉 상태를 통해 이 잠재 상태와 이전 은닉 상태를 함께 고려하여 네트워크가 전달할 출력 상태를 나타냅니다. 이러한 과정을 통해 GRU는 긴 시간에 걸친 데이터 시퀀스에서 장기 의존성을 효과적으로 처리하고 정보 전달을 학습하는 능력을 향상시킵니다.

이러한 특성 덕분에 GRU는 텍스트 처리, 음성 인식, 시계열 예측 등 다양한 응용 분야에서 효과적으로 작동합니다. GRU는 LSTM의 게이트 구조를 더 단순화하여 계산량을 줄이고 학습 속도를 높이는 구조지만 네트워크의 표현력은 일부 복잡한 문제에서 LSTM보다 성능이 제한될 수도 있습니다. 이렇듯 GRU는 데이터 양이 적은 경우 매개 변수의 양이 적은 GRU가 조금 더 낫고 반대의 경우는 LSTM이 더 낫다고 알려져 있습니다. 결과적으로 GRU는 기존 LSTM보다 단순한 구조를 가지고 점점 더 유명해지고 있다고 합니다.

ⅴ. GRU 구현

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
def call(self, inputs, prev_hidden_state):
    
    # x.shape = (batch_size, input_size) => (input_size, batch_size)
    x = tf.transpose(inputs)

    # 이전 hidden state와 input x를 가지고 선형변환을 진행하는 gate식.
    gates = tf.matmul(self.W, x) + tf.matmul(self.U, prev_hidden_state) + self.b

    # tf.matmul(self.W, x) 계산식 
    # => (hidden_size * 3, input_size) x (input_size, batch_size) 
    # tf.matmul(self.U, prev_hidden_state) 계산식
    # => (hidden_size * 3, hiddne_size) x (hidden_size, batch_size)
    # gates.shape = (hidden_size * 3, batch_size)

    z, r, h_tilde = tf.split(gates, num_or_size_splits=3, axis=0)

    z = self.sigmoid(z) # update gate
    r = self.sigmoid(r) # reset gate
    h_tilde = self.tanh(h_tilde) # 모델의 단순화를 위해 b_c 편향 제거.

    hidden_state = (1-z) * prev_hidden_state + z * self.tanh(h_tilde)

    return hidden_state

Ⅱ. Conclusion

전체적인 구현 내용은 URL에서 확인 가능합니다.

해당 아키텍쳐와 각 게이트들을 공부하면서 LSTM의 기본적인 배경과 이론, 생각 들을 들여다 볼 수 있었다. 이전 RNN 과 마찬가지로 수식들을 풀고 원리적인 개념을 이해하는 것을 반복하다 보니 여러 아이디어들을 수식으로 풀어 내는 과정이 얼마나 중요한 지 다시금 느끼게 되었다.

LSTM 다음으로 BERT, GPT 등 현재 큰 이슈를 끌고 있는 LLM 들의 기초가 되는 Transformer와 Attention 개념에 대해 공부할 예정이다.

Ⅲ. REFERENCES

  1. temp
  2. ‘Long Short-Term Memory’,Hochreiter, S., & Schmidhuber, J. (1997).
  3. RNN, LSTM, GRU란?



This post is licensed under CC BY 4.0 by the author.