CNN의 구조와 Convolution 연산
CNN은 Convolution Neural Network으로 특성을 추출하는 Feature Extraction 구조와 Classification으로 크게 나뉩니다.
Filter 이해하기
Padding, 패딩 이해하기
(미션) 손으로 convolution 연산을 해봅시다!
stride = 1, padding = valid 일 때 output의 사이즈와 결과
3 1 0 -1 -5
5 -5 0 -4 -5
5 -6 0 6 -5
7 -2 0 4 -6
5 -6 0 5 -5
5x5
stride = 2, padding = valid 일 때 output의 사이즈와 결과
5 0 -5
5 0 -5
5 0 -5
3x3
stride = 1, padding = same 일 때 output의 사이즈와 결과
5x5
3채널의 컨볼루션 연산
커널 3개가 필터 1개를 구성하는데 이때, RGB 각 채널에 따라 3개의 커널이 있다.
각 채널에 대하여 기존 1채널 컨볼루션과 연산을 동일하게 진행한다.
각 연산 결과의 합에 bias를 더한다.
결과적으로 필터 1개가 feature map 하나를 만든다.
(미션) 텐서플로우에서 convolution 레이어는 링크를 통해 구현할 수 있습니다. filter 개수와 kernel 사이즈, stride, padding를 변화시켜 보면서 convolution 레이어를 지난 이후의 shape이 어떻게 변화하는 지 알아봅시다.
import numpy as np
import tensorflow as tf
# batch size = 1, height, width = 28, channel = 3 인 dummy data 생성
input_image = np.random.rand(1, 28, 28, 3)
conv_layer = tf.keras.layers.Conv2D(
# filter 개수
64,
kernel_size=(3, 3),
strides=1,
padding='valid'
)
# input_image가 conv_layer를 통과한 결과물을 output에 할당한 후 shape 출력
output = conv_layer(input_image)
print(output.shape)
26x26x64
Kernel Size, Channel Size, Stride
컨볼루션 연산에서 고민해야 할 하이퍼파라미터는 크게 위 3가지가 있습니다.
(미션) 하이퍼파라미터 각각이 연산과 피쳐맵에 미치는 영향을 정리해봅시다.
1. kernel size
커널의 사이즈가 커질수록 더 많은 연산이 필요함
큰 커널은 더 넓은 영역을 한 번에 살펴볼 수 있고, 작은 커널은 세부적인 부분을 본다.
2. channel size
채널 수가 많아지면 커널마다 더 많은 채널에 대해 연산이 필요하기 때문에 연산량이 증가한다.
채널 수가 많아지면 피쳐맵의 깊이가 증가하여 더 복잡하고 많은 특징을 추출할 수 있다.
3. stride
스트라이드가 크면 연산량이 줄어들고 작으면 연산량이 늘지만 더 세밀하게 특징을 추출할 수 있다.
스트라이드가 크면 피쳐맵의 크기가 작아진다.
CNN 구현하기
tf 라이브러리를 활용하여 컨볼루션 레이어를 구현 실습합니다.
tf.keras.layers.Conv2D 레이어로 컨볼루션 층을 구현합니다.
(미션) DNN으로 학습했던 MNIST 데이터를 CNN으로 학습시켜 봅시다. Conv2D 레이어와 MaxPooling2D 레이어, Flatten과 Dense layer까지 활용해서 CNN 모델을 구현하고, MNIST 데이터로 학습시켜 봅시다.
import tensorflow as tf
import matplotlib.pyplot as plt
dataset = tf.keras.datasets.mnist # 데이터셋 불러오기
(x_train, t_train), (x_test, t_test) = dataset.load_data() # 훈련용, 시험용 데이터셋
# 데이터 형식을 float으로 바꾼 후 계산의 편의를 위해 값을 255로 나눈다.
# 흑백이미지는 채널이 1개이므로 형변환 (60000,28,28) -> (60000,28,28,1)
x_train.astype('float32')
x_test.astype('float32')
x_train, x_test = x_train/255, x_test/255
x_train = x_train.reshape(60000,28,28,1)
x_test = x_test.reshape(10000,28,28,1)
# 정답 레이블을 원핫 벡터로 변경
num_category = 10
t_train = tf.keras.utils.to_categorical(t_train, num_category)
t_test = tf.keras.utils.to_categorical(t_test, num_category)
# 모델 세팅
"""
1. 64개 필터, 커널 사이즈 3 (필터 크기), 활성화는 relu,
패딩은 원래 인풋과 같은 크기를 갖도록 0을 패딩시킵니다. (padding="same")
입력데이터는 28x28x1 (채널 1개)를 넣는 것입니다.
(스트라이드는 default=1 이므로 따로 적지 않았습니다.)
2. 2x2 MaxPooling을 시킵니다.
3. 필터 개수를 128개로 늘리고 나머지는 같습니다.
4. 2x2 MaxPooling을 시킵니다.
5. 25% 확률의 Dropout을 시킵니다.
6. Flatten을 이용해 1D로 바꿉니다. (정답 레이블은 1D이기 때문에)
7. Dense를 이용하여 64개 노드로 만듭니다. (활성화 relu)
8. 50% 확률의 Dropout을 시킵니다.
9. 정답레이블의 원소가 10개이므로 Dense를 이용해 10개의 노드로 만듭니다.
최종 출력이므로 활성화함수는 softmax를 이용합니다. (손실함수로 crossentropy 이용하기 위해)
"""
model = tf.keras.models.Sequential([
tf.keras.layers.Conv2D(64, 3, activation="relu", padding="same", input_shape=(28,28,1)),
tf.keras.layers.MaxPool2D(2),
tf.keras.layers.Conv2D(128, 3, activation="relu", padding="same"),
tf.keras.layers.MaxPool2D(2),
tf.keras.layers.Dropout(0.25),
tf.keras.layers.Flatten(),
tf.keras.layers.Dense(64, activation="relu"),
tf.keras.layers.Dropout(0.5),
tf.keras.layers.Dense(num_category, activation="softmax")
])
# 모델 컴파일
model.compile(loss="categorical_crossentropy", optimizer="adam", metrics=["accuracy"])
# 하이퍼파라미터 설정
batch_size = 128
num_epoch = 10
History = model.fit(
x_train,
t_train,
batch_size=batch_size,
epochs=num_epoch,
validation_data=(x_test, t_test))