인공신경망 정리, fashion mnist
Fashion MNIST 실습
케라스의 fashion_mnist 데이터의 카테고리를 인공신경망을 활용해 분류해보려 합니다.
처음으로 scikit-leran의 로지스틱 회귀를 통해 간단하게 분류 정확도를 체크하고,
간단한 인공신경망 구축, 은닉층 추가, 옵티마이저 설정과 과적합을 피하기 위한 방법을 추가하며 결과를 확인해보겠습니다.
Keras의 fashion_mnist데이터
-
10개의 패션 아이템 클래스를 가지고 있습니다.
-
28 x 28 픽셀의 흑백 이미지로 이루어져 있습니다.
keras의 fashion mnist 데이터 가져오기
from tensorflow.keras.datasets import fashion_mnist
(X_train, y_train), (X_test, y_test) = fashion_mnist.load_data()
X_train.shape, y_train.shape
((60000, 28, 28), (60000,))
데이터 확인해보기
import matplotlib.pyplot as plt
import numpy as np
# 0 ~ 9번째 X데이터 출력
fig, axs = plt.subplots(1, 10, figsize=(10, 10))
for i in range(10):
axs[i].imshow(X_train[i], cmap='gray_r')
axs[i].axis('off')
plt.show()
# 0 ~ 9번째 Y데이터 출력
print([y_train[i] for i in range(10)])
# 카테고리 번호를 출력해보기(return_counts : 각 카테고리당 몇개씩 들어있는지 확인)
print(np.unique(y_train, return_counts=True))
[9, 0, 0, 3, 0, 2, 7, 2, 5, 5] (array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9], dtype=uint8), array([6000, 6000, 6000, 6000, 6000, 6000, 6000, 6000, 6000, 6000]))
sikit-learn의 로지스틱 회귀로 예측 성능 확인해보기
-
픽셀값은 0 ~ 255까지 이므로 0부터 1사이의 값으로 맞춰주기 위해 X데이터를 255.0으로 나누어줍니다.
-
3차원의 X데이터를 1차원으로 펼쳐주기 위해 reshape를 해줍니다.
X_train = X_train / 255.0
X_test = X_test / 255.0
X_train_scaled = X_train.reshape(-1, 28 * 28)
X_test_scaled = X_test.reshape(-1, 28 * 28)
X_train_scaled.shape, X_test_scaled.shape
((60000, 784), (10000, 784))
-
로지스틱 회귀를 적용하기 위해 loss 매개변수에 손실 함수로 ‘log’를 지정합니다.
-
max_iter 매개변수에 반복횟수로 5를 지정합니다.
-
반복 실행시 결과가 동일하게 나오기 위해 난수 초기값을 random_state 매개변수로 지정합니다.
# scikit_learn의 SGDClassifier 클래스를 활용하여 경사하강법을 이용한 로지스틱 회귀를 사용
from sklearn.linear_model import SGDClassifier
from sklearn.model_selection import cross_validate
sc = SGDClassifier(loss='log', max_iter=5, random_state=42)
scores = cross_validate(sc, X_train_scaled, y_train, n_jobs=-1)
print(np.mean(scores['test_score'])) # 훈련데이터에 있어서 약 82프로의 정확도를 얻음
0.8192833333333333
10개의 클래스를 분류해야하는데 손실함수를 ‘log’로 사용하는 이유
-
SGD는 cross_entropy를 지정하는 곳이 따로 없기 때문에 10개의 클래스를 분류하기 위해 10개의
-
예를 들어, 부츠를 양성, 나머지 9개를 음성으로 분류하여 1개의 모델을 훈련
-
티셔츠를 양성, 나머지 9개를 음성으로 분류하여 1개의 모델을 훈련 -> 이런식으로 10개의 모델을 훈련
-
10개의 계산값이 나오면 softmax함수(이진 분류일땐 sigmoid함수)를 사용하여 확률로 바꿔줍니다.
-
이런식으로 이진분류를 다중분류처럼 사용하는 방법을 OVR(One verses Rest)라고 합니다.
인공 신경망으로 구현
-
로지스틱 회귀 경우 : 픽셀1 x w1 + 픽셀2 x w2 + … + 픽셀784 x w784 + b => 10개의 모델
-
이를 인공 신경망으로 구현해봅니다.
# 검증 데이터 0.2 비율로 분리
from sklearn.model_selection import train_test_split
X_train_scaled, X_val_scaled, y_train, y_val = train_test_split(
X_train_scaled, y_train, test_size=0.2, random_state=42)
X_train_scaled.shape, y_train.shape, X_val_scaled.shape, y_val.shape
((48000, 784), (48000,), (12000, 784), (12000,))
모델 정의
-
Dense 레이어를 사용하여 은닉층과 출력층을 생성합니다.
-
은닉층의 활성화 함수에는 자주 쓰이는 ‘relu’를 사용합니다.
-
출력층의 활성화 함수에는 다중분류이므로 ‘softmax’(이진분류일때는 ‘sigmoid’)를 사용합니다.
모델 컴파일
-
손실 함수
-
이진 분류 : binary_crossentropy
-
다중 분류 : categorical_crossentropy
-
-
sparse_categorical_crossentropy란?
- y데이터의 값은 0 ~ 9 까지의 정수인것을 확인했습니다.
이 정수값을 그대로 사용할 순 없고, 출력층에는 10개의 유닛에서 softmax함수값을 거쳐 10개의 확률값이 나옵니다.
- crossentropy의 공식에 따라 10개의 확률값에 각각 로그를 취하고 타깃값과 곱하게 됩니다.
(샘플이 티셔츠일 확률 : a1 => -log(a1) x target값, -log(a2) x target값, …)
- 여기서, 티셔츠는 첫번째 원소가 1이고 나머지 0([1, 0, 0, … , 0])인 원 핫 인코딩이 되어있어야지만
첫번째 unit을 제외한 나머지 unit에서의 출력값이 모두 0이 곱해져 상쇄되어 티셔츠에 해당되는 뉴런의 출력값만 손실에 반영됩니다.
- 하지만 원 핫 인코딩을 사용하지 않고, Y데이터의 정수값 그대로를 사용하려면 sparse_categorical_crossentropy를 사용합니다.
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Dense
model = Sequential()
# 다중분류이므로 softmax 사용
model.add(Dense(10, activation='softmax', input_shape=(784, )))
model.compile(loss='sparse_categorical_crossentropy',
optimizer='adam',
metrics=['acc'])
# 모델 학습
history = model.fit(X_train_scaled, y_train, epochs=15, batch_size=100,
validation_data=(X_val_scaled, y_val))
Epoch 1/15 480/480 [==============================] - 1s 1ms/step - loss: 0.7469 - acc: 0.7560 - val_loss: 0.5672 - val_acc: 0.8115 Epoch 2/15 480/480 [==============================] - 1s 1ms/step - loss: 0.5187 - acc: 0.8279 - val_loss: 0.5018 - val_acc: 0.8309 Epoch 3/15 480/480 [==============================] - 0s 1ms/step - loss: 0.4758 - acc: 0.8400 - val_loss: 0.4818 - val_acc: 0.8371 Epoch 4/15 480/480 [==============================] - 0s 1ms/step - loss: 0.4544 - acc: 0.8457 - val_loss: 0.4677 - val_acc: 0.8382 Epoch 5/15 480/480 [==============================] - 1s 1ms/step - loss: 0.4401 - acc: 0.8502 - val_loss: 0.4564 - val_acc: 0.8411 Epoch 6/15 480/480 [==============================] - 1s 1ms/step - loss: 0.4293 - acc: 0.8547 - val_loss: 0.4435 - val_acc: 0.8470 Epoch 7/15 480/480 [==============================] - 1s 1ms/step - loss: 0.4214 - acc: 0.8556 - val_loss: 0.4428 - val_acc: 0.8488 Epoch 8/15 480/480 [==============================] - 1s 1ms/step - loss: 0.4149 - acc: 0.8583 - val_loss: 0.4364 - val_acc: 0.8489 Epoch 9/15 480/480 [==============================] - 1s 1ms/step - loss: 0.4089 - acc: 0.8605 - val_loss: 0.4265 - val_acc: 0.8509 Epoch 10/15 480/480 [==============================] - 1s 1ms/step - loss: 0.4049 - acc: 0.8609 - val_loss: 0.4224 - val_acc: 0.8533 Epoch 11/15 480/480 [==============================] - 1s 1ms/step - loss: 0.4026 - acc: 0.8615 - val_loss: 0.4261 - val_acc: 0.8514 Epoch 12/15 480/480 [==============================] - 1s 1ms/step - loss: 0.3986 - acc: 0.8623 - val_loss: 0.4190 - val_acc: 0.8552 Epoch 13/15 480/480 [==============================] - 1s 1ms/step - loss: 0.3952 - acc: 0.8644 - val_loss: 0.4185 - val_acc: 0.8533 Epoch 14/15 480/480 [==============================] - 1s 1ms/step - loss: 0.3925 - acc: 0.8634 - val_loss: 0.4213 - val_acc: 0.8560 Epoch 15/15 480/480 [==============================] - 1s 1ms/step - loss: 0.3901 - acc: 0.8659 - val_loss: 0.4155 - val_acc: 0.8560
# 테스트 데이터를 모델에 적용하여 평가하기 [loss, acc]
model.evaluate(X_test_scaled, y_test)
313/313 [==============================] - 0s 854us/step - loss: 0.4530 - acc: 0.8427
[0.45295682549476624, 0.8427000045776367]
심층 신경망
- 입력층 784개의 뉴런과 은닉층 100개 뉴런, 출력층 10개 뉴런을 생성해보았습니다.
# 모델을 좀 더 깊게(은닉층 추가) 수정
model = Sequential()
model.add(Dense(100, activation='relu', input_shape=(784, ))) # 은닉층
model.add(Dense(10, activation='softmax')) # 출력층
model.compile(loss='sparse_categorical_crossentropy',
optimizer='adam',
metrics=['acc'])
model의 summary() 메서드
-
layer의 정보, 각 layer의 출력의 크기, 파라미터의 갯수를 확인할 수 있습니다.
-
은닉층의 파라미터 갯수 : 784개의 입력 층과 은닉층의 100개의 뉴런이 있고, 각 뉴런은 b(절편)이 존재하므로
100개의 b값이 존재합니다.
따라서, 784 x 100 + 100 = 78500이 됩니다.
- 출력층의 파라미터 갯수 : 은닉층에서 나오는 100개의 출력, 10개의 출력층 뉴런이 있으므로
100 x 10 + 10 = 1010이 됩니다.
model.summary()
Model: "sequential_1" _________________________________________________________________ Layer (type) Output Shape Param # ================================================================= dense_1 (Dense) (None, 100) 78500 dense_2 (Dense) (None, 10) 1010 ================================================================= Total params: 79,510 Trainable params: 79,510 Non-trainable params: 0 _________________________________________________________________
model.fit(X_train_scaled, y_train,
epochs=15, batch_size=128, validation_data=(X_val_scaled, y_val))
Epoch 1/15 375/375 [==============================] - 1s 2ms/step - loss: 0.5933 - acc: 0.7972 - val_loss: 0.4648 - val_acc: 0.8373 Epoch 2/15 375/375 [==============================] - 1s 1ms/step - loss: 0.4281 - acc: 0.8506 - val_loss: 0.4153 - val_acc: 0.8555 Epoch 3/15 375/375 [==============================] - 1s 1ms/step - loss: 0.3919 - acc: 0.8607 - val_loss: 0.3894 - val_acc: 0.8612 Epoch 4/15 375/375 [==============================] - 1s 1ms/step - loss: 0.3631 - acc: 0.8710 - val_loss: 0.3836 - val_acc: 0.8612 Epoch 5/15 375/375 [==============================] - 1s 2ms/step - loss: 0.3414 - acc: 0.8799 - val_loss: 0.3521 - val_acc: 0.8702 Epoch 6/15 375/375 [==============================] - 1s 2ms/step - loss: 0.3286 - acc: 0.8834 - val_loss: 0.3569 - val_acc: 0.8742 Epoch 7/15 375/375 [==============================] - 1s 1ms/step - loss: 0.3144 - acc: 0.8865 - val_loss: 0.3525 - val_acc: 0.8730 Epoch 8/15 375/375 [==============================] - 1s 1ms/step - loss: 0.3006 - acc: 0.8908 - val_loss: 0.3457 - val_acc: 0.8777 Epoch 9/15 375/375 [==============================] - 1s 2ms/step - loss: 0.2924 - acc: 0.8941 - val_loss: 0.3365 - val_acc: 0.8792 Epoch 10/15 375/375 [==============================] - 1s 2ms/step - loss: 0.2800 - acc: 0.8987 - val_loss: 0.3267 - val_acc: 0.8859 Epoch 11/15 375/375 [==============================] - 1s 2ms/step - loss: 0.2723 - acc: 0.9005 - val_loss: 0.3283 - val_acc: 0.8832 Epoch 12/15 375/375 [==============================] - 1s 2ms/step - loss: 0.2653 - acc: 0.9034 - val_loss: 0.3252 - val_acc: 0.8847 Epoch 13/15 375/375 [==============================] - 1s 2ms/step - loss: 0.2559 - acc: 0.9055 - val_loss: 0.3183 - val_acc: 0.8844 Epoch 14/15 375/375 [==============================] - 1s 2ms/step - loss: 0.2508 - acc: 0.9078 - val_loss: 0.3135 - val_acc: 0.8886 Epoch 15/15 375/375 [==============================] - 1s 2ms/step - loss: 0.2430 - acc: 0.9116 - val_loss: 0.3236 - val_acc: 0.8815
<keras.callbacks.History at 0x7f89740a0340>
model.evaluate(X_test_scaled, y_test)
313/313 [==============================] - 0s 714us/step - loss: 0.3513 - acc: 0.8715
[0.3512624502182007, 0.8715000152587891]
Relu함수와 Flatten층
- Relu함수
간단하게 말하면 Max함수입니다.
뉴런의 출력값이 0보다 크면 그대로 출력, 0보다 작으면 0으로 출력해주는 단순한 함수입니다.
- Flatten층
실제로 가중치는 존재하지 않는 층이며, 편의를 위해서 추가하는 층입니다.
위에서 X데이터를 28x28 -> 784의 1차원 배열로 풀어주는 전처리 과정을 포함했습니다.
이러한 작업을 Fletten이 대신 해줍니다.
Flatten의 input_shape에 (28, 28)로 지정해주면 784의 1차원 배열로 전달해줍니다.
summary()에서 보면 Flatten층의 출력 크기는 784로 나오는것을 확인할 수 있습니다.
# Flatten층 사용하기 위해 reshape을 하지 않아도 됩니다.
# 그렇기 위해 귀찮겠지만 다시 데이터를 다시 로드했습니다.
(X_train, y_train), (X_test, y_test) = fashion_mnist.load_data()
X_train = X_train / 255.0
X_test = X_test / 255.0
from tensorflow.keras.layers import Flatten
model = Sequential()
model.add(Flatten(input_shape=(28, 28)))
model.add(Dense(100, activation='relu'))
model.add(Dense(10, activation='softmax'))
model.compile(loss='sparse_categorical_crossentropy',
optimizer='adam',
metrics=['acc'])
model.summary()
Model: "sequential_6" _________________________________________________________________ Layer (type) Output Shape Param # ================================================================= flatten_4 (Flatten) (None, 784) 0 dense_11 (Dense) (None, 100) 78500 dense_12 (Dense) (None, 10) 1010 ================================================================= Total params: 79,510 Trainable params: 79,510 Non-trainable params: 0 _________________________________________________________________
history = model.fit(X_train, y_train, epochs=20,
batch_size=128, validation_split=0.2)
Epoch 1/20 375/375 [==============================] - 1s 2ms/step - loss: 0.5939 - acc: 0.8002 - val_loss: 0.4570 - val_acc: 0.8438 Epoch 2/20 375/375 [==============================] - 1s 1ms/step - loss: 0.4267 - acc: 0.8518 - val_loss: 0.4048 - val_acc: 0.8581 Epoch 3/20 375/375 [==============================] - 1s 2ms/step - loss: 0.3846 - acc: 0.8652 - val_loss: 0.3798 - val_acc: 0.8639 Epoch 4/20 375/375 [==============================] - 1s 1ms/step - loss: 0.3614 - acc: 0.8720 - val_loss: 0.3696 - val_acc: 0.8674 Epoch 5/20 375/375 [==============================] - 1s 1ms/step - loss: 0.3388 - acc: 0.8785 - val_loss: 0.3745 - val_acc: 0.8658 Epoch 6/20 375/375 [==============================] - 1s 2ms/step - loss: 0.3238 - acc: 0.8849 - val_loss: 0.3453 - val_acc: 0.8755 Epoch 7/20 375/375 [==============================] - 1s 2ms/step - loss: 0.3120 - acc: 0.8859 - val_loss: 0.3625 - val_acc: 0.8701 Epoch 8/20 375/375 [==============================] - 1s 2ms/step - loss: 0.2972 - acc: 0.8907 - val_loss: 0.3300 - val_acc: 0.8813 Epoch 9/20 375/375 [==============================] - 1s 2ms/step - loss: 0.2896 - acc: 0.8948 - val_loss: 0.3335 - val_acc: 0.8792 Epoch 10/20 375/375 [==============================] - 1s 2ms/step - loss: 0.2773 - acc: 0.8988 - val_loss: 0.3262 - val_acc: 0.8821 Epoch 11/20 375/375 [==============================] - 1s 2ms/step - loss: 0.2702 - acc: 0.9020 - val_loss: 0.3237 - val_acc: 0.8848 Epoch 12/20 375/375 [==============================] - 1s 1ms/step - loss: 0.2620 - acc: 0.9047 - val_loss: 0.3225 - val_acc: 0.8835 Epoch 13/20 375/375 [==============================] - 1s 2ms/step - loss: 0.2531 - acc: 0.9072 - val_loss: 0.3294 - val_acc: 0.8831 Epoch 14/20 375/375 [==============================] - 1s 1ms/step - loss: 0.2489 - acc: 0.9087 - val_loss: 0.3149 - val_acc: 0.8861 Epoch 15/20 375/375 [==============================] - 1s 2ms/step - loss: 0.2437 - acc: 0.9115 - val_loss: 0.3265 - val_acc: 0.8832 Epoch 16/20 375/375 [==============================] - 1s 2ms/step - loss: 0.2362 - acc: 0.9131 - val_loss: 0.3123 - val_acc: 0.8893 Epoch 17/20 375/375 [==============================] - 1s 2ms/step - loss: 0.2324 - acc: 0.9145 - val_loss: 0.3193 - val_acc: 0.8874 Epoch 18/20 375/375 [==============================] - 1s 2ms/step - loss: 0.2246 - acc: 0.9180 - val_loss: 0.3212 - val_acc: 0.8847 Epoch 19/20 375/375 [==============================] - 1s 2ms/step - loss: 0.2232 - acc: 0.9179 - val_loss: 0.3140 - val_acc: 0.8867 Epoch 20/20 375/375 [==============================] - 1s 2ms/step - loss: 0.2141 - acc: 0.9224 - val_loss: 0.3102 - val_acc: 0.8913
model.evaluate(X_test, y_test)
313/313 [==============================] - 0s 765us/step - loss: 0.3447 - acc: 0.8824
[0.34466418623924255, 0.8823999762535095]
옵티마이저
- 학습률 : 경사하강법을 비유하자면, 산을 내려가면서 최적값을 찾아갈때 이동하는 거리.
학습률을 너무 높게 잡는다면 최적값을 지나칠 수도 있기 때문에 적절한 학습률 조정이 필요합니다.
기본 경사 하강법 옵티마이저
-
SGD
- 기본 학습률은 0.01입니다.
model.compile(optimizer=’sgd’)
- 학습률 조정도 가능합니다. (0.1로 변경)
sgd = tensorflow.keras.optimizers.SGD(learning_rate=0.1)
-
모멘텀
- SGD에서 momentum > 0
sgd = tensorflow.keras.optimizers.SGD(momentum=0.9)
-
네스테로프 모멘텀
- SGD의 설정에서 nesterov = True를 주어 사용합니다.
경사하강법에서 학습률에 따라 최적값을 찾아갈때, 최적값과 멀리 있을 땐 높은 이동 거리로 빠르게 접근하고,
최적값과 가까워질 땐 좁은 거리로 이동하며 최대한 최적값에 수렴해가는게 좋습니다.
이렇게 변화할 수 있는 학습률을 가지고 있는 것이 적응적 학습률 옵티마이저입니다.
적응적 학습률 옵티마이저
-
RMSProp
-
Adam
-
Adagrad
정확도와 손실 시각화해보기
model.fit()의 history는 훈련 과정 중 epoch별로 loss와 acc의 결과를 담고 있습니다.
이를 활용해 모델의 정확도와 손실을 시각화 해볼 수 있습니다.
시각화를 한 결과, epoch가 증가할 수록 훈련 데이터와 검증 데이터의 loss가 같이 줄어들고,
정확도는 동시에 증가하는 것을 볼 수 있습니다.
즉, 과적합이 많이 나타나지 않는 괜찮은 성능의 모델이라 확인할 수 있습니다.
loss = history.history['loss']
val_loss = history.history['val_loss']
acc = history.history['acc']
val_acc = history.history['val_acc']
epochs = range(1, len(loss) + 1)
fig = plt.figure(figsize=(10, 5))
ax1 = fig.add_subplot(1, 2, 1)
ax1.plot(epochs, loss, color='blue', label='train_loss')
ax1.plot(epochs, val_loss, color='orange', label='val_loss')
ax1.set_title('train and val loss')
ax1.set_xlabel('epochs')
ax1.set_ylabel('loss')
ax1.legend()
ax2 = fig.add_subplot(1, 2, 2)
ax2.plot(epochs, acc, color='green', label='train_acc')
ax2.plot(epochs, val_acc, color='red', label='val_acc')
ax2.set_title('train and val acc')
ax2.set_xlabel('epochs')
ax2.set_ylabel('acc')
ax2.legend()
<matplotlib.legend.Legend at 0x7f8940a50910>
드롭아웃
-
가장 많이 사용하는 과적합을 피하는 방법 중 하나입니다.
-
모델을 훈련할 때, 0 ~ 1사이의 확률로 랜덤적으로 뉴런의 계산을 끄고 학습을 진행합니다.
-
제외하지 않은 나머지 뉴런에서만 훈련이 이루어집니다.
-
특정 뉴런의 의존하게 되는 계산을 막을 수 있습니다.
-
모델을 평가할때는 모든 뉴런을 사용합니다.
from tensorflow.keras.layers import Dropout
model = Sequential()
model.add(Flatten(input_shape=(28, 28)))
model.add(Dense(100, activation='relu'))
model.add(Dropout(0.3)) # 0.3비율로 Dropout
model.add(Dense(10, activation='softmax'))
model.compile(loss='sparse_categorical_crossentropy',
optimizer='adam',
metrics=['acc'])
model.summary()
Model: "sequential_9" _________________________________________________________________ Layer (type) Output Shape Param # ================================================================= flatten_7 (Flatten) (None, 784) 0 dense_17 (Dense) (None, 100) 78500 dropout_4 (Dropout) (None, 100) 0 dense_18 (Dense) (None, 10) 1010 ================================================================= Total params: 79,510 Trainable params: 79,510 Non-trainable params: 0 _________________________________________________________________
history = model.fit(X_train, y_train,
epochs=20,
validation_split=0.2)
Epoch 1/20 1500/1500 [==============================] - 2s 1ms/step - loss: 0.5865 - acc: 0.7941 - val_loss: 0.4211 - val_acc: 0.8484 Epoch 2/20 1500/1500 [==============================] - 2s 1ms/step - loss: 0.4389 - acc: 0.8434 - val_loss: 0.4098 - val_acc: 0.8489 Epoch 3/20 1500/1500 [==============================] - 2s 1ms/step - loss: 0.4026 - acc: 0.8535 - val_loss: 0.3723 - val_acc: 0.8635 Epoch 4/20 1500/1500 [==============================] - 2s 1ms/step - loss: 0.3810 - acc: 0.8605 - val_loss: 0.3629 - val_acc: 0.8709 Epoch 5/20 1500/1500 [==============================] - 2s 1ms/step - loss: 0.3659 - acc: 0.8661 - val_loss: 0.3551 - val_acc: 0.8708 Epoch 6/20 1500/1500 [==============================] - 2s 1ms/step - loss: 0.3514 - acc: 0.8716 - val_loss: 0.3622 - val_acc: 0.8639 Epoch 7/20 1500/1500 [==============================] - 2s 1ms/step - loss: 0.3432 - acc: 0.8738 - val_loss: 0.3498 - val_acc: 0.8747 Epoch 8/20 1500/1500 [==============================] - 2s 1ms/step - loss: 0.3318 - acc: 0.8772 - val_loss: 0.3304 - val_acc: 0.8803 Epoch 9/20 1500/1500 [==============================] - 2s 1ms/step - loss: 0.3288 - acc: 0.8775 - val_loss: 0.3341 - val_acc: 0.8805 Epoch 10/20 1500/1500 [==============================] - 2s 1ms/step - loss: 0.3150 - acc: 0.8828 - val_loss: 0.3430 - val_acc: 0.8723 Epoch 11/20 1500/1500 [==============================] - 2s 1ms/step - loss: 0.3119 - acc: 0.8841 - val_loss: 0.3254 - val_acc: 0.8847 Epoch 12/20 1500/1500 [==============================] - 2s 1ms/step - loss: 0.3086 - acc: 0.8851 - val_loss: 0.3414 - val_acc: 0.8780 Epoch 13/20 1500/1500 [==============================] - 2s 1ms/step - loss: 0.3001 - acc: 0.8880 - val_loss: 0.3325 - val_acc: 0.8831 Epoch 14/20 1500/1500 [==============================] - 2s 1ms/step - loss: 0.2963 - acc: 0.8879 - val_loss: 0.3212 - val_acc: 0.8842 Epoch 15/20 1500/1500 [==============================] - 2s 1ms/step - loss: 0.2917 - acc: 0.8915 - val_loss: 0.3254 - val_acc: 0.8861 Epoch 16/20 1500/1500 [==============================] - 2s 1ms/step - loss: 0.2840 - acc: 0.8941 - val_loss: 0.3248 - val_acc: 0.8859 Epoch 17/20 1500/1500 [==============================] - 2s 1ms/step - loss: 0.2847 - acc: 0.8938 - val_loss: 0.3197 - val_acc: 0.8875 Epoch 18/20 1500/1500 [==============================] - 2s 1ms/step - loss: 0.2788 - acc: 0.8957 - val_loss: 0.3405 - val_acc: 0.8804 Epoch 19/20 1500/1500 [==============================] - 2s 1ms/step - loss: 0.2766 - acc: 0.8964 - val_loss: 0.3131 - val_acc: 0.8906 Epoch 20/20 1500/1500 [==============================] - 2s 1ms/step - loss: 0.2723 - acc: 0.8971 - val_loss: 0.3260 - val_acc: 0.8868
시각화를 해보면, Dropout을 사용했을 때 train_loss와 val_loss의 폭이 좀 더 줄어들고,
val_loss의 증가율이 좀 더 낮아졌음을 볼 수 있습니다.
loss = history.history['loss']
val_loss = history.history['val_loss']
acc = history.history['acc']
val_acc = history.history['val_acc']
epochs = range(1, len(loss) + 1)
fig = plt.figure(figsize=(10, 5))
ax1 = fig.add_subplot(1, 2, 1)
ax1.plot(epochs, loss, color='blue', label='train_loss')
ax1.plot(epochs, val_loss, color='orange', label='val_loss')
ax1.set_title('train and val loss')
ax1.set_xlabel('epochs')
ax1.set_ylabel('loss')
ax1.legend()
ax2 = fig.add_subplot(1, 2, 2)
ax2.plot(epochs, acc, color='green', label='train_acc')
ax2.plot(epochs, val_acc, color='red', label='val_acc')
ax2.set_title('train and val acc')
ax2.set_xlabel('epochs')
ax2.set_ylabel('acc')
ax2.legend()
<matplotlib.legend.Legend at 0x7f88e63ab9d0>
모델 저장 및 복원
-
model.save_weights() : 모델의 구조는 저장하지않고, 파라미터만 저장합니다. (가중치, 편향)
-
model.load_weights() : 저장된 모델 객체를 불러옵니다.
-
model.save() : 모델 구조 자체를 모두 저장합니다.
-
model.load_model() : 저장된 모델을 불러옵니다.
# 모델 저장
model.save('./fashion_mnist_model-whole.h5')
model.predict() : 10개의 클래스가 존재하므로 각 샘플마다 10개의 확률을 출력해줍니다.
10개의 확률 중 np.argmax()를 통해 가장 높은 값을 찾아서 그 인덱스를 예측값으로 사용합니다.
얻은 예측값과 실제 테스트 데이터를 비교하여 평균을 내어 정확도를 내줍니다.
아래에서 예측을 수행한 결과 약 88%의 정확도를 확인할 수 있습니다.
# model.predict() : 각 샘플마다 10개의 확률을 출력해주는 메서드
# 10개의 확률 중 가장 높은 값을 찾아서 그 인덱스를 예측 값으로 사용합니다.
predictions = model.predict(X_test)
val_labels = np.argmax(predictions, axis=1) # axis=1 : 행 기준으로 연산 수행(<-> axis=0 : 열 기준)
print(np.mean(val_labels == y_test)) # True : 1, False : 0
0.8728
조기종료
학습이 진행될수록 학습셋의 정확도는 올라가지만 과적합으로 인해 테스트셋의 실험 결과가 점점 나빠질 수 있습니다.
이렇게 학습이 진행되어도 텟트셋 오차가 줄지 않을 경우 학습을 멈추게 하는 함수입니다.
-
patience = 2 : 검증셋의 손실이 2번 증가하면 중지
-
restore_best_weights=True : 가장 손실이 낮았던 곳으로 되돌리기
import keras
checkpoint_cb = keras.callbacks.ModelCheckpoint('fashion_mnist_model-whole.h5')
early_stopping_cb = keras.callbacks.EarlyStopping(patience=2,
restore_best_weights=True)
history = model.fit(X_train, y_train, epochs=50, verbose=0,
validation_split=0.2, callbacks=[checkpoint_cb, early_stopping_cb])
print(f"종료될 떄의 epoch : {early_stopping_cb.stopped_epoch}")
loss = history.history['loss']
val_loss = history.history['val_loss']
plt.plot(loss)
plt.plot(val_loss)
plt.xlabel('epoch')
plt.ylabel('loss')
plt.legend(['train', 'val'])
plt.show()
종료될 떄의 epoch : 11
댓글남기기