합성곱 딥러닝 아키텍처


합성곱 신경망

합성곱(Convolution) 이란?


콘볼루션 연산 과정

합성곱(Convolution)은 행렬의 합, 곱 연산을 하는데 사용되며, 다시말해 선형 함수(Linear Function)이다. (머신 러닝에 필요한 선형대수 기초 지식)

합성곱은 딥러닝에서 주로 픽셀 이미지(행렬)의 특징 맵(feature map)을 만드는데 사용된다.

합성곱 예시


각종 이미지 프로그램들의 필터링 기능들

경계선 검출 소벨 알고리즘

경계 검출 필터 (미분 원리)

경계선 검출 소벨 알고리즘

패딩(Padding)


padding

패딩 사이즈가 1인 경우 콘볼루션 연산을 하더라도 원본 행렬의 크기를 유지한다.

합성곱을 딥러닝에 왜 사용하는가?


합성곱은 선형 함수이기 때문에 이미지 본래의 성질을 유지한 채 중요한 부분(특징)만 찾아낼 수 있어 학습의 정확도를 높일 수 있다.

합성곱 신경망에 활성화 함수(activation function)를 왜 사용하는가?


선형 함수나 아핀 함수는 활성화 함수를 사용하지 않으면 다층 신경망을 구성하는 의미가 없다.

선형 레이어는 아무리 중첩해도 선형 레이어가 되기 때문이다.

풀링 (pooling)


풀링은 입력 데이터를 압축하여 연산량을 줄이는 작업이다.

최대 풀링(max pooling)은 가장 큰 값을 선택하는 것이다.

맥스 풀링

콘볼루션 연산 출력 데이터 크기


피처 맵 높이

H: 입력 높이, W: 입력 폭, P: 패딩 폭, 스트라이드: S

$$
OH = \frac{H+2P-FH}{S} + 1
$$

피처 맵 폭

$$
OW = \frac{W+2P-FW}{S} + 1
$$

풀링(Pooling)을 이용한 서브 샘플링


pooling

풀링은 샘플의 크기를 줄이기 위하여 사용하는 방식이다.

  • 최대 풀링 (max-pooling) - 이웃한 픽셀에서 가장 큰 값을 취한다.

  • 평균 풀링 (menn-pooling) - 이웃한 픽셀의 평균 값을 취한다.

드롭아웃(dropout)으로 신경망 규제


크기가 큰 네트워크에서 ‘과대적합’을 피하기 위해 일부 데이터 셋을 일부러 버리는 것이다.

과대적합(overfitting)이란 모델을 학습할 때 학습 데이터셋에 지나치게 최적화하여 발생하는 문제이다.

dropout

CNN을 이용한 MNIST 손글씨 검증


cnn-mnist

텐서 차원

  • 입력: [batchsize x 28 x 28 x 1]

  • 합성곱 1: [batchsize x 28 x 28 x 32]

  • 풀링 1: [batchsize x 14 x 14 x 32]

  • 합성곱 2: [batchsize x 14 x 14 x 64]

  • 풀링 2: [batchsize x 7 x 7 x 46]

  • 완전 연결 1: [batchsize x 1024]

  • 완전 연결과 소프트맥스 층: [batchsize x 10]

합성곱 포맷

  • batch N, channels C, depth D, height H, width W

  • NHWC Format (batch, height, width, channels)

  • NCHW Format (batch, channels, height, width)

이번 네트워크에서는 NHWC 포맷을 사용할 것이다. (data_format=’channel_last’)

학습 코드

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
import tensorflow as tf
import numpy as np
from tensorflow.keras.datasets import mnist
from tensorflow.keras import Sequential
from tensorflow.keras.layers import Flatten, Dense, Conv2D, MaxPool2D, Dropout

# 데이터 로드
(x_train_orig, y_train_orig), (x_test, y_test) = mnist.load_data()

# 데이터 정규화
x_train_orig = x_train_orig / 255.
x_test = x_test / 255.

# 학습용 데이터 (3만개)
x_train = x_train_orig[:30000]
y_train = y_train_orig[:30000]

# 검증용 데이터 (3만개)
x_valid = x_train_orig[30000:]
y_valid = y_train_orig[30000:]

tf.random.set_seed(1) # 난수 생성 방식 고정

model = Sequential() # 시퀀셜 모델

model.add(Conv2D(
input_shape=(28, 28, 1), # 입력층 출력 모양 (None, 28, 28, 1)
filters=32, # 콘벌루션 필터 개수
kernel_size=(5,5), # 콘벌루션 필터 사이즈
strides=(1,1), # 스트라이드 크기
padding='same', # 패딩 사이즈 = 1
data_format='channels_last', # NHWC 포맷
activation='relu',
name='conv_1'
))

model.add(MaxPool2D(
pool_size=(2,2),
name='pool_1'
))

model.add(Conv2D(
filters=64,
kernel_size=(5,5),
strides=(1,1),
padding='same',
activation='relu',
name='conv_2'
))

model.add(MaxPool2D(
pool_size=(2,2),
name='pool_2'
))

model.add(Flatten())

model.add(Dense(
units=1024,
activation='relu',
name='fc_1'
))

model.add(Dropout(
rate=0.5
))

model.add(Dense(
units=10,
activation='softmax',
name='fc_2'
))

model.compile(
optimizer='adam',
loss='SparseCategoricalCrossentropy',
metrics=['accuracy']
) # 모델 컴파일

history = model.fit(x_train, y_train, epochs=10, batch_size=64, validation_data=(x_valid, y_valid))
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
Epoch 1/10
469/469 [==============================] - 106s 224ms/step - loss: 0.1883 - accuracy: 0.9422 - val_loss: 0.0629 - val_accuracy: 0.9809
Epoch 2/10
469/469 [==============================] - 105s 224ms/step - loss: 0.0571 - accuracy: 0.9820 - val_loss: 0.0518 - val_accuracy: 0.9842
Epoch 3/10
469/469 [==============================] - 105s 224ms/step - loss: 0.0384 - accuracy: 0.9871 - val_loss: 0.0410 - val_accuracy: 0.9874
Epoch 4/10
469/469 [==============================] - 105s 224ms/step - loss: 0.0249 - accuracy: 0.9923 - val_loss: 0.0419 - val_accuracy: 0.9887
Epoch 5/10
469/469 [==============================] - 105s 224ms/step - loss: 0.0200 - accuracy: 0.9933 - val_loss: 0.0469 - val_accuracy: 0.9876
Epoch 6/10
469/469 [==============================] - 105s 224ms/step - loss: 0.0192 - accuracy: 0.9935 - val_loss: 0.0438 - val_accuracy: 0.9880
Epoch 7/10
469/469 [==============================] - 105s 223ms/step - loss: 0.0173 - accuracy: 0.9943 - val_loss: 0.0395 - val_accuracy: 0.9893
Epoch 8/10
469/469 [==============================] - 105s 224ms/step - loss: 0.0106 - accuracy: 0.9964 - val_loss: 0.0539 - val_accuracy: 0.9865
Epoch 9/10
469/469 [==============================] - 105s 223ms/step - loss: 0.0114 - accuracy: 0.9965 - val_loss: 0.0442 - val_accuracy: 0.9894
Epoch 10/10
469/469 [==============================] - 104s 222ms/step - loss: 0.0099 - accuracy: 0.9966 - val_loss: 0.0475 - val_accuracy: 0.9898

히스토리 분석


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import matplotlib.pyplot as plt
import numpy as np

def draw_history(hist, figsize=None):
if figsize:
fig = plt.figure(figsize=figsize)
else:
fig = plt.figure()

x_arr = np.arange(len(hist['loss'])) + 1

loss_ax = fig.add_subplot(1, 2, 1)
acc_ax = fig.add_subplot(1, 2, 2)

loss_ax.plot(x_arr, hist['loss'], '-o', label='Train Loss')
loss_ax.plot(x_arr, hist['val_loss'], '--<', label='Validation Loss')
acc_ax.plot(x_arr, hist['accuracy'], '-o', label='Train acc.')
acc_ax.plot(x_arr, hist['val_accuracy'], '--<', label='Validation acc.')

loss_ax.legend(fontsize=15)
acc_ax.legend(fontsize=15)

draw_history(history.history, figsize=(12, 4))
history

테스트 데이터 예측 결과 시각화


1
2
3
test_loss, test_acc = model.evaluate(x_test, y_test, verbose=2)

predicted = model.predict(x_test)
1
313/313 - 8s - loss: 0.0318 - accuracy: 0.9903 - 8s/epoch - 24ms/step
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
import matplotlib.pyplot as plt
import numpy as np

def show_predicted_with_img(predicted, x_img, y_label, grid, figsize=None):
if figsize:
fig = plt.figure(figsize=figsize)
else:
fig = plt.figure()

for i, pred in enumerate(predicted):
p_label = np.argmax(pred)

if p_label == y_label[i]:
color = 'green'
else:
color = 'red'

ax = fig.add_subplot(grid[0], grid[1], i+1)
ax.set_xticks([])
ax.set_yticks([])
ax.text(0.85, 0.1, f'{p_label}({y_label[i]})', size=15, color=color,
horizontalalignment='center', verticalalignment='center',
transform=ax.transAxes)
ax.imshow(x_img[i], cmap='gray_r')

plt.show()

show_predicted_with_img(predicted[:18], x_test[:18], y_test[:18], grid=(3, 6), figsize=(12, 6))
predicted

참고 사항


머신 러닝을 위한 파이썬 한조각

머신 러닝 교과서 with 파이썬, 사이킷런, 텐서플로

바이오메디컬비전 및 응용 (숭실대학교)