RNN과 LSTM 을 공부하기 위해 아래의 포스팅을 참고하여 공부했습니다.
http://karpathy.github.io/2015/05/21/rnn-effectiveness/
기존의 CNN과 같은 신경망은 연결된 데이터를 다루기에는 적합하지 않는 딥러닝 방식입니다.
연결된 데이터란 예를 들어 I am a boy 와 같은 문장이 있다 할 때, 사실상 세상의 모든 문장을 하나의 input 데이터로 할 수 없기에
I, am, a, boy 와 같이 하나 하나의 단어를 input 데이터로 넣어 문장을 파악하려 할 것입니다.
이런데 이때 기존의 신경망을 이용할 경우 위 4개의 단어는 그저 각각의 단어로 인식될 뿐 연속적인 데이터에 대한 정보는 이용하지 못할 것입니다.
이런 문제를 해결하기 위해 I 다음의 am 이라는 단어를 input 으로 넣어 단어를 인식할 때 바로 앞의 I 나 뒤에 a, boy 를 같이 넣어 신경망에서 처리한다면
이 문제를 해결할 수 있을 것입니다. 이를 위해 아래와 같은 신경망을 구성하게 되고 이를 RNN 이라 합니다.
위 이미지에서 분홍색 네모는 input, 녹색은 rnn cell, 파란색은 output 입니다. 여기서 중요한 rnn cell 이 하는 일은 자신의 input 을 처리할 때 자신과 연결된 rnn cell 에서 온 값도 같이 이용하여 처리를 하는 것입니다.
RNN 은 다음과 같은 원리로 동작을 하게 됩니다.
https://en.wikipedia.org/wiki/Recurrent_neural_network
Variables and functions
- : input vector
- : hidden layer vector
- : output vector
- , and : parameter matrices and vector
- and : Activation functions
참고한 포스팅에서는 RNN 을 설명하기 위해 hello 라는 단어를 인식하는 신경망을 만드는 것을 예로 들었습니다.
이 신경망은 h 를 입력하면 l 을 반환하고 e 는 l, l은 l, 그리고 다음 l 에는 e 가 반환되게 하는 것이 목표인 신경망입니다.
기존의 신경망이라면 3번째 문자열 l 과 4번째 문자열 l 의 차이점을 알 수 없기에 여기서 기존의 신경망과 RNN 의 차이를 알 수 있습니다.
다음으로 LSTM 이란 RNN의 변형 방법 중 하나로써 학습이 이뤄지는 back propagation 에서 loss 값이 잘 유지되며 전달되게 만든 방법입니다.
LSTM 의 각 cell 은 3개의 gate(write, read, keep)로 구성되어 있고 각 gate는 0~1 사이의 값을 가지고 있으며 이 값을 통해 cell 의 정보를 저장할지 불러올지 유지할지 결정합니다. 이 gate 의 0~1사이의 값은 신경망의 weighs 들과 마찬가지 원리로 학습됩니다. 사실 RNN 과 마찬가지로 LSTM 도 일일이 구현을 하기 까다로운 방법이지만 tensorflow 등과 같은 프레임워크를 사용하면 아래와 같이 쉽게 이용할 수 있습니다.
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 | import tensorflow as tf x_data = [ [1., 0., 0., 0.], # h [0., 1., 0., 0.], # e [0., 0., 1., 0.], # l [0., 0., 1., 0.], # l ] y_data = [ 1, #e 2, #l 2, #l 3 #o ] targets = tf.reshape(y_data[:], [-1]) # Parameters learning_rate = 0.01 time_step_size = 4 rnn_num_units = 4 # RNN lstm_cell = tf.contrib.rnn.BasicLSTMCell(rnn_num_units) x = tf.split(x_data, time_step_size) outputs, state = tf.contrib.rnn.static_rnn(lstm_cell, x, dtype=tf.float32) weights = tf.ones([time_step_size]) logits = tf.reshape(tf.concat(outputs, 1), [-1, rnn_num_units]) loss = tf.contrib.legacy_seq2seq.sequence_loss_by_example([logits], [targets], [weights]) cost = tf.reduce_sum(loss) update = tf.train.AdamOptimizer(learning_rate=learning_rate).minimize(cost) with tf.Session() as sess: sess.run(tf.global_variables_initializer()) for i in range(1000): sess.run(update) result = sess.run(tf.arg_max(logits, 1)) print(i, result) | cs |
먼저 input 데이터 x_data 를 one-hot-encoding 방식으로 보기 좋기 list 에 담아둡니다, 여기서 사용되면 문자열은 h e l o 밖에 없으므로 h->0, e->1, l->2, o->3 으로 정의합니다.
다음으로 label Y 인 y_data 도 마찬가지로 list 에 담아둡니다. 첫 번째 h에 대한 라벨은 e 이므로 1, 두 번째 e 에 대한 라벨은 l 이므로 2 와 같은 식으로 1,2,2,3 을 라벨로 만듭니다.
이 y_data 을 tensorflow 에서 사용하기 위해 reshape 하여 1차원의 배열의 tensor 로 만듭니다.
x_data = [ [1., 0., 0., 0.], # h [0., 1., 0., 0.], # e [0., 0., 1., 0.], # l [0., 0., 1., 0.], # l ] y_data = [ 1, #e 2, #l 2, #l 3 #o ] targets = tf.reshape(y_data[:], [-1]) | cs |
다음으로 필요한 인자값들을 정의합니다
여기서 time_step_size 란 아래 이미지와 같이 RNN cells 의 옆으로 늘어선 개수입니다.
rnn_num_units 은 RNN 의 각 cell 에 몇 개의 unit 을 만들지에 대한 값입니다. 여기서는 공교롭게 time_step_size 와 값이 같아 햇갈릴 수 있지만 rnn_num_units 값을 8과 같은 값으로 변경해도 무관합니다. 하지만 실제 학습 시 4가 적당한 값임을 알 수 있습니다.
# Parameters learning_rate = 0.01 time_step_size = 4 rnn_num_units = 4 | cs |
다음으로 RNN 을 구성하는 부분입니다.
먼저 cell 을 LSTM 방식으로 만듭니다. 여기서는 tf.contrib.rnn.BasicLSTMCell 함수를 사용했습니다.
다음으로 x 를 tensorflow 에서 사용하기 위해 1,4 의 배열을 4개 가지고 있는 리스트로 만듭니다.
그리고 만들어진 cells 와 x 로 RNN 신경망을 구성합니다. 함수는 tf.contrib.rnn.static_rnn 을 사용합니다.
여기서 output을 얻을 수 있는데 역시 1,4 의 배열을 4개 가지고 있는 리스트입니다.
다음으로 원소를 4개 가지고 있는 1차 배열의 weights 을 만듭니다.
# RNN lstm_cell = tf.contrib.rnn.BasicLSTMCell(rnn_num_units) x = tf.split(x_data, time_step_size) outputs, state = tf.contrib.rnn.static_rnn(lstm_cell, x, dtype=tf.float32) weights = tf.ones([time_step_size]) | cs |
이제 만들어진 신경망을 이용해 학습을 구성하는 부분입니다.
먼저 predict Y 값을 4,4의 배열로 logits 에 reshape 하여 담아둡니다.
loss 는 tf.contrib.legacy_seq2seq.sequence_loss_by_example 함수를 사용하여 쉽게 계산합니다.
cost 는 loss 의 합으로 합니다.
만들어진 cost 를 통해 AdamOptimizer 으로 학습합니다.
logits = tf.reshape(tf.concat(outputs, 1), [-1, rnn_num_units]) loss = tf.contrib.legacy_seq2seq.sequence_loss_by_example([logits], [targets], [weights]) cost = tf.reduce_sum(loss) update = tf.train.AdamOptimizer(learning_rate=learning_rate).minimize(cost) | cs |