혼자 공부하는 머신러닝+딥러닝 책을 바탕으로 공부한 내용입니다.
CH8 이미지를 위한 인공 신경망 ①②
케라스 API로 합성곱 신경망 구현하기
케라스 API를 사용해 합성곱 신경망 모델을 만들어 패션 MNIST 이미지를 분류해보자
▶ 08-1장
합성곱 신경망의 개념을 설명한 단원이라 키워드만 정리하고 넘어가겠습니다
- 합성곱
- 밀집층과 비슷하게 입력과 가중치를 곱하고 절편을 더하는 선형 계산
- 밀집층과 달리 각 합성곱은 입력 전체가 아닌 일부만 사용하여 선형 계산을 수행함
- 합성곱 층의 필터는 밀집층의 뉴런에 해당 (필터의 가중치와 절편을 종종 커널이라고 부름)
- 자주 사용되는 커널의 크기는 (3, 3) 또는 (5, 5), 커널의 깊이는 입력의 깊이와 같음
- 특성 맵
- 합성곱 층이나 풀링 층의 출력 배열을 의미
- 필터 하나가 하나의 특성 맵을 만든다 (합성곱 층에서 5개의 필터를 적용하면 5개의 특성 맵이 만들어짐)
- 패딩
- 합성곱 층의 입력 주위에 추가한 0으로 채워진 픽셀
- 밸리드 패딩 - 패딩을 사용하지 않는 것
- 세임 패딩 - 합성곱 층의 출력 크기를 입력과 동일하게 만들기 위해 입력에 패딩을 추가하는 것
- 스트라이드
- 합성곱 층에서 필터가 입력 위를 이동하는 크기
- 일반적으로 스트라이드는 1픽셀을 사용합
- 풀링
- 가중치가 없고 특성 맵의 가로세로 크기를 줄이는 역할을 수행
- 대표적으로 최대풀링과 평균풀링이 있으며 (2, 2) 풀링으로 입력을 절반으로 줄임
▶ 패션 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()
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)
완전 연결 신경망에서는 입력 이미지를 밀집층에 연결하기 위해 일렬로 펼쳐야 했지만, 합성곱 신경망은 2차원 이미지를 그대로 사용하기 때문에 일렬로 펼치지 않아도 된다.
- 입력 이미지는 항상 깊이(채널) 차원이 있어야 하기 때문에 reshape() 메서드를 사용하여 마지막에 차원을 추가했다.
▶ 합성곱 신경망 만들기
먼저 Sequential 클래스의 객체를 만들고 첫 번째 합성곱 층인 Conv2D를 추가해주자
# 첫번째 합성곱
model = keras.Sequential()
model.add(keras.layers.Conv2D(32, kernel_size=3, activation='relu',
padding='same', input_shape=(28, 28, 1)))
- 32개의 필터 사용, 커널의 크기는 (3, 3), 렐루 활성화 함수와 세임 패딩을 사용
- 완전 연결 신경망에서처럼 케라스 신경망 모델의 첫번째 층에서 입력의 차원을 지정해주어야 함
(2, 2) 풀링 층을 추가해주면
# 풀링 층 추가
model.add(keras.layers.MaxPooling2D(2)) #(14, 14, 32)
- 32개의 필터를 사용했으므로 이 특성 맵의 깊이는 32가 된다.
- 최대 풀링을 통과한 특성 맵의 크기는 (14, 14, 32)가 될 것이다.
첫 번째 합성곱-풀링 층 다음에 두 번째 합성곱-풀링 층을 추가해보자
# 두번째 합성곱-풀링 층 추가
model.add(keras.layers.Conv2D(64, kernel_size=3, activation='relu',
padding='same'))
model.add(keras.layers.MaxPooling2D(2)) #(7, 7, 64)
- 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'))
- 은닉층과 출력층 사이에 드롭아웃을 넣어 과대적합을 막아주도록 하였다.
- 은닉층은 100개의 뉴런을 사용하고 활성화 함수는 렐루 함수이다.
- 출력층의 활성화 함수는 소프트맥스이다. (다중 분류 문제이므로)
케라스 모델의 구성을 마쳤으니 모델 구조를 출력해보자
# 모델 구조 출력
model.summary()
plot_model() 함수를 사용하면 층의 구성을 그림으로 표현해준다.
keras.utils.plot_model(model)
- 왼쪽에는 층의 이름, 오른쪽에는 클래스가 쓰여있다.
- 맨 처음의 InputLayer 클래스는 케라스가 자동으로 추가해주는 것으로 입력층의 역할을 해준다.
plot_model() 함수의 show_shapes를 True로 설정하면 입력과 출력의 크기도 표시해준다.
keras.utils.plot_model(model, show_shapes=True)
- to_file 매개변수에 파일 이름을 지정하면 출력한 이미지를 파일로 저장한다.
- dpi 매개변수로 해상도를 지정할 수도 있다.
패션 MNIST 데이터에 적용할 합성곱 신경망 모델의 구성을 마쳤다.
이제 모델을 컴파일하고 훈련해보자
▶ 모델 컴파일과 훈련
케라스 API의 장점은 딥러닝 모델의 종류나 구성 방식에 상관없이 컴파일과 훈련 과정이 같다는 점이다.
Adam 옵티마이저, ModelCheckpoint, EarlyStopping 콜백을 사용하여 훈련해보자
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()
- 검증 세트에 대한 손실이 감소하다가 정체되기 시작하고 훈련 세트에 대한 손실은 점점 낮아지고 있다.
- 9번째 에포크가 최적의 에포크라고 생각 된다.
EarlyStopping 클래스에서 restore_best_weights 매개변수를 True로 지정했기 때문에
현재 model 객체가 최적의 모델 파라미터로 복원되어 있다. (best-cnn-model.h5 파일 다시 읽을 필요 X)
이번에는 세트에 대한 성능을 평가해보자
# 성능 평가
model.evaluate(val_scaled, val_target)
- fit() 메서드의 출력 중 9번째 에포크의 출력과 동일하다.
- EarlyStopping 콜백이 model 객체를 최상의 모델 파라미터로 잘 복원한 것으로 보인다.
이번에는 predict() 메서드를 사용해 훈련된 모델을 사용하여 새로운 데이터에 대해 예측을 만들어 볼 것이다.
( 첫 번째 샘플을 처음 본 이미지라고 가정 후 해보자 )
# 첫 번째 이미지 확인
plt.imshow(val_scaled[0].reshape(28, 28), cmap='gray_r')
plt.show()
- 흑백 이미지는 깊이 차원이 없으므로 (28, 28, 1) 크기를 (28, 28)로 바꿔주었다.
이 핸드백 이미지에 대해 모델은 어떤 예측을 만드는지 확인해보자
# 10개 클래스에 대한 예측 확률 출력
preds = model.predict(val_scaled[0:1])
print(preds)
plt.bar(range(1, 11), preds[0])
plt.xlabel('class')
plt.ylabel('prob.')
plt.show()
- 9번째 값일 확률이 1이고 다른 값들은 거의 0에 가까운 것을 확인할 수 있다.
실제로 9번째 값이 무엇인지 MNIST 데이터셋의 레이블을 통해 확인해보면
import numpy as np
classes = ['티셔츠', '바지', '스웨터', '드레스', '코트', '샌달', '셔츠', '스니커즈', '가방', '앵클 부츠']
print(classes[np.argmax(preds)]) # 가방
이 샘플을 '가방'으로 잘 예측한 것을 확인할 수 있다.
마지막으로 이 모델을 실전에 투입했을 때 어느 정도의 성능을 낼 수 있는지 확인해보자
훈련 세트와 검증 세트에서 했던 것처럼 픽셀값의 범위를 0~1 사이로 바꾼 뒤 reshape 해주었다.
test_scaled = test_input.reshape(-1, 28, 28, 1) / 255.0
model.evaluate(test_scaled, test_target)
- 이 모델을 실전에 투입하여 패션 아이템을 분류한다면 약 92%의 성능을 기대할 수 있을 것으로 보인다
▶ 진행한 순서
- 케라스의 Conv2D 클래스를 사용해 32개, 64개의 필터를 둔 2개의 합성곱 층을 추가
- 두 합성곱 층 다음에는 모두 (2, 2) 크기의 최대 풀링 층을 배치
- 두 번째 풀링 층을 통과한 특성 맵을 펼친 다음 밀집 은닉층에 연결
- 최종적으로 10개의 뉴런을 가진 출력층에서 샘플에 대한 확률을 출력
- 7장에서 사용했던 조기 종료 기법을 사용해 모델 훈련 후 검증 세트로 최적의 에포크에서 성능을 평가
- 샘플 데이터 하나를 선택해 예측 클래스를 출력하는 방법을 살펴봄
- 테스트 세트를 사용해 최종 모델의 일반화 성능을 평가
'Python > ML & DL 공부' 카테고리의 다른 글
[DL] 09-2 순환 신경망으로 IMDB 리뷰 분류하기 (+ 09-1) (0) | 2022.04.07 |
---|---|
[DL] 08-3 합성곱 신경망의 시각화 (0) | 2022.04.06 |
[DL] 07-3 신경망 모델 훈련 (0) | 2022.03.27 |
[DL] 07-2 심층 신경망 (0) | 2022.03.23 |
[DL] 07-1 인공 신경망 (0) | 2022.03.20 |