머신러닝

99% MNIST CNN 예제 코드

coding art 2021. 7. 10. 16:47
728x90

Github에서 흔히 구할 수 있는 오픈 소스인 CNN을 사용한 MNIST 문제의 99% 인식률을 보기위한 기본적인 파라메터 설정에 관해서 알아보자. 컴퓨팅 시간을 줄이기 위해서 trainning_epochs 값을 1로 취했을 때 1분의 컴퓨팅 타임으로 97%의 인식률이 얻어진다.

MNIST 숫자 데이터베이스는 데이터 수가 엄청 많으므로 session.run 단계에서는 100개씩 데이터를 샘플링 하여 batch job을 만들어 실행시키게 된다.

가장 간단한 CNN이라 해도 적어도 2단에 걸친 컨볼류션 처리가 필요하다. 28X28 1개 데이터에 대해서 reshaping 한 후 3X3 필터를 32개 준비하여 컨볼루션 처리 후 relu()를 적용하고 최대값을 pooling 하면 14X14 이미지 매트릭스가 얻어진다.

 

64개의 필터를 사용하여 컨볼루션 2단 처리를 하게되면 7X7 이미지 메트릭스가 64개 얻어지며 다시 reshaping 하게되면 3164개의 매트릭스 요소를 가지는 일차원 매트릭스로 바뀌며 이르 fully connected layer 에 연결하여 softmax 처리를 하면 최종 결과가 얻어진다.

 

한편 정밀도를 올리기 위해서 trainning_epochs 값을 15로 취할 경우 컴퓨팅 시간이 10분 이상으로 정말 많이 소요된다. 계산 결과 98.85% 라는 결과가 얻어졌다. 즉 반올림해서 99% 라는 의미이다. 정밀도를 더 올리기 위해서는 fully connected layer에서 wide deep 기법을 적용해야 하면 3단 처리를 할 정도면 99.3% 수준으로 정밀도가 향상된다.

 

원래 AlexNet 개발과정에서는 GPU 사용에 의해 컴퓨팅 속도를 증가시킨 것으로 알려져 있으며 이 방법들은 real time 용은 아니다. 이렇게 컴퓨팅 시간이 많이 소요되는 코드로는 CNN 외에 비트코인 채굴 코드가 GPU를 사용해야만 전기세를 뽑을 수 있을 정도라고 알려져 있다. 인식률을 높여 보겠다고 수고들은 참 많이 하셨는데 글쎄 어느 정도 상업적으로 쓸모가 있을지는 미지수다.

 

특히 주의할 점은 이 코드는 MNIST 문자 판독에만 쓰일 수 있으며 LeCun 이 개발했다고 해서 LeNet 이라고도 한다. 그밖에 힌튼교수 랩에서 개발한 CIFAR-10 같은 1만개 이상의 그래픽 이미지들로 구성된 데이터베이스에 대해서는 AlexNet 처럼 그에 적합한 CNN 코드를 개발해야 한다. 지금 현재는 AlexNet 조차도 구글 텐서플로우 사이트에 그 내용이 오픈 소스로 공개되어 있음을 참조하자. AlexNet 이후로eh ImageNet, ResNet, GoogleNet을 포함하여 여러 코드들이 다수 등장해 있으며 AlexNet의 달성했던 15.7% 의 에러를 5% 이내 범위로 줄여 사람의 인식률을 능가해 가고 있다.

 

한편으로는 이러한 접근 방법이 GPU를 필요로 한다는 점에서 컴퓨팅 타임 부담이 너무 크므로 아예 1만개 이상의 그래픽 이미지들을 일일이 라벨링하여 사전에 학습을 시킨 후 사용자는 단지 테스트 이미지를 넣어보기만 하면 결과를 출력할 수 있는 Pretrainning 기법의 사이트들로서 스탠포드 대학에서 개발한 ImageNet 과 구글 프로젝트로 생성된 ml5js 가 공개되어 있다.

 

#mnist_cnn.py

# MNIST and Convolutional Neural Network
import tensorflow as tf
import random

from tensorflow.examples.tutorials.mnist import input_data

tf.set_random_seed(777)  # reproducibility

mnist = input_data.read_data_sets("MNIST_data/", one_hot=True)

# parameters
learning_rate = 0.001
training_epochs = 15
batch_size = 100

# input place holders
X = tf.placeholder(tf.float32, [None, 784])
X_img = tf.reshape(X, [-1, 28, 28, 1])   # img 28x28x1 (black/white)
Y = tf.placeholder(tf.float32, [None, 10])
# L1 ImgIn shape=(?, 28, 28, 1)
W1 = tf.Variable(tf.random_normal([3, 3, 1, 32], stddev=0.01))
#    Conv     -> (?, 28, 28, 32)
#    Pool     -> (?, 14, 14, 32)
L1 = tf.nn.conv2d(X_img, W1, strides=[1, 1, 1, 1], padding='SAME')
L1 = tf.nn.relu(L1)
L1 = tf.nn.max_pool(L1, ksize=[1, 2, 2, 1],strides=[1, 2, 2, 1], padding='SAME')
'''
Tensor("Conv2D:0", shape=(?, 28, 28, 32), dtype=float32)
Tensor("Relu:0", shape=(?, 28, 28, 32), dtype=float32)
Tensor("MaxPool:0", shape=(?, 14, 14, 32), dtype=float32)
'''

# L2 ImgIn shape=(?, 14, 14, 32)
W2 = tf.Variable(tf.random_normal([3, 3, 32, 64], stddev=0.01))
#    Conv      ->(?, 14, 14, 64)
#    Pool      ->(?, 7, 7, 64)
L2 = tf.nn.conv2d(L1, W2, strides=[1, 1, 1, 1], padding='SAME')
L2 = tf.nn.relu(L2)
L2 = tf.nn.max_pool(L2, ksize=[1, 2, 2, 1],
                    strides=[1, 2, 2, 1], padding='SAME')
L2_flat = tf.reshape(L2, [-1, 7 * 7 * 64])
'''
Tensor("Conv2D_1:0", shape=(?, 14, 14, 64), dtype=float32)
Tensor("Relu_1:0", shape=(?, 14, 14, 64), dtype=float32)
Tensor("MaxPool_1:0", shape=(?, 7, 7, 64), dtype=float32)
Tensor("Reshape_1:0", shape=(?, 3136), dtype=float32)
'''

# Final FC 7x7x64 inputs -> 10 outputs
W3 = tf.get_variable("W3", shape=[7 * 7 * 64, 10],
                     initializer=tf.contrib.layers.xavier_initializer())
b = tf.Variable(tf.random_normal([10]))
logits = tf.matmul(L2_flat, W3) + b

# define cost/loss & optimizer
cost = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(
    logits=logits, labels=Y))
optimizer = tf.train.AdamOptimizer(learning_rate=learning_rate).minimize(cost)

# initialize
sess = tf.Session()
sess.run(tf.global_variables_initializer())

# train my model
print('Learning started. It takes sometime.')
for epoch in range(training_epochs):
    avg_cost = 0
    total_batch = int(mnist.train.num_examples / batch_size)

    for i in range(total_batch):
        batch_xs, batch_ys = mnist.train.next_batch(batch_size)
        feed_dict = {X: batch_xs, Y: batch_ys}
        c, _ = sess.run([cost, optimizer], feed_dict=feed_dict)
        avg_cost += c / total_batch

    print('Epoch:', '%04d' % (epoch + 1), 'cost =', '{:.9f}'.format(avg_cost))

print('Learning Finished!')

# Test model and check accuracy
correct_prediction = tf.equal(tf.argmax(logits, 1), tf.argmax(Y, 1))
accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32))
print('Accuracy:', sess.run(accuracy, feed_dict={
      X: mnist.test.images, Y: mnist.test.labels}))

# Get one and predict
r = random.randint(0, mnist.test.num_examples - 1)
print("Label: ", sess.run(tf.argmax(mnist.test.labels[r:r + 1], 1)))
print("Prediction: ", sess.run(
    tf.argmax(logits, 1), feed_dict={X: mnist.test.images[r:r + 1]}))

# plt.imshow(mnist.test.images[r:r + 1].
#           reshape(28, 28), cmap='Greys', interpolation='nearest')
# plt.show()

'''
Epoch: 0001 cost = 0.340291267
Epoch: 0002 cost = 0.090731326
Epoch: 0003 cost = 0.064477619
Epoch: 0004 cost = 0.050683064
Epoch: 0005 cost = 0.041864835
Epoch: 0006 cost = 0.035760704
Epoch: 0007 cost = 0.030572132
Epoch: 0008 cost = 0.026207981
Epoch: 0009 cost = 0.022622454
Epoch: 0010 cost = 0.019055919
Epoch: 0011 cost = 0.017758641
Epoch: 0012 cost = 0.014156652
Epoch: 0013 cost = 0.012397016
Epoch: 0014 cost = 0.010693789
Epoch: 0015 cost = 0.009469977
Learning Finished!
Accuracy: 0.9885
'''