본문 바로가기

머신러닝 딥러닝 번역

IMPLEMENTING A CNN FOR TEXT CLASSIFICATION IN TENSORFLOW (한글 번역)

다음 원문을 공부목적으로 번역했습니다.


전체 코드는 Github에서 볼수 있다.

이 포스트는 김윤의 Convolutional Neural Networks for Sentence Classification.과 비슷한 모델을 구현할 것이다. 논문에서 소개된 모델은 텍스트 분류 문제에 좋은 분류 성능을 보였고, 새로운 텍스트 분류 문제의 기본적인 baseline 이 되었다. 독자가 NLP 에 적용되는 CNN에 익숙하다고 가정했다. 그렇지 않다면 이글을 먼저 보라

DATA AND PREPROCESSING

데이터셋은 Movie Review data from Rotten Tomatoes를 쓴다. 원 논문에서도 사용했던 데이터이다. 10,662개의 리뷰 예제 문장이 포함되어 있고, 반은 긍정, 반은 부정이다. 데이터셋은 20k정도의 vocabulary을 가지고 있다. 이 데이터는 매우 작으므로, 강력한 모델로는 오버피팅될 가능성이 크다. 또한, 데이터셋은 공식적인 train/test split이 없어서, 10%를 단순히 dev set으로 사용했다. 원 논문은 결과에 대해서 10-fold cross-validation 했다고 기술했다. 이 포스트에서 데이터 전처리 코드를 기술하지는 않지만 Github에서 볼 수 있고 다음과 같은 일을 하고 있는 코드이다.

  1. 원본 데이터에서 긍정/부정 문장을 나눈다.
  2. 원 논문과 같은 코드를 사용해서 텍스트 데이터를 클린징한다.
  3. 각 문장을 최대 문장 길이 59단어로 통일한다. 59 단어로 통일하기 하기 위해 특수 tokens을 각 문장에 추가한다. 문장을 같은 단어길이로 만드는 것은 데이터를 batch 안에 각 예제가 같은 길이로 만들수 있으므로, 효율적으로 batch할수 있게 한다.
  4. vocabulary index를 만들고 각 단어를 0에서 18,765(단어집 수)사이 정수로 매핑한다. 각 문장은 정수의 벡터가 된다.

THE MODEL

이 포스트에서 만들 네트워크는 대충 다음과 같은 모양이 될 것이다.

첫번째 층은 단어를 저차원 벡터로 embed한다. 다음층은 embed된 단어 벡터를 여러개의 필터 사이즈로 합성곱한다. 예를 들어 , 한번에 3,4, 또는 5 길이로 슬라이딩한다. 그 다음, convolutional 층의 결과를 긴 피쳐 벡터로 max-pool 한다. 그 후 dropout regularization를 추가하고, softmax layer를 사용해서 결과를 분류한다. 여기서는 교육용 포스트이기 때문에, 원논문보다 모델을 더 간소화하기로 결정했다.

이 포스트에서의 코드에 위의 확장을 추가하는 것은 상대적으로 쉽다(몇 줄의 코드 추가면 된다) 포스트의 끝에 있는 연습들을 봐라. 이제 시작하자.

IMPLEMENTATION

다양한 하이퍼파라메터 구성을 가능하게 하기위해, init함수에서 모델 그래프를 생성하면서, 우리의 코드를 TextCNN class에 넣는다.

import tensorflow as tf
import numpy as np

class TextCNN(object):
"""
A CNN for text classification.
Uses an embedding layer, followed by a convolutional, max-pooling and softmax layer.
"""

def __init__(
self, sequence_length, num_classes, vocab_size,
embedding_size, filter_sizes, num_filters)
:

# Implementation... 

class 를 인스턴스화하기 위해, 다음 arguments를 전달한다.

  • sequence_length – 문장의 길이. 모든 문장을 같은 길이를 갖도록 pad했음을 기억해라.
  • num_classes – 출력증에서의 class 수, 여기서서는 긍정, 부정 두개가 있다.
  • vocab_size – 단어집의 수. 임베딩 층의 크기를 정의하기 위해 필요하다. [vocabulary_size, embedding_size].
  • embedding_size – 임베딩의 차원
  • filter_sizes – 합성곱 필터가 책임지길 원하는 단어의 수. We will have num_filters for each size specified here. For example, [3, 4, 5] means that we will have filters that slide over 3, 4 and 5 words respectively, for a total of 3 * num_filters filters.
  • num_filters – 필터 사이즈별 필터의 수(위에 설명)

INPUT PLACEHOLDERS

네트워크에 전달할 입력 데이터를 정의한다:

# Placeholders for input, output and dropout
self.input_x = tf.placeholder(tf.int32, [None, sequence_length], name="input_x")
self.input_y = tf.placeholder(tf.float32, [None, num_classes], name="input_y")
self.dropout_keep_prob = tf.placeholder(tf.float32, name="dropout_keep_prob")

tf.placeholder는 학습시간 또는 테스트 시간에 네트워크에 데이터를 전달하는 placeholder 변수를 생성한다. 두번째 인자는 입력 텐서의 모양이다. None는 그 차원이 길이가 어떤 것이든 될 수 있다는 의미이다. 여기서는, 첫번째 차원은 배치 크기이고,None사용하는 것은 네트워크가 임의의 크기의 배치를 다룰 수 있게 해준다.

dropout 층에서 뉴런을 유지할 확률 또한 네트워크의 입력이다. 왜냐하면 dropout은 학습시에만 켜고 모델 평가시에는 그것을 끈다.(나중에 살펴본다)

EMBEDDING LAYER

첫번째 층은 임베딩 층이다. 이 층은 단어집의 단어 인덱스를 저차원 벡터 표현으로 바꾼다. 본질적으로 데이터로부터 학습한 룩업 테이블이다.

with tf.device('/cpu:0'), tf.name_scope("embedding"):
W = tf.Variable(
tf.random_uniform([vocab_size, embedding_size], -1.0, 1.0),
name="W")
self.embedded_chars = tf.nn.embedding_lookup(W, self.input_x)
self.embedded_chars_expanded = tf.expand_dims(self.embedded_chars, -1) 

여기서 두 개의 새로운 기능을 사용하고 있다. 하나씩 살펴보자.

  • tf.device("/cpu:0")는 연산이 CPU에서 실행되도록 강제한다. 디폴트로 텐서플로우는 연산을 GPU에서 수행하도록 시도한다. 그러나, 임베딩 구현은 현재 GPU를 지원하지 않아서 GPU로 실행시 에러가 발생한다.
  • tf.name_scope는 “embedding"이라는 이름을 가진 새로운 Name Scope 를 생성한다. 그래서 텐서보드에서 네트워크를 시각화할 때 멋진 계층을 얻을 수 있게 해준다.

W는 학습시 배우는 임베딩 행렬이다. 그것을 random uniform distribution를 사용해서 초기화한다. tf.nn.embedding_lookup는 실제 임베딩 연산을 생성한다. 임베딩 연산의 결과는 [None, sequence_length, embedding_size] 모양의 3차원 텐서이다.

텐서플로우의 합성곱 연산 conv2d 은 배치, 너비, 높이, 채널의 4차원 텐서를 원한다. 임베딩의 결과는 채널 차원을 포함하지 않으므로, [None, sequence_length, embedding_size, 1] 모양으로 수동으로 추가한다.

CONVOLUTION AND MAX-POOLING LAYERS

이제 max-pooling 전의 합성곱 층을 만들 수 있다. 다른 크기의 필터를 사용함을 기억하라. 각 합성곱 과정은 다른 모양의 텐서를 생성하므로, 그것을 순회하면서 각각에 대한 층을 만들고 하나의 큰 특성 벡터로 만드는 것이 필요하다.

pooled_outputs = []
for i, filter_size in enumerate(filter_sizes):
with tf.name_scope("conv-maxpool-%s" % filter_size):
# Convolution Layer
filter_shape = [filter_size, embedding_size, 1, num_filters]
W = tf.Variable(tf.truncated_normal(filter_shape, stddev=0.1), name="W")
b = tf.Variable(tf.constant(0.1, shape=[num_filters]), name="b")
conv = tf.nn.conv2d(
self.embedded_chars_expanded,
W,
strides=[1, 1, 1, 1],
padding="VALID",
name="conv")
# Apply nonlinearity
h = tf.nn.relu(tf.nn.bias_add(conv, b), name="relu")
# Max-pooling over the outputs
pooled = tf.nn.max_pool(
h,
ksize=[1, sequence_length - filter_size + 1, 1, 1],
strides=[1, 1, 1, 1],
padding='VALID',
name="pool")
pooled_outputs.append(pooled)

# Combine all the pooled features
num_filters_total = num_filters * len(filter_sizes)
self.h_pool = tf.concat(3, pooled_outputs)
self.h_pool_flat = tf.reshape(self.h_pool, [-1, num_filters_total])

W는 필터 행렬이고, h는 합성곱 출력에 비선형성을 적용한 결과이다. 각 필터는 전체 임베딩을 슬라이딩하지만, 얼만큼의 단어를 cover할지는 변한다. "VALID"padding은 [1, sequence_length - filter_size + 1, 1, 1] 모양의 출력을 주는 좁은 합성곱을 수행하면서, 좁은 합성곱은 가장자리 padding 없이 필터를 문장 데이터에 미끄러트리는 것을 의미한다. 특정 필터 크기의 출력에 대한 max-pooling를 수행하는 것은 [batch_size, 1, 1, num_filters]모양의 텐서를 만든다. This is essentially a feature vector, where the last dimension corresponds to our features. 각 filter size로 부터 pool된 출력 텐서를 얻기 되면, 모두 합쳐서[batch_size, num_filters_total] 크기의 하나의 긴 모양의 feature vector를 만든다. tf.reshape에서 -1를 사용하는 것은 텐서플로우에게 가능하면 차원을 flatten하라고 지시한다.

각 연산에 대한 출력 모양을 이해하는데 시간을 조금 써보자. 직관을 얻기 위해 Understanding Convolutional Neural Networks for NLP를 다시 볼 수도 있다. TensorBoard에서 operations을 시각화해보는 것도 도움이 된다.

DROPOUT LAYER

Dropout는 아마도 convolutional neural networks를 regularize하는 가장 인기있는 방법일 것이다. dropout 뒤에 숨겨진 생각은 간단하다. dropout layer는 확률적으로 뉴런의 일부를 불능으로 만든다. 이는 뉴런이 같이 적응(co-adapting)하는 것을 막고, 서로 독립적으로 유용한 특성을 학습하게 만든다. 활성화할 뉴런의 비율은 네트워크의 입력으로 들어가는 dropout_keep_prob에 정의된다. 이를 학습시에는 0.5로 하고 평가시에는 1(disable dropout)로 한다.

# Add dropout
with tf.name_scope("dropout"):
self.h_drop = tf.nn.dropout(self.h_pool_flat, self.dropout_keep_prob)

SCORES AND PREDICTIONS (점수와 예측)

(dropout)이 적용된 max-pooling된 피쳐 벡터를 사용함으로써, 행렬곱으로 예측을 생성하고, class를 선택할 수 있다. softmax함수를 원본 점수를 정규화된 확률로 바꾸는데 사용할 수 있지만, 최종 예측은 바뀌진 않는다.

with tf.name_scope("output"):
W = tf.Variable(tf.truncated_normal([num_filters_total, num_classes], stddev=0.1), name="W")
b = tf.Variable(tf.constant(0.1, shape=[num_classes]), name="b")
self.scores = tf.nn.xw_plus_b(self.h_drop, W, b, name="scores")
self.predictions = tf.argmax(self.scores, 1, name="predictions") 

여기서, tf.nn.xw_plus_b는 s의 행렬곱을 수행하는 편리한 wrapper이다.

LOSS AND ACCURACY (손실과 정확도)

얻은 스코어를 사용해서 손실함수를 정의할 수 있다. 손실은 네트워크 범한 에러의 측정이고, 그것을 최소화하는 것이 우리의 목적이다. 분류문제에서의 표준 손실 함수은 cross-entropy loss이다.

# Calculate mean cross-entropy loss
with tf.name_scope("loss"):
losses = tf.nn.softmax_cross_entropy_with_logits(self.scores, self.input_y)
self.loss = tf.reduce_mean(losses)

tf.nn.softmax_cross_entropy_with_logits는 정답인 입력 labels과 점수가 주어졌을때, 각 클래스에 대해 cross-entropy loss를 계산하는 편리한 함수 이다. 손실들의 평균을 취한다. 그리고 이것을 더할수 있지만, 더하는 것은 서로 다른 배치 크기와 train/dev data에 대해서 비교하기 어렵게 만든다.

또한, 학습과 테스트 시간 동안 유지할 유용한 량인 정확도에 대한 수식을 정의한다.

# Calculate Accuracy
with tf.name_scope("accuracy"):
correct_predictions = tf.equal(self.predictions, tf.argmax(self.input_y, 1))
self.accuracy = tf.reduce_mean(tf.cast(correct_predictions, "float"), name="accuracy")

VISUALIZING THE NETWORK

That’s it, we’re done with our network definition. 
끝이다. 이것으로 네트워크 정의를 마쳤다.

전체 코드는 여기에 있다. 큰 그림을 얻기위해, TensorBoard에서 네트워크를 시각화할 수도 있다.

TRAINING PROCEDURE

정의한 네트워크의 training procedure를 정의하기 전에, 어떻게 TensorFlow가 Sessions과 Graphs를 사용하는지 이해할 필요가 있다. 이 개념에 대해서 이미 익숙하다면, 스킵해라. TensorFlow에서, Session는 graph operations를 실행하는 환경이고, 세션은 Variables과 queues에 대한 상태를 포함한다. 각 session은 하나의 graph 위에 동작한다. 변수와 연산을 만들때, 세션을 명시적으로 사용하지 않으면 TensorFlow가 만든 디폴트 현재 세션을 사용한다. session.as_default()을 사용해서 디폴트 세션은 바꿀 수 있다.

Graph는 operations과 tensors를 포함한다. 다수의 그래프를 사용할수 있지만, 대부분 하나의 프로그램은 하나의 그래프를 필요로한다. 다수의 세션에 같은 그래프를 사용할 수 있지만, 하나의 세션에는 다수의 그래프가 들어갈 수 없다. TensorFlow는 디폴트 그래프를 항상 만들지만, 밑에서 하는 것처럼, 하나의 그래프를 수동으로 만들어서 새로운 디폴트로 만들 수도 있다. 세션과 그래프를 명시적으로 만드는 것은 더이상 그래프가 필요없을 때, 자원을 적절히 release시키도록 한다.

with tf.Graph().as_default():
session_conf = tf.ConfigProto(
allow_soft_placement=FLAGS.allow_soft_placement,
log_device_placement=FLAGS.log_device_placement)
sess = tf.Session(config=session_conf)
with sess.as_default():
# Code that operates on the default graph and session comes here.

The allow_soft_placement setting allows TensorFlow to fall back on a device with a certain operation implemented when the preferred device doesn’t exist. 
allow_soft_placement는 TensorFlow가 선호하는 device가 존재하지 않을 때, 특정 operation이 구현된 device를 대안으로 선택하도록 해준다. 예를 들어, operation이 GPU위에서 돌아가고, GPU가 없는 머신이라면, allow_soft_placement를 사용하지 않는다면 에러를 내뱉을 것이다. log_device_placement가 set되면, TensorFlow는 어떤 devices가 operations에 할당되었는지 기록한다. 이는 디버깅에 유용한다. FLAGS은 커맨드-라인 인자이다.

INSTANTIATING THE CNN AND MINIMIZING THE LOSS

TextCNN모델을 인스턴스화할 때, 정의된 모든 변수와 연산은 디폴트 그래프와 위에서 생성한 세션에 위치한다.

cnn = TextCNN(
sequence_length=x_train.shape[1],
num_classes=2,
vocab_size=len(vocabulary),
embedding_size=FLAGS.embedding_dim,
filter_sizes=map(int, FLAGS.filter_sizes.split(",")),
num_filters=FLAGS.num_filters)

이제, 네트워크의 손실함수를 어떻게 최적화하는지 정의한다. TensorFlow는 여러개의 내장 optimizers가 있는데, 여기서는Adam을 사용한다.

global_step = tf.Variable(0, name="global_step", trainable=False)
optimizer = tf.train.AdamOptimizer(1e-4)
grads_and_vars = optimizer.compute_gradients(cnn.loss)
train_op = optimizer.apply_gradients(grads_and_vars, global_step=global_step) 

train_op는 파라메터에 대한 gradient 업데이트를 수행할 수 있는 여기서 새롭게 만들어진 operation 이다. 각 train_op의 수행은 학습 단계이다. TensorFlow는 자동적으로 어떤 variables가 trainable인지 알아내고 gradients를 계산한다. global_step변수를 정의하고 optimizer에 전달함으로써 TensorFlow는 학습 단계의 횟수를 관리할 수 있다. global step는 train_op이 수행될때마다 자동적으로 1씩 증가한다.

SUMMARIES

TensorFlow는 Summaries의 개념을 가지고 있다. 요약은 학습과 평가 시간동안 다양한 수치들을 유지하고 시각화해준다. 예를 들어, 손실과 정확도가 시간이 흐름에 따라 어떻게 진화하는지 저장하길 원할수 있다. layer activations의 히스토그램과 같은 복잡한 수치를 저장할수도 있다. Summaries는 직렬화된 객체이고 SummaryWriter를 사용해서 디스크에 저장할 수 있다.

# Output directory for models and summaries
timestamp = str(int(time.time()))
out_dir = os.path.abspath(os.path.join(os.path.curdir, "runs", timestamp))
print("Writing to {}\n".format(out_dir))

# Summaries for loss and accuracy
loss_summary = tf.scalar_summary("loss", cnn.loss)
acc_summary = tf.scalar_summary("accuracy", cnn.accuracy)

# Train Summaries
train_summary_op = tf.merge_summary([loss_summary, acc_summary])
train_summary_dir = os.path.join(out_dir, "summaries", "train")
train_summary_writer = tf.train.SummaryWriter(train_summary_dir, sess.graph_def)

# Dev summaries
dev_summary_op = tf.merge_summary([loss_summary, acc_summary])
dev_summary_dir = os.path.join(out_dir, "summaries", "dev")
dev_summary_writer = tf.train.SummaryWriter(dev_summary_dir, sess.graph_d

여기서 학습과 평가에 대한 summaries를 따로 저장한다. 우리의 예제에서는 이것들이 같은 수치이지만, (파라메터 업데이트 값)같은 학습시만 저장을 원하는 수치가 있을수도 있다. tf.merge_summary는 다수의 summary 연산을 하나의 operation으로 합쳐주는 편리한 함수이다.

CHECKPOINTING

다른 TensorFlow 기능은 checkpointing이다 - 모델의 파라테터를 나중의 복원을 위해 저장하는 기능. Checkpoints는 나중에 더 학습하기 위해서 사용할 수도 있고, early stopping을 사용해서 최적의 파라메터 셋팅을 고를 때 사용할 수 있다. Checkpoints는Saver 객체를 사용해서 생성된다.

# Checkpointing
checkpoint_dir = os.path.abspath(os.path.join(out_dir, "checkpoints"))
checkpoint_prefix = os.path.join(checkpoint_dir, "model")
# Tensorflow assumes this directory already exists so we need to create it
if not os.path.exists(checkpoint_dir):
os.makedirs(checkpoint_dir)
saver = tf.train.Saver(tf.all_variables())

INITIALIZING THE VARIABLES

모델을 학습하기전에, 그래프에서 변수를 초기화해야한다.

sess.run(tf.initialize_all_variables())

initialize_all_variables함수는 정의한 모든 변수를 초기화하는 편리한 함수이다. 변수를 수동으로 초기화할수도 있다. 예를 들어, embeddings을 pre-trained된 값으로 초기화할 때 유용하다.

DEFINING A SINGLE TRAINING STEP

하나의 데이터의 하나의 배치에 대해서 모델을 평가하고 모델 파라메터를 업데이트 하는 training step에 대한 함수를 정의하자.

def train_step(x_batch, y_batch):
"""
A single training step
"""

feed_dict = {
cnn.input_x: x_batch,
cnn.input_y: y_batch,
cnn.dropout_keep_prob: FLAGS.dropout_keep_prob
}
_, step, summaries, loss, accuracy = sess.run(
[train_op, global_step, train_summary_op, cnn.loss, cnn.accuracy],
feed_dict)
time_str = datetime.datetime.now().isoformat()
print("{}: step {}, loss {:g}, acc {:g}".format(time_str, step, loss, accuracy))
train_summary_writer.add_summary(summaries, step)

feed_dict은 네트워크에 전달할 placeholder 노드를 위한 데이터를 포함한다. 모든 placeholder 노드에 대해서 값을 주지 않으면 TensorFlow는 에러를 뱉는다. 입력 데이터를 다루는 다른 방법은 queues을 사용하는 것이지만, 이 포스트에서는 다루지 않는다.

그 후, session.run를 사용해서 train_op을 실행한다. 이는 묻는 모든 operations에 대해서 값을 리턴한다. 
train_op는 아무것도 리턴하지 않고, 네트워크의 파라메터만을 업데이트한다는 것에 주목하라.

마지막으로 현재 학습 배치에서의 loss 와 accuracy를 프린트하고 디스크에 summaries를 기록한다. 배치 크기가 작다면, 하나의 학습 배치에 대한 loss 와 accuracy는 배치마다 매우 다를 수 있다. dropout을 사용하기 때문에, training metrics는 평가 metric보다 좋지않게 시작할 수 있다.

validation set 또는 전체 training set같은 임의의 데이터에 대해서, loss 와 accuracy를 평가하는 비슷한 함수를 작성한다.

본질적으로 이 함수는 위와 같은 일을 하지만 training operation이 없다면, dropout를 끈다.

def dev_step(x_batch, y_batch, writer=None):
"""
Evaluates model on a dev set
"""

feed_dict = {
cnn.input_x: x_batch,
cnn.input_y: y_batch,
cnn.dropout_keep_prob: 1.0
}
step, summaries, loss, accuracy = sess.run(
[global_step, dev_summary_op, cnn.loss, cnn.accuracy],
feed_dict)
time_str = datetime.datetime.now().isoformat()
print("{}: step {}, loss {:g}, acc {:g}".format(time_str, step, loss, accuracy))
if writer:
writer.add_summary(summaries, step)

TRAINING LOOP

마침내 training loop를 작성할 준비가 되었다. 데이터의 배치를 돌면서, train_step함수를 각 배치마다 호출하고 땨로는 평가와 모델 checkpoint를 한다.

# Generate batches
batches = data_helpers.batch_iter(
zip(x_train, y_train), FLAGS.batch_size, FLAGS.num_epochs)
# Training loop. For each batch...
for batch in batches:
x_batch, y_batch = zip(*batch)
train_step(x_batch, y_batch)
current_step = tf.train.global_step(sess, global_step)
if current_step % FLAGS.evaluate_every == 0:
print("\nEvaluation:")
dev_step(x_dev, y_dev, writer=dev_summary_writer)
print("")
if current_step % FLAGS.checkpoint_every == 0:
path = saver.save(sess, checkpoint_prefix, global_step=current_step)
print("Saved model checkpoint to {}\n".format(path))

batch_iter는 데이터를 배치로 나누기위한 도우미함수이고 tf.train.global_step는 global_step를 리턴하는 편리한 함수이다.

The full code for training is also available here.

VISUALIZING RESULTS IN TENSORBOARD

Our training script writes summaries to an output directory, and by pointing TensorBoard to that directory we can visualize the graph and the summaries we created. 
학습 스크립트는 결과 디렉토리에 summaries를 쓴다. TensorBoard에게 그 디렉토리를 알려줌으로써 그래프와 요약을 생성할 수 있다.

tensorboard --logdir /PATH_TO_CODE/runs/1449760558/summaries/ 

디폴트 파라메터 (128-dimensional embeddings, filter sizes of 3, 4 and 5, dropout of 0.5 and 128 filters per filter size) 로 학습 프로시져를 수행하는 것은 다음과 같은 손실과 정확도를 결과로 준다(파란색은 학습데이터, 빨간색은 10%의 dev 데이터이다)

두드러지는 몇가지 것들이 있다:

  • training metrics은 작은 배치 크기로 인해 부드럽지 않다. 큰 배치크기를 사용한다면, 부드러운 파란선을 얻을 것이다.
  • dev accuracy는 training accuracy보다 현저히 낮기 때문에, 네트워크는 훈련셋에 오버피팅된것으로 보인다. 이는 좀 더 많은 데이터(MR 데이터셋은 작다), 강한 regularization, 적은 모델 파라메터를 제안한다. 예를 들어, 마지막 층에서 추가적인 가중치에 대한 L2 페널티를 실험했는데, 원논문과 비슷한 성능인 76% 정확률을 얻었다.
  • 학습 loss 와 정확률은 dev metrics보다 매우 낮게 시작한다. 이는 dropout 때문이다.

You can play around with the code and try running the model with various parameter configuration. Code and instructions are available on Github.

EXTENSIONS AND EXERCISES

모델의 성능을 올릴 수 있는 몇가지 연습이 있다.

  • embeddings을 pre-trained된 word2vec vectors를 사용해라. 이를 위해 300차원 임베딩을 사용하고, 그것을 pre-train된 값으로 초기화할 수 있다.
  • original paper에서 처럼 마지막 층의 가중치에 L2-norm 제약을 걸아라. 각 훈련스텝 후에 가중치값을 업데이트하는 새로운 operation를 정의하면 된다,
  • 오버피팅과 싸우기 위해 네트워크에 L2 regularization를 추가하고 dropout률을 올려라. Github의 코드는 L2 regularization는 적용됐지만, dropout은 꺼놨다.
  • 가중치 업데이트와 층 행동에 대한 히스토그램 요약를 추가하고 텐서보드로 시각화하라.