Impove MNIST Training

참고

참고 소스코드

기본 모델

기본 모델은 softmax cross_entropy를 비용함수로 사용하고 AdamOptimizer를 사용해 학습을 진행했다.

hypothesis = tf.matmul(X, W) + b
cost = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(logits=hypothesis, labels=Y))
optimizer = tf.train.AdamOptimizer(learning_rate=learning_rate).minimize(cost)

트레이닝의 결과 약 90.11%의 정확도를 나타냈다.

Layer 추가

해당 뉴럴 네트워크의 레이어를 두개 추가해보자. 레이어를 이을 때 Activation Function으로는 ReLU를 사용한다.

# 기존 layer 1개
# W = tf.Variable(tf.random_normal([784, nb_classes]))
# b = tf.Variable(tf.random_normal([nb_classes]))
# hypothesis = tf.matmul(X, W) + b

# 2개의 layer를 추가해서 3-layer 구현
W1 = tf.Variable(tf.random_normal([784, 256]))
b1 = tf.Variable(tf.random_normal([256]))
L1 = tf.nn.relu(tf.matmul(X, W1) + b1)

W2 = tf.Variable(tf.random_normal([256, 256]))
b2 = tf.Variable(tf.random_normal([256]))
L2 = tf.nn.relu(tf.matmul(L1, W2) + b2)

W3 = tf.Variable(tf.random_normal([256, 10]))
b3 = tf.Variable(tf.random_normal([10]))
hypothesis = tf.matmul(L2, W3) + b3

결과

('Epoch:', '0001', 'cost =', '152.685525038')
('Epoch:', '0002', 'cost =', '38.833293562')
('Epoch:', '0003', 'cost =', '24.555723563')
('Epoch:', '0004', 'cost =', '16.916167007')
('Epoch:', '0005', 'cost =', '12.372642395')
('Epoch:', '0006', 'cost =', '9.211656850')
('Epoch:', '0007', 'cost =', '6.762751078')
('Epoch:', '0008', 'cost =', '5.102382672')
('Epoch:', '0009', 'cost =', '3.734673123')
('Epoch:', '0010', 'cost =', '2.867742062')
('Epoch:', '0011', 'cost =', '2.060030074')
('Epoch:', '0012', 'cost =', '1.609305068')
('Epoch:', '0013', 'cost =', '1.197766857')
('Epoch:', '0014', 'cost =', '0.865810280')
('Epoch:', '0015', 'cost =', '0.746588653')
Learning finished
('Accuracy: ', 0.9409)

정확도가 94.09%로 향상된 것을 확인할 수 있다.

초기값 설정

기본 모델은 weight의 값을 랜덤하게 설정했다. 더 나은 정확도를 위해 xavier initialization을 사용해서 설정한다.

# with xavier initialization
W1 = tf.get_variable("W1", shape=[784, 256], initializer=tf.contrib.layers.xavier_initializer())
b1 = tf.Variable(tf.random_normal([256]))
L1 = tf.nn.relu(tf.matmul(X, W1) + b1)

W2 = tf.get_variable("W2", shape=[256, 256], initializer=tf.contrib.layers.xavier_initializer())
b2 = tf.Variable(tf.random_normal([256]))
L2 = tf.nn.relu(tf.matmul(L1, W2) + b2)

W3 = tf.get_variable("W3", shape=[256, 10], initializer=tf.contrib.layers.xavier_initializer())
b3 = tf.Variable(tf.random_normal([10]))
hypothesis = tf.matmul(L2, W3) + b3

결과

('Epoch:', '0001', 'cost =', '0.301504518')
('Epoch:', '0002', 'cost =', '0.114356917')
('Epoch:', '0003', 'cost =', '0.075476892')
('Epoch:', '0004', 'cost =', '0.054008337')
('Epoch:', '0005', 'cost =', '0.039032495')
('Epoch:', '0006', 'cost =', '0.030570493')
('Epoch:', '0007', 'cost =', '0.024361739')
('Epoch:', '0008', 'cost =', '0.020248147')
('Epoch:', '0009', 'cost =', '0.015587771')
('Epoch:', '0010', 'cost =', '0.015434952')
('Epoch:', '0011', 'cost =', '0.013758246')
('Epoch:', '0012', 'cost =', '0.010258016')
('Epoch:', '0013', 'cost =', '0.009600071')
('Epoch:', '0014', 'cost =', '0.014543132')
('Epoch:', '0015', 'cost =', '0.008170652')
Learning finished
('Accuracy: ', 0.9815)

결과가 98.15% 로 향상되었다. 흥미로운 것은 weight를 랜덤하게 설정했을 때 시작했던 cost(약 152)와 비교하면 처음부터 cost가 상당히 낮은 것을 확인할 수 있다.

Deep Learning

그렇다면 레이어를 더 추가하고 차원도 더 늘리면 확률이 더 높아지지 않을까? 레이어를 두 개 더 추가해봤다.

W1 = tf.get_variable("W1", shape=[784, 512], initializer=tf.contrib.layers.xavier_initializer())
b1 = tf.Variable(tf.random_normal([512]))
L1 = tf.nn.relu(tf.matmul(X, W1) + b1)

W2 = tf.get_variable("W2", shape=[512, 512], initializer=tf.contrib.layers.xavier_initializer())
b2 = tf.Variable(tf.random_normal([512]))
L2 = tf.nn.relu(tf.matmul(L1, W2) + b2)

W3 = tf.get_variable("W3", shape=[512, 512], initializer=tf.contrib.layers.xavier_initializer())
b3 = tf.Variable(tf.random_normal([512]))
L3 = tf.nn.relu(tf.matmul(L2, W3) + b3)

W4 = tf.get_variable("W4", shape=[512, 512], initializer=tf.contrib.layers.xavier_initializer())
b4 = tf.Variable(tf.random_normal([512]))
L4 = tf.nn.relu(tf.matmul(L3, W4) + b4)

W5 = tf.get_variable("W5", shape=[512, 10], initializer=tf.contrib.layers.xavier_initializer())
b5 = tf.Variable(tf.random_normal([10]))
hypothesis = tf.matmul(L4, W5) + b5

레이어와 차원이 늘어나니 트레이닝 속도가 줄어드는 것을 확인할 수 있었다. 그리고 정확도는…

('Epoch:', '0001', 'cost =', '0.296932600')
('Epoch:', '0002', 'cost =', '0.106179351')
('Epoch:', '0003', 'cost =', '0.071373077')
('Epoch:', '0004', 'cost =', '0.052599245')
('Epoch:', '0005', 'cost =', '0.041444699')
('Epoch:', '0006', 'cost =', '0.033571568')
('Epoch:', '0007', 'cost =', '0.029018834')
('Epoch:', '0008', 'cost =', '0.025650986')
('Epoch:', '0009', 'cost =', '0.023551717')
('Epoch:', '0010', 'cost =', '0.022069219')
('Epoch:', '0011', 'cost =', '0.020021558')
('Epoch:', '0012', 'cost =', '0.014267542')
('Epoch:', '0013', 'cost =', '0.016667521')
('Epoch:', '0014', 'cost =', '0.015644723')
('Epoch:', '0015', 'cost =', '0.015559883')
Learning finished
('Accuracy: ', 0.9799)

오히려 감소했다. 해당 현상은 레이어가 필요보다 많이 추가되어 벌어지는 overfitting 현상으로 학습 데이터에 과적합 된 것이다. 해당 현상은 여러가지 방법으로 해결할 수 있지만 여기에서는 dropout 기법을 사용한다.
dropout 기법은 레이어를 통과하는 값 중 일부를 일부러 누락시키는 방법이다.
dropout 을 적용한 코드는 다음과 같다.

keep_prob = tf.placeholder(tf.float32)

W1 = tf.get_variable("W1", shape=[784, 512], initializer=tf.contrib.layers.xavier_initializer())
b1 = tf.Variable(tf.random_normal([512]))
L1 = tf.nn.relu(tf.matmul(X, W1) + b1)
L1 = tf.nn.dropout(L1, keep_prob=keep_prob)
...중략

다음처럼 keep_prob이라는 placeholder를 정의하고 레이어 가장 마지막에 dropout을 해준다. 주의할 점은 keep_prob 값은 placeholder로 세션이 실행될 때 값이 입력되는 것으로 정의했으며, 트레이닝을 할 때에는 0.7로 넣고 실제 예측을 할 때에는 1로 넣어준다. 트레이닝 할 때의 값은 dropout을 원하는 정도로 값을 입력하면 되지만 실제 예측을 할 때에는 1로 설정해서 dropout이 되지 않도록 설정해야 한다.

# 트레이닝 할 때
c, _ = sess.run([cost, optimizer], feed_dict={
                            X: batch_xs, Y: batch_ys, keep_prob:0.7})
# 실체 추측할 때
print("Accuracy: ", accuracy.eval(session=sess, feed_dict={
          X: mnist.test.images, Y: mnist.test.labels, keep_prob:1}))

dropout을 적용한 뒤 결과는 다음과 같다.

('Epoch:', '0001', 'cost =', '0.406563665')
('Epoch:', '0002', 'cost =', '0.186642536')
('Epoch:', '0003', 'cost =', '0.143258018')
('Epoch:', '0004', 'cost =', '0.119243401')
('Epoch:', '0005', 'cost =', '0.102207923')
('Epoch:', '0006', 'cost =', '0.088779936')
('Epoch:', '0007', 'cost =', '0.080237208')
('Epoch:', '0008', 'cost =', '0.072164918')
('Epoch:', '0009', 'cost =', '0.065138250')
('Epoch:', '0010', 'cost =', '0.060656772')
('Epoch:', '0011', 'cost =', '0.053306809')
('Epoch:', '0012', 'cost =', '0.052601467')
('Epoch:', '0013', 'cost =', '0.051706527')
('Epoch:', '0014', 'cost =', '0.046858657')
('Epoch:', '0015', 'cost =', '0.044673255')
Learning finished
('Accuracy: ', 0.9827)

xavier initialization 을 적용했을 때와 비슷하지만(그 때 이상하게 정확도가 높게 나왔다;;) 단순히 레이어를 추가했을 때보다 정확도가 다시 올라간 것을 확인할 수 있다.

결론

DNN에 성능을 높일 수 있는 여러가지 기법들에 대해 알아봤다. 초기값 설정, 레이어 추가, Dropout 등을 사용했으며 결과적으로 약 90%의 정확도를 98%까지 끌어올렸다.