[딥러닝] 신경망 모델 훈련

4 분 소요



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

model = model_fn()

model.summary()
Model: "sequential"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
flatten (Flatten)            (None, 784)               0         
_________________________________________________________________
dense (Dense)                (None, 100)               78500     
_________________________________________________________________
dense_1 (Dense)              (None, 10)                1010      
=================================================================
Total params: 79,510
Trainable params: 79,510
Non-trainable params: 0
_________________________________________________________________



손실 곡선

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 변수에 담는다. 여기서 verbose 매개변수는 훈련 과정 출력을 조절한다. 기본값은 1로 이전 절에서처럼 에포크마다 진행 막대와 함께 손실 등의 지표가 출력된다. 2로 바꾸면 진행 막대를 빼고 출력된다. 그래프로 나타내는 대신 verbose 매개변수를 0으로 지정하여 훈련 과정을 나타내지 않는다.

history 객체에는 훈련 측정값이 담겨 있는 history 딕셔너리가 들어있다. 손실과 정확도가 포함되어 있다.

import matplotlib.pyplot as plt

plt.plot(history.history['loss'])
plt.xlabel('epoch')
plt.ylabel('loss')
plt.savefig('7_3-01', dpi=300)
plt.show()

plt.plot(history.history['accuracy'])
plt.xlabel('epoch')
plt.ylabel('accuracy')
plt.savefig('7_3-02', dpi=300)
plt.show()

그래프를 그리는 코드는 위 코드이다.



검증 손실

에포크에 따른 과대적합과 과소적합을 파악하려면 훈련 세트에 대한 점수뿐만 아니라 검증 세트에 대한 점수도 필요하다.

인공 신경망 모델이 최적화하는 대상은 정확도가 아니라 손실 함수이다. 손실 감소에 비례해 정확도가 높아지지 않는 경우도 있다. 따라서 모델이 잘 훈련되었는지 판단하려면 정확도보다는 손실 함수의 값을 확인하는 것이 더 좋다.

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())
dict_keys(['loss', 'accuracy', 'val_loss', 'val_accuracy'])

history.history 딕셔너리에 검증 세트에 대한 손실과 정확도가 들어있다.

plt.plot(history.history['loss'])
plt.plot(history.history['val_loss'])
plt.xlabel('epoch')
plt.ylabel('loss')
plt.legend(['train', 'val'])
plt.savefig('7_3-04', dpi=300)
plt.show()

그래프를 그리는 코드이다.



드롭아웃

드롭아웃은 훈련 과정에서 층에 있는 일부 뉴런을 랜덤하게 꺼서(즉 뉴런의 출력을 0으로 만들어) 과대적합을 막는다.

이전 층의 일부 뉴런이 랜덤하게 꺼지면 특정 뉴런에 과대하게 의존하는 것을 줄일 수 있고 모든 입력에 대해 주의를 기울여야되 드롭아웃은 과대적합을 막을 수 있다.

다른 해석은 드롭아웃을 사용해 훈련하는 것은 마치 2개의 신경망을 앙상블 하는 것처럼 상상할 수 있다. 앙상블은 과대적합을 막아주는 기법이다.

model = model_fn(keras.layers.Dropout(0.3))

model.summary()
Model: "sequential_4"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
flatten_4 (Flatten)          (None, 784)               0         
_________________________________________________________________
dense_8 (Dense)              (None, 100)               78500     
_________________________________________________________________
dropout (Dropout)            (None, 100)               0         
_________________________________________________________________
dense_9 (Dense)              (None, 10)                1010      
=================================================================
Total params: 79,510
Trainable params: 79,510
Non-trainable params: 0
_________________________________________________________________

모델을 보면 드롭아웃 층은 훈련되는 모델 파라미터가 없다. 또 입력과 출력의 크기가 같다. 일부 뉴런의 출력을 0으로 만들지만 전체 출력 배열의 크기는 바뀌지 않는다.

훈련이 끝난 후 평가나 예측을 수행할 때는 드롭아웃을 적용하지 말아야한다. 훈련된 모든 뉴런을 사용해야 올바른 예측을 수행할 수 있다. 텐서플로와 케라스는 모델을 평가와 예측에 사용할 때는 자동으로 드롭아웃을 적용하지 않는다.



모델 저장과 복원

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

model.save_weights('model-weights.h5')

model.save('model-whole.h5')

케라스 모델은 훈련된 모델의 파라미터를 저장하는 save_weights 메서드를 제공한다. 기본적으로 이 메서드는 텐서플로의 체크포인트 포맷으로 저장하지만 파일의 확장자가 .h5일 경우 HDF5 포맷으로 저장한다.

또한 모델 구조와 모델 파라미터를 함께 저장하는 save 메서드도 제공한다. 기본적으로 이 메서드는 텐서플로의 SavedModel 포맷으로 저장하지만 파일의 확장자가 .h5일 경우 HDF5 포맷으로 저장한다.

저장된 파라미터 로드해서 사용하기

model = model_fn(keras.layers.Dropout(0.3))

model.load_weights('model-weights.h5')

훈련하지 않은 새로운 모델을 만들고 이전에 저장했던 모델 파라미터를 적재한다. 모델 파라미터를 적재하는 조건은 save_weights 메서드로 저장했던 모델과 정확히 같은 구조를 가져야 한다. 그렇지 않으면 에러가 발생한다.

import numpy as np

val_labels = np.argmax(model.predict(val_scaled), axis=-1)
print(np.mean(val_labels == val_target))
0.88125

predict 메서드를 통해 정확도를 구한다.

여기서 evaluate 메서드를 사용하지 않는 이유는 evaluate 메서드를 사용해 정확도를 출력할 수 있지만, 손실을 계산하기 위해서는 먼저 compile 메서드를 실행해야 한다.

모델 전체를 읽어 사용하기

model = keras.models.load_model('model-whole.h5')

model.evaluate(val_scaled, val_target)
375/375 [==============================] - 1s 2ms/step - loss: 0.3307 - accuracy: 0.8813
[0.33071279525756836, 0.8812500238418579]

모델이 저장된 파일을 읽는다.

여기서 evaluate 메서드를 사용할 수 있는 이유는 load_model 함수는 모델 파라미터 뿐만 아니라 모델 구조와 옵티마이저 상태까지 모두 복원하기 때문에 사용할 수 있다.



콜백

콜백(callback)은 훈련 과정 중간에 어떤 작업을 수행할 수 있게 하는 객체로 fit 메서드의 callbacks 매개변수에 리스트로 전달해 사용한다.

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)

model.fit(train_scaled, train_target, epochs=20, verbose=0, 
          validation_data=(val_scaled, val_target),
          callbacks=[checkpoint_cb])

여기서 사용하는 ModelCheckpoint 콜백은 기본적으로 최상의 검증 점수를 만드는 모델을 저장한다.

model = keras.models.load_model('best-model.h5')

model.evaluate(val_scaled, val_target)
375/375 [==============================] - 1s 2ms/step - loss: 0.3230 - accuracy: 0.8865
[0.3230079412460327, 0.8865000009536743]

모델 훈련이 끝난 후 저장된 모델을 로드해 예측을 한다.

중간에 훈련을 그만두면 컴퓨터 자원과 시간을 아낄 수 있다. 이렇게 과대적합이 시작되기 전에 훈련을 미리 중지하는 것을 조기 종료라고 부르며, 딥러닝 분야에서 널리 사용한다.

조기 종료는 훈련 에포크 횟수를 제한하는 역할을 하지만 모델이 과대적합되는 것을 막아 주기 때문에 규제 방법 중 하나로 생각할 수도 있다.

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 콜백을 제공한다. 이 콜백의 patience 매개변수는 검증 점수가 향상되지 않더라도 참을 에포크 횟수를 지정한다. 예로 들어 위 코드처럼 2로 지정하면 2번 연속 검증 점수가 향상되지 않으면 훈련을 중지한다. 또 restore_best_weigths 매개변수를 True로 지정하면 가장 낮은 검증 손실을 낸 모델 파라미터로 되돌린다.

EarlyStopping 콜백을 ModelCheckpoint 콜백과 함께 사용하면 가장 낮은 검증 손실의 모델을 파일에 저장하고 검증 손실이 다시 상승할 때 훈련을 중지할 수 있다. 또한 훈련을 중지한 다음 현재 모델의 파라미터를 최상의 파라미터로 되돌린다.

print(early_stopping_cb.stopped_epoch)
9

훈련을 마친 후 몇 번째 에포크에서 훈련이 중지되었는지 확인하기 위해서는 위 코드로 확인할 수 있다.

model.evaluate(val_scaled, val_target)
375/375 [==============================] - 1s 2ms/step - loss: 0.3262 - accuracy: 0.8786
[0.3261638879776001, 0.8785833120346069]

조기 종료로 얻은 모델을 사용해 검증 세트에 대한 성능을 확인한다.