본문 바로가기
DeepLearning Framework & Coding/Tensorflow 1.X

[코드로 이해하는 딥러닝 16] - CNN(Convolutional Neural Network)

by 노마드공학자 2021. 1. 2.

[코드로 이해하는 딥러닝 0] - 글연재에 앞서 https://limitsinx.tistory.com/27

[코드로 이해하는 딥러닝 1] - Tensorflow 시작 https://limitsinx.tistory.com/28 

[코드로 이해하는 딥러닝 2] - Tensorflow 변수선언 https://limitsinx.tistory.com/29

[코드로 이해하는 딥러닝 3] - Tensorflow placeholder변수 https://limitsinx.tistory.com/30

[코드로 이해하는 딥러닝 4] - 선형회귀(Linear Regression) https://limitsinx.tistory.com/31

[코드로 이해하는 딥러닝 5] - 다중선형회귀(Multiple Linear Regression) https://limitsinx.tistory.com/32

[코드로 이해하는 딥러닝 6] - 회귀(Regression)에 대한 다른 접근 https://limitsinx.tistory.com/33

[코드로 이해하는 딥러닝 7] - .txt(.csv)파일 불러오기 https://limitsinx.tistory.com/34

[코드로 이해하는 딥러닝 8] - Logistic Regression(sigmoid) https://limitsinx.tistory.com/35

[코드로 이해하는 딥러닝 9] - Softmax Regression(multiple classification) https://limitsinx.tistory.com/36

[코드로 이해하는 딥러닝 10] - MNIST 데이터 분류/One hot encoding https://limitsinx.tistory.com/37

[코드로 이해하는 딥러닝 11] - Deep Neural Network/XOR https://limitsinx.tistory.com/38

[코드로 이해하는 딥러닝 11-EX] - MNIST를 DNN으로 학습해보기/Adam optimizer https://limitsinx.tistory.com/39

[코드로 이해하는 딥러닝 12] - RELU(Rectified Linear Unit) https://limitsinx.tistory.com/40

[코드로 이해하는 딥러닝 13] - .txt(.csv)파일로 저장하기 https://limitsinx.tistory.com/44

[코드로 이해하는 딥러닝 14] - Drop out https://limitsinx.tistory.com/45

[코드로 이해하는 딥러닝 15] - 초기화(Initialization)의 중요성 https://limitsinx.tistory.com/46

 

※이 전글에서 정리한 코드/문법은 재설명하지 않으므로, 참고부탁드립니다

※해당 글은 PC에서 보기에 최적화 되어있습니다.

 

CNN

이번에는 드디어, 그 유명한 CNN에 관해 정리를 해보도록 하겠습니다.

CNN은 제가 딥러닝을 적용하는 방식은 아닙니다. 왜냐하면, 저는 실수형 데이터를 주로 다룰 뿐 아니라

 

제가 하는것은 Classification이 아닌, Following이기 때문이죠!

즉, 일반적으로 "딥러닝!" 이라고 하면 떠오르는 장르(classification)를 하고 있는것은 아니기에..

 

이런 연유로 저는 주로 RNN쪽을 많이 사용합니다.:)

하지만, CNN에 대해서 이해를 하고 넘어가는것과, 안쓴다고 그냥 넘어가는것은 큰 차이가있죠!

 

정리를 시작해보겠습니다.

 

CNN은 근본적으로는, 이미지를 학습시키는데 사용되는 방법이기에 전자공학과의 "영상처리"에서 배우는 내용들이 상당수 들어 있습니다.

 

일단 Convolution이 무엇인지부터 알아야하는데요,

사실 학부생에게는 컨벌루션이 뭔지만 2주정도 강의를 하는 내용입니다.

 

코드를 돌리는데는 아무지장 없으니, 문과출신이시거나 전자과,공학수학 수강자분이 아니시면 그냥 넘어가셔도 됩니다.

 

★ 기본적으로 이미지는 RGB3차원 값 혹은, 조/명/채도 3차원값으로, 숫자로 변환 하여 학습을 진행합니다.

 

"컨벌루션??"

컨벌루션, 출처 : 위키백과
컨벌루션 수식, 출처 : https://pinkwink.kr/156

컨벌루션이란, 이미지 2개를 합친다고 하면 하나를 Y축(세로)기준으로 대칭을 시킨후 차례차례 곱해나가는 것입니다.

즉, 첫번째 이미지에서  파란색과 빨간색 함수를 이미지라고 가정해보면

 

빨간색 함수를 Y축 기준 대칭시키고, 파란색 이미지를 향해 오른쪽으로 1씩 움직이면서 차츰차츰 곱한 결과값들을 누적하는것입니다.

 

이것을 수학적으로 정의하면, 두번째 이미지와 같은 수식이 얻어지게 됩니다.

이런 원리를 토대로 Neural Network의 이미지 학습에도 적용한것이 바로 "CNN"인데요

 

요것을 이미지로 옮기면 이렇게 해석 됩니다.

 

출처 : https://github.com/hunkim/DeepLearningZeroToAll/blob/master/lab-11-0-cnn_basics.ipynb

 

맨왼쪽 그림이 이미지라고 가정하고, [1 1, 1 1]이라는 필터가 있다고 생각해보면,

필터와 각 위치에 해당하는 값들을 모두 곱하고 더해서 맨오른쪽 이미지처럼 만들어 주게 됩니다.

 

이렇게 되면 기존의 3X3 이미지가 2X2로 줄어들게 되겠죠?

 

즉, 정보의 손실이 일어남과 동시에 특징들을 뭉뚱그려서 더 작은값으로 저장하게 되는것입니다.

이 "정보의 손실" 이라는것을 최소화 하고자 "Zero Padding"이라는 기술이 존재하는데요

 

Zero padding이란, 영상처리에서 아주 흔히 쓰이는 기술로

 

 

출처 : https://github.com/hunkim/DeepLearningZeroToAll/blob/master/lab-11-0-cnn_basics.ipynb

 

이렇게, 학습하는 이미지의 끝자락 부분을 0으로 채워(padding)줘서, 필터를 통과시킨 후에도 3X3의 사이즈를

유지하도록 하는것입니다.

 

그다음, 필터를 거쳐 나온 값들 중에 숫자가 가장 큰것이 있겠죠?

 

숫자가 가장 크다는것은, 특징이 가장 뚜렷하게 나타나는 것이라고 할 수 있습니다

 

필터를 거쳐서 나온 것들 중, 가장 큰 값 1개만 남기고 다 없애버리는 것이 바로 'Max pooling' 이라는 기술입니다.

 

출처 : Andrej karpathy CNN 강의

이렇게 Max pooling을하게되면, 정보의 손실이 그야말로 엄청납니다!

 

위의 이미지만 봐도 16개의 포인트를 4개의 포인트만 남기고 12개를 죽여버리는것을 확인하실 수 있죠

하지만, 가장 강한 특징들만 남겼기에 이정도는 감수를 해야 앞으로 진행할 이미지 연산이 가능하게 됩니다.

 

위에서 정리한, Convolution을 통한 Filtering과 Max pooling을 몇차레 반복하다보면

 

진짜 진짜 진짜 중요한 특징 몇가지만 딱 남게되고, 나머지는 모두 사라지게 되겠죠!

 

이렇게 정제된 특징들을 기반으로 최종적으로, 이 이미지가 무엇이냐?를 분류하는것이 바로 CNN입니다.

 

이것을 크게 보면 다음과 같습니다.

 

출처 : https://www.researchgate.net/figure/The-overall-architecture-of-the-Convolutional-Neural-Network-CNN-includes-an-input_fig4_331540139
출처 : https://towardsdatascience.com/a-comprehensive-guide-to-convolutional-neural-networks-the-eli5-way-3bd2b1164a53

 

이미지 자체의 특징 하나하나를 보기에는 1024 * 860 *3 (이미지 사이즈 * RGB 3차원) = 2,580,480개의 데이터를 가집니다.

즉, 이미지 한개에 250만개의 특징을 가지는데, 이런 사진 수만장~수백만장까지 학습을 진행하기란 사실상 불가능하죠

 

설령 가능하다고해도 1개 사진에서 250만개의 특징을 모두 살리면서, 제대로 인식을 할지조차 미지수입니다.

 

따라서, CNN을 딱 한줄로 요약하자면

 

 

"Convolution을 통한 Filtering과 Max pooling을 반복하여, 정말 중요한 특징들을 정제한 후, Classification을 하는것"

 

 

이라고 할 수 있습니다.

 

개념설명은 잘 안하려고하는데요, CNN이 무엇인지는 알아야 코드를 정리하는게 의미가 있을것 같아 간략히 설명드렸고, 이제는 코드를 정리해보겠습니다.

 

 

[코드 전문]

import tensorflow.compat.v1 as tf

tf.disable_v2_behavior()

import random

# import matplotlib.pyplot as plt

 

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)

 

# hyper parameters

learning_rate = 0.001

training_epochs = 15

batch_size = 100

 

# input place holders

X = tf.placeholder(tf.float32, [None784])

X_img = tf.reshape(X, [-128281])   # img 28x28x1 (black/white)

Y = tf.placeholder(tf.float32, [None10])

 

# L1 ImgIn shape=(?, 28, 28, 1)

W1 = tf.Variable(tf.random_normal([33132], stddev=0.01))

#    Conv     -> (?, 28, 28, 32)

#    Pool     -> (?, 14, 14, 32)

L1 = tf.nn.conv2d(X_img, W1, strides=[1111], padding='SAME')

L1 = tf.nn.relu(L1)

L1 = tf.nn.max_pool(L1, ksize=[1221],

                    strides=[1221], 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([333264], stddev=0.01))

#    Conv      ->(?, 14, 14, 64)

#    Pool      ->(?, 7, 7, 64)

L2 = tf.nn.conv2d(L1, W2, strides=[1111], padding='SAME')

L2 = tf.nn.relu(L2)

L2 = tf.nn.max_pool(L2, ksize=[1221],

                    strides=[1221], padding='SAME')

L2_flat = tf.reshape(L2, [-17 * 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 * 6410],

                     initializer=tf.truncated_normal_initializer(stddev=0.1))

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]}))

 

 

[코드 분석-1]

X_img=tf.reshape(X,[-1,28,28,1]) 이라는 코드를 통해

 

28 by 28 pixel의 이미지 형태로 X를 저장해둡니다.

 

원래는 [784(28*28),1]이라는 형태로 이미지가 벡터형식으로 저장되어 있습니다.

 

그다음, L1 Layer를 통과시킬때

 

tf.nn.conv2d(X_img,w1,strides=[1,1,1,1], padding='SAME')

 

이라는 코드를 작성해주는데요

 

conv2d는 위에서 정리한 convolution filter를 한다는 뜻이고, 이 Filter는 W1인 [3,3,1,32]에 해당합니다.

 

Strides=[1,1,1,1]은 아래위로 1칸씩 움직이며 필터링을 하겠다는 뜻입니다. (두칸씩 움직이면서 필터링을 할수도 있죠)

 

padding = 'same'은 3X3이미지를 넣으면, 결과도 3X3이 나오도록 Zero padding을 해주겠다는 의미입니다.

 

그다음 ReLU activation function을 통과시키고,

 

Max pooling을 해주면서 한개 레이어를 마무리하는데요

 

tf.nn.max_pool(L1, ksize=[1,2,2,1], strides=[1,2,2,1], padding='same')이라는 코드는

 

Max pooling을 할때는 strides=[1,2,2,1] 한번에 2칸씩 움직이며, 커널(필터) 사이즈는 2X2가 되도록 하겠다는 뜻입니다.

 

그러면 이 첫번째 레이어를 통과했을때, 결과는 14X14 이미지로 나오게 되며(Max pooling때문에)

 

기존 28X28보다 절반이 줄어들게 된거죠!

 

[코드 분석-2]

 

그다음 Layer가 hidden layer로써의 마지막인데요

 

hidden layer의 끝은 L2_flat=tf.reshape(L2,[-1,7*7*64])의 형태처럼 Flat하게 만들어주어야 합니다.

 

즉, 초기에 학습을 위해 [784,1]을 [28,28]이미지로 reshape해주었던것 처럼, 학습을 다하고 나면 다시 원래대로

 

[A,1]의 벡터형태로 남겨두어야 하는것입니다.

 

이후에는 이제까지 해온것(이전글)과 동일하게 학습을 진행해주시면 됩니다.

 

 

[결과값]

CNN을 쓰니 MNIST 추정 정확도가 98.82%까지 올라가게 되는것을 확인할 수 있습니다.

 

사실상, 이정도면 왠만한 이미지는 다 맞춘다고 생각하시면 되는데요,

 

CNN을 쓰면서 Drop out을 얼마나쓸지, 초기화는 어떻게할지 이런 디테일들을 업데이트하면서 진행해보면

 

99%에 충분히 도달할 수 있습니다.

 

CNN자체가 워낙 복잡한 수학적 원리?를 태생적으로 가지고 있는 부분이라..글들이 조금 길어졌는데요

 

수학적인 부분은 최대한 배재하고 한번 정리를 해보았습니다.

 

 

댓글