CNN을 활용한 Image Classification

변현섭·2024년 8월 12일
0

지금은? AI 전성시대!

목록 보기
20/21
post-thumbnail

1. CNN 만들기

1) 데이터 준비

Fashion MNIST 데이터 Set을 불러온 후, 특성 데이터를 Convolution Layer가 기대하는 3차원 배열로 변환한다.

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
train_scaled, val_scaled, train_target, val_target = train_test_split(train_scaled, train_target, test_size=0.2, random_state=42)

2) CNN 구성

① Sequential 클래스의 객체를 만들고, 첫번째 Convolution Layer인 Conv2D 클래스를 추가한다.

  • 32개의 필터를 사용한다.
  • (3, 3) 크기의 커널을 사용한다.
  • Same Padding을 적용한다.
model = keras.Sequential()
model.add(keras.layers.Conv2D(32, kernel_size=3, activation='relu', padding='same', input_shape=(28, 28, 1)))

② Pooling Layer를 추가한다.

  • (2, 2) 크기의 풀링을 사용한다.
  • Max Pooling을 적용한다.
  • 결과적으로 32개의 필터에 의해 생선된 (28, 28, 32) 크기의 특성맵이, Pooling에 의해 (14, 14, 32)의 크기로 줄어들 것이다.
model.add(keras.layers.MaxPooling2D(2))

③ 다음으로, 두번째 Convolution-Pooling Layer를 추가해보기로 하겠다.

  • 64개의 필터와 Pooling을 사용하여 특성 맵의 크기를 (7, 7, 64)로 변환한다.
model.add(keras.layers.Conv2D(64, kernel_size=2, activation='relu', padding='same'))
model.add(keras.layers.MaxPooling2D(2))

④ 밀집층에서 클래스별 확률을 계산할 수 있도록, 3차원 특성 맵을 Flattening 한다.

model.add(keras.layers.Flatten())

⑤ 과대 적합을 방지하기 위해 은닉층과 출력층 사이에 Dropout을 적용한다.

  • Fashion MNIST 데이터 Set은 10개의 클래스로 구분되는 다중 분류 문제이므로, 마지막 밀집층에서는 Softmax 활성화 함수를 사용해야 한다.
model.add(keras.layers.Dense(100, activation='relu'))
model.add(keras.layers.Dropout(0.4))
model.add(keras.layers.Dense(10, activation='softmax'))

summary() 메서드를 통해 완성된 모델의 구조를 확인해보자.

  • 특성 맵의 크기 변화를 확인할 수 있다.
  • 참고로, 파라미터의 개수는 32개의 필터가 가지고 있는 (3, 3) 크기의 가중치와 절편의 개수이므로, 3 X 3 X 32 + 32 = 320으로 계산된다.
model.summary()

summary() 대신 plot_model() 메서드를 사용할 수도 있다.

keras.utils.plot_model(model)

⑧ show_shapes 매개변수를 사용하여 입력과 출력의 크기를 표시할 수도 있다.

keras.utils.plot_model(model, show_shapes=True)

2. Image Classification

1) CNN 훈련

① Adam Optimizer와 ModelCheckpoint 및 EarlyStopping Callbak을 적용하여 모델을 컴파일하고 훈련시킨다.

  • ANN 모델에서 0.8549 정도의 성능을 기록했던 것과 비교하면, 확실히 성능이 향상된 모습이다.
model.compile(optimizer='adam', loss='sparse_categorical_crossentropy', metrics=['accuracy'])

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

result = model.fit(train_scaled, train_target, epochs=20, validation_data=(val_scaled, val_target), callbacks=[checkpoint_cb, early_stopping_cb])

② 조기 종료가 잘 이루어진 것인지 확인해보기 위해, Loss Curve를 그려보자.

  • 검증 손실이 제일 낮아지는 9번째 Epoch가 최적이다. 즉, 11번째 Epoch에서 잘 조기 종료되었음을 알 수 있다. (실행할 때마다 결과가 조금씩 달라지지만, 조기 종료는 잘 동작한다.)
import matplotlib.pyplot as plt

plt.plot(result.history['loss'])
plt.plot(result.history['val_loss'])
plt.xlabel('Epochs')
plt.ylabel('Loss')
plt.legend(['Train', 'Validataion'])
plt.show()


③ 이번에는 검증 Set에서의 성능을 확인해보자.

model.evaluate(val_scaled, val_target)

predict() 메서드를 사용해 모델의 예측을 확인해보기 위해 먼저는 첫번째 Sample의 이미지를 출력해보자.

  • 첫번째 Sample은 가방 이미지이다.
plt.imshow(val_scaled[0].reshape(28, 28), cmap='gray_r')
plt.show()

⑤ 10개의 클래스에 대한 예측 확률을 출력해보자.

  • 배열의 차원을 유지하기 위해서는 인덱싱 연산이 아닌 슬라이싱을 사용해야 한다.
  • 9번째 값만 1이고, 나머지는 0에 가까운 것을 확인할 수 있다. (참고로, e는 10의 거듭제곱을 의미한다.)
preds = model.predict(val_scaled[0:1])
print(preds)

⑥ 출력 결과를 막대 그래프로 그려보면 아래와 같다.

  • 실제로 9번째 레이블은 가방이기 때문에 이 모델이 예측을 잘 수행한 것으로 볼 수 있다.
plt.bar(range(1, 11), preds[0])
plt.xlabel('Class')
plt.ylabel('Prob')
plt.show()

⑦ 마지막으로 테스트 Set에서의 성능을 평가해보자.

  • 마찬가지로, 픽셀 값의 범위를 0~1 사이의 값으로 표준화하고 3차원 배열로 변환해주어야 한다.
test_scaled = test_input.reshape(-1, 28, 28, 1) / 255
model.evaluate(test_scaled, test_target)

profile
LG전자 Connected Service 1 Unit 연구원 변현섭입니다.

0개의 댓글