[혼공딥] CHAPTER 07-3 신경망 모델 훈련
손실 곡선
fit 메서드는 metrics에 지정된 정확도 값과 손실값을 각 에포크마다 계산해서 저장해주고 이를 history 객체로 반환해준다.
model.compile(loss='sparse_categorical_crossentropy', metrics='accuracy')
history = model.fit(train_scaled, train_target, epochs=5, verbose=0)
print(history.history.keys())
history 객체를 받아서 history 객체에 있는 history 속성의 key값을 확인하면 loss와 accuracy가 있음을 확인할 수 있다.
dict_keys(['loss', 'accuracy'])
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()
이번엔 정확도를 그래프로 그려본다.
에포크가 증가할수록 정확도가 커지고 있음을 알 수 있다.
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()
이번엔 에포크를 20으로 증가시켜 훈련한 다음 그래프를 그려보면
여전히 손실값이 감소하고 있음을 볼 수 있다. ->훈련세트에 맞는 모델이 잘 만들어지고 있다
하지만, 훈련세트에 잘 맞는 모델을 만드는 것은 좋지만, 실전에 투입했을 때 새로운 데이터에는 일반화 되지 못하는 경향이 있다. 검증세트나 테스트세트에서 적절한 일반화 성능을 얻을 수 있어야한다.
케라스에서 검증 손실을 자동으로 계산해주는 매개변수인 validation_data를 제공한다.
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))
validation_data에 특성과 타깃값을 전달하면 각 에포크마다 훈련 손실과 정확도도 함께 계산해준다.
print(history.history.keys())
history 객체의 key를 출력해보면
dict_keys(['loss', 'accuracy', '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()
훈련세트의 손실은 에포크가 증가함에 따라 감소하지만, 검증세트는 초반에는 좋아진 듯 보이나 후반에 갈수록 손실값이 커짐을 볼 수 있다.
즉, 이 모델은 훈련세트에는 잘 맞지만 검증세트에는 잘 맞지 않는 과대적합된 모델이라고 할 수 있다.
드롭아웃
드롭아웃(dropout) : 신경망 모델에만 있는 규제 방법이다.
훈련할 때만 은닉층의 임의의 뉴런 하나를 없는 것처럼 계산하지 않는다.
각 샘플을 처리할 때마다, 랜덤하게 은닉층의 뉴런의 몇 퍼센트의 계산을 끈다.
->뉴런이 훈련세트에 잘 맞지 않게 될 것이다. 어떤 한 특정 뉴런에 과도하게 의존하게 되는 현상을 막을 수 있다.
->테스트할 때나 평가할 때는 모든 뉴런을 사용하고 훈련할 때만 임의의 뉴런을 끈다
드롭아웃은 층으로 라이브러리에서 제공한다.
Dropout이라고 층이 제공된다. 은닉층, 밀집층 다음에 드롭아웃층을 추가하면 은닉층의 뉴런의 계산이 일부가 랜덤하게 적용되지 않는다.
tf.keras.layers.Dropout(rate, noise_shape=None, seed=None, **kwargs)
rate : 삭제 할 입력 단위의 비율, 0~1 사이의 값을 갖는다. 드롭아웃 하지 않는 뉴런의 출력은 1/(1-r)만큼 증가시켜 출력의 총합이 같도록 만든다
model = model_fn(keras.layers.Dropout(0.3))
model.summary()
100개의 뉴런 중 30개가 계산에 참여하지 않도록 했다.
Model: "sequential_8"
_________________________________________________________________
Layer (type) Output Shape Param #
=================================================================
flatten_5 (Flatten) (None, 784) 0
_________________________________________________________________
dense_15 (Dense) (None, 100) 78500
_________________________________________________________________
dropout (Dropout) (None, 100) 0
_________________________________________________________________
dense_16 (Dense) (None, 10) 1010
=================================================================
Total params: 79,510
Trainable params: 79,510
Non-trainable params: 0
_________________________________________________________________
밀집층과 드롭아웃층의 출력 크기가 큰 것을 볼 수 있다. 드롭아웃은 계산을 수행하는게 아니라 몇 개의 계산을 끄는 용도이다.
학습되는 파라미터가 없어 모델 파라미터가 0이다.
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번만 하고 모델을 저장한 다음 모델을 다시 불러서 20번 더 훈련시키고 이런 식으로 할 수 있다.
- save, load_model
model.save('model-whole.h5')
- 가중치뿐만 아니라 모델 구조 자체를 저장하고 싶을 때 사용(모델 구조와 가중치를 한꺼번에 저장)
model = keras.models.load_model('model-whole.h5')
- 모델과 가중치 파일을 불러오는 메서드는 keras.models.load_model함수이다.
- 모델의 구조에 대해 신경 쓸 필요가 없다. -> 매우 간편!
model = keras.models.load_model('model-whole.h5')
model.evaluate(val_scaled, val_target)
모델 전체를 읽은 다음 검증 세트의 정확도를 출력해본다.
375/375 [==============================] - 1s 3ms/step - loss: 0.3254 - accuracy: 0.8791
[0.32539382576942444, 0.8790833353996277]
- save_weights, load_weights
model.save_weights('model-weights.h5')
- 가중치만 저장을 한다. -> 모델 구조를 동일하게 만들어야하기 때문에 모델 구조를 알고 있을 때만 사용 가능
- 모델의 구조는 저장하지 않고, 모델 파라미터(가중치, 절편 등)만 저장을 한다.
- 확장자가 '.h5'일 경우 HDF5포맷으로 저장 된다
* HDF5 : 대용량 데이터를 저장하고 구성하도록 설계된 파일 형식(계층적 데이터 형식)
model.load_weights('model-weights.h5')
- 저장한 모델을 불러오는 메서드
import numpy as np
val_labels = np.argmax(model.predict(val_scaled), axis=-1)
predict 매서드는 각 샘플마다 10개의 확률을 출력해준다.
10개의 확률 중 가장 높은 값의 인덱스를 찾아서 그 인덱스를 예측 클래스로 사용하면 된다. 그렇게 하기 위해 argmax()함수를 사용했다.
np.argmax(입력 배열, axis = None, out = None)
axis=-1로 두면 마지막 인덱스가 된다. 여기에서 검증세트는 2차원 배열이기 때문에 axis=1로 지정한 것과 동일하다.
열을 따라 각 샘플에서 가장 큰 값을 가진 인덱스를 반환한다.
axis = 1이면 열을 따라 각 행의 최댓값의 인덱스를 선택하고, axis = 0이면 행을 따라 각 열의 최댓값의 인덱스를 선택한다
print(np.mean(val_labels == val_target))
argmax()로 고른 인덱스(val_labels)와 타깃(val_target)을 비교한다. 두 배열에서 각 위치의 값이 같으면 1이 되고 다르면 0이 된다.
이를 평균하면 정확도가 된다
0.8790833333333333
같은 모델을 저장하고 불러들였기 때문에 동일한 정확도를 얻었다.
콜백
콜백(callback) : 훈련 과정 중간에 어떤 작업을 수행할 수 있게 하는 객체
keras.callbacks 패키지 아래에 있는 클래스들 이다.
tf.keras.callbacks.ModelCheckpoint(
filepath, monitor='val_loss', verbose=0, save_best_only=False,
save_weights_only=False, mode='auto', save_freq='epoch',
options=None, **kwargs
)
- 첫 번째 매개변수에 저장할 파일을 지정
- monitor 매개변수 : 모니터링 할 지표를 지정, 기본값은 'val_loss'로 검증 손실을 관찰
- save_best_only : 기본값은 True로 모델의 가중치와 절편만 저장, False는 전체 모델을 저장
ModelCheckpoint 콜백은 모델을 훈련하는 도중에 가장 낮은 손실값이 되는 모델 가중치를 저장해준다.
검증 점수가 증가하기 시작하면 그 이후에는 과대적합이 되어서 굳이 훈련을 계속할 필요가 없다.
-> 검증 세트의 손실이 증가하기 시작하면 중지하는 조기 종료(early stopping)이라고 하며 케라스 콜백 함수로 제공된다.
tf.keras.callbacks.EarlyStopping(
monitor="val_loss",
min_delta=0,
patience=0,
verbose=0,
mode="auto",
baseline=None,
restore_best_weights=False,
)
- monitor 매개변수 : 모니터링 할 지표를 지정, 기본값은 'val_loss'로 검증 손실을 관찰
- patience 매개변수 : 모델이 더 이상 향상되지 않고 지속할 수 있는 최대 에포크 횟수를 지정
- restore_best_weights : 최상의 모델 가중치를 복원할지 지정, 기본값 False
EarlyStopping 콜백과 ModelCheckpoint 콜백을 같이 사용하면 가장 낮은 검증 손실의 모델을 파일에 저장하고 검증 손실이 다시 상승할 때 훈련을 중지할 수 있다.
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')
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])
patience는 검증세트 손실이 증가했다가 감소할수도 있어서 몇 번 에포크까지 참고 기다릴 것인가의 횟수, 검증세트의 손실이 계속 두 번의 에포크 이상 증가하면 훈련을 멈추고 가중치를 에포크가 가장 낮은 위치로 되돌린다.
몇 번째 에포크에서 멈췄는지 확인할 수 있다.
print(early_stopping_cb.stopped_epoch)
에포크가 0부터 시작하기 때문에 13번째 에포크에서 멈췄다는 것을 알 수 있다.
patience를 2로 지정해 놓았기 때문에 11번째 에포크가 낮은 검증 손실을 가지고 있음을 알 수 있다.
12
즉, 열 한번째 에포크에서 가장 낮은 손실을 기록했고, 열 세번째 에포크에서 훈련이 중지되었음을 알 수 있다.
○ 조기종료 기법을 사용하면 에포크 횟수를 크게 지정해도 알아서 멈추기 때문에 상관이 없다
○ 컴퓨터 자원과 시간을 아낄 수 있고 ModelCheckpoint 콜백과 함께 사용하면 최상의 모델을 자동으로 저장해주어서 편리하다
model.evaluate(val_scaled, val_target)
조기종료로 얻은 모델을 사용해 검증세트에 대한 성능을 확인해보면
375/375 [==============================] - 1s 2ms/step - loss: 0.3203 - accuracy: 0.8851
[0.32029989361763, 0.8850833177566528]
이 나온 것을 알 수 있다.