본문 바로가기

파이썬/머신러닝

[#E20] Recurrent Neural Network (RNN - PART 2/3)

저번 [#E20 : Recurrent Neural Network (RNN - PART 1/3)] 포스트에서는 Long Sequence RNN를 직접 구현했습니다.


그러나 우리가 구현한 RNN은 테스트 시 좋은 성능을 내지 못하는데, 그 이유는 다음과 같습니다.


1. 셀의 크기가 너무 작다.


solution : 셀을 wide & deep 하게 구성한다. (use Deep Learning - Stacked RNN)


이것은 텐서플로우에서 제공하는 MultiRNNCell 메소드를 이용하여 쉽게 해결할 수 있습니다.


1
2
3
4
5
6
# 기존 1개의 Cell
cell = tf.contrib.rnn.BasicLSTMCell(hidden_size, state_is_tuple=True)
 
# 2개의 Cell
cell = tf.contrib.rnn.BasicLSTMCell(hidden_size, state_is_tuple=True)
cell = tf.contrib.rnn.MultiRNNCell([cell]*2, state_is_tuple=True)
cs


다음과 같이 기본 Cell을 구성하고, 구성한 Cell을 몇 개 쌓을 것인지만 임의의 숫자로 설정해주면 됩니다.

 

2. 가공되지 않은 output을 직접 사용한다.


solution : output을 SoftMax 가공한다. (use SoftMax Layer - FCLayer)


CNN (Convolution Neural Network) 를 사용하여 MNIST 데이터를 분류할 때 최종적으로 나온 3136개의 데이터를 FCLayer에 연결 및 학습했습니다.


이와 동일하게 RNN에서도 도출된 output을 FCLayer에 연결하여 사용할 수 있는데, 이것을 그림으로 표현하면 다음과 같습니다.



즉 RNN으로 도출된 output을 Softmax 함수가 이용할 수 있는 형태로 reshape한 후, Softmax 가공 후 다시 데이터를 펼치는 Reshape를 진행합니다.


각각의 과정은 다음과 같이 코드로 나타낼 수 있습니다.


1
2
3
4
5
6
7
8
9
10
# reshape(1)
X_for_softmax = tf.reshape(outputs, [-1, hidden_size])
 
# softmax (hidden_size -> num_classes)
softmax_w = tf.get_variable("softmax_w", [hidden_sizenum_classes])
softmax_b = tf.get_variable("softmax_b", [num_classes])
outputs = tf.matmul(X_for_softmax, softmax_w) + softmax_b
 
# reshape(2) -> expend the data (revive the batches)
outputs = tf.reshape(outputs, [batch_size, sequence_length, num_classes])
cs


Softmax 함수를 이용하기 위해서 H(x) = W*x + B의 형태를 구성합니다. W의 shape에서 입력값은 RNN output의 출력값과 같으므로 hidden_size입니다.


출력값 또한 RNN output의 출력값과 같아야 하므로 마찬가지로 hidden_size입니다.


(num_classes = hidden_size)


이외의 것은 저번의 RNN 구성과 동일합니다.


SOURCE CODE


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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
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.")
 
idx2char = list(set(sentence))
char2idx = {w: i for i, w in enumerate(idx2char)}
 
data_dim = len(idx2char)
hidden_size = len(idx2char)
num_classes = len(idx2char)
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 = [char2idx[c] for c in x_str]  # x str to index
    y = [char2idx[c] for c in y_str]  # y str to index
 
    dataX.append(x)
    dataY.append(y)
 
batch_size = len(dataX)
 
= tf.placeholder(tf.int32, [None, sequence_length])
= tf.placeholder(tf.int32, [None, sequence_length])
 
# One-hot encoding
X_one_hot = tf.one_hot(X, num_classes)
print(X_one_hot)  # check out the shape
 
cell = tf.contrib.rnn.BasicLSTMCell(hidden_size, state_is_tuple=True)
cell = tf.contrib.rnn.MultiRNNCell([cell]*30, state_is_tuple=True)
 
initial_state = cell.zero_state(batch_size, tf.float32)
 
outputs, _states = tf.nn.dynamic_rnn(
    cell, X_one_hot, initial_state=initial_state, dtype=tf.float32)
 
# reshape(1)
X_for_softmax = tf.reshape(outputs, [-1, hidden_size])
 
# softmax (hidden_size -> num_classes)
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
 
# reshape(2) -> expend the data (revive the batches)
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())
 
#learning
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([idx2char[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([idx2char[t] for t in index]), end='')
    else:
        print(idx2char[index[-1]], end=''# get last index
cs