Deep Learning [Day 3]

 Published On

6. Convolution Neural Networks(CNN)

6.1. CNN Theory

CNN, Convolutional Neural Network 요약

  1. CNN이란?
    • ANN은 Fully Connected Layer만으로 구성되어 있기 때문에, 입력 데이터는 1차원(배열) 형태로 입력되어야 한다(지난 ANN Implementation에서 reshape을 하는 이유 참조).
    • 이미지나 영상 데이터는 흑백이 아닌 컬러 사진의 경우 3채널이기 때문에 3차원 데이터로 구성된다. 이를 ANN을 사용하려면 1차원으로 평면화시키는 과정에서 당연히 공간 정보가 손실될 수밖에 없다.
    • 이러한 문제를 해결하는(이미지 공간 정보를 유지) 모델이 바로 CNN(Convolution Neural Network)이다.
    • 특징
      • 객체, 얼굴, 장면을 인식하기 위한 패턴을 찾는 데 유용하다.
      • 2차원 이상의 데이터의 정보를 그대로 유지
      • 복수의 필터를 사용하여 정보의 feature를 직접 학습하기 때문에 수동으로 feature를 추출할 필요가 없음.
      • (convolution + pooling) 형태로 구성
      • 필터를 공유 파라미터로 사용하기 때문에, 학습 파라미터가 적다.
      • CNN은 결과값이 원하는 방향으로 설정(특징 a만 모으기, 특징 b만 모으기, …)할 수 있기 때문에 성능이 정말 좋다.


    • 이미지의 feature를 추출하는 부분과 class를 분류하는 부분으로 나뉜다.
      • feature 추출 영역: convolution layer, pooling layer
      • class 분류 영역: fully connected layer
    • 이미지의 feature를 추출하기 위해 입력 데이터를 필터(filter)가 순회하며 합성곱(convolution)을 계산하고, 그 계산 결과를 이용해 feature map을 만든다.
      • convolution layer는 filter, stride, padding, pooling에 따라 출력 데이터의 shape이 달라진다.
  2. CNN 주요 용어
    1) 합성곱(Convolution)
    - 합성곱 처리는 아래의 사진과 같으며, 그 결과로 Feature Map을 만든다.
    - convolution layer에는 항상 Relu 함수가 함께 계산된다.


    2) 채널(Channel)
    - 이미지의 픽셀의 값은 모두 실수이다. 각 픽셀은 RGB 3개의 실수로 표현한 3차원 데이터이다.
    - 컬러 이미지는 3개의 채널로 구성도지만, 흑백 이미지는 1개의 채널로 구성된다.
    - 합성곱 레이어에 유입되는 입력 데이터에는 한 개 이상의 필터가 적용된다. 만약, n개의 필터가 적용된다면 출력 데이터는 n개의 채널을 갖는다.


    3) 필터(Filter) & 스트라이드(Stride)
    - 필터는 이미지의 특징을 찾아내기 위한 공용 파라미터이다.
    - CNN에서는 filter를 kernel과 같은 의미이다(OpenCV에서는 mask)
    - 필터는 일반적으로 정사각 행렬로 정의되며, 입력 데이터를 지정된 간격으로 순회하며 채널별로 합성곱을 하고 모든 채널의 합성곱의 합을 feature map으로 만든다.
    - 지정된 간격으로 필터를 순회하는 간격을 Stride라고 한다. Stride를 사용하면 정보는 조금 잃더라도 빠르게 학습이 가능하기 때문에, 대용량 데이터의 경우 자주 사용된다.
    - 입력 데이터가 여러 채널을 갖을 경우(컬러 이미지, 영상) 필터는 각각의 채널을 순회하며 합성곱을 계산하고, 채널별 feature map을 만든다.
    - 그리고 각 채널의 feature map을 합산하여 최종 feature map을 반환한다.
    - 계산된 결과로 만들어진 feature mapactivation map이라고도 하며, convolution layer의 최종 출력 결과물이다(다음 단계는 pooling).




    4) 패딩(Padding)
    - OpenCV에서 mask 필터를 적용하면 이전의 이미지보다 압축된다는 것을 알 수 있다.
    - 마찬가지로 convolution layer에서 filter와 stride를 적용하면서 feature map의 크기는 입력 데이터보다 작다.
    - 이렇게 줄어드는 것을 방지하기 위해 사용하는 방법이 패딩(padding)이다.
    - 패딩은 입력 데이터의 외각에 지정된 픽셀만큼 특정값으로 채워 넣는 것을 의미하며, 보통 0으로 채운다.
    - 예를 들어 6x6 이미지의 1만큼의 패딩을 준다는 것은 6x6 이미지의 외곽을 1 픽셀만큼 더 크게 만드는 것으로, 이미지의 사이즈는 8x8이 된다.
    이를 3x3 필터로 합성곱을 해주어도 그대로 6x6 이미지의 크기를 유지할 수 있다.


    5) 풀링(Pooling)
    - 풀링은 CNN의 구성 중 2단계에 해당하는 layer로, convolution layer의 출력 데이터(feature map, activation map)를 입력으로 받아서 크기를 줄이거나 특정 데이터를 강조하는 용도로 사용한다.
    - 풀링의 방법은 Max Pooling, Average Pooling, Min Pooling이 있다.
    - 정사각 행렬의 특정 영역 안에 값의 최댓값/평균값/최솟값을 구하는 방식으로 동작한다.
    - pooling layer는 convolution layer에 비해 학습 파라미터가 없으며, 행렬의 크기가 감소한다는 특징이 있다.


  3. CNN 구성


6.2. CNN Implementation

MNIST 데이터 분류하기

Tensorflow

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

import tensorflow as tf
import time

training_epochs = 15
batch_size = 100

X = tf.placeholder(tf.float32, [None, 784]) # MNIST의 이미지 사이즈는 28x28 = 784
Y = tf.placeholder(tf.float32, [None, 10]) # 라벨링은 0~9까지의 10개
X_img = tf.reshape(X, [-1, 28, 28, 1]) # 전체 사이즈(-1)를 받을 수 있도록, 28x28사이즈로, 1채널(흑백 이미지이므로, 컬러 이미지는 3채널로 변경)

# Convolution Layer 1
W1 = tf.Variable(tf.random_normal([3, 3, 1, 32], stddev=0.01)) # 하나의 이미지(1)를 3x3 사이즈의 32개 필터로 설정
CL1 = tf.nn.conv2d(X_img, W1, strides=[1, 1, 1, 1], padding='SAME') # stride에서 맨 앞과 맨 뒤는 의미가 없고, 1x1 간격으로 계산한다, padding에서 SAME은 패딩을 해준다 / VALID는 패딩을 안해준다의 뜻이다.
CL1 = tf.nn.relu(CL1) # convolution layer의 마지막에는 항상 relu 함수를 사용

# Pooling Layer 1
PL1 = tf.nn.max_pool(CL1, ksize=[1, 2, 2, 1], strides=[1, 2, 2, 1], padding='SAME') # 마찬가지로 ksize 역시 맨 앞과 맨 뒤는 의미가 없다. 2x2의 max pooling을 사용하여 반으로 나눈다는 뜻이다. ksize만큼 strides도 써준다.

# Convolution Layer 2
W2 = tf.Variable(tf.random_normal([3, 3, 32, 64], stddev=0.01)) # 32개의 이미지(32)를 3x3 사이즈의 64개 필터로 설정(0.01 표준편차는 0~9까지라는 것을 알기 때문)
CL2 = tf.nn.conv2d(PL1, W2, strides=[1, 1, 1, 1], padding='SAME')
CL2 = tf.nn.relu(CL2)

# Pooling Layer 2
PL2 = tf.nn.max_pool(CL2, ksize=[1, 2, 2, 1], strides=[1, 2, 2, 1], padding='SAME')

# Fully Connected Layer
L_flat = tf.reshape(PL2, [-1, 7*7*64]) # 처음 이미지의 사이즈는 28x28이었지만, Conv 1을 통과하면서 pooling 1에 의해서 반으로 줄어들고(14x14), 다시 Conv 2를 통과하면서 pooling 2에 의해서 반으로 줄어듦(7x7)
W3 = tf.Variable(tf.random_normal([7*7*64, 10], stddev=0.01))
b3 = tf.Variable(tf.random_normal([10]))

# # 더 간결하게
# # Convolution Layer1
# CL1 = tf.layers.conv2d(inputs=X_img, filters=32, kernel_size=[3, 3], padding='SAME', strides=1, activation=tf.nn.relu)
# # Pooling Layer1
# PL1 = tf.layers.max_pooling2d(inputs=CL1, pool_size=[2, 2], padding='SAME', strides=2)
# Convolution Layer2
# CL2 = tf.layers.conv2d(inputs=PL1, filters=64, kernel_size=[3, 3], padding='SAME', strides=1, activation=tf.nn.relu)
# # Pooling Layer2
# PL2 = tf.layers.max_pooling2d(inputs=CL2, pool_size=[2, 2], padding='SAME', strides=2)
# # Fully Connected (FC) Layer
# L_flat = tf.reshape(PL2, [-1, 7*7*64])
# W3 = tf.Variable(tf.random_normal([7*7*64, 10], stddev=0.01))
# b3 = tf.Variable(tf.random_normal([10]))

# Model, Cost, Train
model_LC = tf.add(tf.matmul(L_flat, W3), b3)
model = tf.nn.softmax(model_LC)
cost = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits_v2(logits=model_LC, labels=Y))
train = tf.train.AdamOptimizer(0.01).minimize(cost)

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

# Session
with tf.Session() as sess:
  sess.run(tf.global_variables_initializer())

  # Training
  t1 = time.time()
  for epoch in range(training_epochs):
    total_batch = int(mnist.train.num_examples / batch_size)
    for i in range(total_batch):
      train_images, train_labels = mnist.train.next_batch(batch_size)
      c, _ = sess.run([cost, train], feed_dict={X: train_images, Y: train_labels})

      if i % 10 == 0:
        print('epoch: ', epoch, ', batch number: ', i)

  t2 = time.time()

  # Testing
  print('Training Time (Seconds): ', t2 - t1)
  print('Accuracy: ', sess.run(accuracy, feed_dict={X: mnist.test.images, Y: mnist.test.labels}))


Keras

from keras.utils import np_utils
from keras.datasets import mnist
from keras.models import Sequential
from keras.layers import Conv2D, pooling, Flatten, Dense

# MNIST data
(train_images, train_labels), (test_images, test_labels) = mnist.load_data()
print(train_images.shape, train_labels.shape, test_images.shape, test_labels.shape)

train_images = train_images.reshape(train_images.shape[0], 28, 28, 1).astype('float32')/255.0
test_images = test_images.reshape(test_images.shape[0], 28, 28, 1).astype('float32')/255.0
train_labels = np_utils.to_categorical(train_labels)
test_labels = np_utils.to_categorical(test_labels)

# Model
model = Sequential()
model.add(Conv2D(32, (3, 3), padding='same', strides=(1, 1), activation='relu', input_shape=(28, 28, 1)))
print(model.output_shape)

model.add(pooling.MaxPooling2D(pool_size=(2, 2)))
print(model.output_shape)

model.add(Conv2D(64, (3, 3), padding='same', strides=(1, 1), activation='relu'))
print(model.output_shape)

model.add(pooling.MaxPooling2D(pool_size=(2, 2)))
print(model.output_shape)

model.add(Flatten())
model.add(Dense(10, activation='softmax'))
model.compile(loss='categorical_crossentropy', optimizer='sgd', metrics=['accuracy'])

# Training
model.fit(train_images, train_labels, epochs=5, batch_size=100, verbose=1)
# Testing
_, accuracy = model.evaluate(test_images, test_labels)
print('Accuracy ', accuracy)
model.summary()




CIFAR-10 데이터 분류하기


Tensorflow

from keras.utils import np_utils
from keras.datasets import cifar10
import tensorflow as tf
import time

(train_images, train_labels), (test_images, test_labels) = cifar10.load_data()
train_labels = np_utils.to_categorical(train_labels)
test_labels = np_utils.to_categorical(test_labels)

training_epochs = 15
batch_size = 100

X = tf.placeholder(tf.float32, [None, 32, 32, 3])
Y = tf.placeholder(tf.float32, [None, 10])
X_img = tf.reshape(X, [-1, 32, 32, 3])

# Convolution Layer 1
W1 = tf.Variable(tf.random_normal([3, 3, 3, 32], stddev=0.01))
CL1 = tf.nn.conv2d(X_img, W1, strides=[1, 1, 1, 1], padding='SAME')
CL1 = tf.nn.relu(CL1)

# Pooling Layer 1
PL1 = tf.nn.max_pool(CL1, ksize=[1, 2, 2, 1], strides=[1, 2, 2, 1], padding='SAME')

# Convolution Layer 2
W2 = tf.Variable(tf.random_normal([3, 3, 32, 64], stddev=0.01))
CL2 = tf.nn.conv2d(PL1, W2, strides=[1, 1, 1, 1], padding='SAME')
CL2 = tf.nn.relu(CL2)

# Pooling Layer 2
PL2 = tf.nn.max_pool(CL2, ksize=[1, 2, 2, 1], strides=[1, 2, 2, 1], padding='SAME')

# Fully Connected Layer
L_flat = tf.reshape(PL2, [-1, 8*8*64])
W3 = tf.Variable(tf.random_normal([8*8*64, 10], stddev=0.01))
b3 = tf.Variable(tf.random_normal([10]))

# Model, Cost, Train
model_LC = tf.matmul(L_flat, W3) + b3
model = tf.nn.softmax(model_LC)
cost = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits_v2(logits=model_LC, labels=Y))
train = tf.train.AdamOptimizer(0.01).minimize(cost)

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

# Session
with tf.Session() as sess:
  sess.run(tf.global_variables_initializer())

  # Training
  t1 = time.time()
  for epoch in range(training_epochs):
    total_batch = int(train_images.shape[0] / batch_size)
    for i in range(total_batch):
      batch_train_images = train_images[i*batch_size : (i+1)*batch_size]
      batch_train_labels = train_labels[i*batch_size : (i+1)*batch_size]
      c, _ = sess.run([cost, train], feed_dict={X: batch_train_images, Y: batch_train_labels})

      if i % 10 == 0:
        print('epoch: ', epoch, ', batch number: ', i)

  t2 = time.time()
  
  # Testing
  print('Training Time (Seconds): ', t2 - t1)

  total_batch = int(test_images.shape[0] / batch_size)
  for i in range(total_batch):
    batch_test_images = test_images[i*batch_size : (i+1)*batch_size]
    batch_test_labels = test_labels[i*batch_size : (i+1)*batch_size]

  print('Accuracy: ', sess.run(accurcay, feed_dict={X: batch_test_images, Y: batch_test_labels}))

CIFAR-10 Dataset은 현재 노트북으로 실행 불가능

Keras

# 138p에 6번 7번 사이에 train_images test_images 255로 나눈 것 추가
from keras.utils import np_utils
from keras.datasets import cifar10
from keras.models import Sequential
from keras.layers import Conv2D, pooling, Flatten, Dense

(train_images, train_labels), (test_images, test_labels) = cifar10.load_data()
print(train_images.shape, train_labels.shape, test_images.shape, test_labels.shape)

# mnist 데이터와는 다르게 cifar10 데이터는 이미 shape에 3채널이 있기 때문에 reshpae을 할 필요는 없다
# 0과 1로 변환하여야 하기 때문에 255로 나눈다
train_images = train_images.reshape(train_images.shape[0], 32, 32, 3).astype('float32') / 255.0
test_images = test_images.reshape(test_images.shape[0], 32, 32, 3).astype('float32') / 255.0

train_labels = np_utils.to_categorical(train_labels)
test_labels = np_utils.to_categorical(test_labels)

model = Sequential()
model.add(conv2D(32, (3, 3), padding='same', strides=(1, 1), activation='relu', input_shape=(32, 32, 3)))
model.add(pooling.MaxPooling2D(pool_size=(2, 2)))
model.add(conv2D(64, (3, 3), padding='same', strides=(1, 1), activation='relu'))
model.add(pooling.MaxPooling2D(pool_size=(2, 2)))
model.add(Flatten())
model.add(Dense(10, activation='softmax'))
model.compile(loss='categorical_crossentropy', optimizer='sgd', metrics=['accuracy'])

model.fit(train_images, train_labels, epochs=5, batch_size=100, verbose=1)
_, accuracy = model.evaluate(test_images, test_labels)
print('Accuracy: ', accuracy)
model.summary()

Tags: DeepLearning

Comments:

comments powered by Disqus

© 2021 - MH.Ji. All rights reserved
Built using Jekyll