혼자 공부하는 머신러닝+딥러닝 책을 바탕으로 공부한 내용입니다.
CH7 딥러닝 시작 ③
최상의 신경망 모델 얻기
케라스 API를 사용해 모델을 훈련하는데 필요한 다양한 도구들을 알아보자
▶ 손실 곡선
먼저, 패션 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 / 255.0
train_scaled, val_scaled, train_target, val_target = train_test_split(
train_scaled, train_target, test_size=0.2, random_state=42)
이전과는 다르게 모델을 만드는 함수를 정의한 후 사용할 것이다.
# 모델 만들기
def model_fn(a_layer=None):
model = keras.Sequential()
model.add(keras.layers.Flatten(input_shape=(28,28)))
model.add(keras.layers.Dense(100, activation='relu'))
if a_layer:
model.add(a_layer)
model.add(keras.layers.Dense(10, activation='softmax'))
return model
- if 구문의 역할은 model_fn() 함수에 a_layer 매개변수로 케라스 층을 추가하면 은닉층 뒤에 하나의 층을 추가하는 것
model = model_fn()
model.summary()
model.compile(loss='sparse_categorical_crossentropy', metrics='accuracy')
history = model.fit(train_scaled, train_target, epochs=5, verbose=0)
print(history.history.keys()) #dict_keys(['loss', 'accuracy'])
- fit() 메서드의 결과를 history 변수에 담았다
- fit() 메서드는 History 클래스 객체를 반환한다. (훈련 과정에서 계산한 지표인 손실, 정확도 값이 저장되어 있음)
에포크 횟수에 따른 loss와 accuracy를 그래프로 그려보자
# loss 그래프
import matplotlib.pyplot as plt
plt.plot(history.history['loss'])
plt.xlabel('epoch')
plt.ylabel('loss')
plt.show()
# 정확도 그래프
plt.plot(history.history['accuracy'])
plt.xlabel('epoch')
plt.ylabel('accuracy')
plt.show()
- 에포크 횟수가 많아질수록 손실이 감소하고 정확도가 향상한다.
에포크 횟수를 20으로 늘려서 모델을 훈련하고 손실 그래프를 그려보자
# 에포크 20으로 늘리기
model = model_fn()
model.compile(loss='sparse_categorical_crossentropy', metrics='accuracy')
history = model.fit(train_scaled, train_target, epochs=20, verbose=0)
plt.plot(history.history['loss'])
plt.xlabel('epoch')
plt.ylabel('loss')
plt.show()
- 손실이 잘 감소하는 것을 확인할 수 있다.
▶ 검증 손실
에포크에 따른 과대적합, 과소적합을 파악하려면 훈련 세트 뿐만 아니라 검증 세트의 그래프도 그려봐야한다.
( 인공 신경망 모델이 최적화하는 대상은 정확도가 아니라 손실 함수이다.)
에포크마다 검증 손실을 계산하기 위해 fit() 메서드에 검증 데이터를 전달하였다.
model = model_fn()
model.compile(loss='sparse_categorical_crossentropy', metrics='accuracy')
history = model.fit(train_scaled, train_target, epochs=20, verbose=0,
validation_data=(val_scaled, val_target))
print(history.history.keys())
- val_loss에는 검증 세트에 대한 손실, val_accuracy에는 검증 세트에 대한 정확도가 들어있다.
과대/과소적합 문제를 조사하기 위해 훈련 손실과 검증 손실을 그래프로 그려보자
# 훈련 손실, 검증 손실 그래프
plt.plot(history.history['loss'])
plt.plot(history.history['val_loss'])
plt.xlabel('epoch')
plt.ylabel('loss')
plt.legend(['train', 'val'])
plt.show()
- 검증 손실이 감소하다가 2.5번째 에포크 부터 손실이 다시 상승하는 것을 확인할 수 있다.
- 검증 손실이 상승하는 시점을 뒤로 늦추면 손실이 줄어들뿐만 아니라 검증 세트에 대한 정확도도 증가한다.
옵티마이저 하이퍼파라미터를 조정하여 과대적합을 완화시킬 수 있는지 확인해보자
기본 RMSprop 옵티마이저 대신 Adam 옵티마이저를 적용해 볼 것이다.
# Adam 옵티마이저 적용
model = model_fn()
model.compile(optimizer='adam', loss='sparse_categorical_crossentropy', metrics='accuracy')
history = model.fit(train_scaled, train_target, epochs=20, verbose=0,
validation_data=(val_scaled, val_target))
plt.plot(history.history['loss'])
plt.plot(history.history['val_loss'])
plt.xlabel('epoch')
plt.ylabel('loss')
plt.legend(['train', 'val'])
plt.show()
- 과대적합이 훨씬 줄어든 것을 확인할 수 있다.
▶ 드롭아웃
- 은닉층에 있는 뉴런의 출력을 랜덤하게 꺼서 과대적합을 막는 기법
- 드롭아웃은 훈련 중에 적용되며 평가나 예측에서는 적용하지 않음(텐서플로우는 이를 자동으로 처리함)
- 케라스에서는 keras.layers 패키지 아래 Dropout 클래스로 제공함
앞서 정의한 model_fn() 함수에 드롭아웃 객체를 전달하여 층을 추가해보자
model = model_fn(keras.layers.Dropout(0.3))
model.summary()
- 30% 정도를 드롭아웃 함
- Dropout은 훈련되는 모델 파라미터가 없는 것을 확인할 수 있다.
이전처럼 훈련 손실과 검증 손실의 그래프를 그려 비교해보자
model.compile(optimizer='adam', loss='sparse_categorical_crossentropy', metrics='accuracy')
history = model.fit(train_scaled, train_target, epochs=20, verbose=0,
validation_data=(val_scaled, val_target))
plt.plot(history.history['loss'])
plt.plot(history.history['val_loss'])
plt.xlabel('epoch')
plt.ylabel('loss')
plt.legend(['train', 'val'])
plt.show()
- 과대적합이 확실히 줄어든 것을 확인할 수 있다.
이 모델은 20번의 에포크 동안 훈련했으므로 다소 과대적합 되어 있다.
과대적합 되지 않은 모델을 얻기 위해서 에포크 횟수를 10으로 하고 다시 훈련해보자
▶ 모델 저장과 복원
# 에포크 횟수 10으로 변경
model = model_fn(keras.layers.Dropout(0.3))
model.compile(optimizer='adam', loss='sparse_categorical_crossentropy', metrics='accuracy')
history = model.fit(train_scaled, train_target, epochs=10, verbose=0,
validation_data=(val_scaled, val_target))
① 케라스 모델은 훈련된 모델의 파라미터를 저장하는 save_weights() 메서드를 제공한다.
② 모델 구조와 모델 파라미터를 함께 저장하는 save() 메서드도 제공한다.
# 훈련된 모델의 파라미터 저장
model.save_weights('model-weights.h5')
# 모델 구조, 모델 파라미터 저장
model.save('model-whole.h5')
!ls -al *.h5
- 두 파일이 잘 만들어졌다.
①, ②를 사용하여 모델을 복원해볼 것이다.
① 훈련하지 않은 새로운 모델을 만들고 model-weights.h5 파일에서 훈련된 모델 파라미터를 읽어 사용하기
# model-weights.h5 사용
model = model_fn(keras.layers.Dropout(0.3))
model.load_weights('model-weights.h5')
- load_weights() 메서드를 사용하려면 save_weights() 메서드로 저장했던 모델과 정확히 같은 구조를 가져야 한다.
이 모델의 검증 정확도를 확인해보자
케라스의 predict() 메서드는 사이킷런과 달리 샘플마다 10개의 클래스에 대한 확률을 반환한다.
10개의 확률 중 가장 큰 값의 인덱스를 골라 타깃 레이블과 비교해 정확도를 계산해보자
import numpy as np
val_labels = np.argmax(model.predict(val_scaled), axis=-1)
np.mean(val_labels == val_target) # 타깃값도 0부터 시작함 (같으면 1, 다르면 0)
- 넘파이 argmax() 함수는 배열에서 가장 큰 값의 인덱스를 반환한다.
- argmax() 함수의 axis=-1은 배열의 마지막 차원을 따라 최댓값을 고른다
- axis=1이면 열을 따라 각 행의 최댓값의 인덱스를 선택, axis=0이면 행을 따라 각 열의 최댓값의 인덱스를 선택
② 모델 전체를 model-whole.h5 파일에서 읽어 바로 사용하기
# model-whole.h5 사용
model = keras.models.load_model('model-whole.h5')
model.evaluate(val_scaled, val_target)
- 모델이 저장된 파일을 읽을 때는 load_model() 함수 사용
- 같은 모델을 저장하고 다시 불렀기 때문에 위와 동일한 정확도를 얻었다.
우리는 20번의 에포크동안 모델을 훈련한 후 검증 점수가 상승하는 지점을 확인했다.
그 다음 모델을 과대적합 되지 않는 에포크만큼(10) 다시 훈련했다.
이렇게 모델을 두 번씩 훈련하지 않고 한 번에 끝낼 수 있는 방법은 콜백을 사용하는 것이다!
▶ 콜백
- 케라스 모델을 훈련하는 도중 어떤 작업을 수행할 수 있도록 도와주는 도구
- 대표적으로 최상의 모델을 자동으로 저장해주거나 검증 점수가 더 이상 향상되지 않으면 일찍 종료해주는 것이 있다.
- 조기종료 - 검증 점수가 더 이상 감소하지 않고 상승하여 과대적합이 일어나면 훈련을 멈추는 기법 (계산 비용, 시간을 절약할 수 있음)
model = model_fn(keras.layers.Dropout(0.3))
model.compile(optimizer='adam', loss='sparse_categorical_crossentropy',
metrics='accuracy')
# callback
checkpoint_cb = keras.callbacks.ModelCheckpoint('best-model.h5',
save_best_only=True)
model.fit(train_scaled, train_target, epochs=20, verbose=0,
validation_data=(val_scaled, val_target),
callbacks=[checkpoint_cb])
- ModelCheckpoint 클래스의 객체 checkpoint_cb를 만든 후 callbacks 매개변수에 리스트로 전달
- best-model.h5에 최상의 검증 점수를 낸 모델이 저장된다.
이 모델을 load_model()로 다시 읽어서 예측을 수행해보자
model = keras.models.load_model('best-model.h5')
model.evaluate(val_scaled, val_target)
- ModelCheckpoint 콜백이 가장 낮은 검증 점수의 모델을 자동으로 저장해주었다.
여전히 20번의 에포크를 수행하는데, 검증 점수가 상승하기 시작하면 과대적합이 커지므로 훈련을 계속 할 필요가 없다. 따라서 과대적합이 시작되기 전에 훈련을 미리 중지하는 조기 종료를 사용할 것이다.
- 케라스는 조기 종료를 위한 EarlyStopping 콜백을 제공함
- patience=2로 지정하면 2번 연속 검증 점수가 향상되지 않았을 때 훈련을 중지함
- restore_best_weights=True로 지정하면 가장 낮은 검증 손실을 낸 모델 파라미터로 되돌림
# 조기 종료
model = model_fn(keras.layers.Dropout(0.3))
model.compile(optimizer='adam', loss='sparse_categorical_crossentropy',
metrics='accuracy')
checkpoint_cb = keras.callbacks.ModelCheckpoint('best-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, verbose=0,
validation_data=(val_scaled, val_target),
callbacks=[checkpoint_cb, early_stopping_cb])
- EarlyStopping 콜백과 ModelCheckpoint 콜백을 함께 사용하면 가장 낮은 검증 손실의 모델을 파일에 저장하고 검증 손실이 다시 상승할 때 훈련을 중지할 수 있다.
- 훈련을 중지한 뒤 현재 모델의 파라미터를 최상의 파라미터로 되돌린다.
print(early_stopping_cb.stopped_epoch)
- stoppen_epoch 속성에서 몇 번째 에포크에서 훈련이 중지되었는지 확인할 수 있다.
- 13번째 에포크에서 훈련이 중지되었다. (11번째 에포크가 최상의 모델, 에포크 횟수는 0부터 시작함)
훈련 손실과 검증 손실을 출력해서 확인해보자
plt.plot(history.history['loss'])
plt.plot(history.history['val_loss'])
plt.xlabel('epoch')
plt.ylabel('loss')
plt.legend(['train', 'val'])
plt.show()
- 11번째 에포크에서 가장 낮은 손실을 기록했고 13번째 에포크에서 훈련이 중지되었다.
마지막으로 조기 종료로 얻은 모델을 사용해 검증 세트에 대한 성능을 확인해보면
model.evaluate(val_scaled, val_target)
▶ 정리
- 과대적합을 막기 위해 신경망에서 즐겨 사용하는 대표적인 규제 방법인 드롭아웃을 알아보았다.
- 케라스는 훈련된 모델의 파라미터를 저장하고 다시 불러오는 메서드를 제공한다.
- 모델 전체를 파일에 저장하고 파일에서 모델을 만들수도 있다.
- 과대적합이 되기 전의 에포크를 수동으로 찾아 모델을 다시 훈련하는 대신 콜백을 사용하면 자동으로 최상의 모델을 유지할 수 있다.
'Python > ML & DL 공부' 카테고리의 다른 글
[DL] 08-3 합성곱 신경망의 시각화 (0) | 2022.04.06 |
---|---|
[DL] 08-2 합성곱 신경망을 사용한 이미지 분류 (+ 08-1) (0) | 2022.04.06 |
[DL] 07-2 심층 신경망 (0) | 2022.03.23 |
[DL] 07-1 인공 신경망 (0) | 2022.03.20 |
[ML] 06-3 주성분 분석 (0) | 2022.03.12 |