8-1에서 배운 내용을 토대로 패션 MNIST 구현해보자.

 

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.reshape(-1, 28, 28, 1) / 255.0
train_scaled, val_scaled, train_target, val_target = train_test_split(
train_scaled, train_target, test_size=0.2, random_state=42 )

reshape.(-1,28,28,1) -> 4차원 배열로 

 

이제 합성곱 신경망을 만들어보자..!

합성곱 신경망 만들기

model = keras.Sequential()
model.add(keras.layers.Conv2D(32, kernel_size=3, activation='relu',
			padding = 'same', input_shape=(28,28,1)))

매개변수 설명 : 필터 32개를 사용

커널의 크기는 3x3

활성화 함수 : 렐루 함수

세임패딩 사용 

입력의 차원 28,28,1

 

풀링 층 추가  ( 차원 축소시키는것 )

model.add(keras.MaxPooling2D(2))

최대 풀링 = MaxPooling

평균 풀링 = AveragePooling

 

위 코드에서는 최대값 풀링을 사용했고, (2,2)로  설정했다.   ---> 가로세로가 같으면 숫자 하나만 써도 가능

 

패션 MNIST 이미지 => 28x28크기에 세임 패딩 -> 출력된 특성 맵 또한 28x28 -> 2,2 풀링했으므로 특성맵 크기 절반으로 됨. 특성 맵의 깊이 32가 됨, 따라서 최대 풀링을 통과한 특성 맵의 크기는 (14,14,32)가 될 것.

 

첫 번째 CNN풀링층 다음에 두 번째 CNN 풀링 층을 추가해보자.

model.add(keras.layers.Conv2D(64, kernel_size=3, activation = 'relu',
			padding = 'same'))
model.add(keras.layers.MaxPooling2D(2))

처음과 동일 (필터개수 64개 만 바뀌었다.)

최종 출력되는 특성 맵은 (7,7,64)가 될 것이다.

 

이제 이 특성맵을 일렬로 펼쳐보자.

 

model.add(keras.layers.Flatten())
model.add(keras.layers.Dense(100, activation = 'relu'))
model.add(keras.layers.Dropout(0.4))
model.add(keras.layers.Dense(10, activation = 'softmax'))

은닉층과 출력층 사이에 드롭아웃을 넣었다.

 

이제 summary() 해보자.

Model: "sequential"
_________________________________________________________________
 Layer (type)                Output Shape              Param #   
=================================================================
 conv2d (Conv2D)             (None, 28, 28, 32)        320       
                                                                 
 max_pooling2d (MaxPooling2D  (None, 14, 14, 32)       0         
 )                                                               
                                                                 
 conv2d_1 (Conv2D)           (None, 14, 14, 64)        18496     
                                                                 
 max_pooling2d_1 (MaxPooling  (None, 7, 7, 64)         0         
 2D)                                                             
                                                                 
 flatten (Flatten)           (None, 3136)              0         
                                                                 
 dense (Dense)               (None, 100)               313700    
                                                                 
 dropout (Dropout)           (None, 100)               0         
                                                                 
 dense_1 (Dense)             (None, 10)                1010      
                                                                 
=================================================================
Total params: 333,526
Trainable params: 333,526
Non-trainable params: 0
_________________________________________________________________

출력된 것을 보고 각 층을 통과하면서 특성맵의 깊이가 32, 64로 각각 변하는것을 볼 수 있다.

 

모델 컴파일과 훈련

model checkpont 콜백, early stopping 콜백을 같이 사용

model.compile(optimizer = 'adam', loss='sparse_categorical_crossentropy',metrics='accuracy')
checkpoint_cb = keras.callbacks.ModelCheckpoint('best-cnn-model.h5',
                                                save_best_only=True)
early_stopping_cb = keras.callbacks.EarlyStopping(patience = 2,
                                                  restore_best_weights=True)
early_stopping_cb = keras.callbacks.EarlyStopping(patience = 2,
                                                  restore_best_weights=True)
history = model.fit(train_scaled, train_target, epochs = 20,
                   validation_data=(val_scaled, val_target),
                   callbacks=[checkpoint_cb, early_stopping_cb])

실행

Epoch 1/20
1500/1500 [==============================] - 27s 17ms/step - loss: 0.5075 - accuracy: 0.8179 - val_loss: 0.3253 - val_accuracy: 0.8817
Epoch 2/20
1500/1500 [==============================] - 25s 17ms/step - loss: 0.3334 - accuracy: 0.8809 - val_loss: 0.2711 - val_accuracy: 0.9009
Epoch 3/20
1500/1500 [==============================] - 24s 16ms/step - loss: 0.2825 - accuracy: 0.8971 - val_loss: 0.2612 - val_accuracy: 0.9053
Epoch 4/20
1500/1500 [==============================] - 23s 16ms/step - loss: 0.2540 - accuracy: 0.9085 - val_loss: 0.2359 - val_accuracy: 0.9146
Epoch 5/20
1500/1500 [==============================] - 25s 17ms/step - loss: 0.2302 - accuracy: 0.9148 - val_loss: 0.2358 - val_accuracy: 0.9121
Epoch 6/20
1500/1500 [==============================] - 26s 18ms/step - loss: 0.2109 - accuracy: 0.9232 - val_loss: 0.2291 - val_accuracy: 0.9134
Epoch 7/20
1500/1500 [==============================] - 30s 20ms/step - loss: 0.1924 - accuracy: 0.9289 - val_loss: 0.2284 - val_accuracy: 0.9178
Epoch 8/20
1500/1500 [==============================] - 26s 17ms/step - loss: 0.1774 - accuracy: 0.9330 - val_loss: 0.2292 - val_accuracy: 0.9195
Epoch 9/20
1500/1500 [==============================] - 28s 19ms/step - loss: 0.1652 - accuracy: 0.9387 - val_loss: 0.2363 - val_accuracy: 0.9193

에포크 절반도 안되어서 조기종료가 되었다.

 

손실그래프를 그려보자.

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

최적 = 7~8번째 에포크 같다

 

EarlyStopping클래스에서 restore_best_weights 매개변수를 True로 지정해서 

현재 model 객체가 최적의 모델 파라미터로 복원되어 있다.

 

즉 ModelCheckpoint 콜백이 저장한 best-cnn-model.h5파일을 다시 읽을 필요 X

 

이제 세트에 대한 성능을  평가해보자.

model.evaluate(val_scaled, val_target)

375/375 [==============================] - 2s 5ms/step - loss: 0.2284 - accuracy: 0.9178
[0.22835350036621094, 0.9178333282470703]

fit()의 결과 중 에포크 7번째 때와 결과값이 같다.!! --> 7번째가 최적의 에포크

 

첫 번째 샘플 이미지를 확인해보자.!!

핸드백인것을 볼 수 있다.

 

predict() 메소드를 이용해서 10개의 클래스에 대한 예측을 해보자.

preds = model.predict(val_scaled[0:1])
print(preds)

1/1 [==============================] - 0s 75ms/step
[[1.8688267e-19 4.8781057e-26 4.4762546e-23 1.3098311e-21 1.7252202e-19
  4.3341434e-19 1.1465990e-20 3.6049053e-22 1.0000000e+00 2.2943934e-20]]

결과를 보면 아홉번 째가 1이고 나머지는 0에 가까운 숫자들뿐이다.

 

시각화를 해보면 더 직관적일 것이다.

이제 리스트로 정리해보자

classes= ['티셔츠','바지','스웨터','드레스','코트','샌달','셔츠','스니커즈','가방','앵클 부츠']
import numpy as np
print(classes[np.argmax(preds)])

가방

+ Recent posts