CNN은 Convolutional Neural Network의 약자로, 컴퓨터 비전 분야에서 이미지 인식, 객체 탐지, 얼굴 인식 등 다양한 작업에 사용되는 딥러닝 모델이다.
CNN은 입력 이미지를 여러 층의 컨볼루션(Convolution) 레이어와 풀링(Pooling) 레이어, 그리고 일반적인 신경망 레이어인 완전 연결(Fully Connected) 레이어로 구성된다.
컨볼루션 레이어는 입력 이미지에서 특징을 추출하는 역할을 한다. 이때 컨볼루션 필터를 사용하여 입력 이미지를 슬라이딩하면서 특징 맵(Feature Map)을 생성한다.
풀링 레이어는 컨볼루션 레이어에서 생성된 특징 맵의 크기를 줄이는 역할을 한다. 이를 통해 특징을 보다 간단하게 표현할 수 있으며, 모델의 파라미터 수를 줄이고 연산량을 감소한다.
완전 연결 레이어는 모델의 최종 출력을 계산하는 역할을 한다. 이때 입력 이미지에서 추출된 특징들을 펼쳐서 1차원 벡터로 만들어 주는 Flatten 레이어를 사용하곤 한다.
CNN은 이미지 분류, 객체 검출, 얼굴 인식 등 다양한 분야에서 좋은 성능을 보이며, 현재까지도 다양한 연구가 이루어지고 있다.
풀링 기법에는 정해진 구역 안에서 최댓값을 뽑아내는 맥스 풀링(max pooling)과 평균값을 뽑아내는 평균 풀링(average pooling) 등이 있다.
맥스 풀링은 컨볼루션 신경망에서 사용되는 풀링 기법 중 하나로 주로 이미지 처리에서 특징 맵을 줄이기 위해 사용된다.
맥스 풀링은 입력 이미지에서 윈도우 크기 내에서 가장 큰 값을 선택하여 출력한다. 윈도우는 이동 가능하며, 일반적으로 2x2 크기의 윈도우를 사용한다. 예를 들어, 2x2 크기의 윈도우를 이동하면서 각 윈도우에서 가장 큰 값을 선택하여 2x2 크기의 출력 맵을 생성한다.
맥스 풀링의 장점은 다음과 같다.
특징 맵의 크기를 줄일 수 있다.
불필요한 특징을 제거하여 모델의 파라미터 수를 줄이고 과적합을 방지한다.
이동 불변성(Translation Invariance)을 제공하여 이미지에서 물체의 위치가 변해도 특징을 인식할 수 있다.
맥스 풀링의 단점은 다음과 같다.
최대값만 사용하기 때문에, 노이즈와 이상값에 민감하게 반응할 수 있다.
특징의 위치 정보가 유실될 수 있다. 즉, 풀링 층 이후에 위치 정보를 복원하는 것이 어려울 수 있다.
드롭아웃은 딥러닝 모델에서 과적합을 방지하기 위한 정규화 기법 중 하나로 드롭아웃은 특정 뉴런을 임의로 선택하여 제거하고 모델을 학습하는 기법이다.
드롭아웃을 적용하면, 각 학습 단계에서 일부 뉴런이 무작위로 제거된다. 이때 제거되는 뉴런의 비율을 드롭아웃 비율이라고 한다. 드롭아웃은 모델 내의 연결을 공유하면서, 여러 개의 서로 다른 모델을 학습시키는 것과 유사한 효과를 얻을 수 있다.
드롭아웃의 장점은 다음과 같다.
과적합을 방지한다. 드롭아웃을 사용하면 모델이 특정 뉴런에 지나치게 의존하는 것을 방지할 수 있다.
앙상블 효과를 얻는다. 드롭아웃은 여러 개의 모델을 학습시키는 것과 유사한 효과를 가져오며 일반화 성능을 향상시키는 데 도움이 됩니다.
불확실성을 반영한다. 드롭아웃은 모델이 불확실한 상황에서도 잘 작동할 수 있도록 한다.
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Dense, Dropout, Flatten, Conv2D, MaxPooling2D
from tensorflow.keras.callbacks import ModelCheckpoint,EarlyStopping
from tensorflow.keras.datasets import mnist
from tensorflow.keras.utils import to_categorical
import matplotlib.pyplot as plt
import numpy as np
# 데이터
(X_train, y_train), (X_test, y_test) = mnist.load_data()
X_train = X_train.reshape(X_train.shape[0], 28, 28, 1).astype('float32') / 255
X_test = X_test.reshape(X_test.shape[0], 28, 28, 1).astype('float32') / 255
y_train = to_categorical(y_train)
y_test = to_categorical(y_test)
# 컨볼루션 신경망의 설정
model = Sequential()
model.add(Conv2D(32, kernel_size=(3, 3), input_shape=(28, 28, 1), activation='relu'))
model.add(Conv2D(64, (3, 3), activation='relu'))
model.add(MaxPooling2D(pool_size=(2,2)))
model.add(Dropout(0.25))
model.add(Flatten())
model.add(Dense(128, activation='relu'))
model.add(Dropout(0.5))
model.add(Dense(10, activation='softmax'))
# 모델의 실행 옵션 설정
model.compile(loss='categorical_crossentropy',
optimizer='adam',
metrics=['accuracy'])
# 모델 최적화를 위한 설정 구간
modelpath="./MNIST_CNN.hdf5"
checkpointer = ModelCheckpoint(filepath=modelpath, monitor='val_loss',
verbose=1, save_best_only=True)
early_stopping_callback = EarlyStopping(monitor='val_loss',
patience=10)
# 모델 실행
history = model.fit(X_train, y_train, validation_split=0.25, epochs=30,
batch_size=200, verbose=0, callbacks=
[early_stopping_callback,checkpointer])
# 테스트 정확도 출력
print("\n Test Accuracy: %.4f" % (model.evaluate(X_test, y_test)[1]))
Epoch 00001: val_loss improved from inf to 0.08178, saving model to
../data/model\MNIST_CNN.hdf5
...
Epoch 00008: val_loss improved from 0.04101 to 0.03858, saving model to
../data/model\MNIST_CNN.hdf5
...
Epoch 00023: val_loss did not improve from 0.03742
313/313 [===========================] - 2s 6ms/step - loss: 0.0341 - accuracy: 0.9917
Test Accuracy: 0.9917
# 검증셋과 학습셋의 오차 저장
y_vloss = history.history['val_loss']
y_loss = history.history['loss']
# 그래프로 표현
x_len = np.arange(len(y_loss))
plt.plot(x_len, y_vloss, marker='.', c="red", label='Testset_loss')
plt.plot(x_len, y_loss, marker='.', c="blue", label='Trainset_loss')
# 그래프에 그리드를 주고 레이블 표시
plt.legend(loc='upper right')
plt.grid()
plt.xlabel('epoch')
plt.ylabel('loss')
plt.show()

8번째 에포크에서 베스트 모델을 만들었고 23번째 에포크에서 학습이 중단되었다.
이전에 만든 얕은 구조의 딥러닝 모델보다 학습셋과 테스트셋의 오차가 작아졌다.
또한 이전 모델에 비해 101개의 이미지를 더 맞혔다.