본문 바로가기

코딩/텐서플로우

비전공자의 코딩 독학 - 파이썬&텐서플로우(13) <CNN>

반응형

안녕하세요.

오늘의 파이썬 코딩 독학 주제는 CNN(Convolutional Neural Network)입니다.

 

 

1. CNN이란?

CNN은 합성곱(Convolution) 신경망을 의미합니다.

합성곱 신경망은 이미지의 특징을 추출하는 단계와 이미지를 분류하는 단계를 

반복하며 학습을 진행하게 됩니다.

 

 

2. 파라미터의 종류

(1) Convolution Filter의 개수

각 레이어에서의 연산시간과 양을 일정하게 유지하기 위해 개수를 증가시켜줍니다.

 

(2) Filter 사이즈

작은 필터를 여러개 중첩하면 원하는 특징을 더 돋보이게 하면서 연산량을 줄일 수 있습니다.

 

(3) Padding 여부

Convolution전에 입력데이터 주변을 특정 픽셀값으로 채워 늘려줍니다.

입력 이미지의 크기를 줄이지 않고 학습할 수 있습니다.

 

(4) Stride

필터의 이동간격을 정해주는 파라미터입니다. 커질수록 데이터 사이즈가 작아집니다.

 

 

3. 전체 소스코드

전체 소스코드는 지난시간에 다룬 MNIST 코드가 뼈대입니다.

import tensorflow as tf

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

keep_prob = tf.placeholder(tf.float32)

X = tf.placeholder(tf.float32, [None, 28, 28, 1])
Y = tf.placeholder(tf.float32, [None, 10])

W1 = tf.Variable(tf.random_normal([3, 3, 1, 32], stddev=0.01))
L1 = tf.nn.conv2d(X, 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')

W2 = tf.Variable(tf.random_normal([3, 3, 32, 64], stddev=0.01))
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')

W3 = tf.Variable(tf.random_normal([7*7*64, 256], stddev=0.01))
L3 = tf.reshape(L2, [-1, 7*7*64])
L3 = tf.matmul(L3, W3)
L3 = tf.nn.relu(L3)
L3 = tf.nn.dropout(L3, keep_prob)

W4 = tf.Variable(tf.random_normal([256, 10], stddev=0.01))
model = tf.matmul(L3, W4)

cost = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits_v2(logits=model, labels=Y))
optimizer = tf.train.AdamOptimizer(0.001).minimize(cost)

init = tf.global_variables_initializer()
sess = tf.Session()
sess.run(init)

batch_size = 100
total_batch = int(mnist.train.num_examples / batch_size)

for epoch in range(15):
    total_cost = 0

    for i in range(total_batch):
        batch_xs, batch_ys = mnist.train.next_batch(batch_size)
        batch_xs = batch_xs.reshape(-1, 28, 28, 1)

        _, cost_val = sess.run([optimizer, cost], feed_dict = {X:batch_xs, Y:batch_ys, keep_prob: 0.8})

        total_cost += cost_val

    print('Epoch:', '%04d' % (epoch + 1), 'Avg. cost=', '{:.3f}'.format(total_cost / total_batch))

print('최적화 완료')

is_correct = tf.equal(tf.argmax(model, 1), tf.argmax(Y, 1))

accuracy = tf.reduce_mean(tf.cast(is_correct, tf.float32))

print('정확도:', sess.run(accuracy, feed_dict={X: mnist.test.images.reshape(-1, 28, 28, 1), Y: mnist.test.labels, keep_prob: 1}))

 

 

4. 소스코드 설명

우선 입력값에 대한 코드를 작성하겠습니다.

이전에는 입력값을 28*28의 단일 차원형태로 구성하여

X = tf.placeholder(tf.float32, [None, 784])와 같이 코드를 작성했지만

 

이번에는 2차원 평면으로 모델을 구성해야하므로 다음과 같이 코드를 작성하겠습니다.

참고로 마지막에 오는 숫자1은 특징의 개수를 의미하는데

MNIST 데이터의 색상이 모두 회색으로 동일하므로 1을 넣어주었습니다.

X = tf.placeholder(tf.float32, [None, 28, 28, 1])

 

이번에는 CNN계층을 구성해 보겠습니다.

 

총 4개의 CNN계층을 만들 예정이며 첫번째 CNN계층부터 만들어보겠습니다.

우선 3*3의 커널을 가진 Convolution계층을 만들어줍니다.

W1 = tf.Variable(tf.random_normal([3, 3, 1, 32], stddev=0.01))
L1 = tf.nn.conv2d(X, W1, strides=[1, 1, 1, 1], padding='SAME')
L1 = tf.nn.relu(L1)

여기서 tf.nn.conv2d()함수를 사용하여 간단하게 Convolution계층을 만들어주었고

Strides에는 1, 1, 1, 1을 입력해주어 커널이 오른쪽과 아래쪽으로 한칸씩만 움직이도록 하였습니다.

padding='SAME'은 커널이 이동할때 이미지의 외곽에서 한칸씩 움직이도록 하는 옵션입니다.

 

이번에는 Pooling계층을 만들겠습니다.

L1 = tf.nn.max_pool(L1, ksize=[1, 2, 2, 1], strides=[1, 2, 2, 1], padding='SAME')

tf.nn.max_pool()함수를 이용하여 간단하게 계층 구성이 가능하며

바로 위에 있는 Convolution계층을 입력층으로 활용하고,

2*2사이즈의 커널을 사용하도록 만들었습니다.

Strides에는 1, 2, 2, 1을 입력해주어 커널이 두칸씩 움직이도록 하였습니다.

 

이제 두번째 CNN계층을 만들어보겠습니다.

W2 = tf.Variable(tf.random_normal([3, 3, 32, 64], stddev=0.01))
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')

첫번째와 비슷하게 3*3크기의 커널 64개로 구성된 Convolution계층을 만들었으며

2*2의 Pooling계층을 만들었습니다.

W2변수는 3, 3, 32, 64로 구성되어있는데, 

여기서 32는 첫번째  Convolution계층의 커널 개수이자 출력층의 개수입니다.

 

이제 추출된 특징을 활용해 분류를 만들어주는 계층을 구성해보겠습니다.

W3 = tf.Variable(tf.random_normal([7*7*64, 256], stddev=0.01))
L3 = tf.reshape(L2, [-1, 7*7*64])
L3 = tf.matmul(L3, W3)
L3 = tf.nn.relu(L3)
L3 = tf.nn.dropout(L3, keep_prob)

총 10개의 1차원 배열로 이루어진 분류를 만들 예정이므로 차원을 줄여주어야하는데 

풀링계층의 크기가 7*7*64이므로 다음과 같이

tf.reshape()함수를 사용해 동일한 크기의 1차원 계층으로 만들고,

배열 전체를 최종 출력값의 중간단계인 256개의 뉴런으로 연결하는 신경망을 만들어줍니다.

이와 같은 인접한 계층의 모든 뉴런과 상호작용하는 계층을 완전연결계층이라고 부릅니다.

 

이제 마지막 계층을 만들어보겠습니다.

W4 = tf.Variable(tf.random_normal([256, 10], stddev=0.01))
model = tf.matmul(L3, W4)

은닉층인 L3의 출력값 256개를 0부터 9까지의 값을 갖는 최종 출력값으로 변환해줍니다.

 

이제 변경시킨 모델 코드들에 맞춰 학습코드를 작성해보겠습니다.

for epoch in range(15):
    total_cost = 0

    for i in range(total_batch):
        batch_xs, batch_ys = mnist.train.next_batch(batch_size)
        batch_xs = batch_xs.reshape(-1, 28, 28, 1)
        
        _, cost_val = sess.run([optimizer, cost], feed_dict = {X:batch_xs, Y:batch_ys, keep_prob: 0.8})
        
        total_cost += cost_val
    print('Epoch:', '%04d' % (epoch + 1), 'Avg. cost=', '{:.3f}'.format(total_cost / total_batch))

print('최적화 완료')

모델에 입력값을 전달하기 위해 reshape()함수를 사용하여 데이터를 28*28로 구성해주었습니다.

 

마지막으로 결과를 확인하는 코드를 작성해보겠습니다.

is_correct = tf.equal(tf.argmax(model, 1), tf.argmax(Y, 1))
accuracy = tf.reduce_mean(tf.cast(is_correct, tf.float32))

print('정확도:', sess.run(accuracy, feed_dict={X: mnist.test.images.reshape(-1, 28, 28, 1), Y: mnist.test.labels, keep_prob: 1}))

역시 reshape()함수를 사용하여 데이터를 28*28로 재구성해주면 됩니다.

 

 

5. 결과 확인

정확도가 99%에 육박하는군요.

수고하셨습니다.

반응형