본문 바로가기
DeepLearning Framework & Coding/Tensorflow 2.X

[코드로 이해하는 딥러닝 2-13] - LSTM으로 문단 학습시키기

by 노마드공학자 2021. 1. 18.

[코드로 이해하는 딥러닝 0] - 글연재에 앞서 https://limitsinx.tistory.com/27

[코드로 이해하는 딥러닝 1] - Tensorflow 시작 https://limitsinx.tistory.com/28 

[코드로 이해하는 딥러닝 2] - Tensorflow 변수선언 https://limitsinx.tistory.com/29

[코드로 이해하는 딥러닝 3] - Tensorflow placeholder변수 https://limitsinx.tistory.com/30

[코드로 이해하는 딥러닝 4] - 선형회귀(Linear Regression) https://limitsinx.tistory.com/31

[코드로 이해하는 딥러닝 5] - 다중선형회귀(Multiple Linear Regression) https://limitsinx.tistory.com/32

[코드로 이해하는 딥러닝 6] - 회귀(Regression)에 대한 다른 접근 https://limitsinx.tistory.com/33

[코드로 이해하는 딥러닝 7] - .txt(.csv)파일 불러오기 https://limitsinx.tistory.com/34

[코드로 이해하는 딥러닝 8] - Logistic Regression(sigmoid) https://limitsinx.tistory.com/35

[코드로 이해하는 딥러닝 9] - Softmax Regression(multiple classification) https://limitsinx.tistory.com/36

[코드로 이해하는 딥러닝 10] - MNIST 데이터 분류/One hot encoding https://limitsinx.tistory.com/37

[코드로 이해하는 딥러닝 11] - Deep Neural Network/XOR https://limitsinx.tistory.com/38

[코드로 이해하는 딥러닝 11-EX] - MNIST를 DNN으로 학습해보기/Adam optimizer https://limitsinx.tistory.com/39

[코드로 이해하는 딥러닝 12] - RELU(Rectified Linear Unit) https://limitsinx.tistory.com/40

[코드로 이해하는 딥러닝 13] - .txt(.csv)파일로 저장하기 https://limitsinx.tistory.com/44

[코드로 이해하는 딥러닝 14] - Drop out https://limitsinx.tistory.com/45

[코드로 이해하는 딥러닝 15] - 초기화(Initialization)의 중요성 https://limitsinx.tistory.com/46

[코드로 이해하는 딥러닝 16] - CNN(Convolutional Neural Network) https://limitsinx.tistory.com/47

[코드로 이해하는 딥러닝 2-11] - RNN(Recurrent NN)/LSTM(Long Short Term Memory) https://limitsinx.tistory.com/62

[코드로 이해하는 딥러닝 2-12] - LSTM으로 문장 학습시키기 https://limitsinx.tistory.com/63

 

※이 전글에서 정리한 코드/문법은 재설명하지 않으므로, 참고부탁드립니다

※해당 글은 PC에서 보기에 최적화 되어있습니다.

 

출처 : https://www.researchgate.net/figure/RNN-and-LSTM-comparison-chart_fig3_332662013

 

RNN 첫번째 글은, " hi hello"라는 짧은 문장,

 

두번째 글은, "if you want you"라는 문장,

 

그리고 이번에는 문단을 학습시켜보겠습니다.

"if you want to build a ship, don't drum up people together to "
"collect wood and don't assign them tasks and work, but rather "
"teach them to long for the endless immensity of the sea."

※참고

Text를 학습시킬때는, 대문자와 소문자를 구별하셔야 합니다.

예를들어 "you"를 idx2char을 하면 3이 나오고, "youYOU" 라고하면 6이 나옵니다.

즉, 소문자와 대문자를 다른 데이터로 구분하여 저장해놓습니다.

 

 

[코드 전문]

 

import tensorflow as tf

import numpy as np

 

sentence = ("if you want to build a ship, don't drum up people together to "

            "collect wood and don't assign them tasks and work, but rather "

            "teach them to long for the endless immensity of the sea.")

 

char_set = list(set(sentence))

char_dic = {w: i for i, in enumerate(char_set)}

 

data_dim = len(char_set)

hidden_size = len(char_set)

num_classes = len(char_set)

sequence_length = 10  # Any arbitrary number

learning_rate = 0.1

 

dataX = []

dataY = []

for i in range(0len(sentence) - sequence_length):

    x_str = sentence[i:i + sequence_length]

    y_str = sentence[i + 1: i + sequence_length + 1]

    print(i, x_str, '->', y_str)

 

    x = [char_dic[c] for c in x_str]  # x str to index

    y = [char_dic[c] for c in y_str]  # y str to index

 

    dataX.append(x)

    dataY.append(y)

 

batch_size = len(dataX)

 

# One-hot encoding

X_one_hot = tf.one_hot(dataX, num_classes)

Y_one_hot = tf.one_hot(dataY, num_classes)

 

print(X_one_hot.shape)  # check out the shape (170, 10, 25)

print(Y_one_hot.shape)  # check out the shape



tf.model = tf.keras.Sequential();

tf.model.add(tf.keras.layers.

             LSTM(units=num_classes, input_shape=(sequence_length, X_one_hot.shape[2]), return_sequences=True))

tf.model.add(tf.keras.layers.LSTM(units=num_classes, return_sequences=True))

tf.model.add(tf.keras.layers.TimeDistributed(tf.keras.layers.Dense(units=num_classes, activation='softmax')))

tf.model.summary()

tf.model.compile(loss='categorical_crossentropy'optimizer=tf.keras.optimizers.Adam(lr=learning_rate),

                 metrics=['accuracy'])

tf.model.fit(X_one_hot, Y_one_hot, epochs=100)



results = tf.model.predict(X_one_hot)

for j, result in enumerate(results):

    index = np.argmax(result, axis=1)

    if j is 0:  # print all for the first result to make a sentence

        print(''.join([char_set[t] for t in index]), end='')

    else:

        print(char_set[index[-1]], end='')

 

 

[코드 분석-1]

해당 문단은, 전체 한문장이지만 적당히 큰따옴표(")를 사용하여 나누어주었습니다.

 

① char_set = list(set(sentence))

    char_dic = {w: i for i, w in enumerate(char_set)}

: Sentence에서 중복되는 알파벳을 제외하고, 모두 one hot encoding을 위해 list를 정리하는 코드

 

② sequence_length = 10

: 10개씩 끊어서 학습을 시키겠다는 의미로 batch_size와 갔다고 보시면됩니다.

이것은, 개발자 임의로 변경을 할 수 있는부분입니다.

학습을 진행할때는 이렇게 10개씩 끊어서 x_data와 y_data가 나뉘어 저장됩니다.

 

 

[코드 분석-2]

① dataX= [], dataY=[]

: 초기값으로는 빈 array로 선언

 

② for ~

: 전체 문장을 처음부터 제일끝의 sequence_length까지 학습시켜 주는걸 의미하는데요

 

예를들어 i love you를 3개단위로 학습시키면,

"i l", "lov", "ove" ....이렇게 가다가 마지막에는 "y  "가 학습되겠죠??

그런데 "y  "는 내가 원하는 학습데이터가아니죠.. 따라서 전체 문장길이에서 3을 빼준곳 까지 학습을 시키면

"you"까지 딱 학습이 완료되는 원리입니다.

 

나머지는 sequence_length단위로 끊어서 dataX.append(X)라는 함수를 통해 붙여주면서 학습데이터 셋을 만들어 놓는것입니다.

내부는 아래이미지와 같이 생겼습니다.                  dataX -> dataY

 

 

[결과값(softmax)]

첫 "i"만 주고 그문장 전체를 불러올 수 있나 검증해보는 것입니다.

정확도는 89%로 문장을 읽어보면 맨처음 m이 제대로 잘 학습이 안된걸 볼수있는데요

 

이건 다른 문장들 대비 학습횟수가 현저하게 적기 때문입니다.

 

무슨말이냐면, sequence_length대로 windowing을하면서 점점 지나가는데요, 문장 중간에 있는 단어들은 sequence_length의 갯수번만큼 학습을 진행하지만.. 맨처음 문장이 시작하는 부분들은 학습횟수가 작죠 

 

특히, 문장시작하는 "if"의 f는 2번밖에 학습을 못합니다. window가 지나가버리기 때문이죠..

따라서, 학습을 많이시켜도 이부분은 틀릴 확률이 다른부분 대비 높습니다.

 

나머지 문장들은 모두 맞네요 :)

 

[결과값(sigmoid)]

아 이제 확실히 softmax랑 차이가 나네요

 

문장 중간중간에 단어들이 틀린부분이 보이네요

teach를 toach라 썼다던가.. drum을 arum으로.. 등등...

 

똑같이 normalizing을 해도 0~100을 0~1사이로 줄여버리면 resolution이 0.01밖에 안되죠

 

하지만, softmax로 -1~1사이로 줄여버리면 resolution이 0.02입니다. 즉, 똑같은 normalizing을 해도 단어들간에 구분할 수 있는 간격이 넓으면 성능이 훨씬 좋게나오겠죠

 

문장이 길어지거나, 학습하는 데이터의 양이 많아질수록 이 차이는 점점 벌어질것입니다.

따라서, softmax로 사용하시는것을 권장드립니다!

 

[결과값(relu)]

네 뭐 볼것도 없지만, 어떻게 나오나 싶어서 한번해봤습니다.

relu는 문장의 길이랑 상관없이 발산해버리는 순간의 알파벳으로 그냥 처음부터끝까지 다 동일하게 예측해버리는것같네요..

CNN에서는 혁명적이었던 그 ReLU가... RNN에서는 이렇게 개차반이되다니..ㅠㅠ

 

 

Summary

 

RNN을 통해 Text나 Voice같은 Time sequential한 데이터들을 다루기에 아주 유용합니다!

 

유용하다기보단, 거의 유일한 해결책이죠 .. CNN이나 DNN은 시간에 대해 독립적으로 노는 데이터학습방법이니..

 

이번 글에서는 LSTM에서, 학습데이터의 양이 일정수준 이상이 되면 softmax가 sigmoid보다 성능이 훨씬 괜찮다는것을 확인해보았습니다.

댓글