상세 컨텐츠

본문 제목

딥러닝 (7) - RNN(Recurrent Neural Network), LSTM, GRU

IT/Deep Learning

by HarimKang 2020. 2. 24. 22:31

본문

Writer: Harim Kang

해당 포스팅은 '시작하세요! 텐서플로 2.0 프로그래밍'책의 흐름을 따라가면서, 책 이외에 검색 및 다양한 자료들을 통해 공부하면서 정리한 내용의 포스팅입니다. 해당 내용은 RNN, LSTM, GRU에 대한 내용을 담고 있습니다. 사용 라이브러리는 Tensorflow 2.0 with keras, sklearn입니다.

순환 신경망(RNN)

  • 순환 신경망(Recurrent Neural Network)은 은닉 계층 안에 하나 이상의 순환 계층을 갖는 신경망을 의미합니다.
  • 순환 신경망은 다른 네트워크들과 입력을 받는 방식에서 다릅니다. 순서가 있는 데이터에 주로 사용되며, 해당 데이터를 입력으로, 하나의 네트워크를 통해서 순서대로 출력을 얻습니다.
  • 순서가 있는 데이터는 소리, 언어, 날씨, 주가 등의 데이터처럼 시간의 변화에 함께 변화하면서 그 영향을 받는 데이터를 의미합니다.

순환 계층

  • 시계열 데이터 처리에 알맞게 고안된 퍼셉트론 계층입니다.
  • 가중치와 편향을 각 시간대 데이터에 반복해서 사용합니다.
  • 순환 벡터를 사용하여 정보를 전달합니다.

RNN의 구조

  • RNN은 되먹임 구조를 가지고 있습니다. 레이어의 출력을 다시 입력으로 받아서 사용하는 것으로서, 이전의 데이터가 함께 결과에 영향을 미칩니다.

  • RNN은 입력과 출력의 길이에 제한이 없다는 특징이 있습니다. 구조를 바꾸면 다양한 형태, 스타일의 네트워크를 형성할 수 있습니다.
  • 기본적으로 Fully connected 구조를 가지고 있습니다.

RNN 파라미터

  • RNN은 모든 시간대에 동일한 변수를 사용합니다.
  • units 파라미터는 RNN 신경망에 존재하는 뉴런의 개수입니다.

  • return_sequences

    • RNN 계산 과정에 있는 hidden state를 출력할 것인지에 대한 값을 의미합니다.

    • 해당 값은 다층으로 이루어진 RNN 또는 one-to-many, many-to-many 출력을 위해서 사용됩니다.

    • 위의 그림과 같이 False의 경우, 마지막 출력값 하나를 출력합니다. True의 경우, 모든 과정의 출력 값을 출력합니다.

  • return_state

    • LSTM 레이어에서 주로 사용되는 값으로, cell_state를 출력할 것인지를 결정하는 값입니다.
  • activation

    • 활성화 함수를 선언하는 변수입니다. tanh, relu 등과 같은 함수를 사용할 수 있습니다.

SimpleRNN 레이어

SimpleRNN 구조

  • 가장 간단한 형태의 RNN 레이어입니다. 구조는 아래의 그림과 같습니다.

  • 위의 그림에서 x를 입력, h를 출력이라고 했을 때, 각각의 x와 h는 순서가 있는 수열이라고 볼 수 있습니다.

SimpleRNN in Tensorflow 2.0

  • tf.keras.layers.SimpleRNN에서 import가 가능합니다.

    model = tf.keras.Sequential([
                               tf.keras.layers.SimpleRNN(units=1,
                                                         activation='tanh',
                                                         return_sequences=True,
                                                         return_state=True)
    ])
  • SimpleRNN 네트워크 예제

    • [0.0 0.1 0.2 0.3]이라는 0.1씩 늘어나는 수열을 줄 때, 이후의 값들도 예측을 하는 네트워크를 만들어보겠습니다.

    • Test 환경

        X = []
        Y = []
        for i in range(6):
          lst = list(range(i,i+4))
          X.append(list(map(lambda c: [c/10], lst)))
          Y.append((i+4)/10)
        X = np.array(X)
        Y = np.array(Y)
        print(X)
        print(Y)

      X에 0, 0.1, 0.2, 0.3 같은 수열들을 0.1씩 증가하면서 나열해줍니다. 해당 수열의 답인 Y는 0.4부터 0.1씩 증가하면서 들어가게 됩니다.

    • 모델 만들기 & 학습

        model = tf.keras.Sequential([
                                     tf.keras.layers.SimpleRNN(units=10,
                                                               return_sequences=False,
                                                               input_shape=[4,1]),
                                     tf.keras.layers.Dense(1)
        ])
        model.compile(optimizer='adam', loss='mse')
        history = model.fit(X, Y, epochs=100, verbose=0)

      모델은 Sequential에 SimpleRNN과 Dense레이어를 하나씩 추가해 주었고, input_shape는 4 time-step마다 하나의 답이라서 [4,1]을 선언하였습니다.

      위와 같은 학습을 반복하여서 MSE값을 낮추는 훈련을 하게 됩니다.

    • 예측

        print(model.predict(X))

      기존의 훈련 데이터 세트를 넣어서 예측을 해보겠습니다.

      위와 같이, 답에 가깝게 예측이 되는 것이 확인이 됩니다. 이제는 새로운 데이터를 넣어서 예측해봅시다.

        X_test = np.array([[[0.8],[0.9],[1.0],[1.1]]])
        print(model.predict(X_test))

      위의 데이터를 넣어서 원하는 결과는 1.2에 가까운 수입니다.

      그러나 위와 같은 결과를 얻었습니다. 훈련 데이터를 증가시켜서 해당 결과를 개선시킬 수 있습니다.

RNN의 문제점 : 장기 의존성

  • 기존의 RNN은 단점이 있습니다. 입력 데이터가 커지면, 학습 능력이 저하된다는 점입니다. 데이터의 뒤쪽으로 갈수록, 앞쪽의 입력 데이터를 까먹게 된다고 이해할 수 있습니다.
  • 입력 데이터와 출력 데이터 사이의 길이가 멀어질수록 연관 관계가 줄어듭니다. 이를 장기 의존성(Long-Term Dependency)문제 라고 합니다.

  • 장기의존성 문제를 해결하기 위해서 RNN의 변형 구조인 LSTM(Long Short Term Memory)가 나오게 되었습니다.

LSTM(Long Short Term Memory) 레이어

간단한 RNN의 구조로는 위와 같은 장기 의존성 문제가 발생합니다. 이를 해결하기위해서 제안된 RNN의 변형 구조가 바로 LSTM입니다.

LSTM의 구조

  • LSTM 레이어는 출력 값 이외에, LSTM셀 사이에서 공유되는 셀의 상태가 존재한다는 점이 특징입니다.

  • 위의 그림과 같이 기존 RNN에서 c라는 셀 상태(cell state)라는 변수가 추가적으로 공유가 됩니다.
  • 이러한 셀 상태가 함께 다음 레이어로 전달되면서, 기존의 상태를 보존하여 장기의존성 문제를 해결합니다.

활성화 함수는 두 가지로 아래와 같습니다.

  • tanh: 기존의 RNN에서 사용되는 활성화 함수입니다. 해당 함수는 relu로 대체될 수 있습니다.
  • sigmoid: 정보가 0과 1 사이에서 얼마나 통과할지를 결정하는 함수입니다.

좀 더 자세히 살펴보겠습니다.

  • 위의 그림처럼 LSTM 레이어는 Input Gate, Forget Gate, Output Gate라는 c(cell state)의 값을 제어하는 게이트들이 존재합니다. 각 게이트들은 sigmoid layer와 pointwise곱 연산을 통해서 값을 제어합니다.

  • 각각의 게이트들은 cell state를 보호하고 제어하는 역할을 합니다.

  • Input gate layer

    • sigmoid layer가 새로운 정보 중에 어떤 것을 cell state에 담을 것인지를 결정하는 게이트입니다.
    • 그다음으로 tanh layer에서 새로운 데이터의 후보 값을 만들어냅니다. 이것을 cell layer에 합칠 준비를 합니다.
  • Forget gate layer

    • 얼마나 잊어버릴지에 대한 가중치를 사용하여 잊어버리기로 정한 것을 실제로 잊는 게이트입니다.
    • 이전 state c에 forget gate 출력 값을 곱해서 state를 업데이트합니다. 또한 input 게이트를 통해 나온 출력 값들을 곱하여 state에 더합니다.
  • Output gate layer

    • 어떤 값을 출력할지를 결정하는 게이트입니다.
    • cell state의 어떤 부분을 출력할 지를 정하는 것이 바로 게이트의 sigmoid layer입니다.
    • cell state를 tanh 하여 나온 -1과 1 사이의 값을 sigmoid 출력 값과 곱하여 최종 출력 값을 연산합니다.

LSTM in Tensorflow 2.0

  • 텐서 플로우 2.0에서 앞서 정리한 SimpleRNN에서 LSTM으로 클래스 명만 바꾸어주면 됩니다.
    model = tf.keras.Sequential([
                               tf.keras.layers.LSTM(units=30,
                                                         return_sequences=True,
                                                         input_shape=[100,2]),
                                                           tf.keras.layers.LSTM(units=30),
                               tf.keras.layers.Dense(1)
    ])  
    model.compile(optimizer='adam', loss='mse')  
    history = model.fit(X, Y, epochs=100, verbose=0)
  • tf.keras.layers.LSTM 클래스를 사용하여 모델을 구성할 수 있습니다.

LSTM Project using Matlab

제가 전공 프로젝트를 했던 LSTM을 이용한 청바지 가격 예측 프로젝트입니다.

https://davinci-ai.tistory.com/11?category=907787

 

딥러닝 프로젝트 - Time Series Data Prediction (Matlab, LSTM)

Writer : Harim Kang 상품 온라인 판매 가격 데이터를 이용한 Time Series Data Prediction 프로젝트 포스팅입니다. Matlab 코드로 작성하였고, LSTM(Long Short-Term Memory models) 네트워크 모델을 사용하였습..

davinci-ai.tistory.com

GRU(Gated Recurrent Unit) 레이어

GRU는 LSTM과 비슷한 역할을 하지만, 더 간단한 구조로 이루어져 있어서 계산상으로 효율적입니다. 이것은 기존의 LSTM에서 사용되는 셀 상태 계산(은닉 상태 업데이트)을 줄였습니다. 또한 특정 문제에서는 LSTM보다 더 적합한 레이어입니다.

GRU 구조

  • GRU는 LSTM에서의 셀 상태(cell state) 역할의 c가 없습니다. cell state의 역할을 다음의 출력 h에서 그 역할을 함께 합니다.
  • GRU에서는 Update Gate, Reset Gate 두 가지만 존재합니다. 또한 사용되는 활성화 함수는 sigmoid 2번과 tanh 1번 사용됩니다.

좀 더 자세히 살펴보겠습니다.

  • 위의 그림처럼 GRU레이어는 Reset Gate, Update Gate들로 이루어져 있습니다. 각 게이트들은 sigmoid layer와 pointwise곱 연산을 통해서 값을 제어합니다.

  • Reset Gate

    • Reset Gate는 과거의 데이터를 리셋시키는 것을 목적으로 하는 게이트입니다. sigmoid연산을 통해 과거의 데이터를 얼마나 리셋시킬지에 대한 값인 r(0과 1 사이)을 출력합니다.
  • Update Gate

    • 과거와 현재의 정보 업데이트 비율을 결정하는 게이트입니다.
    • 출력 값 u는 현시점에서의 가져가야 할 데이터 양을 결정하는 값입니다. 1-u는 잊어버려야 할 데이터 양이라고 생각하면 됩니다.
  • 데이터 선정

    • 데이터를 이제 선정하는 단계입니다. 과거의 출력 값 데이터를 그대로 이용하지 않고, 리셋 데이터로 출력한 값을 이용하여 pointwise 곱 연산을 합니다.
    • 연산 결과를 tanh 함수를 한번 거치게 합니다.
  • 출력 값 계산

    • u는 데이터 중 얼마나 가져갈 것인지를, 1-u는 얼마나 잊을 것인지를 의미합니다.
    • tanh를 거친 h~는 현시점에서 리셋된 데이터를 의미하며 해당 데이터중 가져갈 것을 연산합니다.
    • 이전의 h에서 얼마나 잊을지를 연산하여 이것들을 모두 합한 값이 출력 값이 됩니다.

GRU in Tensorflow 2.0

  • LSTM과 같이 클래스명만 GRU로 바꾸어주면 됩니다.
    model = tf.keras.Sequential([
                               tf.keras.layers.GRU(units=30,
                                                         return_sequences=True,
                                                         input_shape=[100,2]),
                                                           tf.keras.layers.GRU(units=30),
                               tf.keras.layers.Dense(1)
    ])  
    model.compile(optimizer='adam', loss='mse')  
    history = model.fit(X, Y, epochs=100, verbose=0)

임베딩 레이어(Embedding Layer)

임베딩 레이어는 주로 자연어 처리에서 사용되며, 자연어를 수치화된 정보로 바꾸기 위한 레이어입니다.

임베딩이란?

  • 임베딩은 값, 텍스트 내의 단어들을 밀집 벡터(dense vector)로 만드는 것을 말합니다.
  • 단어를 의미론적 기하 공간에 매칭 시킬 수 있도록 수치 및 벡터화시키는 것을 의미합니다.
  • 밀집 벡터는 대부분의 값이 실수이고, 저차원 적인 벡터를 의미합니다. 원-핫 벡터와 같은 희소 벡터(0 또는 1로 이루어진 벡터)와 달리 훈련 데이터로부터 학습을 하는 벡터입니다.

자연어 처리를 위한 전처리

RNN은 자연어 처리에 좋은 신경망입니다. 하지만, 신경망은 기본적으로 숫자로 이루어진 가중치와 편향들로 구성되어있습니다. 이를 위해 우리는 자연어를 의미가 있는 숫자 벡터로 바꾸어주어야 합니다.

  • tf.keras.preprocessing.text.Tokenizer()

    • 토큰화와 정수 인코딩을 위해서 제공되는 클래스입니다.

      t = tf.keras.preprocessing.text.Tokenizer()  
      fit_text = "I love Deep Learning"  
      t.fit_on_texts([fit_text])
      
      test_text = "I like Deep Learning"  
      sequences = t.texts_to_sequences([test_text])[0]
      
      print("sequences : ",sequences) # great는 단어 집합(vocabulary)에 없으므로 출력되지 않는다.  
      print("word_index : ",t.word_index) # 단어 집합(vocabulary) 출력

      위와 같이 'I love Deep Learning'이라는 문장을 토크 나이저에 적용시키고 'I like Deep Learning'이라는 문장을 sequence로 변환시키면 아래와 같은 결과가 나옵니다.

      해당 토크 나이저는 i-1, love-2, deep-3, learning-4로 index가 지정되었고, 두 번째 문장에서는 1, 3, 4에 해당되는 단어들만 찾은 것을 알 수 있습니다.

  • tensorflow.keras.preprocessing.sequence.pad_sequence()

    • 데이터 세트에서 문장들의 길이는 서로 다를 가능성이 높습니다. 또한 각 문장마다 단어의 수는 다를 확률이 큽니다.

    • 모델을 학습시키기 위해서는 모든 데이터의 길이가 동일하여야 하는 경우가 많습니다. 이를 맞추기 위하여 패딩(padding) 작업을 합니다. 0을 넣어서 크기를 유지해 줍니다.

      tf.keras.preprocessing.sequence.pad_sequences([[1, 2, 3], [3, 4, 5, 6], [7, 8]], maxlen=3, padding='pre')
    • maxlen: 맞추고자 하는 길이를 의미합니다.

    • padding: 'pre'는 앞쪽에 패딩을 0으로 넣는 것을 의미하고, 'post'는 뒤쪽에 패딩을 0으로 넣는 것을 의미합니다.

      위와 같이 text to sequence 작업을 마친 데이터 3개를 넣으면 아래와 같은 패딩 작업이 이루어집니다.

Embedding in Tensorflow 2.0

model = tf.keras.Sequential([
                             tf.keras.layers.Embedding(2000, 128, input_length=X.shape[1]),
                             tf.keras.layers.SpatialDropout1D(0.4),
                             tf.keras.layers.LSTM(196, dropout=0.2, recurrent_dropout=0.2),
                             tf.keras.layers.Dense(2, activation='softmax')
])

model.compile(loss = 'categorical_crossentropy', optimizer='adam',metrics = ['accuracy'])
model.summary()

위와 같이, tf.keras.layers.Embedding클래스를 사용하여 선언할 수 있습니다.

  • input_dim: int, 단어의 크기를 의미합니다. 위의 예제에서 2000은 총 2000개의 단어 종류가 있다는 의미입니다.
  • output_dim: int, 임베딩의 출력 차원을 의미합니다. 임베딩 벡터의 차원은 주로 256, 512, 1024등의 차원을 가집니다.
  • input_length: 일정한 입력 데이터의 크기를 의미합니다. Flatten이전의 임베딩 시, 반드시 필요합니다.

RNN 실습

영화 리뷰를 사용한 텍스트 분류

https://www.tensorflow.org/tutorials/keras/text_classification

Naver sentiment movie Corpus v1.0

https://github.com/wikibook/tf2/blob/master/Chapter7.ipynb

First GOP Debate Twitter Sentiment

https://www.kaggle.com/crowdflower/first-gop-debate-twitter-sentiment

https://github.com/harimkang/tensorflow2_deeplearning/blob/master/RNN_kaggle.ipynb

 

harimkang/tensorflow2_deeplearning

tensorflow2, deep learning study example codes. Contribute to harimkang/tensorflow2_deeplearning development by creating an account on GitHub.

github.com

Reference

그림들은 대부분 다양한 정보를 바탕으로 직접 작성한 그림들입니다.
LSTM: http://colah.github.io/posts/2015-08-Understanding-LSTMs/
GRU: https://yjjo.tistory.com/18
Embedding: https://www.tensorflow.org/api_docs/python/tf/keras/layers/Embedding
https://wikidocs.net/32105

관련글 더보기