Basic Classification with CNN 이번에는 케라스의 패션 MNIST 데이터셋을 사용해 10개의 카테고리로 옷을 분류하는 문제를 해결해보겠다. 텐서플로 기본 튜토리얼을 참고했습니다.
다음 튜토리얼 을 참고했습니다.
첫번째 신경망 훈련하기 : 기초적인 분류 문제
운동화, 셔츠 등의 옷 이미지를 분류하는 신경망 모델 을 구축한다.
10개의 카테고리, 7만개의 이미지로 구성된 fasion mnist 데이터셋
을 사용한다(기본 mnist 데이터셋은 손글씨 숫자로 이루어져 있다.)
: 네트워크 훈련에 6만개의 이미지를 사용한다. 테스트 데이터는 1만개의 이미지를 사용한다.
1) 데이터셋 로드하기 1 2 3 4 5 6 7 import tensorflow as tffrom tensorflow import kerasfashion_mnist = keras.datasets.fashion_mnist (train_images, train_labels), (test_images, test_labels) = fashion_mnist.load_data()
load_data()
: 4개의 NumPy 배열이 반환된다. (학습에 사용되는 훈련세트, 테스트에 사용되는 훈련세트)
각각의 이미지 는 28 * 28 크기의 numpy배열 이다.
각 이미지는 카테고리를 나타내는 0에서 9사이의 정수 인 하나의 label
과 매핑되어 있다.
2) 데이터 전처리
3) 모델 구성 1 2 3 4 5 model = keras.Sequential([ keras.layers.Flatten(input_shape=(28 , 28 )), keras.layers.Dense(128 , activation='relu' ), keras.layers.Dense(10 , activation='softmax' ) ])
tf.keras.layers.Flatten
: 2차원 배열의 이미지포맷을 28 * 28 = 784 픽셀의 1차원 배열로 변환 한다.
이미지 내 픽셀의 행을 펼쳐서 일렬로 늘린다.
학습되는 가중치는 없다. 데이터 변환만 진행한다.
tf.keras.layers.Dense
: 밀집 연결층 혹은 완전 연결층이라고 지칭.
위의 코드에서 첫번째 Dense층은 128개의 노드(뉴런)를 갖는다.
두번째 층은 10개의 카테고리에 이미지가 속할 확률을 출력해내는 softmax층이다.
1 2 3 model.compile (optimizer='adam' , loss='sparse_categorical_crossentropy' , metrics=['accuracy' ])
optimizer
: 데이터와 손실 값을 바탕으로 모델의 가중치를 업데이트하는 방법
loss
: 훈련하는 동안 모델의 오차를 측정하는 방법. 모델의 학습이 올바른 방향으로 향하도록 이 손실함수의 값을 최소화할 필요가 있다.
metrics
: 훈련단계와 테스트단계를 모니터링 하기 위해 사용한다. 위의 코드에서 지표로 사용하는 accuracy는 올바르게 분류된 이미지의 비율을 의미한다.
4) 모델 훈련 1 model.fit(train_images, train_labels, epochs=5 )
5) 정확도 평가
1 2 3 test_loss, test_acc = model.evaluate(test_images, test_labels, verbose=2 ) print ('\n테스트 정확도:' , test_acc)
나의 풀이 : 일단 먼저 튜토리얼의 코드를 그대로 사용해봤다. 썩 성능이 좋지 않았다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 import tensorflow as tffrom tensorflow import kerasfashion_mnist = tf.keras.datasets.fashion_mnist (train_images, train_labels), (test_images, test_labels) = fashion_mnist.load_data() train_images = train_images / 255.0 test_images = test_images / 255.0 def solution_model (): model = keras.Sequential([ keras.layers.Flatten(input_shape=(28 , 28 )), keras.layers.Dense(128 , activation='relu' ), keras.layers.Dense(10 , activation='softmax' ) ]) model.compile (optimizer='adam' , loss='sparse_categorical_crossentropy' , metrics=['accuracy' ]) model.fit(train_images, train_labels, epochs=5 ) return model if __name__ == '__main__' : model = solution_model() test_loss, test_acc = model.evaluate(test_images, test_labels, verbose=2 ) print ('\n테스트 정확도:' , test_acc)
1 2 3 4 5 6 7 8 9 10 11 12 13 Epoch 1/5 1875/1875 [==============================] - 3s 2ms/step - loss: 0.4936 - accuracy: 0.8256 Epoch 2/5 1875/1875 [==============================] - 3s 2ms/step - loss: 0.3743 - accuracy: 0.8652 Epoch 3/5 1875/1875 [==============================] - 3s 2ms/step - loss: 0.3343 - accuracy: 0.8778 Epoch 4/5 1875/1875 [==============================] - 3s 2ms/step - loss: 0.3118 - accuracy: 0.8846 Epoch 5/5 1875/1875 [==============================] - 3s 2ms/step - loss: 0.2913 - accuracy: 0.8923 313/313 - 0s - loss: 0.3574 - accuracy: 0.8679 테스트 정확도: 0.867900013923645
실제 시험에서 테스트의 정확도는 89%, loss는 33% 이하여야 합격이다.
튜토리얼의 코드가 생각보다 성능이 안나와서 이것저것 개선을 시도해봤다.
시도 1) : 단순히 생각해봤을때 가장 기본적이라고 생각되는 조건들을 추가해봤다.
epoch 증가
: 학습 횟수를 5에서 30회로 늘렸다.
검증 데이터셋
사용 : fasion mnist에서 제공되는 테스트 데이터셋을 실제 학습이 잘 되고있는지를 검증하는 validation set으로 추가했다.
조기종료 조건
추가 : 학습 횟수를 늘린만큼 잘못 학습이 될 경우를 방지하기 위해 조기종료 조건을 추가했다.
대충 바뀐 코드부분은 아래와 같다.
1 2 3 es = EarlyStopping(monitor='val_loss' , mode='min' , verbose=1 , patience=4 ) model.fit(train_images, train_labels, epochs=30 , callbacks=es, validation_data=(test_images, test_labels))
검증 데이터셋의 오차가 4번 이상 증가하면 과적합으로 판단하고 학습을 종료시키도록 했다.
1 2 3 4 5 6 Epoch 16/30 1875/1875 [==============================] - 4s 2ms/step - loss: 0.1993 - accuracy: 0.9251 - val_loss: 0.3415 - val_accuracy: 0.8849 Epoch 00016: early stopping 313/313 - 0s - loss: 0.3415 - accuracy: 0.8849 테스트 정확도: 0.8848999738693237
전반적으로 성능이 조금 좋아졌다. 아직 합격기준에는 미치지 못한다.
그리고 계속 이런저런 코드를 찾아보니 categorical_crossentropy라는 손실함수를 사용하는 경우도 있어서 내 코드의 sparse_categorical_crossentropy 손실함수와의 차이점이 궁금해졌다. 찾아봄.
categorical_crossentropy
와 sparse_categorical_crossentropy
의 차이점은?
-> 원핫코딩한 데이터의 분류 = categorical_crossentropy. input shape과 output shape의 크기가 같다.
시도 2)
조기종료 조건 수정 : 데이터셋 오차의 증가 허용을 4번 -> 3번으로 줄였다.
최적 모델 저장 : 모델 체크포인트 를 설정해서 val_accuracy가 증가한 경우에만 모델을 저장했다.
1 2 3 4 5 6 7 8 9 10 11 12 from tensorflow.keras.callbacks import EarlyStopping, ModelCheckpoint... es = EarlyStopping(monitor='val_loss' , mode='min' , verbose=1 , patience=3 ) mc = ModelCheckpoint('best_model.h5' , monitor = 'val_accuracy' , mode = 'max' , verbose = 1 , save_best_only = True ) model.fit(train_images, train_labels, epochs=30 , callbacks=[es, mc], validation_data=(test_images, test_labels)) loaded_model = keras.models.load_model('best_model.h5' ) return loaded_model
변화가 사알짝 있었다. 5번 시도해본 결과 1번 합격기준에 맞췄다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 Epoch 00011: early stopping 313/313 - 0s - loss: 0.3351 - accuracy: 0.8876 테스트 정확도: 0.8876000046730042 Epoch 00009: early stopping 313/313 - 1s - loss: 0.3240 - accuracy: 0.8842 테스트 정확도: 0.8841999769210815 Epoch 00013: early stopping 313/313 - 0s - loss: 0.3313 - accuracy: 0.8928 테스트 정확도: 0.892799973487854 Epoch 00014: early stopping 313/313 - 0s - loss: 0.3192 - accuracy: 0.8916 테스트 정확도: 0.8916000127792358 Epoch 00010: early stopping 313/313 - 0s - loss: 0.3199 - accuracy: 0.8863 테스트 정확도: 0.8863000273704529
하지만 아직 한참 모자란다ㅠ 특히 loss부분이 크게 줄지 않는 것 같다.
시도 3) : 모델 복잡도를 증가시키고 Dropout을 추가해줘봤다
1 2 3 4 5 6 7 8 model = keras.Sequential([ keras.layers.Flatten(input_shape=(28 , 28 )), keras.layers.Dense(256 , activation='relu' ), keras.layers.Dropout(0.2 ), keras.layers.Dense(128 , activation='relu' ), keras.layers.Dropout(0.2 ), keras.layers.Dense(10 , activation='softmax' ) ])
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 Epoch 00018: early stopping 313/313 - 0s - loss: 0.3267 - accuracy: 0.8895 테스트 정확도: 0.8895000219345093 Epoch 00009: early stopping 313/313 - 0s - loss: 0.3385 - accuracy: 0.8802 테스트 정확도: 0.8802000284194946 Epoch 00011: early stopping 313/313 - 0s - loss: 0.3385 - accuracy: 0.8805 테스트 정확도: 0.8805000185966492 Epoch 00009: early stopping 313/313 - 0s - loss: 0.3637 - accuracy: 0.8756 테스트 정확도: 0.8755999803543091 Epoch 00017: early stopping 313/313 - 0s - loss: 0.3238 - accuracy: 0.8895 테스트 정확도: 0.8895000219345093
오히려 결과가 더 안좋아졌다. 음.. 어떻게 해야하지
시도 4) : CNN을 사용 하고 모델 복잡도 를 확 올려봤다. 컨볼루션 레이어를 생성하는 Conv2D
클래스는 4차원 텐서를 입력으로 받는다. 따라서 fasion_mnist의 28 * 28 이미지 데이터를 reshape해줄 필요가 있다.
1 2 train_images = tf.reshape(train_images, shape=[60000 ,28 ,28 ,1 ]) test_images = tf.reshape(test_images, shape=[10000 ,28 ,28 ,1 ])
모델은 구글링으로 적당히 짜깁기해서 아래처럼 구성해봤다.
1 2 3 4 5 6 7 8 9 10 11 model = keras.Sequential([ keras.layers.Conv2D(filters=32 , kernel_size=3 , strides=(2 , 2 ), activation='relu' , input_shape=(28 , 28 , 1 )), keras.layers.Conv2D(filters=64 , kernel_size=3 , strides=(2 , 2 ), activation='relu' ), keras.layers.MaxPooling2D(pool_size=2 ), keras.layers.Flatten(), keras.layers.Dense(1024 , activation='relu' ), keras.layers.Dropout(0.2 ), keras.layers.Dense(256 , activation='relu' ), keras.layers.Dropout(0.2 ), keras.layers.Dense(10 , activation='softmax' ) ])
오 유의미한 변화가 있었다. 전반적인 loss가 0.3 이하로 줄어들고 정확도는 90%를 평균적으로 넘었다. 이정도면 턱걸이지만 시험의 통과는 가능한 수준이다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 Epoch 00008: early stopping 313/313 - 1s - loss: 0.2781 - accuracy: 0.9010 테스트 정확도: 0.9010000228881836 Epoch 00008: early stopping 313/313 - 2s - loss: 0.2836 - accuracy: 0.9018 테스트 정확도: 0.9017999768257141 Epoch 00008: early stopping 313/313 - 1s - loss: 0.3023 - accuracy: 0.9014 테스트 정확도: 0.9014000296592712 Epoch 00008: early stopping 313/313 - 1s - loss: 0.3116 - accuracy: 0.9024 테스트 정확도: 0.902400016784668 Epoch 00010: early stopping 313/313 - 1s - loss: 0.3102 - accuracy: 0.9064 테스트 정확도: 0.9064000248908997
이게 CNN을 사용해서 올라간건지 아니면 모델 복잡도를 증가시킨것도 변화에 의미가 있었는지 궁금해서 시도2) 의 모델에 CNN만 추가해서 다시 테스트해봤다.
배치를 쓰지않고 느려터진 CNN을 돌리려니 모델 학습되는걸 기다리는것만 백년걸린다. 아 힘드러.
1 2 3 4 5 6 7 8 model = keras.Sequential([ keras.layers.Conv2D(filters=32 , kernel_size=3 , strides=(2 , 2 ), activation='relu' , input_shape=(28 , 28 , 1 )), keras.layers.Conv2D(filters=64 , kernel_size=3 , strides=(2 , 2 ), activation='relu' ), keras.layers.MaxPooling2D(pool_size=2 ), keras.layers.Flatten(), keras.layers.Dense(128 , activation='relu' ), keras.layers.Dense(10 , activation='softmax' ) ])
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 Epoch 00011: early stopping 313/313 - 1s - loss: 0.2710 - accuracy: 0.9044 테스트 정확도: 0.9043999910354614 Epoch 00011: early stopping 313/313 - 1s - loss: 0.2746 - accuracy: 0.9055 테스트 정확도: 0.9054999947547913 Epoch 00008: early stopping 313/313 - 1s - loss: 0.2923 - accuracy: 0.8992 테스트 정확도: 0.8992000222206116 Epoch 00010: early stopping 313/313 - 1s - loss: 0.2896 - accuracy: 0.8998 테스트 정확도: 0.8998000025749207 Epoch 00012: early stopping 313/313 - 1s - loss: 0.2843 - accuracy: 0.9051 테스트 정확도: 0.9050999879837036
굉장히 애매한 결과가 나왔다. 정확도의 maximum이 모델 복잡도를 올렸을때보다 조금 높아졌지만 minimum도 그만큼 살짝 낮아졌다. 혹시나 해서 최종 레이어 이전에 20%의 비율로 dropout을 추가해주고 다시 학습시켜봤다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 Epoch 00013: early stopping 313/313 - 1s - loss: 0.2861 - accuracy: 0.9081 테스트 정확도: 0.9081000089645386 Epoch 00013: early stopping 313/313 - 1s - loss: 0.2754 - accuracy: 0.9056 테스트 정확도: 0.9056000113487244 Epoch 00013: early stopping 313/313 - 1s - loss: 0.2760 - accuracy: 0.9051 테스트 정확도: 0.9050999879837036 Epoch 00011: early stopping 313/313 - 1s - loss: 0.2751 - accuracy: 0.9064 테스트 정확도: 0.9064000248908997 Epoch 00013: early stopping 313/313 - 1s - loss: 0.2877 - accuracy: 0.9019 테스트 정확도: 0.9018999934196472
오오오 확실히 변화가 있었다. 전반적으로 정확도가 90.5% 이상으로 상승했다. 결국 Dense 레이어를 추가해서 단순히 모델 복잡도를 올려버리는 것 보다는 dropout을 추가해서 복잡도를 낮추는게 더 효과가 있었다.
여기에서 궁금해서 몇가지 더 실험을 해봤다.
생각보다 loss가 많이 낮아졌기에 손실이 높아지는걸 감수하고 학습을 좀더 진행시켜보면 정확도가 올라갈지 궁금했다. 조기종료 조건의 patience를 5로 높여봤다.
1 2 3 4 5 6 7 Epoch 00016: early stopping 313/313 - 1s - loss: 0.3029 - accuracy: 0.9076 테스트 정확도: 0.9075999855995178 Epoch 00013: early stopping 313/313 - 1s - loss: 0.2799 - accuracy: 0.9045 테스트 정확도: 0.9045000076293945
손실이 증가한거에 비해 정확도의 증가는 미미한 편이다.
dropout 비율이 학습에 미치는 영향이 궁금했다. dropout 비율을 높여봤다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 # Dropout : 0.5 Epoch 00012: early stopping 313/313 - 1s - loss: 0.2802 - accuracy: 0.9046 테스트 정확도: 0.9046000242233276 Epoch 00016: early stopping 313/313 - 1s - loss: 0.2810 - accuracy: 0.9080 테스트 정확도: 0.9079999923706055 # Dropout : 0.25 Epoch 00010: early stopping 313/313 - 3s - loss: 0.2662 - accuracy: 0.9120 테스트 정확도: 0.9120000004768372 Epoch 00011: early stopping 313/313 - 3s - loss: 0.2499 - accuracy: 0.9139 테스트 정확도: 0.9139000177383423 # Dropout : 0.3 Epoch 00012: early stopping 313/313 - 3s - loss: 0.2442 - accuracy: 0.9173 테스트 정확도: 0.9172999858856201 Epoch 00009: early stopping 313/313 - 3s - loss: 0.2510 - accuracy: 0.9128 테스트 정확도: 0.9128000140190125
대박!!! 드롭아웃 비율을 조절한 것 만으로도 엄청난 변화가 생겼다. 드디어 0.5는 썩 성능이 좋지 않았고 0.3정도가 적당한 듯 싶다. 신기하네.
최종모델 내가 구성한 모델 중 가장 높은 성능을 보여준놈
1 2 3 4 5 6 7 8 9 10 model = keras.Sequential([ keras.layers.Conv2D(filters=32 , kernel_size=3 , activation='relu' , input_shape=(28 , 28 , 1 )), keras.layers.MaxPooling2D(pool_size=2 ), keras.layers.Conv2D(filters=64 , kernel_size=3 , activation='relu' ), keras.layers.MaxPooling2D(pool_size=2 ), keras.layers.Flatten(), keras.layers.Dense(128 , activation='relu' ), keras.layers.Dropout(0.3 ), keras.layers.Dense(10 , activation='softmax' ) ])
추가 (+) 어디 책에서 본 모델 1 2 3 4 5 6 7 8 model = keras.Sequential([ keras.layers.Conv2D(10 , (3 , 3 ), padding='same' , activation='relu' , input_shape=(28 , 28 , 1 )), keras.layers.MaxPooling2D(pool_size=2 ), keras.layers.Flatten(), keras.layers.Dropout(0.5 ), keras.layers.Dense(100 , activation='relu' ), keras.layers.Dense(10 , activation='softmax' ) ])
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 Epoch 00021: early stopping 313/313 - 2s - loss: 0.2330 - accuracy: 0.9183 테스트 정확도: 0.9182999730110168 Epoch 00017: early stopping 313/313 - 2s - loss: 0.2335 - accuracy: 0.9160 테스트 정확도: 0.9160000085830688 Epoch 00013: early stopping 313/313 - 2s - loss: 0.2499 - accuracy: 0.9131 테스트 정확도: 0.913100004196167 Epoch 00014: early stopping 313/313 - 2s - loss: 0.2392 - accuracy: 0.9165 테스트 정확도: 0.9164999723434448
간단하게 생겼는데 성능이 꽤 좋다. 이유가 뭘까?