[딥러닝] 합성곱 신경망

3 분 소요



합성곱

합성곱(convolution)은 마치 입력 데이터에 마법의 동장을 찍어 유용한 특성만 드러나게 하는 것으로 비유할 수 있다.

합성곱 신경망은 밀집층의 계산과 조금 다르게 입력 데이터 전체에 가중치를 적용하는 것이 아니라 일부에 가중치를 곱한다.

합성곱 신경망 CNN에서는 완전 연결 신경망과 달리 뉴런을 필터라고 부른다. 혹은 커널이라고 부른다.

합성곱 계산을 통해 얻은 출력을 특성맵(feature map)이라고 부른다.

합성곱 신경망은 일반적으로 1개 이상의 합성곱 층을 쓴 인공 신경망을 합성곱 신경망이라고 한다.



패딩

입력 배열의 주위를 가상의 원소로 채우는 것을 패딩이라고 한다. 실제 입력값이 아니므로 0으로 채운다.

입력과 특성 맵의 크기를 동일하게 하기 위해 입력 주위에 0으로 패딩 하는 것을 세임 패딩(same padding)이라고 부른다. 합성곱 신경망에서는 세임 패딩이 많이 사용된다. 즉 입력과 특성 맵의 크기를 동일하게 만드는 경우가 많다.

패딩 없이 순수한 입력 배열에서만 합성곱을 하여 특성 맵을 만드는 것을 밸리드 패딩(valid padding)이라고 한다. 밸리드 패딩은 특성 맵의 크기가 줄어들 수밖에 없다.

스트라이드(stride) 매개변수는 이동의 크기이고 기본적으로 1이다. 대부분 기본값을 그대로 사용한다.



풀링

풀링(pooling)은 합성곱 층에서 만든 특성 맵의 가로세로 크기를 줄이는 역할을 수행한다. 하지만 특성 맵의 개수는 줄이지 않는다.

풀링에는 가중치가 없다. 도장을 찍는 영역에서 가장 큰값을 고르는 것을 최대 풀링, 평균값을 계산하는 것을 평균 풀링이라고 한다. 풀링은 겹치지 않고 이동한다. 풀링의 크기가 (2,2)이면 2칸씩, (3,3)이면 3칸씩 이동한다.

풀링의 첫번째 매개변수는 풀링의 크기로 대부분 2로 지정한다. 즉 가로세로 크기를 절반으로 줄인다. 튜플로 (2,3)이런식으로 지정이 가능하지만 이런 경우는 극히 드물다.

추가적으로 strides와 padding 매개변수를 제공하는데, strides는 기본적으로 풀링의 크기이고 패딩의 기본값은 valid이다. 풀링은 패딩을 하지 않기 때문에 이 매개변수를 바꾸는 일은 거의 없다.



패션 MNIST 데이터 가져오기

from tensorflow import keras
from sklearn.model_selection import train_test_split

(train_input, train_target), (test_input, test_target) = \
    keras.datasets.fashion_mnist.load_data()

train_scaled = train_input.reshape(-1, 28, 28, 1) / 255.0

train_scaled, val_scaled, train_target, val_target = train_test_split(
    train_scaled, train_target, test_size=0.2, random_state=42)



합성곱 신경망 만들기

model = keras.Sequential()
model.add(keras.layers.Conv2D(32, kernel_size=3, activation='relu', 
                              padding='same', input_shape=(28,28,1)))
model.add(keras.layers.MaxPooling2D(2))
model.add(keras.layers.Conv2D(64, kernel_size=(3,3), activation='relu', 
                              padding='same'))
model.add(keras.layers.MaxPooling2D(2))     
model.add(keras.layers.Flatten())
model.add(keras.layers.Dense(100, activation='relu'))
model.add(keras.layers.Dropout(0.4))
model.add(keras.layers.Dense(10, activation='softmax'))

model.summary()
Model: "sequential"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
conv2d (Conv2D)              (None, 28, 28, 32)        320       
_________________________________________________________________
max_pooling2d (MaxPooling2D) (None, 14, 14, 32)        0         
_________________________________________________________________
conv2d_1 (Conv2D)            (None, 14, 14, 64)        18496     
_________________________________________________________________
max_pooling2d_1 (MaxPooling2 (None, 7, 7, 64)          0         
_________________________________________________________________
flatten (Flatten)            (None, 3136)              0         
_________________________________________________________________
dense (Dense)                (None, 100)               313700    
_________________________________________________________________
dropout (Dropout)            (None, 100)               0         
_________________________________________________________________
dense_1 (Dense)              (None, 10)                1010      
=================================================================
Total params: 333,526
Trainable params: 333,526
Non-trainable params: 0
_________________________________________________________________
keras.utils.plot_model(model)

위 코드는 층의 구성을 그림으로 표현해준다.

keras.utils.plot_model(model, show_shapes=True, to_file='cnn-architecture.png', dpi=300)

위 코드는 그림에 입력과 출력의 크기를 표현해 준다.



모델 컴파일과 훈련

model.compile(optimizer='adam', loss='sparse_categorical_crossentropy', 
              metrics='accuracy')

checkpoint_cb = keras.callbacks.ModelCheckpoint('best-cnn-model.h5', 
                                                save_best_only=True)
early_stopping_cb = keras.callbacks.EarlyStopping(patience=2,
                                                  restore_best_weights=True)

history = model.fit(train_scaled, train_target, epochs=20,
                    validation_data=(val_scaled, val_target),
                    callbacks=[checkpoint_cb, early_stopping_cb])
Epoch 1/20
1500/1500 [==============================] - 36s 3ms/step - loss: 0.5075 - accuracy: 0.8212 - val_loss: 0.3403 - val_accuracy: 0.8714
Epoch 2/20
1500/1500 [==============================] - 5s 3ms/step - loss: 0.3394 - accuracy: 0.8766 - val_loss: 0.2822 - val_accuracy: 0.8932
Epoch 3/20
1500/1500 [==============================] - 5s 3ms/step - loss: 0.2921 - accuracy: 0.8952 - val_loss: 0.2566 - val_accuracy: 0.9053
Epoch 4/20
1500/1500 [==============================] - 5s 3ms/step - loss: 0.2606 - accuracy: 0.9047 - val_loss: 0.2596 - val_accuracy: 0.9039
Epoch 5/20
1500/1500 [==============================] - 5s 3ms/step - loss: 0.2377 - accuracy: 0.9124 - val_loss: 0.2302 - val_accuracy: 0.9128
Epoch 6/20
1500/1500 [==============================] - 5s 3ms/step - loss: 0.2186 - accuracy: 0.9183 - val_loss: 0.2292 - val_accuracy: 0.9165
Epoch 7/20
1500/1500 [==============================] - 5s 3ms/step - loss: 0.2011 - accuracy: 0.9252 - val_loss: 0.2217 - val_accuracy: 0.9156
Epoch 8/20
1500/1500 [==============================] - 5s 3ms/step - loss: 0.1889 - accuracy: 0.9306 - val_loss: 0.2235 - val_accuracy: 0.9195
Epoch 9/20
1500/1500 [==============================] - 5s 3ms/step - loss: 0.1751 - accuracy: 0.9340 - val_loss: 0.2254 - val_accuracy: 0.9212

훈련 세트의 정확도가 심층 신경망보다 좋아졌다.

model.evaluate(val_scaled, val_target)
375/375 [==============================] - 1s 2ms/step - loss: 0.2217 - accuracy: 0.9156
[0.22167474031448364, 0.9155833125114441]

성능도 비슷하다. EarlyStopping 콜백이 model 객체를 최상의 모델 파라미터로 잘 복원한 것 같다.

test_scaled = test_input.reshape(-1, 28, 28, 1) / 255.0
model.evaluate(test_scaled, test_target)
313/313 [==============================] - 1s 3ms/step - loss: 0.2362 - accuracy: 0.9094
[0.23616333305835724, 0.9093999862670898]

훈련 세트와 검증 세트처럼 픽셀값의 범위를 0~1 사이로 바꾸고 이미지 크기를 (28,28)에서 (28,28,1)로 바꾼다. 그후 테스트 세트에 대한 성능을 측정한다.