합성곱 신경망(Convolutional Neural Network, CNN)

튜토리얼

합성곱 : 곱한자료를 더하여 한 개의 값으로 만듦

image.png

import tensorflow as tf

from tensorflow.keras import datasets, layers, models
import matplotlib.pyplot as plt
import numpy as np
# 데이터셋 다운로드
(train_images, train_labels), (test_images, test_labels) = datasets.cifar10.load_data()

# Normalize pixel values to be between 0 and 1
train_images, test_images = train_images / 255.0, test_images / 255.0
Downloading data from https://www.cs.toronto.edu/~kriz/cifar-10-python.tar.gz
170500096/170498071 [==============================] - 587s 3us/step
170508288/170498071 [==============================] - 587s 3us/step
train_labels
array([[6],
       [9],
       [9],
       ...,
       [9],
       [1],
       [1]], dtype=uint8)
np.shape(train_images), np.shape(train_labels), np.shape(test_images), np.shape(test_labels)
((50000, 32, 32, 3), (50000, 1), (10000, 32, 32, 3), (10000, 1))

훈련과 테스트 데이터의 수 및 32x32 사이즈와 컬러이미지가 동일함을 반드시 확인해야함.

class_names = ['airplane', 'automobile', 'bird', 'cat', 'deer',
               'dog', 'frog', 'horse', 'ship', 'truck']

plt.figure(figsize=(10,10))
for i in range(25):
    plt.subplot(5,5,i+1)
    plt.xticks([])
    plt.yticks([])
    plt.grid(False)
    plt.imshow(train_images[i])
    # The CIFAR labels happen to be arrays, 
    # which is why you need the extra index
    plt.xlabel(class_names[train_labels[i][0]])
plt.show()

# conv2d는 반드시 input_shape의 개수가 3이어야 함. | r,g,b 별로 다른 필터를 생성하기 때문
# 흑백이미지 일때는 32,32,1 이라고 적어야 함.
# 필터 처리하는 부분
model = models.Sequential()
model.add(layers.Conv2D(32, (3, 3), activation='relu', input_shape=(32, 32, 3))) # 필터가 3*3사이즈이며, rgb필터 3개가 있음 | ((3*3*3)+1) * 32 = 896의 파라미터
model.add(layers.MaxPooling2D((2, 2)))
model.add(layers.Conv2D(64, (3, 3), activation='relu'))
model.add(layers.MaxPooling2D((2, 2)))
model.add(layers.Conv2D(64, (3, 3), activation='relu'))

Maxpooling2D

  • pool_size = (2,2) : 2*2 커널(필터) 크기

  • strides = None : 2칸 건너가기(겹치게 하지 않음) strides가 1이면 1칸씩 겹침

  • padding= ‘valid’ : 감싸지 않음

즉, 사이즈를 줄이는 합성곱이다. 하드웨어 자원이 좋은 최근은 굳이 사이즈를 줄이면서 갈 필요가 없어서 잘 사용하지 않는다.

image.png

model.summary()
Model: "sequential"
_________________________________________________________________
 Layer (type)                Output Shape              Param #   
=================================================================
 conv2d (Conv2D)             (None, 30, 30, 32)        896       
                                                                 
 max_pooling2d (MaxPooling2D  (None, 15, 15, 32)       0         
 )                                                               
                                                                 
 conv2d_1 (Conv2D)           (None, 13, 13, 64)        18496     
                                                                 
 max_pooling2d_1 (MaxPooling  (None, 6, 6, 64)         0         
 2D)                                                             
                                                                 
 conv2d_2 (Conv2D)           (None, 4, 4, 64)          36928     
                                                                 
=================================================================
Total params: 56,320
Trainable params: 56,320
Non-trainable params: 0
_________________________________________________________________

conv2D 레이어의 파라미터 수 계산

첫 번째 Conv2D 레이어:

  • 입력 채널 수: 3 (input_shape=(32, 32, 3)에서 마지막 차원) x변수가 r,g,b라는 의미
  • 커널 수: 32

  • 커널 크기: (3, 3)

  • 활성화 함수: relu

  • 파라미터 개수 계산:

    • 커널당 파라미터 개수: (입력 채널 수 * 커널 크기)+1 = (3 * 3 * 3 )+1 = 28

    • 전체 파라미터 개수: 커널당 파라미터 개수 * 커널 수 = 28 * 32 = 896

두 번째 Conv2D 레이어:

  • 입력 채널 수: 이전 레이어의 커널 수인 32

  • 커널 수: 64

  • 커널 크기: (3, 3)

  • 활성화 함수: relu

  • 파라미터 개수 계산:

    • 커널당 파라미터 개수: (입력 채널 수 * 커널 크기)+1 = (32 * 3*3) + 1 = 289

    • 전체 파라미터 개수: 커널당 파라미터 개수 * 커널 수 = 289 * 64 = 18496

세 번째 Conv2D 레이어:

  • 입력 채널 수: 이전 레이어의 커널 수인 64

  • 커널 수: 64

  • 커널 크기: (3, 3)

  • 활성화 함수: relu

  • 파라미터 개수 계산:

    • 커널당 파라미터 개수: (입력 채널 수 * 커널 크기)+1 = (64 * 3*3) + 1 = 577

    • 전체 파라미터 개수: 커널당 파라미터 개수 * 커널 수 = 577 * 64 = 36928

# 1차원 신경망에서 공부했던 dense 부분
model.add(layers.Flatten())
model.add(layers.Dense(64, activation='relu'))
model.add(layers.Dense(10, activation='softmax'))
model.summary()
Model: "sequential"
_________________________________________________________________
 Layer (type)                Output Shape              Param #   
=================================================================
 conv2d (Conv2D)             (None, 30, 30, 32)        896       
                                                                 
 max_pooling2d (MaxPooling2D  (None, 15, 15, 32)       0         
 )                                                               
                                                                 
 conv2d_1 (Conv2D)           (None, 13, 13, 64)        18496     
                                                                 
 max_pooling2d_1 (MaxPooling  (None, 6, 6, 64)         0         
 2D)                                                             
                                                                 
 conv2d_2 (Conv2D)           (None, 4, 4, 64)          36928     
                                                                 
 flatten (Flatten)           (None, 1024)              0         
                                                                 
 dense (Dense)               (None, 64)                65600     
                                                                 
 dense_1 (Dense)             (None, 10)                650       
                                                                 
=================================================================
Total params: 122,570
Trainable params: 122,570
Non-trainable params: 0
_________________________________________________________________

컨볼루션 레이어의 출력텐서 높이, 출력너비 크기

$O = (\frac{I-K+2P}{S}) +1$

  • $O$ : 출력 이미지의 크기

  • $I$ : 입력 이미지의 크기

  • $K$ : conv레이어에서 사용하는 커널의 크기(곱한 값이 가로 세로 각각)

  • $N$ : 커널 수

  • $S$ : 컨볼루션 연산의 스트라이드

  • $P$ : 패딩 사이즈


  • 첫번째 레이어 : ((32 - 3 +2*0)/1) +1 = 30

  • 두번째 레이어 : maxpoolign2d 는 절반(반내림)으로 이미지 사이즈를 줄인다.(pool_size를 지정하지 않는다면) = 15

  • 세번째 레이어 : ((15 - 3 +2*0)/1) +1 = 13

  • 네번째 레이어 : 6

  • 다섯번째 레이어 : ((6 -3 + 2*0)/1) +1 = 4

  • 여섯번째 레이어 : flatten은 1차원 배열로 높이,넓이,채널수를 곱한 값이다. = 1024

fit, compile

model.compile(optimizer='adam',
              loss=tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True),
              metrics=['accuracy']) # model에서 원핫인코딩을 처리해주는 sparcecategoricalcorssentropy

history = model.fit(train_images, train_labels, epochs=10, 
                    validation_data=(test_images, test_labels))
Epoch 1/10
c:\devtools\Miniconda3\envs\gpu\lib\site-packages\tensorflow\python\util\dispatch.py:1082: UserWarning: "`sparse_categorical_crossentropy` received `from_logits=True`, but the `output` argument was produced by a sigmoid or softmax activation and thus does not represent logits. Was this intended?"
  return dispatch_target(*args, **kwargs)
1563/1563 [==============================] - 12s 4ms/step - loss: 1.5446 - accuracy: 0.4375 - val_loss: 1.2451 - val_accuracy: 0.5507
Epoch 2/10
1563/1563 [==============================] - 6s 4ms/step - loss: 1.1639 - accuracy: 0.5877 - val_loss: 1.0682 - val_accuracy: 0.6223
Epoch 3/10
1563/1563 [==============================] - 5s 3ms/step - loss: 1.0157 - accuracy: 0.6440 - val_loss: 1.0435 - val_accuracy: 0.6350
Epoch 4/10
1563/1563 [==============================] - 5s 3ms/step - loss: 0.9280 - accuracy: 0.6757 - val_loss: 1.0041 - val_accuracy: 0.6590
Epoch 5/10
1563/1563 [==============================] - 5s 3ms/step - loss: 0.8512 - accuracy: 0.7038 - val_loss: 0.9263 - val_accuracy: 0.6783
Epoch 6/10
1563/1563 [==============================] - 5s 3ms/step - loss: 0.7950 - accuracy: 0.7207 - val_loss: 0.9054 - val_accuracy: 0.6895
Epoch 7/10
1563/1563 [==============================] - 5s 3ms/step - loss: 0.7459 - accuracy: 0.7402 - val_loss: 0.8709 - val_accuracy: 0.7016
Epoch 8/10
1563/1563 [==============================] - 5s 3ms/step - loss: 0.6981 - accuracy: 0.7560 - val_loss: 0.9180 - val_accuracy: 0.6853
Epoch 9/10
1563/1563 [==============================] - 6s 4ms/step - loss: 0.6576 - accuracy: 0.7702 - val_loss: 0.8590 - val_accuracy: 0.7122
Epoch 10/10
1563/1563 [==============================] - 6s 4ms/step - loss: 0.6209 - accuracy: 0.7809 - val_loss: 0.8630 - val_accuracy: 0.7119
# 모델 검증
plt.plot(history.history['accuracy'], label='accuracy')
plt.plot(history.history['val_accuracy'], label = 'val_accuracy')
plt.xlabel('Epoch')
plt.ylabel('Accuracy')
plt.ylim([0.5, 1])
plt.legend(loc='lower right')

test_loss, test_acc = model.evaluate(test_images,  test_labels, verbose=2)
313/313 - 0s - loss: 0.8630 - accuracy: 0.7119 - 495ms/epoch - 2ms/step

2 에포크 이후로 과적합 됌

print(test_acc)
0.711899995803833

댓글남기기