기본 데이터 정리 및 model 함수 정의 후 호출
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: 79510 (310.59 KB)
Trainable params: 79510 (310.59 KB)
Non-trainable params: 0 (0.00 Byte)
_________________________________________________________________
지난 모형과 동일한 값이 호출되었다.
verbose: default = 1 (모든 진행과정 표시) = 0 (훈련과정 출력 생략) = 2 (진행막대 생략한 출력)
model.compile(loss='sparse_categorical_crossentropy', metrics='accuracy')
history = model.fit(train_scaled, train_target, epochs=5, verbose=0) #fit을 담는다.
print(history.history.keys())
dict_keys(['loss', 'accuracy'])
이걸 시각화해보자
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()
epoch가 늘어날수록 loss는 줄고 정확도는 늘어난다. -> epoch를 늘리면 다다익선일까?
이번엔 epoch = 20으로 설정하였다
model = model_fn()
model.compile(loss='sparse_categorical_crossentropy', metrics='accuracy')
history = model.fit(train_scaled, train_target, epochs=20, verbose=0) # verbose = 0: 출력 생략!
plt.plot(history.history['loss'])
plt.xlabel('epoch')
plt.ylabel('loss')
plt.savefig('7_3-03', dpi=300)
plt.show()
loss가 줄어드긴 하는데.. 과적합 문제는 정말 없는걸까?
(!) Deep learning model 최적화 대상은 lose function이다. accuracy가 아니다! (정확도를 따로 확인 X)
DL에서는 두 훈련과 검증세트 각각의 손실을 사용하여 over(under)fitting 문제를 파악한다.
validation_data 옵션에 검증에 사용할 train set과 검증 set을 튜플로 입력한다.
RMSprop(default) 최적화 기준으로 실행해보면
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))
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()
val의 global minimum지점은 epoch = 5인 때이다.
다른 최적화기법을 사용하고싶다면 가장 무난한 방법은 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.savefig('7_3-05', dpi=300)
plt.show()
epoch가 7.5인 지점에서 loss가 최소화된다. 해당 loss값과 위 RMSprop경우의 loss값과 비교해보면 Adam의 loss가 적다.
이는 Adam이 이 데이터 셋에 적절하게 잘 맞는 방법이라는 의미다.
Dropout: 출력이 0인 뉴런을 임의로 집어넣어 ovefitting 방지하는 방법
why?) 임의 0 뉴런 삽입 -> 특정 뉴런 의존 불가 -> 모든 input에 더욱 더 세밀한 점검이 적용됨 -> 안정적 예측
정의한 함수 model_fn()에 keras.layers.Dropout()을 적용한다.
model = model_fn(keras.layers.Dropout(0.3)) #전체 데이터의 30% dropout
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: 79510 (310.59 KB)
Trainable params: 79510 (310.59 KB)
Non-trainable params: 0 (0.00 Byte)
_________________________________________________________________
-> dropout은 일부 뉴런 출력을 0으로 만들지만 전체 출력 배열 크기에 영향 無
원칙적으로 훈련 후 평가나 예측시에 dropout을 제외하고 수행해야하지만, Keras는 자동으로 이를 제외하고 수행
그러므로 별다른 추가작업 없이 손실양상을 다시 살펴본다.
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.savefig('7_3-06', dpi=300)
plt.show()
해당그래프에서는 약 12 epoch가 최소 손실이다.
따라서, epoch 수행시 overfitting되므로 그래프 결과에 따른 값(12)만큼 epoch 수행으로 다시 바꿔야한다.
model save and 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))
model.save_weights('model-weights.h5') # <- 가중치만 저장 (파라미터, 절편 등)
model = model_fn(keras.layers.Dropout(0.3)) # <- 불러오기를 위한 model 객체 설정
model.load_weights('model-weights.h5') # <- 불러오기
model.save('model-whole.h5') # <- 모델 구조 포함 전체 저장
(잠깐!) .h5?
- 학습한 model 을 파일로 저장할 때, 보통 .h5 확장자로 저장.
- .h5 확장자는, HDF5 포맷으로 데이터 저장 의미.
- .h5 has model and 가중치(weight) info.
h5파일이 잘 저장되었는지 다음 외부 명령을 통해 알아보자
!ls -al *.h5
#!: 외부명령 실행 때 사용. Is: dir 내 파일 밑 하위 dir 목록나열
#-a: 숨겨진 파일 및 dr 전부 표시.
# -l: 자세한 목록형식 표시,파일 권한 소유자 그룹크기 등
# *.h5: 확장자가 .h5인 파일만 나열. *는 일치하는 모든 파일 선택
'ls' is not recognized as an internal or external command,
operable program or batch file.
만약 위와 같은 오류가 출력되었다면 이 코드로 대체한다
!dir *.h5 # 인식이 안되는 이유는 unix/Linux명령으로 간주되었기 때문. 이걸로 쓰자
Volume in drive C has no label.
Volume Serial Number is D001-AFD7
Directory of C:\Users\사용자이름\Desktop\2023_study\Python\Python_ML
09/18/2023 01:30 PM 4,046,672 best-cnn-model.h5
09/19/2023 09:19 AM 981,176 best-model.h5
09/19/2023 09:18 AM 333,320 model-weights.h5
09/19/2023 09:18 AM 981,176 model-whole.h5
4 File(s) 6,342,344 bytes
0 Dir(s) 261,669,965,824 bytes free
아주 인식이 잘되고 리스트도 잘 보인다
이번엔 훈련되지 않은 새 model을 설정하고 파라미터를 weights h5 파일에서 로드한다.
model = model_fn(keras.layers.Dropout(0.3))
model.load_weights('model-weights.h5')
만약 load에 오류가 있다면 경로에 띄어쓰기 및 한글이 있기 때문이다.
np.argmax()로 최댓값이 있는 자리의 index를 = label로 지정. axis = -1 <- 각 행의 최댓값을 인덱스. (0이면 열의 최댓값)
import numpy as np
val_labels = np.argmax(model.predict(val_scaled), axis=-1)
print(np.mean(val_labels == val_target)) # 예측=실제인지 bool data 평균 구하기(True=1, False=0)
375/375 [==============================] - 0s 770us/step
0.8830833333333333 <- 정답률 약 88퍼
load_model()로 통째로 로드하여 평가해보자
model = keras.models.load_model('model-whole.h5')
# 텐서플로 2.3에서는 버그(https://github.com/tensorflow/tensorflow/issues/42890) 때문에
# compile() 메서드를 호출해야 합니다.
# model.compile(optimizer='adam', loss='sparse_categorical_crossentropy', metrics='accuracy')
model.evaluate(val_scaled, val_target)
375/375 [==============================] - 0s 853us/step - loss: 0.3257 - accuracy: 0.8831
[0.32570087909698486, 0.8830833435058594]
위와 동일한 결과 출력되었다.
Callback(콜백)
근데 지금까지 그래프로 over(under)fitting 여부를 확인하고 재훈련했는데 이 2번 과정 없이 한 번에 하는 방법은 없을까?
이게 바로 Callback(콜백)개념이다. callbacks.Modelcheckpoint(파일이름, 조건)을 객체화한다.
이떄 save_best_only= True 입력시, 최소 손실 최대 정확도 지점만 저장하는 옵션으로 인식된다.
그리고 fit 훈련시, callbacks 옵션을 리스트로 설정한다. 그러면 fit 20번 epoch과정에서 최고 값의 h5가 저장된다.
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]) # 이 부분에 callbacks 설정이 있음을 유의한다! (리스트!)
# 최고값이 저장된 h5를 load하고 평가한다.
model = keras.models.load_model('best-model.h5')
# 텐서플로 2.3에서는 버그(https://github.com/tensorflow/tensorflow/issues/42890) 때문에 compile() 메서드를 호출해야 합니다.
# model.compile(optimizer='adam', loss='sparse_categorical_crossentropy', metrics='accuracy')
model.evaluate(val_scaled, val_target)
375/375 [==============================] - 0s 793us/step - loss: 0.3127 - accuracy: 0.8889
[0.31265023350715637, 0.8889166712760925]
이렇게 20번 epoch과정에서 최고의 결과를 뽑을 수 있다. 그런데 20번 과정중에서 중간에 이미 목표를 달성했다면 중단하는게 더 효율적이지 않았을까? 이 생각으로 나온것이 바로 Early Stopping(조기종료)이다.
조기종료는 값이 향상되지 않을때 종료하는 콜백 기법이다.
그럼 이 두 콜백기법을 같이 적용해보자. 기존 callbacks.ModelCheckpoint에 더불어서 callbacks.EarlyStopping을 추가한다. 여기에 지정된 patience = 2는 2번 연속 점수가 향상되지 않으면 중단함을 의미한다. 또한 retore_best_weights=True로 최상의 model parameter 형태를 유지한다.
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]) #callbacks에 리스트 2개가 추가!
print(early_stopping_cb.stopped_epoch) # 몇 번째의 epoch에서 멈추었는지 출력한다.
13 <- epoch는 0부터시작하므로 14번째에서 중단되었다는 의미이다. patience = 2이므로 최고의 epoch 지점은 12(13번째)이다.
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-07', dpi=300)
plt.show()
model.evaluate(val_scaled, val_target)
model.evaluate(val_scaled, val_target)
model.evaluate(val_scaled, val_target)
375/375 [==============================] - 0s 815us/step - loss: 0.3272 - accuracy: 0.8811
[0.3272111713886261, 0.8810833096504211]
이런식으로 그래프와 결과에서 확인할 수 있다.
'Programming > Python' 카테고리의 다른 글
기본 개념 정리 1 (데이터, EDA, 데이터 전처리) (1) | 2024.01.16 |
---|---|
ETL (Extract, Transform, Load) with command-line tools (1) | 2024.01.03 |
[ML] SGDClassifier (확률적 경사하강법 분류모형) (0) | 2023.09.17 |
[ML] Logistic regression (0) | 2023.09.15 |
[DL] tensorflow, Deep Neural Network(DNN) (0) | 2023.09.15 |