딥러닝(AI학습 38)

이유진·2024년 7월 8일

--20.신경망 모델훈련.ipynb--

"""
● 사이킷런의 머신러닝 알고리즘 :

  • 모델의 구조는 어느정도 '고정'
  • 좋은 성능을 위해 '매개변수를 조정'하고 훈련하는 과정 반복
    ● 딥러닝 :
  • 모델의 구조를 '직접' 만든다 (층을 추가, 뉴런의 개수, 활성화 함수 설정 등...)
    """
    None

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import os

import tensorflow as tf
from tensorflow import keras

실행마다 동일한 결과를 얻기 위해 keras에 랜덤시드 사용

tf.keras.utils.set_random_seed(42)
tf.config.experimental.enable_op_determinism()

base_path = r'/content/drive/MyDrive/dataset'

손실 곡선

History

fit() 의 리턴값은 History 객체

훈련과정에서 계산한 지표, 즉, loss와 accuracy 값이 담겨있다.

이 값들을 사용하여 시각화 할 수 있다.

FashionMNIST 다운로드

(train_input, train_target), (test_input,test_target) = keras.datasets.fashion_mnist.load_data()

train_scaled = train_input / 255.0

from sklearn.model_selection import train_test_split

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) # 앞의 hidden layer 뒤에 하나의 층 추가하기
model.add(keras.layers.Dense(10, activation='softmax'))
return model

model = model_fn()
model.summary()

model.compile(loss='sparse_categorical_crossentropy', metrics='accuracy')

↓ verbose= 는 훈련과정 출력을 조절

1 : (기본값) 이전에 보았던 것처럼 에포크마다 진행막대와 손실등의 지표를 출력

2 : 진행막대를 빼고 출력.

0 : 훈련과정을 나타내지 않기.

history = model.fit(train_scaled, train_target, epochs=5, verbose=0)

type(history.history) # 훈련 측정값이 담겨있는 history dict 가 있다.

history.history.keys()

history.history['loss']

history.history['accuracy']

Training Loss (훈련손실)

epoch 진행에 따른 loss를 시각화

plt.plot(history.history['loss'])
plt.xlabel('epoch')
plt.ylabel('loss')
plt.show()

epoch 진행에 따른 accuracy를 시각화

plt.plot(history.history['accuracy'])
plt.xlabel('epoch')
plt.ylabel('accuracy')
plt.show()

epoch를 20으로 늘려서 손실그래프 그려보자

model = model_fn()

model.compile(loss='sparse_categorical_crossentropy', metrics='accuracy')
history = model.fit(train_scaled, train_target, epochs=20, verbose=0)

history.history['loss']

epoch 진행에 따른 loss를 시각화

plt.plot(history.history['loss'])
plt.xlabel('epoch')
plt.ylabel('loss')
plt.show()

epoch 진행에 따른 accuracy를 시각화

plt.plot(history.history['accuracy'])
plt.xlabel('epoch')
plt.ylabel('accuracy')
plt.show()

Validation Loss (검증손실)

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

history.history.keys()

epoch 진행에 따른 loss를 시각화

plt.plot(history.history['loss'])
plt.plot(history.history['val_loss'])
plt.xlabel('epoch')
plt.ylabel('loss')
plt.legend(['train', 'val'])
plt.show()

"""
↑ 초기에는 검증손실이 감소하는듯 하나 다섯번째 에포크 즈음부터 다시 '상승'하기 시작
=> 과대적합 (overfit) 모델이 만들어지기 시작하는거다.

검증손실이 '상승'하는 시점을 가능한 뒤로 늦추 수 있으면 그리 해야 높은 모델 성능을 기대할 수 있다.
"""
None

규제 (regularization) 방법은 잠시 뒤에...

옵티마이저의 하이퍼 파라미터를 조정하여 과대적합을 완화시킬 수 있는지 알아보자.

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

"""
↑ validation loss 가 많이 억제되고 있는게 보인다. 대체로 10번째 에포크까지는 전반적인 감소 추세인듯하다.

※ 그 밖에도 learning_rate=, momentum= 값들을 여러가지로 조정해보며 도전해보자
"""
None

Dropout 드롭아웃

신경망에서 사용되는 대표적인 규제 (regularization)

'훈련과정' 에서 층에 있는 일부 뉴런을 랜덤하게 꺼서 (뉴런의 출력을 0으로 만들어)

과대 적합을 억제한다.

왜 과대적합을 막을까?

- 특정 뉴런에 과대하게 의존하는 것을 줄여, 신경망이 더 안정적인 예측을 만들 수 있다.

- 마치 여러개의 신경망을 '앙상블' 하는 것처럼 동작한다. 이 또한 과대적합을 막아주는 좋은 기법.

model.summary()

model = model_fn(keras.layers.Dropout(rate=0.3)) # 30% 정도 드롭아웃
model.summary()

"""
Dropout

  • 모델 파라미터 없다!
  • 입력과 출력의 크기 같다.
  • 출력을 0으로 만들뿐 출력배열의 크기는 동일.
  • '훈련' 할때만 동작.
  • '평가', '예측' 할때는 동작하지 않는다.
    """
    None

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으로 하여 다시 훈련해야한다...
"""
None

모델 저장과 복원

  • save_weight(), save()
  • load_weight(), load()

훈련모델 저장

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

keras 모델은

save_weights() : 훈련된 파라미터를 저장

기본적으로 이 메소드는 '텐서플로의 체크포인트 포맷' 으로 저장하지만

확장자가 '.h5' 일 경우 'HDF5' 포맷으로 저장합니다.

model.save_weights(os.path.join(base_path, 'model-weights.h5'))

save() : 모델구조와 모델파라미터를 함께 저장

기본적으로 텐서플로의 SavedModel 포맷으로 저장

확장자가 '.h5' 일 경우 'HDF5' 포맷으로 저장합니다.

model.save(os.path.join(base_path, 'model-whole.h5'))

복원1 : 새로운 모델 만들고 load_weights()

load_weights() 를 사용하려면 save_weights() 로 저장했던 모델과 정확히 같은 구조를 가져야 한다.

model = None
model = model_fn(keras.layers.Dropout(0.3))
model.load_weights(os.path.join(base_path,'model-weights.h5'))

  • predict()

keras 의 predict는 샘플마다 10개의 클래스에 대한 확률을 리턴할거다.

검증세트 개수가 12,000개 -> predict()의 결과는 (12000, 10)

model.predict(val_scaled).shape

model.predict(val_scaled)[0]

val_labels = np.argmax(model.predict(val_scaled), axis=-1)
val_labels.shape

val_labels

np.mean(val_labels == val_target)

복원2: load_model() 모델 + weight

model = None
model = keras.models.load_model(os.path.join(base_path, 'model-whole.h5'))

model.summary()

model.evaluate(val_scaled, val_target)

evaluate()

load_model()은 모델 파라미터 뿐만 아니라 모델 구조와 옵티마이저의 상태까지 모두 복원하기 때문에

evaluate() 사용 가능

Callback 콜백

keras.callbacks

20번의 에포크 결과 검증손실이 상승하는 부분이 보였다면..

과대 적합이 되지 않을 만큼의 에포크로 다시 훈련해야 했다.

꼭 이렇게 모델을 두번 훈련해야하나?

'콜백'을 사용해서 해결하자!

"""
콜백(callback) 은 '훈련 과정 중간' 에 어떤 작업을 수행할 수 있게 하는 객체
fit() 의 callbacks= 매개변수로 전달하여 사용 (list)
"""
None

  • ModelCheckPoint

에포크마다 모델을 저장

model = model_fn(keras.layers.Dropout(0.3))
model.compile(optimizer='adam', loss='sparse_categorical_crossentropy', metrics='accuracy')

checkpoint_cb = keras.callbacks.ModelCheckpoint(os.path.join(base_path, 'best-model.h5'),
save_best_only=True)

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

model = None
model = keras.models.load_model(os.path.join(base_path, 'best-model.h5'))
model.evaluate(val_scaled, val_target)

Early stopping (조기종료)

검증 손실이 상승하기 시작하면 그 이후에는 과대 적합이 더 커지기 때문에

훈련을 계속 할 필요 없다. 과대적합이 발생하기 전에 훈련을 미리 중지하는 것을 early stopping이라 한다.

  • patience = 2 <- 2번 연속 검증 손실이 향상되지 않으면 훈련을 중지.
  • restore_best_weight = True <- 가장 낮은 검증손실을 낸 파라미터로 복원

model = model_fn(keras.layers.Dropout(0.3))
model.compile(optimizer='adam', loss='sparse_categorical_crossentropy', metrics='accuracy')

checkpoint_cb = keras.callbacks.ModelCheckpoint(os.path.join(base_path, '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])

몇번째 에포크에서 훈련 중지했나?

early_stopping_cb.stopped_epoch

"""
9
epoch 횟수는 0부터 시작. 9는 10번째 에포크에서 훈련 중지되었다.
patience = 2 를 지정 최상의 모델은 8번째 에포크 (7) 일거다
"""
None

plt.plot(history.history['loss'])
plt.plot(history.history['val_loss'])
plt.xlabel('epoch')
plt.ylabel('loss')
plt.legend(['train', 'val'])
plt.show()

model.evaluate(val_scaled, val_target)

profile
독해지자

0개의 댓글