티스토리 뷰

모두의 딥러닝

Recurrent Neural Network

강의-1

이번 강의는 Recurrent Neural Network이다. RNN으로도 불리는 학습법은 시간 개념이 + 된 학습법이라고 생각하면 된다.
보통 자연어 처리에서 많이 사용 되는 방법인데. 
자연어를 이해할 때는 하나의 언어 만을 가지고는 판단할 수 없다. 이전에 무슨 단어가 나왔는지에 따른 합(이전 단어와 현재 단어)으로 해당 문장이 의미하는 문장을 이해 할 수 있다.


위의 이미지(왼쪽)를 보면 하나의 화살표가 계속 재귀 하는 걸 알 수 있다. 이것이 CNN과 같은 학습법과의 차이점이다.




위의 이미지를 보면, RNN 어떻게 학습이 되는지 알 수 있다.

새로운 상태값은, 이전의 상태값과 입력값을 처리하는 함수를 통해 출력된다.


바닐라 RNN은 가장 기본적인 RNN이다. 이전에 사용하던 뉴럴 네트워크와는 다르게, 여기서는 tanh를 사용한다. 정확한 이유는 모르지만, 학습률이 좋은거 같다.

위의 이미지에서 Ht는 상태값을 나타낸다.

현재 상태값에 대한 함수의 구성은 이전값(Ht-1)*w과 입력값(Xt)*w의 합에 tanh을 처리한 것이다.

현재 상태값에 W를 곱해 결과값인 Yt가 나온다.


우리는 hello라는 단어를 h,e,l,l로 각각 입력을 넣어서 학습을 시켜볼 것 이다.

위의 이미지는 학습을 시킬 때, 모델이 어떻게되는지 보여 준다.


우선 우리가 사용할 단어는 4개 [h,e,l,o] 이다. 글자수가 4개이기 때문에, 우선 크기(벡터)는 4이다. 차례대로 h=(1,0,0,0), e=(0,1,0,0)... 와 같은 형태로 나타나는 걸 확인 할 수 있다.


tanh함수는 sigmoid를 개량한 버전인데, sigmoid는 0<=x<=1 의 값을 보내준다면, tanh함수는 -1<=x<=1 사이의 값을 보내준다. 그래서 hidden layer에 있는 값은 -1<=x<=1 사이의 값이 된다.


위의 이미지에서 보여지듯이, hidden layer 값은 이전 hidden layer + input data 가 된다. 물론, 첫번째 hidden layer는 ht-1값이 0이므로 Wxh*Xt = ht가 된다.

이미지를 보면 hidden layer에 값이 3개가 되는데 그 이유는 정확히 모르겠다... 알면 알려주세요...


이제는 output layer 즉, 결과값을 확인할 수 있다.

각 index가 높은 수(one-hot)가 output값으로 예측한 값이다. 그래서 해석을 해보면, 1번째 결과는 o, 2번째 결과는 o, 3번째 결과는 l, 4번째 결과는 o인걸 알 수 있다.


위의 이미지와 같이 RNN은 다양한 곳에서 사용할 수 있다.


위의 이미지는 RNN을 구성하는 방법들에 대한 이미지이다. 우선 

1. 가장 기본적인 One To One(Vanilla Neural Networks)

2. Image Captioning으로 사용하는 One To Many (한장의 이미지에 대해 여러개의 문장으로 해석하는 형태, "소년이 사과를 고르고 있다")

3. Sentiment Classification으로 사용되는 Many To One (여러개의 문장으로 구성된 글을 해석하여, 감정상태를 나타내는 구성, "슬픔", "기쁨"...)

4. Machine Translation으로 사용되는 Many To Many (여러개의 문장에서 각각의 문장들을 다른 언어로 해석해주는 형태, "Hello" -> "안녕")

5. Video classification으로 사용되는 Many To Many (여러개의 이미지에 대해 여러개의 설명, 번역을 하는 형태)

가 있다.


위의 이미지처럼 RNN 또한 Deep하게 구성할 수 있다.


LSTM이란 RNN이 가지고 있는 장기의존성 문제를 해결한 네트워크 구성 방식이다.

더욱 자세한 내용은 아래의 링크를 참조....

(https://brunch.co.kr/@chris-song/9)


GRU는 대한민국 조교수님이 만드신 방법이다.

(http://aikorea.org/blog/rnn-tutorial-4/)


Lab-1

RNN을 학습시키는 방법이다. BasicRNNCell이 기본적인 학습방법이나, BasicLSTMCell 방법을 추천한다. 이유는 LSTM이 성능이 좋다. 장기의존성 문제를 해결할 수 있으므로...


위의 이미지는 Hidden Size를 2로 설정하여, RNN학습을 시킨 결과를 나타낸다. 입력으로는 (1,1,4)의 형태로 입력되나, hidden size의 설정으로 인해 Output 형태는 (1,1,2)의 형태가 되는걸 알 수 있다.


이제 위의 이미지는 Sequence에 대해 알려준다. RNN의 특징은 이전의 상태값에 따라, 결과값이 달라진다는 점이다. 그래서 Sequence에 대한 설정이 필요한데, 위의 이미지는 Sequence를 5로 설정하여, Cell이 5개가 나타나는 걸 보여주는 이미지이다. 이로 인해, Input 데이터 형태도 (1,5,4)로 Ouput 형태도 (1,5,2)로 바뀐걸 알 수 있다.

위의 이미지는 batch_size에 대한 부분이다. hello, eolll, lleel으로 3가지 타입의 문장을 Input으로 넣는다. 그래서 (3, 5, 4)와 같은 형태가 된다.

소스로 구현한 모습이다.


Lab-2

이번 강의에서 우리는 Hi Hello를 학습시킬 계획이다.

위의 이미지는 RNN Cell을 풀어서 나타낸 형태이다. 위의 이미지로 Sequence는 6인걸 알 수 있다.


우선, RNN을 학습시키기 전, 각 Character에 대한 One-hot으로의 변환이 필요하다. hihello에서 Character의 종류는 5개이고, 이것을 One-hot으로 구현하면 위의 이미지와 같다.

위의 이미지는 RNN model 종류를 말해준다. 여기서 우린 LSTMCell을 사용한다.

위의 이미지를 하나씩 해석해보자. 우리는 우선 hihello를 학습시킨다. Character의 종류는 5개(input_dim)이고, 예측할 수 있는 단어의 수도 5개(hidden_size)이다.

학습시킬 문장은 1개(hihello)이고, 각각의 Character에 대한 결과값은 ihello로 6개(sequence_length)이다.


import tensorflow as tf
import numpy as np

idx2char = [
'h', 'i', 'e', 'l', 'o']

# Input 'hihell'
x_data = [[0, 1, 0, 2, 3, 3]]

# To make format fo one-hot
x_one_hot = [[[1, 0, 0, 0, 0],
             
[0, 1, 0, 0, 0],
             
[1, 0, 0, 0, 0],
              
[0, 0, 1, 0, 0],
             
[0, 0, 0, 1, 0],
             
[0, 0, 0, 1, 0]]]

# Label 'ihello'
y_data = [[1, 0, 2, 3, 3, 4]]

num_claases =
5
input_dim = 5
hidden_size = 5
sequence_length = 6
batch_size = 1
learning_rate = 0.1

# X.shape = (batch_size, sequence_length, voca_length)
X = tf.placeholder(tf.float32, [None, sequence_length, input_dim])
Y = tf.placeholder(tf.int32
, [None, sequence_length])

cell = tf.contrib.rnn.BasicLSTMCell(
num_units=hidden_size, state_is_tuple=True)

# Why? Zero? first state is 0.
initial_state = cell.zero_state(batch_size, tf.float32)

outputs
, state_ = tf.nn.dynamic_rnn(cell, X, initial_state=initial_state, dtype=tf.float32)

# outputs.shape = (batch_size, sequence_length, hidden_size)
X_for_fc = tf.reshape(outputs, [-1, hidden_size])
outputs = tf.contrib.layers.fully_connected(
inputs=X_for_fc, num_outputs=num_claases, activation_fn=None)

outputs = tf.reshape(outputs
, [batch_size, sequence_length, num_claases])

# Set rate of important indexes
weights = tf.ones([batch_size, sequence_length])

sequence_loss = tf.contrib.seq2seq.sequence_loss(
logits=outputs, targets=Y, weights=weights)
loss = tf.reduce_mean(sequence_loss)
train = tf.train.AdamOptimizer(
learning_rate=learning_rate).minimize(loss)

prediction = tf.argmax(outputs
, axis=2)

with tf.Session() as sess:
    sess.run(tf.global_variables_initializer())


   
for i in range(1):
        l
, _ = sess.run([loss, train], feed_dict={X: x_one_hot, Y: y_data})
        result = sess.run(prediction
, feed_dict={X: x_one_hot})
       
print(i, "loss:", l, "prediction: ", result, "true Y: ", y_data)

        result_str = [idx2char[c]
for c in np.squeeze(result)]
       
print("\tPrediction str: ", ''.join(result_str))

위의 이미지는 hihello를 구현한 코드이다.


idx2char = ['h', 'i', 'e', 'l', 'o']

x_data = [[0, 1, 0, 2, 3, 3]]

x_one_hot = [[[1, 0, 0, 0, 0],
             
[0, 1, 0, 0, 0],
             
[1, 0, 0, 0, 0],
              
[0, 0, 1, 0, 0],
             
[0, 0, 0, 1, 0],
             
[0, 0, 0, 1, 0]]]

y_data = [[1, 0, 2, 3, 3, 4]]

num_claases =
5
input_dim = 5
hidden_size = 5
sequence_length = 6
batch_size = 1
learning_rate = 0.1

우선 고유 character는 h, i, e, l, o 5개이다.


우리가 입력할 값은 h, i, h, e, l, l 6개이고, 

h = 0, i = 1, e = 2, l = 3, o = 4로 두고 one-hot으로 형태를 바꾸면 위의 x_one_hot과 같은 형태가 된다.


입력이 위와 같을 경우, y_data는 i, h, e, l, l, o가 된다.


Hi hello에서 고유한 단어의 개수를 세면 5개(h, i, e, l, o)이다. 그리고 우리는 이것을 num_classes로 정의 한다.

input_dim은 우리가 입력으로 넣을 수 있는 단어의 수이다. 우리가 넣을 수 있는 단어는 5개(h, i, e, l, o)이고 input_dim으로 정의 한다. 

hidden 값의 경우, 결과값에 각각의 확률 값이다. 우리가 예측할 수 있는 단어의 수는 5개 이므로 이 또한 5개(hidden_size)이다.

sequence의 경우, 각 각의 단어를 입력했을 때, 나올 수 있는 단어, cell의 개수를 말한다. 우리가 받을 데이터는 ihello이므로 총 6개(sequence_length)가 된다.

우리가 학습 시킬 단어는 hihello 하나이므로 batch는 1개가 되고, 마지막으로 learning rate는 0.1로 설정하겠다.


X = tf.placeholder(tf.float32, [None, sequence_length, input_dim])
Y = tf.placeholder(tf.int32
, [None, sequence_length])

cell = tf.contrib.rnn.BasicLSTMCell(
num_units=hidden_size, state_is_tuple=True)

initial_state = cell.zero_state(batch_size, tf.float32)

outputs
, state_ = tf.nn.dynamic_rnn(cell, X, initial_state=initial_state, dtype=tf.float32)

X_for_fc = tf.reshape(outputs, [-1, hidden_size])
outputs = tf.contrib.layers.fully_connected(
inputs=X_for_fc, num_outputs=num_claases, activation_fn=None)

X의 shape는 (batch size, sequen length, input dim)이 된다.

그리고 Y의 shape는 (batch size, sequence length)가 된다. 나중에 단어를 출력하기 위해, int 형으로 데이터형태를 지정해준다.


여러개의 학습형태가 있지만 우리는 LSTM을 사용하겠다. num_units으로 hidden_size 를 입력한다. (state_is_tuple에 대한 정확한 정의(https://www.tensorflow.org/api_docs/python/tf/contrib/rnn/LSTMCell)는 잘모르겠으나, 곧 없어질꺼라고 한다...)


다음으로 처음 상태값에 대해서는 0으로 설정해주겠다. cell_zero_state(batch_size, tf.float32)


dynamic_rnn으로 학습을 시키면 결과값(outputs), 상태값(state_)를 받을 수 있다.


fully_connected(https://www.tensorflow.org/api_docs/python/tf/contrib/layers/fully_connected)를 통해 학습시키기 위해, reshape를 해준다음, layers를 이용하여 학습을 시켜준다.(inputs은 입력값이고, num_outputs은 출력되는 character의 수, 그리고 activation_fn은 default일 경우, Relu로 처리되고, None인 경우, Skip된다.)


outputs = tf.reshape(outputs, [batch_size, sequence_length, num_claases])

# Set rate of important indexes
weights = tf.ones([batch_size, sequence_length])

sequence_loss = tf.contrib.seq2seq.sequence_loss(
logits=outputs, targets=Y, weights=weights)
loss = tf.reduce_mean(sequence_loss)
train = tf.train.AdamOptimizer(
learning_rate=learning_rate).minimize(loss)

prediction = tf.argmax(outputs
, axis=2)

with tf.Session() as sess:
    sess.run(tf.global_variables_initializer())


   
for i in range(1):
        l
, _ = sess.run([loss, train], feed_dict={X: x_one_hot, Y: y_data})
        result = sess.run(prediction
, feed_dict={X: x_one_hot})
       
print(i, "loss:", l, "prediction: ", result, "true Y: ", y_data)

        result_str = [idx2char[c]
for c in np.squeeze(result)]
       
print("\tPrediction str: ", ''.join(result_str))

sequence_loss함수를 사용하기 위해, outputs을 reshape(batch_size, sequence_length, num_classes)해준다.


각 값에 대한 중요도는 1로 공통적으로 주기 위해, weights = tf.ones([batch_size, sequence_length])로 처리한다.


우리는 각 sequence에 대한 loss값을 알아야 학습을 시킬 수 있다. 그것을 해결해 주는게,  seq2seq.sequence_loss이다.

각각의 sequence에 cross_entropy와 같이 학습을 시킬 수 있겠지만, 아주 복잡할 것이다.

그래서 우리는 sequence_loss를 사용하여 아주 간단하게 학습을 시킬 수 있다.

단, 나중 강의에서 나오는 부분이나, logits의 경우, 조금 가공?이 필요한데, 우선, 이번 강의에서는 그대로 넣어보겠다. 그리고 target은 정답(label), 그리고 각각 값에 대한 중요도 weight를 넣어준다.


sequence_loss에 대해 간략히 설명하는 이미지이다. 1번과 2번째 두개의 값으로 loss를 비교하니, 1번째 값보단 2번째 값이 더 loss가 좋을(낮을) 것이라고 추측할 수 있다. 그리고 sequence_loss를 동작하면, 예상과 같이 더 낮은 loss값은 2번째 값이란걸 알 수 있다.


나머지는 기존의 내용들과 크게 다르지 않으므로 생략하겠다.




Lab-3

이번 강의는 이전에 학습 시켰던 문장을 조금 더 길게 해서 학습시켜보겠다.

sample = " if you want you"

# set(sample) =  {'w', 'u', 'o', 'i', 'a', ' ', 't', 'f', 'n', 'y'} # list(set(sample)) ['w', 'u', 'o', 'i', 'a', ' ', 't', 'f', 'n', 'y'] idx2char = list(set(sample))
# {'u': 0, 'o': 1, 'y': 2, 'w': 3, 'n': 4, 't': 5, 'a': 6, 'i': 7, 'f': 8, ' ': 9} char2idx = {c: i for i, c in enumerate(idx2char)}
# basic setting dic_size = len(char2idx) hidden_size = len(char2idx) num_classes = len(char2idx) batch_size = 1 sequence_length = len(sample) - 1 learning_rate = 0.1
# [9, 7, 8, 9, 2, 1, 0, 9, 3, 6, 4, 5, 9, 2, 1, 0] sample_idx = [char2idx[c] for c in sample]
x_data = [sample_idx[:-
1]] y_data = [sample_idx[1:]]
X = tf.placeholder(tf.int32
, [None, sequence_length]) Y = tf.placeholder(tf.int32, [None, sequence_length])
x_one_hot = tf.one_hot(X
, num_classes)
cell = tf.contrib.rnn.BasicLSTMCell(
num_units=hidden_size, state_is_tuple=True)
initial_state = cell.zero_state(batch_size
, tf.float32)
outputs
, state_ = tf.nn.dynamic_rnn(cell, x_one_hot, initial_state=initial_state, dtype=tf.float32)
X_for_fc = tf.reshape(outputs
, [-1, hidden_size])
outputs = tf.contrib.layers.fully_connected(X_for_fc
, num_classes, activation_fn=None)
outputs = tf.reshape(outputs
, [batch_size, sequence_length, hidden_size])
weights = tf.ones([batch_size
, sequence_length])
sequence_loss = tf.contrib.seq2seq.sequence_loss(
logits=outputs, targets=Y, weights=weights)
loss = tf.reduce_mean(sequence_loss)
train = tf.train.AdamOptimizer(
learning_rate=learning_rate).minimize(loss)
prediction = tf.argmax(outputs
, axis=2)
with tf.Session() as sess:
    sess.run(tf.global_variables_initializer())
   
for i in range(50):
        l
, _ = sess.run([loss, train], feed_dict={X: x_data, Y: y_data})
        result = sess.run(prediction
, feed_dict={X: x_data})
        result_str = [idx2char[i]
for i in np.squeeze(result)]
       
print(i, "loss:", l, "Prediction:", ''.join(result_str))

위의 내용은 "if you want you" 를 학습 시키는 코드 이다. 사실 이 부분은 이전과 글자 수가 크게 달라지지 않았다.

하지만 추가된 내용이 몇개 있으므로, 이부분들을 설명하고 다음 단계로 넘어가자.


우선 sample은 " if you want you"이다. 왜 " "가 있을까? 우리가 얻고자 하고자 하는 값이 "if you want you" 이기 때문이다. 이 부분은 코드를 이해하는데 크게 중요한 부분은 아니나, 헤깔리수도 있기 때문에...


idx2char = list(set(sample))
char2idx = {c: i for i, c in enumerate(idx2char)}

# basic setting
dic_size = len(char2idx)
hidden_size = len(char2idx)
num_classes = len(char2idx)
batch_size = 1
sequence_length = len(sample) - 1
learning_rate = 0.1

# [9, 7, 8, 9, 2, 1, 0, 9, 3, 6, 4, 5, 9, 2, 1, 0]
sample_idx = [char2idx[c] for c in sample]
x_data = [sample_idx[:-1]]
y_data = [sample_idx[1:]]

X = tf.placeholder(tf.int32, [None, sequence_length])
Y = tf.placeholder(tf.int32, [None, sequence_length])

x_one_hot = tf.one_hot(X, num_classes)

 

위의 내용은 python을 이용해, 이전과는 다르게 X값과 Y값을 쉽게 출력하는 방법을 나타낸 코드이다.

set을 이용해 고유의 character 값을 추출한 다음. dictionary형태로 {character:number} 와 같은 형태로 데이터를 만든다.


해당 부분외에는 다른 부분은 기존과 다르지 않아 이해하는데 어려움이 없었다.


이제 위의 문장을 학습시켜보자. 학습 시키는 방법은 batch를 사용하여, 문장을 나눠서 학습을 시킨다는 점을 제외하고는 기존과 다르지 않다.


그러나 결과는... 잘 안된다. 이 부분에 대해서 다음 강의에서 알려주신다.


Lab-4

위의 문장을 학습시키는게 이번 강의의 핵심이다. 왜 기존의 방식을 적용하면 학습이 잘 안됐을까?


우리는 위의 이미지와 같은 wide & deep 에 대해서 다뤘다. RNN에서 역시, 이 부분을 적용 시킬 수 있는데. 한번 적용 시켜 보겠다.


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 = {c: i
for i, c in enumerate(char_set)}

data_dim =
len(char_set)
hidden_size =
len(char_set)
num_classes =
len(char_set)
# to do, language size is 10.
sequence_length = 10
learning_rate = 0.1

dataX = []
dataY = []

for i in range(0, len(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]
    y = [char_dic[c]
for c in y_str]

    dataX.append(x)
    dataY.append(y)

batch_size =
len(dataX)

X = tf.placeholder(tf.int32
, [None, sequence_length])
Y = tf.placeholder(tf.int32
, [None, sequence_length])

X_one_hot = tf.one_hot(X
, num_classes)

cell = tf.contrib.rnn.BasicLSTMCell(hidden_size
, state_is_tuple=True)
# Stacked RNN
multi_cells = tf.contrib.rnn.MultiRNNCell([cell] * 2, state_is_tuple=True)

outputs
, state_ = tf.nn.dynamic_rnn(cell, X_one_hot, dtype=tf.float32)

# SoftMax
X_for_softmax = tf.reshape(outputs, [-1, hidden_size])
softmax_w = tf.get_variable(
"softmax_w", [hidden_size, num_classes])
softmax_b = tf.get_variable(
"softmax_b", [num_classes])
outputs = tf.matmul(X_for_softmax
, softmax_w) + softmax_b

# Fully Connected
X_for_fc = tf.reshape(outputs, [batch_size, sequence_length, hidden_size])
outputs = tf.contrib.layers.fully_connected(X_for_fc
, num_classes, activation_fn=None)

outputs = tf.reshape(outputs
, [batch_size, sequence_length, num_classes])

weights = tf.ones([batch_size
, sequence_length])

sequence_loss = tf.contrib.seq2seq.sequence_loss(
logits=outputs, targets=Y, weights=weights)
loss = tf.reduce_mean(sequence_loss)
train = tf.train.AdamOptimizer(
learning_rate=learning_rate).minimize(loss)

sess = tf.Session()
sess.run(tf.global_variables_initializer())

for i in range(500):
    _
, l, results = sess.run(
        [train
, loss, outputs], feed_dict={X: dataX, Y: dataY})
   
for j, result in enumerate(results):
        index = np.argmax(result
, axis=1)
       
print(i, j, ''.join([char_set[t] for t in index]), l)

# Let's print the last char of each result to check it works
results = sess.run(outputs, feed_dict={X: dataX})
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='')

우선 코드를 보면 다음과 같다. 우리는 이전에 설명한 것과 같이 Deep하고 Wide중 Deep하게 한번 RNN을 구성해보겠다.


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 = {c: i
for i, c in enumerate(char_set)}

data_dim =
len(char_set)
hidden_size =
len(char_set)
num_classes =
len(char_set)
# to do, language size is 10.
sequence_length = 10
learning_rate = 0.1

# 배열로 처리하기 위한 리스트

dataX = []
dataY = []

for i in range(0, len(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)
    

# character에 해당되는 인덱스로 생성된 리스트를 만들어준다.
x = [char_dic[c] for c in x_str]
y = [char_dic[c] for c in y_str]

dataX.append(x)
dataY.append(y)

batch_size = len(dataX)


X = tf.placeholder(tf.int32, [None, sequence_length]) Y = tf.placeholder(tf.int32, [None, sequence_length])
X_one_hot = tf.one_hot(X
, num_classes)

우선 기존의 문장보단 길기 때문에, batch형태로 문장을 학습시켜보겠다. 해당 소스에 대한 설명은 소스에 주석으로 처리하였다.


# Stacked RNN

cell = tf.contrib.rnn.BasicLSTMCell(hidden_size, state_is_tuple=True) multi_cells = tf.contrib.rnn.MultiRNNCell([cell] * 2, state_is_tuple=True) outputs, state_ = tf.nn.dynamic_rnn(cell, X_one_hot, dtype=tf.float32)
# SoftMax X_for_softmax = tf.reshape(outputs, [-1, hidden_size]) softmax_w = tf.get_variable("softmax_w", [hidden_size, num_classes]) softmax_b = tf.get_variable("softmax_b", [num_classes]) outputs = tf.matmul(X_for_softmax, softmax_w) + softmax_b

RNN에서는 Deep하게 학습을 시킬 수 있도록, MultiRNNCell API를 제공한다.

LSTMCell을 단지, 첫번째 인수에 [cell]*n 을 통해 n개의 Layer를 만들 수 있다.


CNN과 같이 RNN 역시 softmax를 사용할 수 있는데, 실질적으로 RNN Cell은 하나의 Cell로 구성이 되어있다. 그래서 위의 이미지와 같이 reshape를 해서 그림과 같이 정렬하여 Softmax처리를 할 수 있다. 그리고, 다시 fully connected에 input하기 위한 shape를 만들어야되므로, reshape를 해준다. 위의 정렬에 대한 정확한 이해는 우선은 이렇게 된다고 기억해두는 정도로 하자.


# Fully Connected
X_for_fc = tf.reshape(outputs, [batch_size, sequence_length, hidden_size])
outputs = tf.contrib.layers.fully_connected(X_for_fc, num_classes, activation_fn=None)

outputs = tf.reshape(outputs, [batch_size, sequence_length, num_classes])

weights = tf.ones([batch_size, sequence_length])

sequence_loss = tf.contrib.seq2seq.sequence_loss(logits=outputs, targets=Y, weights=weights)
loss = tf.reduce_mean(sequence_loss)
train = tf.train.AdamOptimizer(learning_rate=learning_rate).minimize(loss)

sess = tf.Session()
sess.run(tf.global_variables_initializer())

for i in range(500):
_, l, results = sess.run(
[train, loss, outputs], feed_dict={X: dataX, Y: dataY})
for j, result in enumerate(results):
index = np.argmax(result, axis=1)
print(i, j, ''.join([char_set[t] for t in index]), l)

# Let's print the last char of each result to check it works
results = sess.run(outputs, feed_dict={X: dataX})
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='')


나머지 부분은 위와 같고 기존의 코드와는 다르지 않다.

그 결과는 위와 같다. 앞에 g가 아닌 f가 되어야되는 부분 빼고는 우리가 원하는 값과 다르지 않은 것을 확인할 수 있다.

RNN을 이용하여 만든 내용들이다, 좌측은 세익스피어 소설을 모델링으로 학습 시키고, 소설을 만든 내용이고, 우측은 개발 코드를 모델로 학습시켜, 코드를 개발한 모습이다.



Lab-5

다음은 Dynamic RNN에 대해 알아본다.
우리가 어떤 문장에 대해 번역을 할 때, 이 문장의 Sequence는 동일하지 않을 것이다. 이런 모델을 학습시킬 때 유용하게 쓰이는 방법이다.


위의 이미지와 같이 Hello, Hi, why를 학습 시키는 경우, 나머지 값에는 Padding이라는 값을 줘, 값을 0으로 만들어 버리는 형태이다.

방법은 아주 간단하다. 위의 이미지 처럼 sequence_length를 다르게 설정을 하면 된다.


위의 이미지는 dynamic_rnn을 이용하여 학습시키는 코드 이다.

특이한 점으로는 dynamic_rnn을 학습 시킬때, sequence_length 설정값을 5,3,4로 설정하여, sequence를 조절할 수 있는 부분이다.

Lab-6

이번 강의는 RNN을 이용하여 주식 주가를 예측하는 모델을 학습시켜 본다.

우선 우리가 사용할 데이터 셋이다.


모델은 그림과 같이 Many To One 형태이고, 8일 날의 마감 데이터를 예측해보기로 하겠다.


import tensorflow as tf
import numpy as np
import matplotlib.pyplot as plt


def MinMaxScaler(data):
   
# np.max(data, axis), np.min(data, axis)
   
numerator = data - np.min(data, 0)
    denominator = np.max(data
, 0) - np.min(data, 0)
   
return numerator / (denominator + 1e-7)


sequence_length =
7
data_dim = 5
# ?
hidden_dim = 10
output_dim = 1
learning_rate = 0.1

xy = np.loadtxt('data-stock_daily.csv', delimiter=',')
# reverse array
xy = xy[::-1]
xy = MinMaxScaler(xy)
x = xy
y = xy[:
, [-1]]

dataX = []
dataY = []
for i in range(0, len(y) - sequence_length):
    _x = x[i:i + sequence_length]
    _y = y[i + sequence_length]

    dataX.append(_x)
    dataY.append(_y)

# 70% is trainning data
train_size = int(len(dataY) * 0.7)
# 30% is test data
test_size = len(dataY) - train_size

trainX
, testX = np.array(dataX[0:train_size]), np.array(dataX[train_size:])
trainY
, testY = np.array(dataY[0:train_size]), np.array(dataY[train_size:])

X = tf.placeholder(tf.float32
, [None, sequence_length, data_dim])
Y = tf.placeholder(tf.float32
, [None, output_dim])

cell = tf.contrib.rnn.BasicLSTMCell(
num_units=hidden_dim, state_is_tuple=True, activation=tf.tanh)
outputs
, state_ = tf.nn.dynamic_rnn(cell, X, dtype=tf.float32)
print(outputs.shape)
Y_pred = tf.contrib.layers.fully_connected(outputs[:
, -1], output_dim, activation_fn=None)
print(Y_pred.shape)
loss = tf.reduce_sum(tf.square(Y_pred - Y))

train = tf.train.AdamOptimizer(
learning_rate=learning_rate).minimize(loss)

targets = tf.placeholder(tf.float32
, [None, 1])
predictions = tf.placeholder(tf.float32
, [None, 1])
rmse = tf.sqrt(tf.reduce_mean(tf.square(targets - predictions)))

with tf.Session() as sess:
    sess.run(tf.global_variables_initializer())

   
# Training
    
for i in range(500):
        _
, step_loss = sess.run([train, loss], feed_dict={
            X: trainX
, Y: trainY})
       
print("[step: {}] loss: {}".format(i, step_loss))

   
# Test
   
test_predict = sess.run(Y_pred, feed_dict={X: testX})
    rmse_val = sess.run(rmse
, feed_dict={
        targets: testY
, predictions: test_predict})
   
print("RMSE: {}".format(rmse_val))

    plt.plot(testY)
    plt.plot(test_predict)
    plt.xlabel(
"Time Period")
    plt.ylabel(
"Stock Price")
    plt.show()

코드는 위와 같다. 


def MinMaxScaler(data):
   
# np.max(data, axis), np.min(data, axis)
   
numerator = data - np.min(data, 0)
    denominator = np.max(data
, 0) - np.min(data, 0)
   
return numerator / (denominator + 1e-7)


sequence_length =
7
data_dim = 5
# ?
hidden_dim = 10
output_dim = 1
learning_rate = 0.1

xy = np.loadtxt('data-stock_daily.csv', delimiter=',')
# reverse array
xy = xy[::-1]
xy = MinMaxScaler(xy)
x = xy
y = xy[:
, [-1]]

dataX = []
dataY = []
for i in range(0, len(y) - sequence_length):
    _x = x[i:i + sequence_length]
    _y = y[i + sequence_length]

    dataX.append(_x)
    dataY.append(_y)

# 70% is trainning data
train_size = int(len(dataY) * 0.7)
# 30% is test data
test_size = len(dataY) - train_size

 

우선 값을 정규화 시키기 위해 Normalization[(요소값-최소값) / (최대값-최소값)]을 시키는 함수를 만들어 준다.


7일동안의 데이터를 가지고 학습을 하기 때문에 Cell은 7개 이고, sequence가 7이 된다.

우리가 넣을 입력 데이터는 오픈,마감,최대,최소,판매량? 5개이기 때문에 data_dim은 5가 된다.

hidden_dim은 LSTM을 거친 다음 output으로 나오는 값을 정하기 때문에, 어떤 값이든 상관 없다. (아니면 알려주세요...)

우리가 원하는 결과는 7일 이후의 마감 데이터이다. 그래서 output_dim은 1이 된다.


파이썬의 특징중 하나로 xy[::-1]과 같이 처리하면 xy의 배열이 거꾸로 정렬이 된다. (1,2,3)으로 되어있다면, (3,2,1)과 같은 형태로 정렬이 된다.


미리 만들어 둔, Normalization을 통해 값들을 정규화 시키고, 학습 데이터 70%, 테스트 데이터 30%로 분배를 시킨다.

(왜 정규화를 하는지는 ML의 실용과 팁에 설명되어있다.)


trainX, testX = np.array(dataX[0:train_size]), np.array(dataX[train_size:])
trainY
, testY = np.array(dataY[0:train_size]), np.array(dataY[train_size:])

X = tf.placeholder(tf.float32
, [None, sequence_length, data_dim])
Y = tf.placeholder(tf.float32
, [None, output_dim])

cell = tf.contrib.rnn.BasicLSTMCell(
num_units=hidden_dim, state_is_tuple=True, activation=tf.tanh)
outputs
, state_ = tf.nn.dynamic_rnn(cell, X, dtype=tf.float32)


Y_pred = tf.contrib.layers.fully_connected(outputs[:
, -1], output_dim, activation_fn=None)

loss = tf.reduce_sum(tf.square(Y_pred - Y))

train = tf.train.AdamOptimizer(
learning_rate=learning_rate).minimize(loss)

targets = tf.placeholder(tf.float32
, [None, 1])
predictions = tf.placeholder(tf.float32
, [None, 1])
rmse = tf.sqrt(tf.reduce_mean(tf.square(targets - predictions)))

 

학습 데이터와, 테스트 데이터를 분배 시키고, 기존과 같은 방법으로 학습을 시킨다.

여기서 주의할 부분은, loss 부분인데, FC(fully connected)를 통해 나오는 값이 1개 밖에 없으므로(우리가 원하는 결과값은 마감 때의 주가 이므로 1개의 결과값이다), 기존과는 달리 평균을 내지 않고, 값들을 더해준다.


그리고 얼마만큼 값이 차이가 나는지 확인하기 위해 rmse를 만들어준다. (정확하게 해당하는 변수가 뭘 나타내는지는 확인해봐야겠다.)


with tf.Session() as sess:
    sess.run(tf.global_variables_initializer())

   
# Training
    
for i in range(500):
        _
, step_loss = sess.run([train, loss], feed_dict={
            X: trainX
, Y: trainY})
       
print("[step: {}] loss: {}".format(i, step_loss))

   
# Test
   
test_predict = sess.run(Y_pred, feed_dict={X: testX})
    rmse_val = sess.run(rmse
, feed_dict={
        targets: testY
, predictions: test_predict})
   
print("RMSE: {}".format(rmse_val))

    plt.plot(testY)
    plt.plot(test_predict)
    plt.xlabel(
"Time Period")
    plt.ylabel(
"Stock Price")
    plt.show()

 

그리고 기존의 방법과 같이 학습을 시키고 테스트 데이터로 테스트 후, 해당 그래프를 나타내어 본다.

위의 이미지와 같이 학습 시킨 결과이다. 약간의 차이는 있지만, 생각보다는 괜찮은 것 같다.





































댓글
공지사항
최근에 올라온 글
링크
«   2024/05   »
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 27 28 29 30 31
글 보관함