머신러닝&딥러닝/Tensorflow&keras

Tensorflow&Keras - LSTM 개념 및 사용법 정리

Like_Me 2020. 7. 1. 18:50

  LSTM 은 Long Short Term Memory의 줄임말로 주로 시계열 처리나 자연어 처리(현재는 잘 사용 안 하지만)를 사용하는 데 사용한다. LSTM을 처음 배울 때 헷갈렸던 것은 데이터의 '순환'에 대한 개념이었다. 흔히 아래와 같은 그림으로 LSTM을 나타낸다. 

순환 신경망

Input으로 x가 들어가면 여러번의 순환을 거쳐 output인 y가 나오는 구조이다. 이때 h는 그 중간다리 역할을 하는데 hidden state라고 한다. 위의 구조를 펼쳐서 보면 아래와 같다.

순환신경망을 펼쳐 보았을 때

  Input 데이터인 x는 Sequence를 가지는 데이터가 되는데 위의 경우는 길이가 6인 데이터이기 때문에 총 6번의 input이 들어가게 된다.(예를 들면 단어가 6개인 문장) RNN 사이에 있는 화살표는 hidden state의 전달을 표현한다. 최종적으로 y값이 나오는 데는 hidden state와 연속적인 x의 입력에 의해 결정되게 된다.

 

  이런 모델의 구조는 사람의 기억과 이해 메커니즘을 연상케한다. 예를 들어  어떤 글을 읽는다고 해보자. 우리는 한 단어를 읽을 때마다 이전 단어들을 읽으면서 이해했던 내용과 새로운 단어의 이해를 잘 조합하여 새로운 결론을 내게 된다. 이때 단어들이 x이고 단어들을 읽으면서 나오는 새로운 결론이 hidden state가 된다.

 

  RNN은 보통 LSTM이나 GRU를 사용한다. LSTM은 input값을 받으면 hidden state와 cell state를 가지게 된다. cell state는 LSTM이 굴러가게 하는데 일종의 체인역할을 하며 기억을 오랫동안 유지할 수 있는 구조로 되어있다. hidden state는 위의 설명처럼 계층의 출력이 되며 다음 타임 스텝으로 정보를 넘기게 된다. 

LSTM 구조

   


Tensorflow에서 LSTM으로 cell state와 hidden state에 대한 출력을 바꾸는 변수로는 return_state와 return_sequences이다. 두 개의 변수는 boolean의 형태로 주어지게 되며 둘 다 false일 경우 아래와 같이 마지막 결괏값(마지막 hidden state) 값만 출력하게 된다.

 

# lstm을 그냥 쓸 경우 최종 결과값만 도출한다.
from tensorflow.keras.models import Model
from tensorflow.keras.layers import Input
from tensorflow.keras.layers import LSTM
from numpy import array

inputs1 = Input(shape=(3, 1))
lstm1 = LSTM(1)(inputs1)
model = Model(inputs=inputs1, outputs=lstm1)

data = array([0.1, 0.2, 0.3]).reshape((1,3,1))

print(model.predict(data)) # [[0.04836999]]

 

  return_sequences = True로 설정할 경우 각 time step별 hidden state를 모두 출력하게 된다. 예를들어 위의 예시에서 처럼 길이가 6인 경우에는 총 6개의 hidden state값이 모두 나오는 것이다. 이렇게 출력을 하는 것은 보통 Attention을 사용하기 위함이다.

# return_sequences = True를 한 경우에는 각 time step별 hidden state들이 출력된다.
from tensorflow.keras.models import Model
from tensorflow.keras.layers import Input
from tensorflow.keras.layers import LSTM
from numpy import array

inputs1 = Input(shape=(3, 1))
lstm1 = LSTM(1, return_sequences=True)(inputs1)
model = Model(inputs=inputs1, outputs=lstm1)

data = array([0.1, 0.2, 0.3]).reshape((1,3,1))

print(model.predict(data)) 
# [[[0.01184901]
#  [0.03267556]
#  [0.05920978]]]

 

  return_state = True 를 한 경우에는 마지막 time step에서의 output(hidden state), hidden state와 cell state가 출력된다. 즉 마지막 output값이 2번 출력이 되고 cell state가 나온다.

# return_state = True 를 한 경우에는 마지막 time step에서의 output(hidden state),hidden state와 cell state가 출력된다.
# 즉 같은 값이 2번 출력된다.
from tensorflow.keras.models import Model
from tensorflow.keras.layers import Input
from tensorflow.keras.layers import LSTM
from numpy import array

inputs1 = Input(shape=(3, 1))
lstm1, state_h, state_c = LSTM(1, return_state=True)(inputs1)
model = Model(inputs=inputs1, outputs=[lstm1, state_h, state_c])

data = array([0.1, 0.2, 0.3]).reshape((1,3,1))

print(model.predict(data))
# [array([[0.00329698]], dtype=float32), array([[0.00329698]], dtype=float32), array([[0.00614673]], dtype=float32)]

 

  return_sequences와 return_state를 모두 True로 하게 되면 각 time step별 hidden state와 마지막 hidden state, 마지막 cell state 값이 출력된다.

# return_sequences와 return_state 모두 True로 하게 되면 각 time step별 hidden state와 마지막 hidden state, 마지막 cell state 값이 출력된다.
from tensorflow.keras.models import Model
from tensorflow.keras.layers import Input
from tensorflow.keras.layers import LSTM
from numpy import array
# define model
inputs1 = Input(shape=(3, 1))
lstm1, state_h, state_c = LSTM(1, return_sequences=True, return_state=True)(inputs1)
model = Model(inputs=inputs1, outputs=[lstm1, state_h, state_c])
# define input data
data = array([0.1, 0.2, 0.3]).reshape((1,3,1))
# make and show prediction
print(model.predict(data))
#[array([[[0.01530219],
#        [0.04227088],
#        [0.07738266]]], dtype=float32), array([[0.07738266]], dtype=float32), array([[0.14669698]], dtype=float32)]

 

  LSTM을 각 타임 스텝별로 분리해서 넣어주는 경우가 있을 수 있다. 그럴 때는 initial_state를 사용해야 한다.

전체 sequence길이가 T이고 feature가 n이면 LSTM에 하나의 타임 스텝별로 넣기 때문에 input shape이 batch,1,n이 된다. 이런 식으로 T번 반복하여 넣으면 되는데 기존의 hidden state와 cell state를 전달해줘야 하므로 initial_state를 사용한다. 코드로 나타내면 다음과 같다.

 

class CustomLstm(keras.layers.Layer):
    def __init__(self):
        super(CustomLstm, self).__init__()
        self.initial_state = None
        self.lstm = LSTM(units, return_state=True,dropout=0.3)

    def call(self, data, training=False):
        for t in range(maxlen):
            # my hope: input_t = inputs[i, t, :]
            input_t = data[:, t, :]  # (batch,n)
            input_t = tf.expand_dims(input_t, 1)  # (batch,1,n)
            output_t, h, c = self.lstm(
                input_t, initial_state=self.initial_state, training=training)
            self.initial_state = h, c # 여기서 다음에 들어갈 initial state를 저장해 놓음.
            states.append(self.state)
            outputs.append(output_t) # 이렇게 모든 hidden state를 얻어서 반환할 수 있다.
        return states, outputs