이 포스팅은 혼자 공부하는 머신러닝 + 딥러닝 책을 공부하고 정리한것 입니다.
이번 시간에는 텐서플로 케라스 API를 사용해 7장에서 만들었던 패션 MNIST 데이터를 합성곱 신경망으로 분류해보겠다.
패션 MNIST 데이터 불러오기
합성곱 신경망은 2차원 이미지를 그대로 사용하기 때문에 일렬로 펼치치 않는다. 다만 입력 이미지는 항상 깊이(채널) 차원이 있어야 한다. 흑백 이미지의 경우 채널 차원이 없는 2차원 배열이지만 Conv2D층을 사용하기 위해 마지막에 이 채널 차원을 추가해야 한다. 넘파이 reshape() 메서드를 사용해 전체 배열 차원을 그대로 유지하면서 마지막에 차원을 간단히 추가할 수 있다.
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.0
train_scaled,val_scaled,train_target,val_target=train_test_split(train_scaled,train_target,test_size=0.2,random_state=42)
train_scaled의 차원은 다음과 같이 바뀌었다.
합성곱 신경망 만들기
전형적인 합성곱 신경망의 구조는 합성곱 층으로 이미지에서 특징을 감지한 후 밀집층으로 클래스에 따른 분류 확률을 계산한다. 케라스의 Sequential 클래스를 사용해 순서대로 이 구조를 정의한다.
model=keras.Sequential()
model.add(keras.layers.Conv2D(32,kernel_size=3,activation='relu',padding='same',input_shape=(28,28,1)))
이 합성곱 층은 32개의 필터를 사용한다. 커널의 크기는 (3,3) 이고 렐루 활성화 함수와 세임 패딩을 사용한다.
완전 연결 신경망에서 처럼 케라스 신경말 모델의 첫 번째 층에서 입력의 차원을 지정해주어야 한다. input_shape에 지정한다.
풀링층을 추가한다. 가로세로 크기가 같으면 정수 하나로 지정할 수 있다.
model.add(keras.layers.MaxPooling2D(2))
합성곱 층에서 32개의 필터를 사용했기 때문에 이 특성 맵의 깊이는 32가 된다. 따라서 최대 풀링을 통과한 특성 맵의 크기는 (14,14,32)가 될 것이다. 풀링을 통과한 특성 맵의 개수 32는 변하지 않는다.
model.add(keras.layers.Conv2D(64,kernel_size=3,activation='relu',padding='same'))
model.add(keras.layers.MaxPool2D(2))
64개의 필터를 사용했으므로 최종적으로 만들어지는 특성 맵의 크기는 (7,7,64)가 될 것이다.
이제 이 3차원 특성맵을 일렬로 펼칠 차례이다. 왜냐하면 마지막에 10개의 뉴런을 가진 (밀집) 출력층에서 확률을 계산하기 때문이다. 여기에서는 특성맵을 일렬로 펼쳐서 바로 출력층에 전달하지 않고 중간에 하나의 밀집 은닉층을 하나 더 두도록 하겠다.
model.add(keras.layers.Flatten())
model.add(keras.layers.Dense(100,activation='relu'))
model.add(keras.layers.Dropout(0.4))
model.add(keras.layers.Dense(10,activation='softmax'))
은닉층와 출력층 사이에 드롭아웃층이 은닉층의 과대적합을 막아 성능을 조금 더 개선해 줄 것이다.
model.summary()
첫 번째 합성곱 층은 32개의 필터를 가지고 있고 크기가 (3,3), 깊이가 1이다. 또 필터마다 하나의 절편이 있다. 따라서 총 3x3x1x32+32=320 개의 파라미터가 있다.
두 번째 합성곱 층은 64개의 필터를 사용하고 크기가 (3,3), 깊이가 32 이다. 역시 필터마다 하나의 절편이 있다. 따라서 총 3x3x32x64+64=18,496 개의 파라미터가 있다.
keras.utils.plot_model(model)
처음에 나오는 InputLayer 클래스는 케라스가 자동으로 추가해 주는 것으로 입력층의 역할을 한다. 이 입력층은 첫 번째 Conv2D 클래스에 추가한 input_shape 매개변수를 사용한다.
keras.utils.plot_model(model,show_shapes=True,to_file='cnn-architecture.png',dpi=300)
지금까지 만든 신경망을 그림으로 다시 살펴보자.
모델 컴파일과 훈련
model.compile(optimizer='adam',loss='sparse_categorical_crossentropy',metrics='accuracy')
checkpoint_cb=keras.callbacks.ModelCheckpoint('best-cnn-model.h5')
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()
이 그래프를 기반으로 아홉 번째 에포크를 최적으로 생각할 수 있다.
EarlyStopping 클래스에서 restore_best_weights 매개변수를 True로 지정했으므로 현재 model 객체가 최적의 모델 파라미터로 복원되어 있다. 즉 ModelCheckpoint 콜백이 저장한 best-cnn-model.h5 파일을 다시 읽을 필요가 없다. 이번에는 세트에 대한 성능을 평가해 보자.
model.evaluate(val_scaled,val_target)
이 결과는 fit() 메서드의 출력 중 여덟 번째 에포크의 출력과 동일하다. EarlyStopping 콜백이 model 객체를 최상의 모델 파라미터로 잘 복원한 것 같다.
7장에서 predict() 메서드를 사용해 훈련된 모델을 사용하여 새로운 데이터에 대한 예측을 해보자. 여기에서는 편의상 검증 세트의 첫 번째 샘플을 처음 본 이미지라고 가정한다. 맷플롯립에서는 흑백 이미지에 깊이 차원은 없다. 따라서 (28,28,1) 크기를 (28,28)로 바꾸어 출력해야 한다.
plt.imshow(val_scaled[0].reshape(28,28),cmap='gray_r')
plt.show()
preds=model.predict(val_scaled[0:1])
print(preds)
출력 결과를 보면 아홉 번쨰 값이 1이고 다른 값은 거의 0에 가깝다.
plt.bar(range(1,11),preds[0])
plt.xlabel('class')
plt.ylabel('prob')
plt.show()
classes=['티셔츠','바지','스웨터','드레스','코트','샌달','셔츠','스니커즈','가방','앵글 부츠']
print(classes[np.argmax(preds)])
마지막으로 맨 처음에 떼어 놓았던 테스트 세트로 합성곱 신경망의 일반화 성능을 가늠해 보자. 즉 이 모델을 실전에 투입했을 때 얻을 수 있는 예상 성능을 측정해 보자.
훈련 세트와 검증 세트에서 했던 것처럼 픽셀값의 범위를 0~1 사이로 바꾸고 이미지 크기를 (28,28) 에서 (28,28,1) 로 바꾸자.
test_scaled=test_input.reshape(-1,28,28,1) /255.0
그다음 evluate() 메서드로 테스트 세트에 대한 성능을 측정한다.
model.evaluate(test_scaled,test_target)
결론
이번 절에서는 케라스 API를 사용해 합성곱 신경망을 만들어 보았다. 최종적으로 10개의 뉴런을 가진 출력층에서 샘플에 대한 확률을 출력했다.
마지막에는 이제까지 사용하지 않았던 테스트 세트를 사용해 최종 모델의 일반화 성능을 평가했다. 항상 테스트 세트는 모델을 출시하기 직전 딱 한 번만 사용해야 한다.
'Machine Learning > Basic' 카테고리의 다른 글
[8-3] 합성곱 신경망의 시각화(2) (0) | 2021.04.15 |
---|---|
[8-3] 합성곱 신경망의 시각화(1) (0) | 2021.04.15 |
[8-1] 합성곱 신경망 (0) | 2021.04.14 |
[7-3] 신경망 모델 훈련 (0) | 2021.04.13 |
[7-2] 심층 신경망 (0) | 2021.04.13 |