텐서플로우로 간단한 CNN(Convolutional neural network) 만들어보기
1. tensorflow import + MNIST 데이터셋 읽어오기
1 2 3 | import tensorflow as tf from tensorflow.examples.tutorials.mnist import input_data mnist = input_data.read_data_sets('MNIST_data', one_hot=True, reshape=False) | cs |
CNN의 내용에 초점을 맞추기 위해 가장 간단하게 데이터셋을 읽어올 수 있는 MNIST를 이용하였습니다.
데이터셋을 읽어올 때 one_hot 인코딩 형태로, 이미지 모양 그대로 받아오기 위해 reshape=False를 해줍니다.
읽어온 이미지는 이런식의 모양으로 되어있습니다.
이미지 사이즈는 세로 28, 가로 28, 채널 1 입니다. 이 사이즈는 CNN의 입력으로 사용되기 때문에 기억하고 있어야합니다.
2. CNN 모델 설정 및 Placeholder 설정
이번에 만들 모델은 2번의 Convolution Layer를 거쳐서 마지막으로 Fully Connected Layer로 만들어서 0~9인지를 판별할것 입니다.
더 좋게하면 한도끝도 없지만, 그냥 구현에 초점을 맞췄기 때문에 마구잡비로 만들어 보았습니다.
※ Fully Connected Layer를 모르시면 그냥 육면체인 Convolution의 결과를 1자로 피는 것이라고 생각하면 될것같습니다...
좀더 자세하게 가보면,
(28 X 28 X 1)의 이미지에서 CONV,RELU,MAXPOOL을 거쳐서 (14 X 14 X 4)를 만들고
(14 X 14 X 4)의 이미지에서 CONV,RELU,MAXPOOL을 거쳐서 (7 X 7 X 8)을 만들고
(7 X 7 X 8)의 이미지에서 10개를 뽑아내서 0~9인지를 맞춰볼 것입니다.
이제 텐서플로우로 다시 돌아와보면 어쨌든 학습을 시키기 위해서
이미지와, 그 이미지가 0~9의 값이 실제로 무엇인지를 알려주어야 합니다.
즉, 입력 이미지와 미리 정한 결과를 계속해서 직접 넣어주어야 한다는 것입니다.
이럴 때는 tf.placeholder를 사용합니다.
1 2 | X= tf.placeholder(tf.float32,shape=[None,28,28,1]) Y_Label = tf.placeholder(tf.float32,shape=[None,10]) | cs |
float32타입, 그리고 위에 설명한대로 shape를 만들었습니다.
배치학습을 할 것이기 때문에 가장 앞의 shape는 None으로 두어서 배치크기만큼 생성되도록 합니다.
3. Convolution Layer 만들기
만들어 놓은 모델대로 변화을 하기위해서 가장 첫번째 변환에 대해서 설명합니다.
(28 X 28 X 1)의 이미지에서 CONV,RELU,MAXPOOL을 거쳐서 (14 X 14 X 4)를 만들기를 하는 겁니다.
중간에 어떤 연산이 이루어지나도 중요한 내용이지만 '구현'에 대해서만 언급합니다.
1 2 3 4 5 | Kernel1 = tf.Variable(tf.truncated_normal(shape=[4,4,1,4],stddev=0.1)) Bias1 = tf.Variable(tf.truncated_normal(shape=[4],stddev=0.1)) Conv1 = tf.nn.conv2d(X, Kernel1, strides=[1,1,1,1], padding='SAME') + Bias1 Activation1 = tf.nn.relu(Conv1) Pool1 = tf.nn.max_pool(Activation1, ksize=[1,2,2,1] , strides=[1,2,2,1], padding='SAME') | cs |
1 | Kernel1 = tf.Variable(tf.truncated_normal(shape=[4,4,1,4],stddev=0.1)) | cs |
1 | Bias1 = tf.Variable(tf.truncated_normal(shape=[4],stddev=0.1)) | cs |
1 | Conv1 = tf.nn.conv2d(X, Kernel1, strides=[1,1,1,1], padding='SAME') + Bias1 | cs |
(24 X 24 X 1)의 이미지에 1칸씩 stride 그리고 (4 X 4 X 1)의 Kernel을 사용하는 상황에서
출력사이즈가stride가 1이기 때문에 (24 X 24 X 1)이 될것이고 이렇게 되기 위해서는 (4 X 4 X 1)의 Kerenl이
모든 방향의 제로패딩이 3으로 되어지고 1칸씩 이동할 때 (24 X 24 X 1)이 나오게 됩니다.
정말 간단하게 생각해보면, 출력사이즈에 알맞게 padding이 주어지게 됩니다.
이것도 직관적으로 예시를 들어서 이해를 해볼수 있습니다.
위의 그림과 같은 상황에서는 stride가 3이기 때문에 제로패딩으로 감싸지 않는다면 Kernel은 더이상 Convolution을 할수 없습니다.
따라서 'VALID' 옵션을 주게 되면, 결과는 [1 X 1] 이 나오게 됩니다.
즉, 'VALID' 옵션을 주면 딱 가능한 크기로만 계산이 되는 것을 알수 있습니다.
1 | Activation1 = tf.nn.relu(Conv1) | cs |
1 | Pool1 = tf.nn.max_pool(Activation1, ksize=[1,2,2,1] , strides=[1,2,2,1], padding='SAME') | cs |
tf.nn.max_pool을 이용해서 원하는 사이즈인 (14 X 14 X 4) 사이즈를 만들어 줍니다.
pooling의 경우 tf.nn에 제공되는 평균,최대,최소 풀링 등 다양하게 존재합니다만, 일반적으로 max_pool을 가장 많이사용하는것으로 알고있습니다.
이렇게 해서 1개의 CNN Layer를 구성해 보았습니다.
2번째 CNN Layer도 똑같은 과정으로 계산을 하면서 만들면 어렵지 않겠죠.
4. Fully Connected Layer 만들기
CNN Layer를 2번 거치게 되면 (7 X 7 X 8) 사이즈의 결과물을 가지고 있을 것입니다.
이제 이것을 가지고 이게 0인지 1인지,....9인지를 판별을 해야 합니다.
1 2 3 4 | W1 = tf.Variable(tf.truncated_normal(shape=[8*7*7,10])) B1 = tf.Variable(tf.truncated_normal(shape=[10])) Pool2_flat = tf.reshape(Pool2,[-1,8*7*7]) OutputLayer = tf.matmul(Pool2_flat,W1)+B1 | cs |
5. Loss Function과 Optimizer 설정, 그리고 Run
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | Loss = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(labels=Y_Label, logits=OutputLayer)) train_step = tf.train.AdamOptimizer(0.005).minimize(Loss) correct_prediction = tf.equal(tf.argmax(OutputLayer, 1), tf.argmax(Y_Label, 1)) accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32)) with tf.Session() as sess: print("Start....") sess.run(tf.global_variables_initializer()) for i in range(10000): trainingData, Y = mnist.train.next_batch(64) sess.run(train_step,feed_dict={X:trainingData,Y_Label:Y}) if i%100 : print(sess.run(accuracy,feed_dict={X:mnist.test.images,Y_Label:mnist.test.labels})) | cs |
이번에도 하나씩 살펴보겠습니다.
1 | Loss = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(labels=Y_Label, logits=OutputLayer)) | cs |
모델에서 결과물을 냈으면 당연히 이것이 얼마나 원하는 모델과 다른지 Loss function(또는 Cost function)을 정의해 주어야 합니다.
보통 여러개의 클래스를 분류할 때에는 SoftMax에 Cross_Entropy를 붙여서 사용하는데, 역시나 텐서플로우에는 기본적으로 내장되어있습니다.
위에 들어가는 labels=실제 클래스값, logits= 출력 값을 넣어주면 됩니다.
1 | train_step = tf.train.AdamOptimizer(0.005).minimize(Loss) | cs |
1 2 | correct_prediction = tf.equal(tf.argmax(OutputLayer, 1), tf.argmax(Y_Label, 1)) accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32)) | cs |
1 2 3 4 5 6 7 8 | with tf.Session() as sess: print("Start....") sess.run(tf.global_variables_initializer()) for i in range(10000): trainingData, Y = mnist.train.next_batch(64) sess.run(train_step,feed_dict={X:trainingData,Y_Label:Y}) if i%100 : print(sess.run(accuracy,feed_dict={X:mnist.test.images,Y_Label:mnist.test.labels})) | cs |
6. 결과
7. 전체 소스코드
'딥러닝 > 텐서플로우' 카테고리의 다른 글
텐서플로우(TensorFlow), Anaconda 설치로 머신러닝 시작하기 (0) | 2016.07.20 |
---|