📗강의노트/핸즈온 머신러닝

[핸즈온 머신러닝] 제 14장 연습문제 풀이

728x90
반응형

Exercise Part.14

RNN

1. 시퀀스-투-시퀀스 RNN을 사용한 애플리케이션에는 어떤 것들이 있나요? 시퀀스-투-벡터 RNN과 벡터-투-시퀀스 RNN은 어떤가요?

  • 시퀀스-투-시퀀스 : 날씨 예측, 기계 번역, 비디오 캡션 생성, 스피치 투 텍스트, 음악 생성, 노래의 화음 식별

  • 시퀀수-투-벡터 : 음악 샘플을 장르로 구분하기, 책 후기에 대한 감성분석

  • 벡터-투-시퀀스 : 이미지 캡션 생성, 일련의 파라미터를 기반으로 한 멜로디 생성, 사진 속에서 보행자 위치 찾기

2. 왜 자동 번역에 시퀀스-투-시퀀스 RNN 대신 인코더-디코더 RNN을 사용하나요?

문장을 번역할 때, 단어 단어 해석하는 것 보다 문장을 읽고 전체적으로 해석하는 것이 더 성능이 좋기 때문이다. 인코더-디코더는 문장을 먼저 읽고 번역을 하는 반면 시퀀스-투-시퀀스는 첫 단어를 읽자마자 번역을 한다.

3. 동영상을 분류하기 위해 합성곱 신경망과 RNN을 어떻게 연결할 수 있나요?

화면 내용을 기초로 동영상을 분류하려면 초당 한 프레임을 받아 각 프레임을 합성곱 신경만에 통과시키고 이 CNN의 출력을 시퀀스-투-벡터 RNN에 주입하고 마지막에 소프트맥스 층을 통과시켜 모든 클래스에 대한 확률을 구하는 구조를 생각해볼 수 있다.

4. static_rnn() 대신 dynamic_rnn()을 사용하여 RNN을 구축할 때의 장점은 무엇인가요?

  • 메모리 부족 에러를 피하기 위해 역전파하는 동안 GPU 메모리를 CPU 메모리로 대체할 수 있는 while_loop()연산을 기반으로 한다.

  • 입력과 출력에 하나의 텐서를 사용하기 때문에 텐서의 리스트를 사용하는 것보다 사용하기 편리합니다. stack, unstack, transpose 연산이 필요하지 않다.

  • 더 작은 계산 그래프를 만들기 때문에 텐서보드에서 확인하기 쉽다.

5. 가변 길이 입력 시퀀스를 어떻게 다룰 수 있나요? 가변 길이 출력 시퀀스는 어떤가요?

가변 길이 입력 시퀀스를 다루기 위한 방법으로는 static_rnn() or dynamic_rnn()을 사용할 때, sequence_length 매개변수를 설정하거나 패딩을 추가하는 것이 있다.

가변 길이 출력 시퀀스를 다루기 위해서는 출력 시퀀스의 길이를 미리 알고 있다면 sequence_length 매개변수를 사용할 수 있다.

6. 여러 GPU에 심층 RNN의 훈련과 실행을 분산시키는 일반적인 방법은 무엇인가요?

일반적으로 각각의 층을 다른 GPU에 배치하는 것 이다.

7. 임베딩된 레버(Reber) 문법

In [1]:
from random import choice, seed

seed(42)
np.random.seed(42)

default_reber_grammar = [
    [("B", 1)],           # (상태 0) =B=>(상태 1)
    [("T", 2), ("P", 3)], # (상태 1) =T=>(상태 2) or =P=>(상태 3)
    [("S", 2), ("X", 4)], # (상태 2) =S=>(상태 2) or =X=>(상태 4)
    [("T", 3), ("V", 5)], # 등등..
    [("X", 3), ("S", 6)],
    [("P", 4), ("V", 6)],
    [("E", None)]]        # (상태 6) =E=>(종료 상태)

embedded_reber_grammar = [
    [("B", 1)],
    [("T", 2), ("P", 3)],
    [(default_reber_grammar, 4)],
    [(default_reber_grammar, 5)],
    [("T", 6)],
    [("P", 6)],
    [("E", None)]]

def generate_string(grammar):
    state = 0
    output = []
    while state is not None:
        production, state = choice(grammar[state])
        if isinstance(production, list):
            production = generate_string(grammar=production)
        output.append(production)
    return "".join(output)
In [6]:
for _ in range(25):
    print(generate_string(default_reber_grammar),end= "  ")
BPTVVE  BPVPXVPXTVPSE  BPVPSE  BPTTVPXTVVE  BTXSE  BTXSE  BPTVPSE  BTSSSXXVPXVVE  BTSSXSE  BTSXXTVVE  BTSXXVPXVVE  BTSSXSE  BPVVE  BTXXVPXVVE  BTXXVPSE  BPVPSE  BPVVE  BPTTVVE  BPTVVE  BPVVE  BPTTVVE  BPTTTTTTVPSE  BPVVE  BTSXXTVPXTVPSE  BPVPSE  
In [7]:
for _ in range(25):
    print(generate_string(embedded_reber_grammar), end="  ")
BTBTXSETE  BPBTSSXXTTVVEPE  BTBTXSETE  BPBTXSEPE  BPBTSXXTVPSEPE  BPBPVVEPE  BPBPTTTTTTTTTTVPXVVEPE  BPBPVVEPE  BPBPVVEPE  BPBPVPSEPE  BPBTSXSEPE  BTBTXXVVETE  BTBPVPSETE  BPBTXSEPE  BPBPVPXTVPXTTVVEPE  BTBPTTVPXVPSETE  BPBTSXSEPE  BTBTXXTTVPSETE  BPBTSXXTTTTTTVVEPE  BPBTSXXVPXTVPXTTVVEPE  BPBPTTVVEPE  BTBTXSETE  BTBPVVETE  BPBTXXVVEPE  BPBTXSEPE  
In [8]:
def generate_corrupted_string(grammar, chars="BEPSTVX"):
    good_string = generate_string(grammar)
    index = np.random.randint(len(good_string))
    good_char = good_string[index]
    bad_char = choice(list(set(chars) - set(good_char)))
    return good_string[:index] + bad_char + good_string[index + 1:]
In [17]:
for _ in range(25):
    print(generate_corrupted_string(embedded_reber_grammar), end="   ")
BPBPVVEPP   XTBTSXSETE   BPBTSSSXSESE   BTBPTVVETX   BTSTSXSETE   BTBTXXVPSETB   BPBTXXTPSEPE   BPBTVVEPE   BTPTSSSXXTTTTVVETE   BTBPXPSETE   BTXTSXSETE   BTBTXXXTTVVETE   BPBPXVPSEPE   BTBPTTVVVTE   BPBPTTPVEPE   BEBPTVPXTTVPSEPE   BPBSVPSEPE   BTBPTVVEPE   BSBPTTVVEPE   BTBPTTVPSBTE   BTBTSSXXPVPSETE   BPBTXXTTVSEPE   BTBTBXSETE   BVBTSXXTVVEPE   BTBPXXTTTVVETE   
In [18]:
def string_to_one_hot_vectors(string, n_steps, chars="BEPSTVX"):
    char_to_index = {char: index for index, char in enumerate(chars)}
    output = np.zeros((n_steps, len(chars)), dtype=np.int32)
    for index, char in enumerate(string):
        output[index, char_to_index[char]] = 1.
    return output
In [19]:
string_to_one_hot_vectors("BTBTXSETE",12)
Out[19]:
array([[1, 0, 0, 0, 0, 0, 0],
       [0, 0, 0, 0, 1, 0, 0],
       [1, 0, 0, 0, 0, 0, 0],
       [0, 0, 0, 0, 1, 0, 0],
       [0, 0, 0, 0, 0, 0, 1],
       [0, 0, 0, 1, 0, 0, 0],
       [0, 1, 0, 0, 0, 0, 0],
       [0, 0, 0, 0, 1, 0, 0],
       [0, 1, 0, 0, 0, 0, 0],
       [0, 0, 0, 0, 0, 0, 0],
       [0, 0, 0, 0, 0, 0, 0],
       [0, 0, 0, 0, 0, 0, 0]], dtype=int32)
In [20]:
def generate_dataset(size):
    good_strings = [generate_string(embedded_reber_grammar)
                    for _ in range(size // 2)]
    bad_strings = [generate_corrupted_string(embedded_reber_grammar)
                   for _ in range(size - size // 2)]
    all_strings = good_strings + bad_strings
    n_steps = max([len(string) for string in all_strings])
    X = np.array([string_to_one_hot_vectors(string, n_steps)
                  for string in all_strings])
    seq_length = np.array([len(string) for string in all_strings])
    y = np.array([[1] for _ in range(len(good_strings))] +
                 [[0] for _ in range(len(bad_strings))])
    rnd_idx = np.random.permutation(size)
    return X[rnd_idx], seq_length[rnd_idx], y[rnd_idx]
In [22]:
X_train, l_train, y_train = generate_dataset(100)
In [23]:
X_train[0]
Out[23]:
array([[1, 0, 0, 0, 0, 0, 0],
       [0, 0, 0, 0, 1, 0, 0],
       [0, 0, 0, 0, 0, 1, 0],
       [0, 0, 1, 0, 0, 0, 0],
       [0, 0, 0, 0, 1, 0, 0],
       [0, 0, 0, 0, 0, 1, 0],
       [0, 0, 1, 0, 0, 0, 0],
       [0, 0, 0, 1, 0, 0, 0],
       [0, 1, 0, 0, 0, 0, 0],
       [0, 0, 0, 0, 1, 0, 0],
       [0, 1, 0, 0, 0, 0, 0],
       [0, 0, 0, 0, 0, 0, 0],
       [0, 0, 0, 0, 0, 0, 0],
       [0, 0, 0, 0, 0, 0, 0],
       [0, 0, 0, 0, 0, 0, 0],
       [0, 0, 0, 0, 0, 0, 0],
       [0, 0, 0, 0, 0, 0, 0],
       [0, 0, 0, 0, 0, 0, 0],
       [0, 0, 0, 0, 0, 0, 0],
       [0, 0, 0, 0, 0, 0, 0],
       [0, 0, 0, 0, 0, 0, 0],
       [0, 0, 0, 0, 0, 0, 0],
       [0, 0, 0, 0, 0, 0, 0]], dtype=int32)
In [24]:
l_train[0]
Out[24]:
11
In [25]:
y_train[0]
Out[25]:
array([0])
In [28]:
import tensorflow as tf
tf.reset_default_graph()

possible_chars = "BEPSTVX"
n_inputs = len(possible_chars)
n_neurons = 30
n_outputs = 1

learning_rate = 0.02
momentum = 0.95

X = tf.placeholder(tf.float32, [None, None, n_inputs], name="X")
seq_length = tf.placeholder(tf.int32, [None], name="seq_length")
y = tf.placeholder(tf.float32, [None, 1], name="y")

gru_cell = tf.contrib.rnn.GRUCell(num_units=n_neurons)
outputs, states = tf.nn.dynamic_rnn(gru_cell, X, dtype=tf.float32,
                                    sequence_length=seq_length)

logits = tf.layers.dense(states, n_outputs, name="logits")
y_pred = tf.cast(tf.greater(logits, 0.), tf.float32, name="y_pred")
y_proba = tf.nn.sigmoid(logits, name="y_proba")

xentropy = tf.nn.sigmoid_cross_entropy_with_logits(labels=y, logits=logits)
loss = tf.reduce_mean(xentropy, name="loss")
optimizer = tf.train.MomentumOptimizer(learning_rate=learning_rate,
                                       momentum=momentum,
                                       use_nesterov=True)
training_op = optimizer.minimize(loss)

correct = tf.equal(y_pred, y, name="correct")
accuracy = tf.reduce_mean(tf.cast(correct, tf.float32), name="accuracy")

init = tf.global_variables_initializer()
saver = tf.train.Saver()
In [29]:
X_val, l_val, y_val = generate_dataset(5000)
In [34]:
n_epochs = 50
batch_size = 50

with tf.Session() as sess:
    init.run()
    for epoch in range(n_epochs):
        X_batches = np.array_split(X_train, len(X_train) // batch_size)
        l_batches = np.array_split(l_train, len(l_train) // batch_size)
        y_batches = np.array_split(y_train, len(y_train) // batch_size)
        for X_batch, l_batch, y_batch in zip(X_batches, l_batches, y_batches):
            loss_val, _ = sess.run(
                [loss, training_op],
                feed_dict={X: X_batch, seq_length: l_batch, y: y_batch})
        acc_train = accuracy.eval(feed_dict={X: X_batch, seq_length: l_batch, y: y_batch})
        acc_val = accuracy.eval(feed_dict={X: X_val, seq_length: l_val, y: y_val})
        print("{:4d}  훈련 손실: {:.4f}, 정확도: {:.2f}%  검증 세트 정확도: {:.2f}%".format(
            epoch, loss_val, 100 * acc_train, 100 * acc_val))
        saver.save(sess, "./my_reber_classifier")
   0  훈련 손실: 0.6778, 정확도: 58.00%  검증 세트 정확도: 48.22%
   1  훈련 손실: 0.6791, 정확도: 68.00%  검증 세트 정확도: 49.30%
   2  훈련 손실: 0.6807, 정확도: 62.00%  검증 세트 정확도: 49.40%
   3  훈련 손실: 0.6823, 정확도: 60.00%  검증 세트 정확도: 50.90%
   4  훈련 손실: 0.6839, 정확도: 62.00%  검증 세트 정확도: 51.98%
   5  훈련 손실: 0.6851, 정확도: 60.00%  검증 세트 정확도: 52.90%
   6  훈련 손실: 0.6861, 정확도: 60.00%  검증 세트 정확도: 53.18%
   7  훈련 손실: 0.6865, 정확도: 62.00%  검증 세트 정확도: 53.42%
   8  훈련 손실: 0.6865, 정확도: 60.00%  검증 세트 정확도: 53.22%
   9  훈련 손실: 0.6860, 정확도: 60.00%  검증 세트 정확도: 52.94%
  10  훈련 손실: 0.6851, 정확도: 60.00%  검증 세트 정확도: 52.76%
  11  훈련 손실: 0.6838, 정확도: 60.00%  검증 세트 정확도: 52.40%
  12  훈련 손실: 0.6824, 정확도: 60.00%  검증 세트 정확도: 51.74%
  13  훈련 손실: 0.6809, 정확도: 60.00%  검증 세트 정확도: 51.52%
  14  훈련 손실: 0.6794, 정확도: 60.00%  검증 세트 정확도: 51.22%
  15  훈련 손실: 0.6780, 정확도: 60.00%  검증 세트 정확도: 50.70%
  16  훈련 손실: 0.6767, 정확도: 60.00%  검증 세트 정확도: 50.28%
  17  훈련 손실: 0.6757, 정확도: 60.00%  검증 세트 정확도: 49.94%
  18  훈련 손실: 0.6749, 정확도: 60.00%  검증 세트 정확도: 49.98%
  19  훈련 손실: 0.6742, 정확도: 60.00%  검증 세트 정확도: 50.12%
  20  훈련 손실: 0.6738, 정확도: 60.00%  검증 세트 정확도: 49.98%
  21  훈련 손실: 0.6736, 정확도: 60.00%  검증 세트 정확도: 50.14%
  22  훈련 손실: 0.6734, 정확도: 58.00%  검증 세트 정확도: 50.70%
  23  훈련 손실: 0.6734, 정확도: 58.00%  검증 세트 정확도: 50.74%
  24  훈련 손실: 0.6733, 정확도: 58.00%  검증 세트 정확도: 50.62%
  25  훈련 손실: 0.6732, 정확도: 58.00%  검증 세트 정확도: 50.76%
  26  훈련 손실: 0.6730, 정확도: 58.00%  검증 세트 정확도: 50.80%
  27  훈련 손실: 0.6727, 정확도: 58.00%  검증 세트 정확도: 51.06%
  28  훈련 손실: 0.6723, 정확도: 58.00%  검증 세트 정확도: 51.02%
  29  훈련 손실: 0.6717, 정확도: 58.00%  검증 세트 정확도: 50.88%
  30  훈련 손실: 0.6710, 정확도: 58.00%  검증 세트 정확도: 50.66%
  31  훈련 손실: 0.6702, 정확도: 58.00%  검증 세트 정확도: 50.52%
  32  훈련 손실: 0.6693, 정확도: 58.00%  검증 세트 정확도: 50.50%
  33  훈련 손실: 0.6685, 정확도: 58.00%  검증 세트 정확도: 50.40%
  34  훈련 손실: 0.6677, 정확도: 58.00%  검증 세트 정확도: 50.12%
  35  훈련 손실: 0.6669, 정확도: 58.00%  검증 세트 정확도: 50.04%
  36  훈련 손실: 0.6663, 정확도: 58.00%  검증 세트 정확도: 50.04%
  37  훈련 손실: 0.6657, 정확도: 58.00%  검증 세트 정확도: 49.94%
  38  훈련 손실: 0.6652, 정확도: 58.00%  검증 세트 정확도: 49.90%
  39  훈련 손실: 0.6647, 정확도: 58.00%  검증 세트 정확도: 49.92%
  40  훈련 손실: 0.6642, 정확도: 60.00%  검증 세트 정확도: 49.92%
  41  훈련 손실: 0.6638, 정확도: 60.00%  검증 세트 정확도: 49.96%
  42  훈련 손실: 0.6633, 정확도: 60.00%  검증 세트 정확도: 49.98%
  43  훈련 손실: 0.6628, 정확도: 60.00%  검증 세트 정확도: 49.98%
  44  훈련 손실: 0.6622, 정확도: 60.00%  검증 세트 정확도: 49.96%
  45  훈련 손실: 0.6616, 정확도: 60.00%  검증 세트 정확도: 49.90%
  46  훈련 손실: 0.6609, 정확도: 60.00%  검증 세트 정확도: 49.92%
  47  훈련 손실: 0.6601, 정확도: 62.00%  검증 세트 정확도: 50.12%
  48  훈련 손실: 0.6593, 정확도: 62.00%  검증 세트 정확도: 50.04%
  49  훈련 손실: 0.6584, 정확도: 62.00%  검증 세트 정확도: 50.00%
In [35]:
test_strings = [
    "BPBTSSSSSSSSSSSSXXTTTTTVPXTTVPXTTTTTTTVPXVPXVPXTTTVVETE",
    "BPBTSSSSSSSSSSSSXXTTTTTVPXTTVPXTTTTTTTVPXVPXVPXTTTVVEPE"]
l_test = np.array([len(s) for s in test_strings])
max_length = l_test.max()
X_test = [string_to_one_hot_vectors(s, n_steps=max_length)
          for s in test_strings]

with tf.Session() as sess:
    saver.restore(sess, "./my_reber_classifier")
    y_proba_val = y_proba.eval(feed_dict={X: X_test, seq_length: l_test})

print()
print("레버 문자열일 추정 확률:")
for index, string in enumerate(test_strings):
    print("{}: {:.2f}%".format(string, 100 * y_proba_val[index][0]))

INFO:tensorflow:Restoring parameters from ./my_reber_classifier 레버 문자열일 추정 확률: BPBTSSSSSSSSSSSSXXTTTTTVPXTTVPXTTTTTTTVPXVPXVPXTTTVVETE: 28.99% BPBTSSSSSSSSSSSSXXTTTTTVPXTTVPXTTTTTTTVPXVPXVPXTTTVVEPE: 41.32%



성능이 좋지 않게 나왔다..! 학습의 Epoch를 늘리면 더 성능이 나아질 것 같다. 500까지 올려봤지만 Overfitting하는 모습을 보였다. 적당히 150 Epoch까지 올리니 10%가량 성능이 올랐다.


728x90
반응형