Just Fighting
LSTM 개념 정리 및 실습 본문
LSTM (Long Short Term Memory)
- 바닐라 RNN(가장 단순한 형태의 RNN)은 출력 결과가 이전의 계산 결과에 의존하지만 짧은 시퀀스에만 효과를 보임
- 바닐라 RNN의 시점이 길어질수록 앞의 정보가 뒤로 충분히 전달되지 못하는 현상 발생 >> "장기 의존성 문제"
- LSTM은 은닉층의 메모리 셀에 입력게이트, 망각게이트, 출력게이트를 추가해 불필요한 기억을 지우고 기억해야할 것을 정함.
- 3개의 게이트에는 공통적으로 시그모이드 함수가 존재하고, 시그모이드 함수를 지난 결과인 0, 1의 값으로 게이트를 조절
입력 게이트
- 현재 정보를 기억하기 위한 게이트
- $i_t$ : 시점 t의 $x$값에 가중치를 곱한 값과 시점 t-1의 은닉상태에 가중치를 곱한 값을 더해 시그모이드 함수에 넣은 값
- $g_t$ : 시점 t의 $x$값에 가중치를 곱한 값과 시점 t-1의 은닉상태에 가중치를 곱한 값을 더해 하이퍼볼릭탄젠트 함수에 넣은 값
- 두개의 값을 이용해 기억할 정보의 양을 정한다.
삭제 게이트
- 기억을 삭제하기 위한 게이트
- $f_t$ : 시점 t의 $x$값과 t-1의 은닉상태에 각각 가중치를 곱해서 합한 뒤 시그모이드 함수를 지난 값
- $f_t$의 값이 0과 1사이의 값이 나오는데, 이 값이 곧 삭제 과정을 거친 정보의 양이 된다.
- 0에 가까울수록 정보가 많이 삭제된 것이고, 1에 가까울수록 정보를 온전히 기억한다.
셀 상태
- 입력게이트에서 선택된 기억을 삭제 게이트의 결과값과 더한 값이 '시점 t의 셀 상태'라고 함.
- 삭제 게이트의 출력값이 0이 된다면 오직 입력게이트의 결과만이 현재 시점의 셀 상태 값을 결정함
=> 삭제 게이트가 완전히 닫히고, 입력게이트를 연 상태를 의미
- 입력 게이트의 값이 0이며, 현재 시점의 셀 상태는 오직 이전 시점의 셀 상태의 값에만 의존
- 즉, 삭제 게이트는 이전 시점의 입력을 얼마나 반영할지를 의미, 입력 게이트는 현재 시점의 입력을 얼마나 반영할지 결정
출력 게이트와 은닉 상태
- $o_t$ : 시점 t의 $x$값과 시점 t-1의 은닉상태가 각각 가중치를 곱해서 합한 뒤 시그모이드 함수를 지난 값
- $o_t$는 시점 t의 은닉상태를 결정하는 일에 쓰임
- $h_t$ : 셀 상태 값이 하이퍼볼릭탄젠트 함수를 지나 -1과 1사이의 값이 되고, 해당 값은 $o_t$ 값과 연산되면서
값이 걸러지는 효과가 발생해 은닉상태가 됨.
- 은닉상태 값은 또한 출력층으로도 향함.
케라스의 LSTM
실제로 SimpleRNN을 사용하는 경우는 거의 없고, LSTM과 GRU를 사용함.
import numpy as np
import tensorflow as tf
from tensorflow.keras.layers import SimpleRNN, LSTM, Bidirectional
# 3D 텐서. 밖에 대괄호 하나 더 추가함.
train_X = [[[0.1, 4.2, 1.5, 1.1, 2.8], [1.0, 3.1, 2.5, 0.7, 1.1], [0.3, 2.1, 1.5, 2.1, 0.1], [2.2, 1.4, 0.5, 0.9, 1.1]]]
train_X = np.array(train_X, dtype=np.float32)
print(train_X.shape)
은닉 상태의 크기 3, return_sequences=False, return_state=True 일 경우에는
마지막 은닉 상태와 셀 상태를 리턴한다.
lstm = LSTM(3, return_sequences=False, return_state=True) # 은닉 상태의 크기 3, return_sequences=False, return_state=True
hidden_state, last_state, last_cell_state = lstm(train_X)
# 마지막 은닉상태와 셀 상태
# return_state = True 인 경우에는 셀 상태까지 반환
print('hidden state : {}, shape: {}'.format(hidden_state, hidden_state.shape))
print('last hidden state : {}, shape: {}'.format(last_state, last_state.shape))
print('last cell state : {}, shape: {}'.format(last_cell_state, last_cell_state.shape))
은닉 상태의 크기 3, return_sequences=True, return_state=True 일 경우에는
모든 시점의 은닉 상태와 셀 상태를 리턴한다.
lstm = LSTM(3, return_sequences=True, return_state=True) # 은닉 상태의 크기 3, return_sequences=True, return_state=True
hidden_state, last_state, last_cell_state = lstm(train_X)
# 모든 시점의 은닉상태와 셀 상태
print('hidden state : {}, shape: {}'.format(hidden_state, hidden_state.shape))
print('last hidden state : {}, shape: {}'.format(last_state, last_state.shape))
print('last cell state : {}, shape: {}'.format(last_cell_state, last_cell_state.shape))
양방향 LSTM
양방향 LSTM의 출력값을 확인해보자.
return_state = True인 경우에는 아래 코드에서 확인할 수 있듯이 5개의 값을 리턴한다.
return_sequences = False인 경우에는 정방향 LSTM의 마지막 시점의 은닉상태와
역방향 LSTM의 첫번째 시점의 은닉상태가 연결된 채 반환된다.
# 은닉상태 값 고정
# return_sequences가 True인 경우와 False 인 경우를 비교하기 위함.
k_init = tf.keras.initializers.Constant(value=0.1)
b_init = tf.keras.initializers.Constant(value=0)
r_init = tf.keras.initializers.Constant(value=0.1)
# return_sequences=False
bilstm = Bidirectional(LSTM(3, return_sequences=False, return_state=True,
kernel_initializer=k_init, bias_initializer=b_init, recurrent_initializer=r_init))
hidden_states, forward_h, forward_c, backward_h, backward_c = bilstm(train_X)
print('hidden states : {}, shape: {}'.format(hidden_states, hidden_states.shape))
print('forward state : {}, shape: {}'.format(forward_h, forward_h.shape))
print('backward state : {}, shape: {}'.format(backward_h, backward_h.shape))
return_sequences=True일 경우에는 모든 시점의 은닉상태가 출력된다.
이때는 역방향 LSTM의 첫번째 시점의 은닉상태와 정방향 LSTM의 첫번째 시점의 은닉상태가 연결된 것을 확인할 수 있다.
# return_sequences=True
bilstm = Bidirectional(LSTM(3, return_sequences=True, return_state=True,
kernel_initializer=k_init, bias_initializer=b_init, recurrent_initializer=r_init))
hidden_states, forward_h, forward_c, backward_h, backward_c = bilstm(train_X)
print('hidden states : {}, shape: {}'.format(hidden_states, hidden_states.shape))
print('forward state : {}, shape: {}'.format(forward_h, forward_h.shape))
print('backward state : {}, shape: {}'.format(backward_h, backward_h.shape))
'ML & DL' 카테고리의 다른 글
GRU 개념정리 (0) | 2022.07.24 |
---|---|
SimpleRNN 이해하기 (0) | 2022.07.24 |
RNN 개념정리 (0) | 2022.07.24 |
[PySpark] 나이브 베이즈 실습 (0) | 2022.06.09 |
[개념 정리] 나이브 베이즈 (Naive Bayes) (0) | 2022.06.08 |