학습 목표
- 딥러닝과 인공 신경망 알고리즘을 이해하고 텐서플로를 사용해 간단한 인공 신경망 모델을 만들어 봅니다.
from tensorflow import keras
(train_input, train_target), (test_input, test_target) = keras.datasets.fashion_mnist.load_data()
print(train_input.shape, train_target.shape)
▶
(60000, 28, 28) (60000,)
print(test_input.shape, test_target.shape)
▶
(10000, 28, 28) (10000,)
import matplotlib.pyplot as plt
fig, axs = plt.subplots(1,10,figsize=(10,10))
for i in range(10) :
axs[i].imshow(train_input[i], cmap='gray_r')
axs[i].axis('off') # on일시 격자와 격자의 크기가 나타남
plt.show();
결과 이미지
이미지별 target 확인
print([train_target[i] for i in range(10)])
▶
[9, 0, 0, 3, 0, 2, 7, 2, 5, 5]
import numpy as np
print(np.unique(train_target, return_counts=True))
▶
(array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9], dtype=uint8), array([6000, 6000, 6000, 6000, 6000, 6000, 6000, 6000, 6000, 6000]))
레이블 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
---|---|---|---|---|---|---|---|---|---|---|
패션 아이템 | 티셔츠 | 바지 | 스웨터 | 드레스 | 코트 | 샌달 | 셔츠 | 스니커즈 | 가방 | 앵클 부츠 |
패션 MNIST 데이터의 train set은 60,000개나 되기 때문에
샘플을 하나씩 꺼내서 모델을 훈련하는 방법이 더 효율적으로 보인다.
따라서 확률적 경사 하강법을 사용할 것이다.
패션 MNIST의 경우 각 픽셀은 0~255 사이의 정숫값을 가지므로 픽셀마다 256으로 나누어 0~1 사이의 값으로 정규화한다.
사이킷런 모델은 2차원 numpy 배열만 사용가능하기에 3차원인 데이터를 2차원으로 reshape해주어야 한다.
train_scaled = train_input / 255.0
train_scaled = train_scaled.reshape(-1, 28*28)
print(train_scaled.shape)
▶
(60000, 784)
from sklearn.model_selection import cross_validate
from sklearn.linear_model import SGDClassifier
sc = SGDClassifier(loss='log', max_iter=5, random_state=42)
scores = cross_validate(sc, train_scaled, train_target, n_jobs=-1) # 기본값은 1이고 -1로 모든 코어를 사용
print(np.mean(scores['test_score']))
▶
0.8195666666666668
❗ discussion
로지스틱 회귀 공식은 다음과 같다
이 식을 패션 MNIST 데이터에 맞게 변형하면 다음과 같다.
9개의 레이블이 있으므로 9개의 z값을 구하는 선형 방정식이 존재한다.
모든 레이블의 선형 방정식은 동일한 784개의 픽셀값을 그대로 사용하지만,
해당 레이블에 대한 출력을 계산하기 위해 가중치(계수)와 절편은 다른 값을 사용한다.
입력층이라고 부르는 각 픽셀에 있는 데이터를 통해 ~을 계산하고 이를 바탕으로 클래스를 예측하기 때문에 신경망의 최종 값을 만든다는 의미에서 출력층이라고 부른다.(지금은 중간 은닉층은 생략한다.)
import tensorflow as tf
from tensorflow import keras
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)
print(train_scaled.shape, train_target.shape)
▶
(48000, 784) (48000,)
print(val_scaled.shape, val_target.shape)
▶
(12000, 784) (12000,)
dense = keras.layers.Dense(10, activation='softmax', input_shape=(784,)) # 뉴런 개수(클래스 개수), 적용할 함수, 입력 크기(input data 크기)
# 이 밀집층을 가진 신경망 모델 만들기
model = keras.Sequential(dense)
밀집층 모식도
Sequential 클래스의 객체로 만들어진 인공 신경망 모델 모식도
model.compile(loss='sparse_cateorical_crossentropy', metrics='accuracy')
❗ discussion
'sparse~' 손실 함수는 원-핫 인코딩을 하지 않은 타깃값에 대한 크로스 엔트로피 손실함수이다.
원-핫 인코딩을 진행한 경우엔 'categorical_crossentropy'로 지정해준다.
model.fit(train_scaled, train_target, epochs=5)
▶
Epoch 1/5
1500/1500 [==============================] - 6s 2ms/step - loss: 0.6146 - accuracy: 0.7919
Epoch 2/5
1500/1500 [==============================] - 3s 2ms/step - loss: 0.4796 - accuracy: 0.8387
Epoch 3/5
1500/1500 [==============================] - 3s 2ms/step - loss: 0.4583 - accuracy: 0.8475
Epoch 4/5
1500/1500 [==============================] - 3s 2ms/step - loss: 0.4447 - accuracy: 0.8520
Epoch 5/5
1500/1500 [==============================] - 3s 2ms/step - loss: 0.4364 - accuracy: 0.8551
<keras.callbacks.History at 0x7f46cc3c2690>
결과
epoch가 지남에 따라 정확도가 점점 높아진다.
test set에 대한 모델 성능 확인
model.evaluate(val_scaled, val_target)
▶
375/375 [==============================] - 1s 2ms/step - loss: 0.4467 - accuracy: 0.8487
[0.4467184543609619, 0.8487499952316284]
결과
train set의 정확도보다 약간 낮은 84%의 정확도를 나타냈다.
로지스틱 회귀 모델의 정확도인 82%보다 약간 높게 나타난다.
사이킷런 모델과 케라스 모델의 구성 코드 차이
인공신경망
생물학적 뉴런에서 영감을 받아 만든 머신러닝 알고리즘으로 종종 딥러닝이라고도 불린다.
텐서플로
구글이 만든 딥러닝 라이브러리로, CPU와 GPU를 사용해 인공 신경망 모델 구축과 서비스에 필요한 다양한 도구를 제공한다.
밀집층
가장 간단한 인공 신경망의 층으로 여러 종류의 층이 있으며 밀집층에서는 뉴런들이 모두 연결되어 있기 때문에 완전 연결 층이라고도 부른다.
특히 출력층에 밀집층을 사용할 때는 분류하려는 클래스와 동일한 개수의 뉴런을 사용한다.
원-핫 인코딩
정수값을 배열에서 해당 정수 위치의 원소만 1이고 나머지는 모두 0으로 변환한다.
학습 목표
- 인공 신경망에 층을 여러개 추가하여 패션 MNIST 데이터셋을 분류하면서
케라스로 심층 신경망을 만드는 방법을 자세히 배웁니다.
from tensorflow import keras
(train_input, train_target), (test_input, test_target) = keras.datasets.fashion_mnist.load_data()
from sklearn.model_selection import train_test_split
train_scaled = train_input / 255.0
train_scaled = train_scaled.reshape(-1,28*28)
train_scale, val_scaled, train_target, val_target = train_test_split(train_scaled, train_target, test_size=0.2, random_state=42)
❗ discussion
클래스 분류를 위한 확률을 출력하기 위해 활성화 함수를 사용하기에
회귀의 출력은 임의의 숫자이므로 활성화 함수를 적용할 필요가 없다.
Dense 층의 activation 매개변수에 아무런 값을 지정하지 않고 출력층의 선형 방정식 계산을 그대로 출력한다.
# dense1 : 100개의 뉴런을 가진 밀집층인 은닉층. 활성화 함수는 'sigmoid', dlqfurrkqtdms (784,)
dense1 = keras.layers.Dense(100,activation='sigmoid', input_shape=(784,))
# dense2 : 10개의 클래스를 구분하는 10개의 뉴런을 가진 출력층. 활성화 함수는 'softmax
dense2 = keras.layers.Dense(10, activation='softmax')
# 은닉층 -> 출력층 순서로 기입
model = keras.Sequential([dense1,dense2])
model.summary()
▶
Model: "sequential"
_________________________________________________________________
Layer (type) Output Shape Param #
=================================================================
dense_2 (Dense) (None, 100) 78500 # 784*100 + 100(절편)
dense_3 (Dense) (None, 10) 1010 # 100*10 + 10(절편)
=================================================================
Total params: 79,510
Trainable params: 79,510
Non-trainable params: 0
_________________________________________________________________
model = keras.Sequential([keras.layers.Dense(100, activation='sigmoid', input_shape=(784,), name='hidden'),
keras.layers.Dense(10, activation='softmax', name='output')], name = '패션 MNIST 모델')
model.summary()
▶
Model: "패션 MNIST 모델"
_________________________________________________________________
Layer (type) Output Shape Param #
=================================================================
hidden (Dense) (None, 100) 78500
output (Dense) (None, 10) 1010
=================================================================
Total params: 79,510
Trainable params: 79,510
Non-trainable params: 0
_________________________________________________________________
❗ discussion
생성자 안에서 바로 Dense 클래스의 객체를 만들면 모델의 이름과 층 이름을 설정해줄 수 있어서 많은 층을 사용할때 유용하다.
model = keras.Sequential()
model.add(keras.layers.Dense(100,activation='sigmoid', input_shape=(784,)))
model.add(keras.layers.Dense(100,activation='softmax'))
model.summary()
▶
Model: "sequential_1"
_________________________________________________________________
Layer (type) Output Shape Param #
=================================================================
dense_6 (Dense) (None, 100) 78500
dense_7 (Dense) (None, 100) 10100
=================================================================
Total params: 88,600
Trainable params: 88,600
Non-trainable params: 0
_________________________________________________________________
model.compile(loss='sparse_categorical_crossentropy', metrics='accuracy')
model.fit(train_scaled, train_target, epochs=5)
▶
Epoch 1/5
1500/1500 [==============================] - 6s 2ms/step - loss: 0.6142 - accuracy: 0.7984
Epoch 2/5
1500/1500 [==============================] - 3s 2ms/step - loss: 0.4115 - accuracy: 0.8519
Epoch 3/5
1500/1500 [==============================] - 3s 2ms/step - loss: 0.3766 - accuracy: 0.8631
Epoch 4/5
1500/1500 [==============================] - 3s 2ms/step - loss: 0.3535 - accuracy: 0.8725
Epoch 5/5
1500/1500 [==============================] - 3s 2ms/step - loss: 0.3379 - accuracy: 0.8776
<keras.callbacks.History at 0x7fb6407ed7d0>
은닉층의 활성화 함수로 시그모이드 함수를 쓰게 된다면
오른쪽, 왼쪽 끝의 그래프가 완만하기 때문에 올바른 출력을 만드는데 대응이 신속하지 못하고, 특히 층이 많을 수록 그 효과가 누적되어 학습이 어려워진다.
이러한 단점을 극복하기 위해 렐루라는 활성화 함수가 등장하였다.
입력이 양수일 경우 그대로 통과시키고, 음수의 경우 0으로 만드는 함수이다.
model = keras.Sequential()
model.add(keras.layers.Flatten(input_shape=(28,28)))
model.add(keras.layers.Dense(100, activation='relu'))
model.add(keras.layers.Dense(10, activation='softmax'))
model.summary()
▶
Model: "sequential_2"
_________________________________________________________________
Layer (type) Output Shape Param #
=================================================================
flatten_1 (Flatten) (None, 784) 0
dense_12 (Dense) (None, 100) 78500
dense_13 (Dense) (None, 10) 1010
=================================================================
Total params: 79,510
Trainable params: 79,510
Non-trainable params: 0
_________________________________________________________________
결과
flatten 클래스의 모델 파라미터는 0개이다.
flatten 층을 모델에 추가하면, 앞서 모델들에서 볼 수 없던 입력값의 차원을 확인 가능하다.
reshape하지 않은 훈련 데이터들 다시 준비해서 모델을 훈련해보자.
(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)
model.compile(loss='sparse_categorical_crossentropy', metrics='accuracy')
model.fit(train_scaled, train_target, epochs=5)
▶
Epoch 1/5
1500/1500 [==============================] - 4s 2ms/step - loss: 0.5292 - accuracy: 0.8123
Epoch 2/5
1500/1500 [==============================] - 3s 2ms/step - loss: 0.3903 - accuracy: 0.8588
Epoch 3/5
1500/1500 [==============================] - 4s 2ms/step - loss: 0.3525 - accuracy: 0.8749
Epoch 4/5
1500/1500 [==============================] - 3s 2ms/step - loss: 0.3345 - accuracy: 0.8812
Epoch 5/5
1500/1500 [==============================] - 3s 2ms/step - loss: 0.3185 - accuracy: 0.8863
<keras.callbacks.History at 0x7fb6300a8950>
# 검증 set 성능 확인
model.evaluate(val_scaled, val_target)
▶
375/375 [==============================] - 1s 3ms/step - loss: 0.3520 - accuracy: 0.8783
[0.3519577980041504, 0.878250002861023]
model.compile(optimizer='sgd', loss='sparse_categorical_crossentropy', metrics='accuracy')
# 위 방법은 tensorflow.keras.optimizers 패키지 아래 구현된 SGD 클래스와 동일하다.
sgd = keras.optimizers.SGD() # learning_rate=0.1 과 같은 식으로 매개변수 지정 가능
model.compile(optimizer=sgd, loss='sparse_categorical_crossentropy', metrics='accuracy')
❗ discussion
학습률의 개념
주로 사용하는 옵티마이저들은 다음과 같다
기본 경사 하강법 옵티마이저는 모두 SGD 클래스에서 제공한다.
SGD 클래스의 momentum 매개변수의 기본값은 0이다.
이를 0보다 큰 값으로 지정하면 이전의 그레디언트를 가속도처럼 사용하는 모멘텀 최적화를 사용한다. 보통 momentum 매개변수는 0.9 이상을 지정한다.
sgd = keras.optimizers.SGD(momentum = 0.9, nesterov = True)
모델이 최적점에 가까이 갈수록 학습률(보폭)을 낮춘다면
이를 통해 안정적으로 최적점에 수렴할 것이다.
이런 학습률을 적응적 학습률이라고 한다.
이 방식은 학습률 매개변수를 튜닝하는 수고를 덜 수 있다.
RMSprop(옵티마이저 기본값)
rmsprop = keras.optimizers.RMSprop()
model.compile(optimizer=rmsprop, loss='sparse_categorical_crossentropy',
metrics = 'accuracy')
adagrad = keras.optimizers.RMSprop()
model.compile(optimizer=rmsprop, loss='sparse_categorical_crossentropy',
metrics = 'accuracy')
model = keras.Sequential()
model.add(keras.layers.Flatten(input_shape=(28,28)))
model.add(keras.layers.Dense(100, activation='relu'))
model.add(keras.layers.Dense(10, activation='softmax'))
model.compile(optimizer='adam', loss='sparse_categorical_crossentropy', metrics='accuracy')
model.fit(train_scaled, train_target, epochs=5)
▶
[31]
17초
model = keras.Sequential()
model.add(keras.layers.Flatten(input_shape=(28,28)))
model.add(keras.layers.Dense(100, activation='relu'))
model.add(keras.layers.Dense(10, activation='softmax'))
model.compile(optimizer='adam', loss='sparse_categorical_crossentropy', metrics='accuracy')
model.fit(train_scaled, train_target, epochs=5)
Epoch 1/5
1500/1500 [==============================] - 4s 2ms/step - loss: 0.5287 - accuracy: 0.8166
Epoch 2/5
1500/1500 [==============================] - 3s 2ms/step - loss: 0.3953 - accuracy: 0.8585
Epoch 3/5
1500/1500 [==============================] - 3s 2ms/step - loss: 0.3553 - accuracy: 0.8707
Epoch 4/5
1500/1500 [==============================] - 3s 2ms/step - loss: 0.3264 - accuracy: 0.8805
Epoch 5/5
1500/1500 [==============================] - 3s 2ms/step - loss: 0.3070 - accuracy: 0.8859
<keras.callbacks.History at 0x7fb5c204ebd0>
model.evaluate(val_scaled, val_target)
375/375 [==============================] - 1s 3ms/step - loss: 0.3459 - accuracy: 0.8747
[0.3459301292896271, 0.874666690826416]
심층 신경망
2개 이상의 층을 포함한 신경망.
렐루 함수
이미지 분류 모델의 은닉층에 많이 사용하는 활성화 함수이다.
시그모이드 함수는 층이 많을수록 활성화 함수의 양쪽 끝에서 변화가 작기 때문에 학습이 어려워진다.
옵티마이저
신경망의 가중치와 절편을 학습하기 위한 알고리즘 또는 방법으로,
케라스에는 다양한 경사 하강법 알고리즘이 구현되어 있다.
대표적으로 SGD, 네스테로프 모멘텀, RMSprop, Adam 등이 있다.
학습 목표
- 인공 신경망 모델을 훈련하는 모범 사례와 필요한 도구들을 살펴본다.
- 이런 도구들을 다뤄보면서 텐서플로와 케라스 API에 더 익숙해지자.
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)
▶
Downloading data from https://storage.googleapis.com/tensorflow/tf-keras-datasets/train-labels-idx1-ubyte.gz
32768/29515 [=================================] - 0s 0us/step
40960/29515 [=========================================] - 0s 0us/step
Downloading data from https://storage.googleapis.com/tensorflow/tf-keras-datasets/train-images-idx3-ubyte.gz
26427392/26421880 [==============================] - 0s 0us/step
26435584/26421880 [==============================] - 0s 0us/step
Downloading data from https://storage.googleapis.com/tensorflow/tf-keras-datasets/t10k-labels-idx1-ubyte.gz
16384/5148 [===============================================================================================] - 0s 0us/step
Downloading data from https://storage.googleapis.com/tensorflow/tf-keras-datasets/t10k-images-idx3-ubyte.gz
4423680/4422102 [==============================] - 0s 0us/step
4431872/4422102 [==============================] - 0s 0us/step
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'])
❗ discussion
verbose 매개변수는 훈련 과정 출력을 조절한다.
기본값은 1로, 에포크마다 진행 막대와 함께 손실 등의 지표가 출력된다.
0은 훈련과정 표시 X, 2는 진행 막대 빼고 출력을 의미한다.
import matplotlib.pyplot as plt
plt.plot(history.history['loss'])
plt.xlabel('epoch')
plt.ylabel('loss')
plt.show();
결과 이미지
matplotlib 활용 epoch별 정확도 확인
import matplotlib.pyplot as plt
plt.plot(history.history['accuracy'])
plt.xlabel('epoch')
plt.ylabel('accuracy')
plt.show();
결과 이미지
결과
에포크마다 손실이 감소하고 정확도가 향상한다.
에포크 횟수를 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();
결과 이미지
결과
에포크가 5일때보다 손실이 잘 감소하며 최종 손실도 작다.
❗ discussion
인공 신경망 모델이 최적화하는 대상은 정확도가 아니라 손실 함수기 때문에,
손실 감소에 비례하여 정확도가 높아지지는 않는다.
따라서 모델의 훈련도를 판단하려면 정확도보다는 손실 함수의 값을 확인해야한다.
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'])
plt.plot(history.history['loss'], label='train set')
plt.plot(history.history['val_loss'], label='validation set')
plt.xlabel('epoch')
plt.ylabel('loss')
plt.legend()
plt.show();
❗ discussion
검증 손실이 상승하는 시점을 뒤로 늦추면 검증 set의 손실과 정확도가 모두 증가할 것이다.
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();
훈련 과정에서 층에 있는 일부 뉴런을 임의로 꺼서 가중치에 대한 과한 의존성을 줄일 수 있다.
샘플 마다 드롭아웃되는 뉴런이 달라지게 된다. 꺼지는 뉴런의 수는 drop rate라는 하이퍼 파라미터로 조정가능하다.
평가나 예측을 수행할 때는 훈련된 모델이 자동으로 드롭아웃을 적용하지 않는다.
dropout 층 추가
model = model_fn(keras.layers.Dropout(0.3))
model.summary()
▶
Model: "sequential_6"
_________________________________________________________________
Layer (type) Output Shape Param #
=================================================================
flatten_6 (Flatten) (None, 784) 0
dense_12 (Dense) (None, 100) 78500
dropout_1 (Dropout) (None, 100) 0
dense_13 (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'], label='train')
plt.plot(history.history['val_loss'], label='val')
plt.xlabel('epoch')
plt.ylabel('loss')
plt.legend()
plt.show();
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')
# 저장된 파일 확인
!ls -al *.h5
▶
-rw-r--r-- 1 root root 333448 Sep 5 17:56 model-weights.h5
-rw-r--r-- 1 root root 982664 Sep 5 17:56 model-whole.h5
model = model_fn(keras.layers.Dropout(0.3))
model.load_weights('model-weights.h5')
import numpy as np
val_labels = np.argmax(model.predict(val_scaled), axis=-1) # 배열에서 가장 큰 값의 인덱스를 반환
print(np.mean(val_labels == val_target))
▶
0.8814166666666666
model = keras.models.load_model('model-whole.h5')
model.evaluate(val_scaled, val_target)
▶
375/375 [==============================] - 1s 2ms/step - loss: 0.3330 - accuracy: 0.8814
[0.3329712748527527, 0.8814166784286499]
# 드롭아웃 층 포함한 모델 생성
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])
model = keras.models.load_model('best-model.h5')
model.evaluate(val_scaled, val_target)
▶
375/375 [==============================] - 1s 2ms/step - loss: 0.3178 - accuracy: 0.8889
[0.31775301694869995, 0.8889166712760925]
콜백을 통해 최적의 모델을 찾은 후에는 남은 에포크를 진행할 필요가 없다.
이렇게 과대적합이 시작되기 전에 훈련을 미리 중지하는 것을 조기종료 라고 한다.
keras에는 조기 종료를 위한 EarlyStopping 콜백을 제공한다.
- patience 매개변수 : 검증 점수가 향상되지 않더라도 중지하지 않고 계속 진행할 에포크 횟수이다.
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', 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])
# 몇번째 에포크에서 훈련이 중지되었는지 확인
print(early_stopping_cb.stopped_epoch)
▶
7
plt.plot(history.history['loss'])
plt.plot(history.history['val_loss'])
plt.xlabel('epoch')
plt.ylabel('loss')
plt.legend(['train','val'])
plt.show();
결과 이미지
결과
patience = 2이므로 최상의 모델은 5번째 에포크일 것이고,
실제 훈련 및 검증 손실 그래프에서도 5번째 에포크에서 모델 성능이
손실 점수가 낮고 과대적합과 과소적합의 사이에 형성되어있다.
검증 세트에 대한 성능 확인
model.evaluate(val_scaled, val_target)
▶
375/375 [==============================] - 1s 4ms/step - loss: 0.3399 - accuracy: 0.8757
[0.3398755192756653, 0.8756666779518127]
드롭아웃
은닉층에 있는 뉴런의 출력을 랜덤하게 꺼서 과대적합을 막는 기법으로,
훈련 중에 적용되며 평가나 예측에서는 적용해선 안된다. 텐서플로는 이를 자동으로 처리한다.
콜백
케라스 모델을 훈련하는 도중에 어떤 작업을 수행할 수 있도록 도와주는 도구로
최상의 모델을 자동으로 저장해주거나, 검증 점수가 더이상 향상되지않으면 조기 종료를 할 수 있다.
조기 종료
검증 점수가 더 이상 향상되지 않고 과대적합이 일어나면 훈련을 계속 진행하지 않고 멈추는 기법이다.