Tensorflow CNN — MINST

Joe
Epopcon Data Science & Engineering
12 min readJul 23, 2018

CNN을 이용한 이미지 인식을 tensorflow로 어떻게 만드는지 아주 간단한 sample code를 읽어 보자. (MNIST image를 인식하는 sample code)

import tensorflow as tf
from tensorflow.examples.tutorials.mnist import input_data
mnist = input_data.read_data_sets("MNIST_data/",one_hot=True)

mnist data를 읽어 오는데 normalize까지 되어 있단다. label은 one hot encoding한다는 뜻.

def init_weights(shape):
init_random_dist = tf.truncated_normal(shape, stddev=0.1)
return tf.Variable(init_random_dist)
def init_bias(shape):
init_random_dist = tf.truncated_normal(shape, stddev=0.1)
return tf.Variable(init_random_dist)

weight하고 bias를 넣어 주는데가 많아서 function을 만든거다. shape를 주면 고만하게 만들어서 램덤값으로 초기값을 넣어서 리턴. weight하고 bias가 training될 값들이라서 tf.Variable을 쓴 거겠지…

def conv2d(x, W):
return tf.nn.conv2d(x, W, strides=[1, 1, 1, 1], padding='SAME')
def max_pool_2by2(x):
return tf.nn.max_pool(x, ksize=[1, 2, 2, 1],
strides=[1, 2, 2, 1], padding='SAME')
def convolutional_layer(input_x, shape):
W = init_weights(shape)
b = init_bias([shape[3]])
return tf.nn.relu(conv2d(input_x, W) + b)

CNN이니까 convolution layer가 있어야지… 이 또한 여러번 나올 수 있어서 function을 만들었네.
tensorflow의 tf.nn.conv2d를 사용했는데, 여기서 x는 input이고 W는 filter (kernel)이고 strides는 한칸씩 가도록 기본으로 [1, 1, 1, 1]를 썼고, padding은 input하고 output의 크기가 같도록 ’SAME’를 썼다. 이거는 왠만하면 그냥 이렇게 해도 될 듯.

tensorflow의 filter는 1D vector로 표현하고 [filter_height, filter_width, in_channels, out_channels]의 형식을 갖는다. in_channel은 예를 들어서 image 입력을 직접받는 conv layer라면 R,G,B channel이나 Gray channel 을 의미하는 것이라, RBG일땐 3, Gray일땐 1이 될 터이고 out_channel은 filter의 output channel 수이다. (filter의 갯수 처럼 이해할 수 도 있겠네). input channel의 수와 filter의 input channel의 수를 맞추어야 하나 보다.

tensorflow의 stride는 1D vector로 표현하고 [batch, height, width, channels]가 default란다. 이만큼씩 건너뛰는 거다.

convolution 다음에 꼭 따라오는 pooling layer를 위해서 tensorflow에서 제공하는 function들 중 tf.nn.max_pool 썼다. 이미지 인식을 위해서는 대체로 max pooling을 쓴다고 하니 이것도 그냥 쓰면 될 듯 하다. ksize에 있는 2,2가 pooling size이고 stride는 2x2 pooling을 하면서 같은 크기로 건너뛰기를 하기위해 2,2를 썼다.

ksize도 stride와 동일한 형식으로 사용된다. pooling하는 크기를 나타내겠지…

convolutional_layer function이 직접 conv layer를 만드는 function. filter의 shape를 입력받아서 그대로 W와 b를 만들고 W를 conv2d에 넣어서 conv layer를 만든 다음에 b를 더하고 그 결과를 relu를 통과시킨 값을 리턴한다. (tensorflow의 tf.nn.relu)

Conv layer에서는 filter와 b가 training될 param이다. fully connected layer에 비하면 상당히 작은 양이다.

def normal_full_layer(input_layer, size):
input_size = int(input_layer.get_shape()[1])
W = init_weights([input_size, size])
b = init_bias([size])
return tf.matmul(input_layer, W) + b

conv layer들을 거치고 나면 의례 fully connected layer 몇 개를 거처서 output하는게 통상적인 방법이라 이를 위한 function을 만들었다.
fully connected layer는 input * output 만큼의 W와 output 만큼의 bias가 필요하고 input과 W를 곱하고 (matrix 곱) 거기에 bias를 더하니까… 그걸 표현한 것.

여기까지는 CNN 구성을 위해서 자주 사용될 부분을 function으로 만든것이다. 다음 내용은 이들을 이용해서 실제 layer를 구성하는 부분이다.

x = tf.placeholder(tf.float32,shape=[None,784])
y_true = tf.placeholder(tf.float32,shape=[None,10])

x는 input image data이다. mnist는 image 한장이 28x28x1=784 크기인 여러장의 그림들이므로 이렇게 표현함. y_true는 그림 한장한장이 어떤 숫자인지를 나타내는지의 label이다. 0에서 9까지 10가지 label이 one hot encoding되어 있어서 이렇게 표현하였다.

x_image = tf.reshape(x,[-1,28,28,1])convo_1 = convolutional_layer(x_image,shape=[6,6,1,32])
convo_1_pooling = max_pool_2by2(convo_1)

2D data input을 4D data로 reshape 했다. conv1 layer는 input image를 입력으로 하고 filter를 shape [6,6,1,32]로 했으며 conv layer의 결과를 2x2 max pooling하는 내용이다.

convo1, convo_1_pooling layer

convo_1과 convo_1_pooling은 위와 같은 형태가 될 것 같다. 2x2 pooling을 하므로 pooling하고 나면 height, width가 반씩 줄어 들었다.

convo_2 = convolutional_layer(convo_1_pooling,shape=[6,6,32,64])
convo_2_pooling = max_pool_2by2(convo_2)

convo_1_pooling의 결과를 input으로 두번째 conv layer를 구성하고 있다. filter는 [6,6,32,64]로 되어 있는데… 32가 convo_1_pooling의 ouput channel이라 이와 맞춘 거겠지

convo_2, convo_2_pooling

convo_2_pooling까지 하고 나면 위와 같은 형태가 될 것이다. conv layer는 이렇게 두 층만 만들었다.

convo_2_flat = tf.reshape(convo_2_pooling,[-1,7*7*64])
full_layer_one = tf.nn.relu(normal_full_layer(convo_2_flat,1024))
hold_prob = tf.placeholder(tf.float32)
full_one_dropout = tf.nn.dropout(full_layer_one,keep_prob=hold_prob)
y_pred = normal_full_layer(full_one_dropout,10)

여기는 convo_2_pooling의 결과를 input으로 하는 fully connected layer 이다. convo_2_pooling 결과가 7x7x64 이므로 이런 이미지 여러장이 연속된 data를 [1, 7*7*64]=[1, 3136] 으로 펼쳐서 fully connected layer의 input으로 만들고 있다.
full_layer_one은 input 3136, ouput 1024의 layer + relu로 구성되어 있다.

다음은 full_layer_one에 dropout (tf.nn.dropout) 을 적용한 것이다. hold_prob에 유지할 비율을 지정하면 됨.

그 다음으로 마지막 fully connected layer를 만들고 있다. output은 당연 10이다.

fully connected layer

drop_out이 없다 치면 위와 같은 그림이 될 것 같다.

cross_entropy = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits_v2(labels=y_true,logits=y_pred))optimizer = tf.train.AdamOptimizer(learning_rate=0.0001)train = optimizer.minimize(cross_entropy)

loss function, optimizer, train 이다. 전형적인 형태이다.

init = tf.global_variables_initializer()steps = 5000with tf.Session() as sess:    
sess.run(init)

for i in range(steps):

batch_x , batch_y = mnist.train.next_batch(20)

sess.run(train,
feed_dict={x:batch_x,y_true:batch_y,hold_prob:0.5})

# PRINT OUT A MESSAGE EVERY 100 STEPS
if i%100 == 0:

print('Currently on step {}'.format(i))
print('Accuracy is:')
# Test the Train Model
matches = tf.equal(
tf.argmax(y_pred,1),tf.argmax(y_true,1))
acc = tf.reduce_mean(tf.cast(matches,tf.float32)) print(sess.run(acc,
feed_dict= {
x : mnist.test.images,
y_true : mnist.test.labels,
hold_prob : 1.0}))
print('\n')

training session이다. 역시 전형적인 형태이다.

실행해 보면…

아래와 같이 98% 내외의 정확도로 MNIST 손글씨 숫자를 인식하는 CNN이 training된 것이 보인다. 엄청 단순한 model 인걸 감안하면 정확도는 꽤 높네…

Currently on step 0
Accuracy is:
0.0469


Currently on step 100
Accuracy is:
0.8707


Currently on step 200
Accuracy is:
0.9143
...Currently on step 4700
Accuracy is:
0.9878


Currently on step 4800
Accuracy is:
0.9847


Currently on step 4900
Accuracy is:
0.9876

다음엔…

이 posting의 내용은 이미지 중에서도 가장 단순한 mnist data를 tensorflow를 이용한 최대한 단순한 cnn을 구성하여 이미지 인식을 해 본 것이다. 일종의 연습 문제 정도 수준? 그렇다면 실전(?)에 써볼만한 구현을 하려면 어떻게 해야 할까? 엄청 복잡하게 layer를 구성해서 수 많은 data로 test를 하면서 오랜 시간 개발을 해야 할까?? 불행인지 다행인지… 이미 남들이 다 만들어 놓아서 그냥 가져다 쓰기만 하면 된다. ㅡㅡ; 다음 posting에서는 진짜 칼라 사진을 아주 쉬운 방법으로 image 인식하는 걸 만들어 볼까 한다. 곧.

--

--