순환신경망을 텐서프로로 구현해보록 부록준비
합성곱신경망은 이미지들 간의 변화를 예측하고자 할 때는 적용하기 어려움.
연속된 데이터를 이용하여 앞으로 다가올 데이터를 추측, 새로운 데이터에 대해 효과적 판단을 필요로 하는 문제에 적합.
예시: 언어모델링, 자연어처리, 번역 등
순환신경망 Recurrent Neural Network(RNN)
순환의 의미: 신경망의 뉴런에서 나온 정보가 다시 재사용되도록 순환됨.
이전의 데이터를 사용하여 학습된 뉴런의 어떤 상태 정보가 다음 데이터를 이용하여 뉴런을 학습시킬 때 다시 사용.
메모리 셀(memory cell), 셀(cell): 순환신경망에서 사용하는 뉴런.
은닉 상태 hidden state
셀에서 만들어지는 상태 데이터
이전의 은닉 상태와 현재 입력 값을 어떤 가중치 W로 곱하고 편향 값 b를 더하는 것.
ht=tanh(W(ht−1,xt)+b)기본적인 순환 신경망은 단기기억(short-term memory)을 저장할 수 있다.
기본적인 순환 신경망은 비교적 짧은 거리에 문맥의 정보를 실어 나름.
=> 멀리 떨어진 데이터 간의 연관정보 파악이 어려움.
LSTM(long short-term memory) 순환 신경망 알고리즘
단기기억을 더 길게 유지시켜주는 알고리즘.
은닉상태와 셀 상태(cell state) 두 가지를 계산.
은닉상태(h)는 상위 계층의 입력으로 전달.
셀 상태(c)는 상위 계층으로 전달되지 않음.
p=W(ht−1,xt)+b이전 셀의 은닉 상태와 현재 입력 값에 가중치를 곱한 선형 계산의 결과 = p
삭제게이트: 이전 셀의 값 중 삭제해야 할 정보를 학습하기 위한 것.
입력게이트: 새롭게 추가되어야 할 정보를 학습하게 도와 줌.
ft=ct−1∗sigmoid(pf)삭제게이트는 이전 셀 상태와 p에 시그모이드 활성화 함수를 적용한 것을 곱해 계산
it=sigmoid(pi)∗tanh(pj)입력게이트는 p에 시그모이드 활성화 함수를 적용한 것과 p에 tanh를 적용한 것을 곱해 계산
ct=ft+it현재 셀 상태는 삭제 게이트 결과와 입력 게이트 결과를 더해서 구함.
ht=tanh(ct)∗sigmoid(po)은닉상태 계산은 현재의 셀 상태에 tanh 활성화 함수 적용 * p에 시그모이드 활성화 함수를 적용
드롭아웃
일반적인 피드포워드(feed forward) 신경망에서 모델이 학습 데이터에 과다하게 치중되어 만들어지는 것을 막아주는 좋은 방법
순환 신경망의 출력 값의 흐름 중 수직 방향에 대해서만 드롭아웃을 적용.
텐서플로 공식 튜토리얼 순환신경망 문서
https://www.tensorflow.org/versions/master/tutorials/recurrent/
팬 트리 뱅크(Penn Treebank) 데이터 셋을 이용하여 언어 모델을 학습
텐서플로 model 깃허브 소스: https://github.com/tensorflow/models/tree/0d9a3abdca7be4a855dc769d6d441a5bfcb77c6d/tutorials/rnn/ptb
학습에 필요한 데이터 다운로드
wget http://www.fit.vutbr.cz/~imikolov/rnnlm/simple-examples.tgz tar xvf simple-examples.tgz
ptb_word_lm.py: 텐서플로 모델을 만들고 학습시키는 주요 파일
reader.py: 학습데이터를 읽거나 전처리하는 데 사용하는 유틸리티 파일
ptb.train.txt: 모델을 학습시킬 때 사용
ptb.valid.txt: 모델을 검증
ptb.test.txt: 모델의 성능을 평가
GPU가 없는 개인용 컴퓨터에서 예제를 실행하려면 SmallConfig 설정을 선택하는 것을 권장
class SmallConfig(object): """Small config.""" init_scale = 0.1 learning_rate = 1.0 max_grad_norm = 5 num_layers = 2 num_steps = 20 hidden_size = 200 max_epoch = 4 max_max_epoch = 13 keep_prob = 1.0 lr_decay = 0.5 batch_size = 20 vocab_size = 10000
init_scale: 가중치 행렬을 랜덤하게 초기화할 때 생성되는 값의 범위 지정
learning_rate: 경사 하강법을 사용할 때 학습 속도를 조절하기 위한 변수
학습속도: learning_rate * lr_decay를 곱해 구해짐. lr_decay가 학습이 반복될 때마다 작아짐
max_grad_norm: 구해진 기울기(gradient) 값이 과다하게 커지는 것을 막아주기 위한 상한 설정하는데 사용
num_layers: 순환 신경망을 구성할 계층의 개수
hidden_size: 한 계층에 배치할 뉴런의 수
순환신경망 전체 뉴런수 = num_layers(2) * hidden_size(200) = 400
num_step: 셀의 가중치를 학습시킨 후 경사 하강법을 사용하여 기울기를 업데이트
keep_prob: 드롭아웃하지 않을 확률을 지정
batch_size: 미니배치 데이터 크기를 지정
reader.py ptb_raw_data 함수를 사용하여 데이터를 읽는 것으로 시작
단어가 나타나는 횟수가 높은 순으로 저열
정렬된 단어 순으로 차례대로 번호를 부여
ptb 관련 txt에 나타나는 단어를 모두 숫자로 바꿀 수 있음
ptp_raw_data를 통해 받는 데이터는 단어를 숫자로 바꾼 리스트
ptb_iterator: 학습 데이터를 배치 개수로 나누어 num_step 크기만큼 나누어 읽어오는 역할
순환 신경망에서는 순차적인 데이터를 처리해야하기 때문에 미니 배치 데이터 추출 방법을 조금 다르게 처리
예시) 문장 단위로 나누어 임의의 순서대로 섞을 수 있음
학습, 검증, 테스트에 모두 동일한 모델을 사용
PTBModel 클래스를 만들어 설정을 바꾸어가며 학습, 검증, 테스트에서 사용
__init__에서 텐서플로를 사용하여 신경망 모델을 모두 구성
BasicSTMCell 클래스를 이용하여 LSTM 셀을 생성
MultiRNNCell 클래스를 이용해 두 개의 계층을 가진 순환 신경망을 만듬
단어의 벡터 표현(word2vec)을 만들기 위해 단어 임베딩(embedding) 작업을 수행
https://www.tensorflow.org/versions/master/tutorials/word2vec/
state_is_tuple을 True로 설정. 셀 상태와 은닉 상태 데이터를 튜플로 관리
BasicSTMCell 셀 상태, 은닉 상태, 각 셀에서 계산할 가중치 행렬 모두 관리
입력 데이터가 20개의 배치에서 하나씩 선택되어 전달
셀의 상태 데이터도 그에 따라 20개의 배치에 대응하여 상태 데이터가 섞이지 않도록 따로 구분하여 저장
# 각 배치마다 순서대로 데이터를 뽑아 셀에 입력합니다. outputs = [] state = self.initial_state with tf.variable_scope("RNN"): for time_step in range(config.num_steps): if time_step > 0: tf.get_variable_scope().reuse_variables() (cell_output, state) = cell(inputs[:, time_step, :], state) outputs.append(cell_output)
cell_output 값은 소프트맥스 함수를 거쳐 만 개의 단어 중 어떤 단어를 예측하는 결과를 만듬.
sequence_loss_by_example을 사용하여 소프트맥스 함수와 크로스 엔트로피 계산을 처리.
# 기울기 클리핑을 수행합니다. grads, _ = tf.clip_by_global_norm(tf.gradients(self.cost, tvars), config.max_grad_norm) optimizer = tf.train.GradientDescentOptimizer(self.lr) self.train_op = optimizer.apply_gradients(zip(grads, tvars))
경사 하강법으로 구해진 기울기를 config.max_grad_norm 기준으로 클리핑.
경사 하강법 최적화 객체를 생성해서 모델의 매개변수를 학습시킴
run_epoch: 텐서플로 모델을 실행시키고 셀에서 나온 은닉 상태와 셀 상태의 데이터를 업데이트하는 역할
max_max_epoch 값만큼 반복을 진행. 오차를 최소화.
session.run() 메서드에 들어갈 매개변수는 cost, train_op, final_state 튜플에 담겨 있는 두 계층의 셀 상태와 은닉 상태 데이터임.
학습의 결과는 언어 모델링에서 자주 사용되는 혼잡도(perplexity)를 계산하여 평가
혼잡도(perplexity): 배치 반복 안에서 누적된 오차 값을 진행된 num_steps를 합한 값으로 나눈 것.
소스참조: https://github.com/rickiepark/first-steps-with-tensorflow/blob/master/rnn_ptb/ptb_word.ipynb
import time
import numpy as np
import tensorflow as tf
from tensorflow.python.ops.rnn_cell import BasicLSTMCell, MultiRNNCell, DropoutWrapper
#from tensorflow.models.rnn.ptb import reader # 텐서플로우 0.12 기준으로 reader.ptb_iterator 오류 발생.
import reader # 오류 발생시키지 않도록 하기 위해 reader.ptb_iterator를 사용가능한 reader 모듈로 변경.
Small Config 정보를 사용합니다.
class SmallConfig(object):
"""Small config."""
init_scale = 0.1
learning_rate = 1.0
max_grad_norm = 5
num_layers = 2
num_steps = 20
hidden_size = 200
max_epoch = 4
max_max_epoch = 13
keep_prob = 1.0
lr_decay = 0.5
batch_size = 20
vocab_size = 10000
트레이닝과 테스트에 사용할 두개의 config 오브젝트를 만듭니다.
config = SmallConfig()
eval_config = SmallConfig()
eval_config.batch_size = 1
eval_config.num_steps = 1
PTB 모델을 만들어 주는 클래스를 작성합니다.
class PTBModel(object):
"""The PTB model."""
def __init__(self, config, is_training=False):
self.batch_size = config.batch_size
self.num_steps = config.num_steps
input_size = [config.batch_size, config.num_steps]
self.input_data = tf.placeholder(tf.int32, input_size)
self.targets = tf.placeholder(tf.int32, input_size)
lstm_cell = BasicLSTMCell(config.hidden_size, forget_bias=0.0, state_is_tuple=True)
# SmallConfig에서는 드롭아웃이 적용되지 않습니다.
if is_training and config.keep_prob < 1:
lstm_cell = DropoutWrapper(lstm_cell, config.keep_prob)
# 두개의 계층을 가진 신경망 구조를 만듭니다.
cell = MultiRNNCell([lstm_cell] * config.num_layers, state_is_tuple=True)
self.initial_state = cell.zero_state(config.batch_size, tf.float32)
with tf.device("/cpu:0"):
embedding_size = [config.vocab_size, config.hidden_size]
embedding = tf.get_variable("embedding", embedding_size)
inputs = tf.nn.embedding_lookup(embedding, self.input_data)
# SmallConfig에서는 드롭아웃이 적용되지 않습니다.
if is_training and config.keep_prob < 1:
inputs = tf.nn.dropout(inputs, config.keep_prob)
# 각 배치마다 순서대로 데이터를 뽑아 셀에 입력합니다.
outputs = []
state = self.initial_state
with tf.variable_scope("RNN"):
for time_step in range(config.num_steps):
if time_step > 0: tf.get_variable_scope().reuse_variables()
(cell_output, state) = cell(inputs[:, time_step, :], state)
outputs.append(cell_output)
# output의 크기를 20x20x200에서 400x200으로 변경합니다.
output = tf.reshape(tf.concat(1, outputs), [-1, config.hidden_size])
softmax_w_size = [config.hidden_size, config.vocab_size]
softmax_w = tf.get_variable("softmax_w", softmax_w_size)
softmax_b = tf.get_variable("softmax_b", [config.vocab_size])
# logits의 크기는 400x10000이 됩니다.
logits = tf.matmul(output, softmax_w) + softmax_b
loss = tf.nn.seq2seq.sequence_loss_by_example(
[logits],
[tf.reshape(self.targets, [-1])],
[tf.ones([config.batch_size * config.num_steps])])
self.cost = tf.reduce_sum(loss) / config.batch_size
self.final_state = state
if not is_training:
return
self.lr = tf.Variable(0.0, trainable=False)
tvars = tf.trainable_variables()
# 기울기 클리핑을 수행합니다.
grads, _ = tf.clip_by_global_norm(tf.gradients(self.cost, tvars),
config.max_grad_norm)
optimizer = tf.train.GradientDescentOptimizer(self.lr)
self.train_op = optimizer.apply_gradients(zip(grads, tvars))
def assign_lr(self, session, lr_value):
session.run(tf.assign(self.lr, lr_value))
에포크를 처리할 함수를 만듭니다.
def run_epoch(session, m, data, is_training=False):
"""Runs the model on the given data."""
epoch_size = ((len(data) // m.batch_size) - 1) // m.num_steps
start_time = time.time()
costs = 0.0
iters = 0
eval_op = m.train_op if is_training else tf.no_op()
# initial_state는 2x20x200 크기의 튜플입니다.
state_list = []
for c, h in m.initial_state:
state_list.extend([c.eval(), h.eval()])
ptb_iter = reader.ptb_iterator(data, m.batch_size, m.num_steps)
for step, (x, y) in enumerate(ptb_iter):
fetch_list = [m.cost]
# final_state 튜플에 담겨있는 상태를 꺼내어 fetch_list에 담습니다.
for c, h in m.final_state:
fetch_list.extend([c, h])
fetch_list.append(eval_op)
# 이전 스텝에서 구해진 state_list가 feed_dict로 주입됩니다.
feed_dict = {m.input_data: x, m.targets: y}
for i in range(len(m.initial_state)):
c, h = m.initial_state[i]
feed_dict[c], feed_dict[h] = state_list[i*2:(i+1)*2]
# fetch_list에 담긴 final_state의 결과를 state_list로 전달 받습니다.
cost, *state_list, _ = session.run(fetch_list, feed_dict)
costs += cost
iters += m.num_steps
if is_training and step % (epoch_size // 10) == 10:
print("%.3f perplexity: %.3f speed: %.0f wps" %
(step * 1.0 / epoch_size, np.exp(costs / iters),
iters * m.batch_size / (time.time() - start_time)))
return np.exp(costs / iters)
raw_data = reader.ptb_raw_data('simple-examples/data')
train_data, valid_data, test_data, _ = raw_data
with tf.Graph().as_default(), tf.Session() as session:
initializer = tf.random_uniform_initializer(-config.init_scale, config.init_scale)
# 학습과 검증, 테스트를 위한 모델을 만듭니다.
with tf.variable_scope("model", reuse=None, initializer=initializer):
m = PTBModel(config, is_training=True)
with tf.variable_scope("model", reuse=True, initializer=initializer):
mvalid = PTBModel(config)
mtest = PTBModel(eval_config)
tf.global_variables_initializer().run()
for i in range(config.max_max_epoch):
# lr_decay는 반복속도를 조절해 주는 역할을 합니다.
lr_decay = config.lr_decay ** max(i - config.max_epoch, 0.0)
m.assign_lr(session, config.learning_rate * lr_decay)
print("Epoch: %d Learning rate: %.3f" % (i + 1, session.run(m.lr)))
perplexity = run_epoch(session, m, train_data, is_training=True)
print("Epoch: %d Train Perplexity: %.3f" % (i + 1, perplexity))
perplexity = run_epoch(session, mvalid, valid_data)
print("Epoch: %d Valid Perplexity: %.3f" % (i + 1, perplexity))
perplexity = run_epoch(session, mtest, test_data)
print("Test Perplexity: %.3f" % perplexity)
Epoch: 1 Learning rate: 1.000 0.004 perplexity: 5820.573 speed: 672 wps 0.104 perplexity: 853.859 speed: 612 wps 0.204 perplexity: 633.710 speed: 616 wps 0.304 perplexity: 513.863 speed: 617 wps 0.404 perplexity: 445.048 speed: 628 wps 0.504 perplexity: 398.785 speed: 636 wps 0.604 perplexity: 358.686 speed: 636 wps 0.703 perplexity: 331.047 speed: 634 wps 0.803 perplexity: 309.146 speed: 632 wps 0.903 perplexity: 289.053 speed: 623 wps Epoch: 1 Train Perplexity: 274.015 Epoch: 1 Valid Perplexity: 178.517 Epoch: 2 Learning rate: 1.000 0.004 perplexity: 212.092 speed: 547 wps 0.104 perplexity: 152.457 speed: 617 wps 0.204 perplexity: 159.878 speed: 602 wps 0.304 perplexity: 154.768 speed: 582 wps 0.404 perplexity: 151.954 speed: 586 wps 0.504 perplexity: 149.485 speed: 601 wps 0.604 perplexity: 144.749 speed: 611 wps 0.703 perplexity: 142.567 speed: 619 wps 0.803 perplexity: 140.490 speed: 625 wps 0.903 perplexity: 136.752 speed: 630 wps Epoch: 2 Train Perplexity: 134.621 Epoch: 2 Valid Perplexity: 142.642 Epoch: 3 Learning rate: 1.000 0.004 perplexity: 147.917 speed: 674 wps 0.104 perplexity: 106.221 speed: 675 wps 0.204 perplexity: 115.247 speed: 670 wps 0.304 perplexity: 112.198 speed: 651 wps 0.404 perplexity: 111.222 speed: 641 wps 0.504 perplexity: 110.446 speed: 635 wps 0.604 perplexity: 107.736 speed: 632 wps 0.703 perplexity: 107.048 speed: 629 wps 0.803 perplexity: 106.365 speed: 627 wps 0.903 perplexity: 104.098 speed: 625 wps Epoch: 3 Train Perplexity: 103.087 Epoch: 3 Valid Perplexity: 131.996 Epoch: 4 Learning rate: 1.000 0.004 perplexity: 118.322 speed: 587 wps 0.104 perplexity: 85.728 speed: 611 wps 0.204 perplexity: 94.048 speed: 611 wps 0.304 perplexity: 91.779 speed: 611 wps 0.404 perplexity: 91.354 speed: 611 wps 0.504 perplexity: 91.008 speed: 611 wps 0.604 perplexity: 89.173 speed: 612 wps 0.703 perplexity: 88.904 speed: 612 wps 0.803 perplexity: 88.630 speed: 612 wps 0.903 perplexity: 86.915 speed: 612 wps Epoch: 4 Train Perplexity: 86.324 Epoch: 4 Valid Perplexity: 128.431 Epoch: 5 Learning rate: 1.000 0.004 perplexity: 100.928 speed: 603 wps 0.104 perplexity: 74.047 speed: 611 wps 0.204 perplexity: 81.642 speed: 611 wps 0.304 perplexity: 79.718 speed: 611 wps 0.404 perplexity: 79.587 speed: 611 wps 0.504 perplexity: 79.495 speed: 610 wps 0.604 perplexity: 78.031 speed: 610 wps 0.703 perplexity: 77.985 speed: 611 wps 0.803 perplexity: 77.882 speed: 611 wps 0.903 perplexity: 76.587 speed: 611 wps Epoch: 5 Train Perplexity: 76.219 Epoch: 5 Valid Perplexity: 126.490 Epoch: 6 Learning rate: 0.500 0.004 perplexity: 87.955 speed: 605 wps 0.104 perplexity: 64.458 speed: 612 wps 0.204 perplexity: 69.789 speed: 612 wps 0.304 perplexity: 67.115 speed: 613 wps 0.404 perplexity: 66.140 speed: 613 wps 0.504 perplexity: 65.323 speed: 613 wps 0.604 perplexity: 63.428 speed: 614 wps 0.703 perplexity: 62.715 speed: 613 wps 0.803 perplexity: 61.998 speed: 613 wps 0.903 perplexity: 60.284 speed: 613 wps Epoch: 6 Train Perplexity: 59.377 Epoch: 6 Valid Perplexity: 120.079 Epoch: 7 Learning rate: 0.250 0.004 perplexity: 72.292 speed: 604 wps 0.104 perplexity: 53.300 speed: 612 wps 0.204 perplexity: 57.905 speed: 613 wps 0.304 perplexity: 55.613 speed: 613 wps 0.404 perplexity: 54.701 speed: 613 wps 0.504 perplexity: 53.949 speed: 613 wps 0.604 perplexity: 52.291 speed: 613 wps 0.703 perplexity: 51.578 speed: 613 wps 0.803 perplexity: 50.839 speed: 613 wps 0.903 perplexity: 49.257 speed: 613 wps Epoch: 7 Train Perplexity: 48.362 Epoch: 7 Valid Perplexity: 120.523 Epoch: 8 Learning rate: 0.125 0.004 perplexity: 64.084 speed: 610 wps 0.104 perplexity: 47.402 speed: 613 wps 0.204 perplexity: 51.544 speed: 614 wps 0.304 perplexity: 49.458 speed: 613 wps 0.404 perplexity: 48.614 speed: 613 wps 0.504 perplexity: 47.919 speed: 613 wps 0.604 perplexity: 46.388 speed: 613 wps 0.703 perplexity: 45.707 speed: 613 wps 0.803 perplexity: 44.993 speed: 613 wps 0.903 perplexity: 43.515 speed: 613 wps Epoch: 8 Train Perplexity: 42.664 Epoch: 8 Valid Perplexity: 121.599 Epoch: 9 Learning rate: 0.062 0.004 perplexity: 60.022 speed: 601 wps 0.104 perplexity: 44.401 speed: 614 wps 0.204 perplexity: 48.353 speed: 613 wps 0.304 perplexity: 46.366 speed: 613 wps 0.404 perplexity: 45.550 speed: 613 wps 0.504 perplexity: 44.890 speed: 613 wps 0.604 perplexity: 43.438 speed: 613 wps 0.703 perplexity: 42.772 speed: 613 wps 0.803 perplexity: 42.064 speed: 613 wps 0.903 perplexity: 40.648 speed: 612 wps Epoch: 9 Train Perplexity: 39.818 Epoch: 9 Valid Perplexity: 122.304 Epoch: 10 Learning rate: 0.031 0.004 perplexity: 58.057 speed: 603 wps 0.104 perplexity: 42.848 speed: 611 wps 0.204 perplexity: 46.711 speed: 611 wps 0.304 perplexity: 44.794 speed: 611 wps 0.404 perplexity: 43.991 speed: 611 wps 0.504 perplexity: 43.343 speed: 612 wps 0.604 perplexity: 41.932 speed: 611 wps 0.703 perplexity: 41.271 speed: 611 wps 0.803 perplexity: 40.557 speed: 612 wps 0.903 perplexity: 39.171 speed: 612 wps Epoch: 10 Train Perplexity: 38.355 Epoch: 10 Valid Perplexity: 122.363 Epoch: 11 Learning rate: 0.016 0.004 perplexity: 56.990 speed: 594 wps 0.104 perplexity: 41.910 speed: 613 wps 0.204 perplexity: 45.741 speed: 612 wps 0.304 perplexity: 43.890 speed: 612 wps 0.404 perplexity: 43.126 speed: 613 wps 0.504 perplexity: 42.486 speed: 613 wps 0.604 perplexity: 41.099 speed: 613 wps 0.703 perplexity: 40.443 speed: 613 wps 0.803 perplexity: 39.726 speed: 614 wps 0.903 perplexity: 38.356 speed: 613 wps Epoch: 11 Train Perplexity: 37.549 Epoch: 11 Valid Perplexity: 122.092 Epoch: 12 Learning rate: 0.008 0.004 perplexity: 56.367 speed: 600 wps 0.104 perplexity: 41.334 speed: 613 wps 0.204 perplexity: 45.146 speed: 614 wps 0.304 perplexity: 43.337 speed: 614 wps 0.404 perplexity: 42.611 speed: 613 wps 0.504 perplexity: 41.985 speed: 614 wps 0.604 perplexity: 40.614 speed: 614 wps 0.703 perplexity: 39.963 speed: 614 wps 0.803 perplexity: 39.253 speed: 614 wps 0.903 perplexity: 37.892 speed: 614 wps Epoch: 12 Train Perplexity: 37.093 Epoch: 12 Valid Perplexity: 121.822 Epoch: 13 Learning rate: 0.004 0.004 perplexity: 55.982 speed: 611 wps 0.104 perplexity: 41.010 speed: 615 wps 0.204 perplexity: 44.807 speed: 614 wps 0.304 perplexity: 43.019 speed: 613 wps 0.404 perplexity: 42.311 speed: 613 wps 0.504 perplexity: 41.698 speed: 613 wps 0.604 perplexity: 40.338 speed: 613 wps 0.703 perplexity: 39.692 speed: 613 wps 0.803 perplexity: 38.988 speed: 613 wps 0.903 perplexity: 37.633 speed: 613 wps Epoch: 13 Train Perplexity: 36.839 Epoch: 13 Valid Perplexity: 121.635 Test Perplexity: 116.635
핍홀(peephole) LSTM
ftp://ftp.idsia.ch/pub/juergen/TimeCount-IJCNN2000.pdf
입력게이트 pi를 계산할 때 입력 데이터(xt)와 이전의 은닉 상태 데이터(ht-1) 외에 이전의 셀 상태(ct-1)도 함께 사용하는 방법
GRU(Gated Recurrent Unit)
https://arxiv.org/pdf/1406.1078v3.pdf
뉴욕대학교 조경현 교수가 발표
입력 게이트와 삭제 게이트를 하나의 업데이트 게이트로 묶음.
셀 상태와 은닉 상태를 합친 것이 특징.