CNN을 활용한 이미지 분류


💡 CNN 모델링 실습

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()

# Conv2D는 사용하기 위해서 깊이 차원을 추가해줘야 한다.
train_scaled = train_input.reshape(-1,28,28,1)/255

train_scaled, val_scaled, train_target, val_target = train_test_split(
    train_scaled, train


모델링

# Sequential() 통한 모델 객체 생성
model = keras.Sequential()

#〓〓〓〓〓〓〓〓〓〓〓 첫 번째 합성곱-풀링 층 〓〓〓〓〓〓〓〓〓〓〓#

# Conv2D
model.add(keras.layers.Conv2D(
    32,
    kernel_size=3,
    activation='relu',
    padding='same',
    input_shape=(28,28,1)
))

# Pooling
model.add(keras.layers.MaxPooling2D(2, strides=2, padding='valid'))

#〓〓〓〓〓〓〓〓〓〓〓 두 번째 합성곱-풀링 층 〓〓〓〓〓〓〓〓〓〓〓#

model.add(keras.layers.Conv2D(
    64,
    kernel_size=3,
    activation='relu',
    padding='same'    
))

model.add(keras.layers.MaxPooling2D(2, strides=2, padding='valid'))

#〓〓〓〓〓〓〓〓〓〓 3차원 특성 맵을 1차원으로 〓〓〓〓〓〓〓〓〓〓#

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

# 은닉층
model.add(keras.layers.Dense(100,activation='relu'))

# Dropout 은닉층의 과대적합을 예방.
model.add(keras.layers.Dropout(0.4))

# 출력층
model.add(keras.layers.Dense(10, activation='softmax'))

# 모델 상세
model.summary()

아래는 summary 출력값이다.

  1. 첫 번째 합성곱층
    • 필터가 : 32개
    • 필터 크기 : (3,3,1)
    • 절편 : 32개

    Param = (3x3x1x32) + 32 = 320


  1. 두 번째 합성곱층
    • 필터가 : 64개
    • 필터 크기 : (3,3,32)
    • 절편 : 64개

    Param = (3x3x32x64) + 64 = 18496


  1. Flatten

    Output Shape : 7x7x62 = (3136,)


  1. 은닉층

    Param = 3136 x 10 + 100 = 313700


  1. 출력층

    Parma = 100 x 10 + 10 = 1010



층의 구성 시각화

show_shapes=True 로 지정해주지 않으면 input,output은 출력되지 않는다.

to_file = "name.확장자" 를 지정해주면 출력 이미지를 저장할 수 있다.

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



모델 컴파일 & 훈련

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])



손실 곡선 시각화

import matplotlib.pyplot as plt

plt.plot(history.history['loss'])
plt.plot(history.history['val_loss'])

plt.xlabel('epoch')
plt.ylabel('loss')

plt.legend(['train','val'])

plt.show()

8번 에포크와 결과가 같다는걸 확인할 수 있다.



test_input 이미지 예측해보기

# test set 성능 측정
test_scaled = test_input.reshape(-1,28,28,1)/255
model.evaluate(test_scaled,test_target)

# test_scaled 예측
test_pred = model.predict(test_scaled)

# 일부만 시각화
fig,axs = plt.subplots(2,2,figsize=(9,9))

for i in range(2):
    axs[i,0].imshow(test_input[i].reshape(28,28), cmap='gray_r')
    axs[i,1].bar(range(1,11),test_pred[i])

plt.show()

추가적으로 predict() 에 샘플 하나를 전달할 때 슬라이싱을 사용해야한다.

keras에서 fit(), predict(), evaluate() 메서드는 모두 입력의 첫 번째 차원이

배치 차원일 것으로 기대한다. 따라서 샘플 하나를 전달할 때 (28,28,1)이 아니라

(1,28,28,1) 크기를 전달해야 한다. 배열 슬라이싱은 인덱싱과 다르게 선택된 원소가

하나이더라도 전체 차원이 유지되어 (1,28,28,1) 크기를 만든다.

아래 사진처럼 슬라이싱 했을 때와 아닐 때 shape의 차이를 확인할 수 있었다.