[Tensorflow] Chapter 5. 분류
본 글은 ‘시작하세요! 텐서플로 2.0 프로그래밍’ 을 바탕으로 작성되었습니다.
Chapter 5. 분류
분류 분류는 머신러닝에서 가장 기초적인 데이터 분석 방법 중 하나입니다. 데이터가 어느 범주 Category에 해당하는지 판단하는 문제입니다. 앞서 배운 실수에서는 성능 측정을 위해 SSE, MSE 등을 사용했지만 분류 문제에서는 예측이 정답을 얼마나 맞혔는지에 대한 정확도를 퍼센트로 나타내게 됩니다.
분류 문제에는 명확한 정답이 있는 경우도 있지만 그렇지 않은 경우도 있습니다. 이런 경우에는 비지도학습 unsupervised learning 이라고 불리며 나중에 오토인코더 AutoEncoder 관련 내용에서 더 자세하게 다루겠습니다. 이번 Chapter 5에서는 정답이 있는 지도학습 supervised learning에 대해서만 다루겠습니다.
이항 분류
이항 분류 Binary Classification 이란 정답의 범주가 두 개인 분류 문제입니다. 가령 개와 고양이를 구분하는 문제가 있습니다. 오랫동안 이런 문제는 사람에게는 쉬운 문제지만 컴퓨터에게는 어려운 문제였습니다. 하지만 딥러닝으로 점점 더 정복해나가고 있습니다. 이미지에 대한 처리는 뒤에서 컨볼루션 신경망 CNN에서 더 자세히 다루고, 여기서는좀 더 쉬운 데이터인 와인 데이터세트로 이항분류를 해보겠습니다. 와인에 대한 데이터를 통해 화이트 와인과 레드 와인을 분류하는 문제입니다.
와인 데이터셋 불러오기
fixed acidity volatile acidity citric acid residual sugar chlorides \
0 7.4 0.70 0.00 1.9 0.076
1 7.8 0.88 0.00 2.6 0.098
2 7.8 0.76 0.04 2.3 0.092
3 11.2 0.28 0.56 1.9 0.075
4 7.4 0.70 0.00 1.9 0.076
free sulfur dioxide total sulfur dioxide density pH sulphates \
0 11.0 34.0 0.9978 3.51 0.56
1 25.0 67.0 0.9968 3.20 0.68
2 15.0 54.0 0.9970 3.26 0.65
3 17.0 60.0 0.9980 3.16 0.58
4 11.0 34.0 0.9978 3.51 0.56
alcohol quality
0 9.4 5
1 9.8 5
2 9.8 5
3 9.8 6
4 9.4 5
fixed acidity volatile acidity citric acid residual sugar chlorides \
0 7.0 0.27 0.36 20.7 0.045
1 6.3 0.30 0.34 1.6 0.049
2 8.1 0.28 0.40 6.9 0.050
3 7.2 0.23 0.32 8.5 0.058
4 7.2 0.23 0.32 8.5 0.058
free sulfur dioxide total sulfur dioxide density pH sulphates \
0 45.0 170.0 1.0010 3.00 0.45
1 14.0 132.0 0.9940 3.30 0.49
2 30.0 97.0 0.9951 3.26 0.44
3 47.0 186.0 0.9956 3.19 0.40
4 47.0 186.0 0.9956 3.19 0.40
alcohol quality
0 8.8 6
1 9.5 6
2 10.1 6
3 9.9 6
4 9.9 6
이번에는 pandas라는 라이브러리를 사용했습니다. pandas는 데이터 프레임 Dataframe 의 형태로 데이터를 불러오는 기능을 제공합니다. 표와 같은 형태로, head() 함수로 여러 행 중 일부를 출력해서 보여줄 수 있습니다. head(10)을 입력한다면 앞에서 10개 행을 보여주며 반대로 tail()을 사용할 경우 뒤에서부터 불러옵니다.
데이터를 살펴보면 12개 속성으로 와인의 종류를 분류해야 하는 것을 알 수 있습니다. 현재는 와인의 종류에 따라 데이터가 분리돼 있기 때문에 두 데이터를 하나로 합치고, 각 데이터의 와인 종류를 새로운 속성으로 추가해보겠습니다.
와인 데이터셋 합치기
fixed acidity volatile acidity citric acid residual sugar chlorides \
0 7.4 0.70 0.0 1.9 0.076
1 7.8 0.88 0.0 2.6 0.098
free sulfur dioxide total sulfur dioxide density pH sulphates \
0 11.0 34.0 0.9978 3.51 0.56
1 25.0 67.0 0.9968 3.20 0.68
alcohol quality type
0 9.4 5 0
1 9.8 5 0
fixed acidity volatile acidity citric acid residual sugar chlorides \
0 7.0 0.27 0.36 20.7 0.045
1 6.3 0.30 0.34 1.6 0.049
free sulfur dioxide total sulfur dioxide density pH sulphates \
0 45.0 170.0 1.001 3.0 0.45
1 14.0 132.0 0.994 3.3 0.49
alcohol quality type
0 8.8 6 1
1 9.5 6 1
fixed acidity volatile acidity citric acid residual sugar \
count 6497.000000 6497.000000 6497.000000 6497.000000
mean 7.215307 0.339666 0.318633 5.443235
std 1.296434 0.164636 0.145318 4.757804
min 3.800000 0.080000 0.000000 0.600000
25% 6.400000 0.230000 0.250000 1.800000
50% 7.000000 0.290000 0.310000 3.000000
75% 7.700000 0.400000 0.390000 8.100000
max 15.900000 1.580000 1.660000 65.800000
chlorides free sulfur dioxide total sulfur dioxide density \
count 6497.000000 6497.000000 6497.000000 6497.000000
mean 0.056034 30.525319 115.744574 0.994697
std 0.035034 17.749400 56.521855 0.002999
min 0.009000 1.000000 6.000000 0.987110
25% 0.038000 17.000000 77.000000 0.992340
50% 0.047000 29.000000 118.000000 0.994890
75% 0.065000 41.000000 156.000000 0.996990
max 0.611000 289.000000 440.000000 1.038980
pH sulphates alcohol quality type
count 6497.000000 6497.000000 6497.000000 6497.000000 6497.000000
mean 3.218501 0.531268 10.491801 5.818378 0.753886
std 0.160787 0.148806 1.192712 0.873255 0.430779
min 2.720000 0.220000 8.000000 3.000000 0.000000
25% 3.110000 0.430000 9.500000 5.000000 1.000000
50% 3.210000 0.510000 10.300000 6.000000 1.000000
75% 3.320000 0.600000 11.300000 6.000000 1.000000
max 4.010000 2.000000 14.900000 9.000000 1.000000
데이터 프레임에 새로운 속성을 추가하기 위해서는 red[‘type’] = 0 처럼 속성과 값을 지정해주면 됩니다. 여기서는 종류를 나타내는 속성의 이름을 type으로 정하고 레드 와인은 0, 화이트 와인은 1의 값을 부여했습니다. 그 후 데이터프레임을 합치기 위해 pd.concat() 함수를 사용했습니다. 이 함수는 데이터프레임을 위아래로 연결합니다. 그 후 describe()를 사용해 간단한 통계 정보를 확인했습니다. type의 평균이 0.75인 것을 보니 1의 값을 가진 화이트 와인이 3배정도 더 많다는 사실도 알 수 있습니다.
모델 학습을 위해 학습 데이터와 테스트 데이터로 나누기 전에 두 종류의 와인이 각각에 비슷한 비율로 들어가도록 데이터를 한번 섞어야 합니다. 한 범주의 데이터 양이 너무 많거나 적다면 다른 처리가 필요할 수 있으나 1:3 정도의 비율은 랜덤하게 섞어도 문제가 거의 발생하지 않습니다.
그 전에 우선 데이터를 더 파악해서 정규화를 하겠습니다.
데이터 요약 정보 확인
<class 'pandas.core.frame.DataFrame'>
Int64Index: 6497 entries, 0 to 4897
Data columns (total 13 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 fixed acidity 6497 non-null float64
1 volatile acidity 6497 non-null float64
2 citric acid 6497 non-null float64
3 residual sugar 6497 non-null float64
4 chlorides 6497 non-null float64
5 free sulfur dioxide 6497 non-null float64
6 total sulfur dioxide 6497 non-null float64
7 density 6497 non-null float64
8 pH 6497 non-null float64
9 sulphates 6497 non-null float64
10 alcohol 6497 non-null float64
11 quality 6497 non-null int64
12 type 6497 non-null int64
dtypes: float64(11), int64(2)
memory usage: 710.6 KB
None
info() 함수는 데이터프레임을 구성하는 속성들의 정보를 알려줍니다. 모두 숫자형이고 결측치가 없기 때문에 문제없이 정규화를 진행해도 되는 것을 확인했습니다.
데이터 정규화
fixed acidity volatile acidity citric acid residual sugar chlorides \
0 0.297521 0.413333 0.000000 0.019939 0.111296
1 0.330579 0.533333 0.000000 0.030675 0.147841
2 0.330579 0.453333 0.024096 0.026074 0.137874
3 0.611570 0.133333 0.337349 0.019939 0.109635
4 0.297521 0.413333 0.000000 0.019939 0.111296
free sulfur dioxide total sulfur dioxide density pH sulphates \
0 0.034722 0.064516 0.206092 0.612403 0.191011
1 0.083333 0.140553 0.186813 0.372093 0.258427
2 0.048611 0.110599 0.190669 0.418605 0.241573
3 0.055556 0.124424 0.209948 0.341085 0.202247
4 0.034722 0.064516 0.206092 0.612403 0.191011
alcohol quality type
0 0.202899 0.333333 0.0
1 0.260870 0.333333 0.0
2 0.260870 0.333333 0.0
3 0.260870 0.500000 0.0
4 0.202899 0.333333 0.0
fixed acidity volatile acidity citric acid residual sugar \
count 6497.000000 6497.000000 6497.000000 6497.000000
mean 0.282257 0.173111 0.191948 0.074283
std 0.107143 0.109758 0.087541 0.072972
min 0.000000 0.000000 0.000000 0.000000
25% 0.214876 0.100000 0.150602 0.018405
50% 0.264463 0.140000 0.186747 0.036810
75% 0.322314 0.213333 0.234940 0.115031
max 1.000000 1.000000 1.000000 1.000000
chlorides free sulfur dioxide total sulfur dioxide density \
count 6497.000000 6497.000000 6497.000000 6497.000000
mean 0.078129 0.102518 0.252868 0.146262
std 0.058195 0.061630 0.130235 0.057811
min 0.000000 0.000000 0.000000 0.000000
25% 0.048173 0.055556 0.163594 0.100829
50% 0.063123 0.097222 0.258065 0.149990
75% 0.093023 0.138889 0.345622 0.190476
max 1.000000 1.000000 1.000000 1.000000
pH sulphates alcohol quality type
count 6497.000000 6497.000000 6497.000000 6497.000000 6497.000000
mean 0.386435 0.174870 0.361131 0.469730 0.753886
std 0.124641 0.083599 0.172857 0.145543 0.430779
min 0.000000 0.000000 0.000000 0.000000 0.000000
25% 0.302326 0.117978 0.217391 0.333333 1.000000
50% 0.379845 0.162921 0.333333 0.500000 1.000000
75% 0.465116 0.213483 0.478261 0.500000 1.000000
max 1.000000 1.000000 1.000000 1.000000 1.000000
pandas에서는 max(), min() 함수를 통해 각 속성의 최댓값과 최솟값을 얻을 수 있어 이를 활용하여 정규화를 진행했습니다. 값들이 0부터 1까지의 범위로 잘 정규화 된 것을 확인할 수 있습니다. 이제 학습을 위해 데이터를 랜덤하게 섞고 넘파이 array로 바꾸겠습니다.
데이터를 섞은 후 넘파이 array로 변환
[[0.38842975 0.1 0.20481928 0.0107362 0.04318937 0.18402778
0.23963134 0.08964719 0.27131783 0.18539326 0.43478261 0.66666667
1. ]
[0.28099174 0.36 0.03614458 0.03220859 0.11295681 0.04861111
0.18202765 0.1995373 0.6124031 0.17977528 0.2173913 0.33333333
0. ]
[0.18181818 0.17333333 0.39759036 0.23466258 0.06146179 0.08680556
0.3640553 0.20802005 0.3255814 0.15730337 0.11594203 0.5
1. ]
[0.31404959 0.22 0.14457831 0.01840491 0.1179402 0.01041667
0.01152074 0.17524581 0.43410853 0.20786517 0.2173913 0.33333333
0. ]
[0.28099174 0.08 0.13253012 0.01533742 0.05813953 0.05555556
0.21889401 0.14652015 0.50387597 0.1741573 0.28985507 0.33333333
1. ]]
pandas의 sample() 함수는 전체 데이터프레임에서 frac 인수로 지정된 비율만큼의 행을 랜덤하게 뽑아서 데이터프레임을 만듭니다. frac = 1은 모든 데이터를 뽑아서 섞는 것을 의미합니다. random_state는 무작위 시드값을 정해준 것으로 같은 결과를 재현하기 위해서는 같은 값을 줄 필요가 있습니다. 결과 array의 마지막 열의 값이 0과 1로 섞여있는 것을 확인할 수 있습니다.
이제 데이터를 훈련 데이터와 테스트 데이터로 나누겠습니다.
훈련 데이터와 테스트 데이터로 분리
[0.38842975 0.1 0.20481928 0.0107362 0.04318937 0.18402778
0.23963134 0.08964719 0.27131783 0.18539326 0.43478261 0.66666667]
1.0
[0.38016529 0.08 0.22891566 0.17177914 0.07641196 0.17361111
0.37788018 0.25621747 0.48062016 0.33707865 0.13043478 0.5 ]
1.0
[0. 1.]
[0. 1.]
데이터를 8:2로 나누기 위해 80%에 해당하는 인덱스 값으로 데이터를 분리 후, 12개의 속성 X와 새롭게 추가한 type 속성인 Y로 다시 분리했습니다. to_categorical은 분류 문제에서 자주 쓰이는 함수로 정답 행렬을 원-핫 인코딩 One-Hot Encoding 방식으로 바꿉니다. num_classes는 정답 클래스의 개수를 의미합니다. 이제 딥러닝 학습을 해보겠습니다.
와인 데이터셋 분류 모델 생성
Model: "sequential_1"
_________________________________________________________________
Layer (type) Output Shape Param #
=================================================================
dense_4 (Dense) (None, 48) 624
_________________________________________________________________
dense_5 (Dense) (None, 24) 1176
_________________________________________________________________
dense_6 (Dense) (None, 12) 300
_________________________________________________________________
dense_7 (Dense) (None, 2) 26
=================================================================
Total params: 2,126
Trainable params: 2,126
Non-trainable params: 0
_________________________________________________________________
회귀 모델과의 차이점은 다음과 같습니다. 마지막 레이어의 활성화함수로 소프트맥스 softmax를 사용했습니다. 이 함수는 분류에서 가장 많이 쓰이며 출력값들을 자연 로그의 밑인 $e$의 지수로 사용해 계싼한 뒤 모든 더한 값으로 나눕니다. 이렇게 나온 결괏값들은 총합이 1.0인 확률값이 됩니다.
\[P(z) = \frac{e^{zj}}{\sum^K_{k=1}e^{zk}} \hspace{15pt} (j=1,2,...,K)\]소프트맥스라는 단어의 뜻은 max 함수의 부드러운 버전이라는 의미입니다. 큰 값을 강조하고 작은 값은 약화하는 특징은 같습니다. 소프트맥스는 분류 문제나 언어 RNN에서의 다음 토큰 예측, 강화학습에서 에이전트의 행동 확률 등 다양한 분야에서 널리 쓰입니다. 여기서는 예측값이 레드 와인일 확률과 화이트 와인일 확률을 구하는데 쓰입니다.
또한 마지막 레이어는 뉴런의 수가 2로 설정되어 있습니다. 원핫 인코딩으로 변환한 정답의 차원 수와 동일한 값입니다. 손실 함수인 loss에서는 mse가 아니라 categorical_crossentropy라는 값이 들어왔습니다. 이 손실 함수인 식은 다음과 같습니다.
\[\text{CCE} = -\frac1n\sum^n_{j=1}p(x)\log q(x)\]모든 범주에 대한 값을 평균을 내는 것입니다. 쉽게 설명하면 정답 클래스에 대해 높은 확률로 예측할수록 값이 작아집니다.
그리고 model.compile 함수에 정확도 accuracy가 추가되었습니다. 분류 문제는 정확도로 성능을 측정하는 경우가 많습니다. 이렇게 인수를 설정하면 모델의 학습 히스토리에 loss와 accuracy가 함께 저장됩니다. 이제 학습을 시작해보겠습니다.
와인 데이터셋에 대한 분류 모델 학습
Epoch 1/25
122/122 [==============================] - 2s 3ms/step - loss: 0.1466 - accuracy: 0.9394 - val_loss: 0.0796 - val_accuracy: 0.9862
Epoch 2/25
122/122 [==============================] - 0s 3ms/step - loss: 0.0549 - accuracy: 0.9841 - val_loss: 0.1346 - val_accuracy: 0.9715
Epoch 3/25
122/122 [==============================] - 0s 2ms/step - loss: 0.0550 - accuracy: 0.9856 - val_loss: 0.0821 - val_accuracy: 0.9869
Epoch 4/25
122/122 [==============================] - 0s 2ms/step - loss: 0.0490 - accuracy: 0.9879 - val_loss: 0.0874 - val_accuracy: 0.9862
...
Epoch 23/25
122/122 [==============================] - 0s 2ms/step - loss: 0.0466 - accuracy: 0.9905 - val_loss: 0.0729 - val_accuracy: 0.9831
Epoch 24/25
122/122 [==============================] - 0s 2ms/step - loss: 0.0405 - accuracy: 0.9895 - val_loss: 0.0691 - val_accuracy: 0.9885
Epoch 25/25
122/122 [==============================] - 0s 2ms/step - loss: 0.0417 - accuracy: 0.9890 - val_loss: 0.0597 - val_accuracy: 0.9869
학습 과정에서 학습 데이터와 검증 데이터의 정확도가 각각 표시됩니다. 이제 이 학습 과정을 시각화 해보겠습니다.
분류 모델의 학습 결과 시각화
정확도가 100에 가깝게 유지되고 있습니다. 이제 학습 과정에서 본 적이 없는 테스트 데이터를 넣어 모델의 성능을 평가해보겠습니다.
분류 모델 평가
41/41 [==============================] - 0s 1ms/step - loss: 0.0275 - accuracy: 0.9923
[0.02750876545906067, 0.9923076629638672]
정확도는 99.23%로 거의 오차 없이 분류를 해내고 있습니다.
다항 분류
다항 분류는 앞선 이항 분류와는 다르게 범주의 수가 2개를 초과하는 경우입니다. 다뤘던 와인 데이터셋의 경우 품질이 0에서부터 10까지의 정수값으로 주어져 있기 때문에 이번에는 와인의 품질을 예측하는 모델을 만들어 보겠습니다.
우선 품질 데이터가 어떤 비율로 구성돼 있는지 살펴보겠습니다.
품질 데이터 확인
count 6497.000000
mean 5.818378
std 0.873255
min 3.000000
25% 5.000000
50% 6.000000
75% 6.000000
max 9.000000
Name: quality, dtype: float64
6 2836
5 2138
7 1079
4 216
8 193
3 30
9 5
Name: quality, dtype: int64
품질의 최솟값은 3, 최댓값은 9인 것을 알 수 있으며 각 클래스의 수가 균일하지 않고 꽤 많은 차이가 납니다. 이렇게 클래스끼리 수가 차이가 많이 나면 학습이 잘 되지 않습니다. 모든 범주에 대한 세세한 분류는 어려울 것 같으므로 크게 세 가지로 재분류 해보겠습니다. 품질 3,4,5는 나쁨, 7,8,9는 좋음, 그리고 6은 보통으로 하겠습니다.
품질을 3개의 범주(좋음, 보통, 나쁨)로 재분류
count 6497.000000
mean 0.829614
std 0.731124
min 0.000000
25% 0.000000
50% 1.000000
75% 1.000000
max 2.000000
Name: new_quality, dtype: float64
1.0 2836
0.0 2384
2.0 1277
Name: new_quality, dtype: int64
데이터프레임의 loc는 특정한 데이터의 인덱스를 골라내는 역할을 합니다. 이를 활용해서 각각의 경우에 해당하는 행을 선택한 뒤 new_quality라는 속성을 선택해 값을 추가했습니다. 이때 기존 데이터에 해당 속성이 없으면 새롭게 만들어집니다. 이렇게 재분류한 뒤에 갯수를 살펴보면 어느 정도 비슷해졌습니다. 이제 앞서 이항 분류에서 했던 것처럼 데이터 정규화 및 분리를 해보겠습니다.
데이터 정규화 및 분리
기존의 품질 속성은 삭제했습니다. 이제 학습을 진행해보겠습니다.
와인 데이터셋 다항 분류 모델 생성 및 학습
Epoch 1/500
61/61 [==============================] - 0s 4ms/step - loss: 0.9593 - accuracy: 0.5040 - val_loss: 0.9227 - val_accuracy: 0.5323
Epoch 2/500
61/61 [==============================] - 0s 2ms/step - loss: 0.8789 - accuracy: 0.5591 - val_loss: 0.8916 - val_accuracy: 0.5446
Epoch 3/500
61/61 [==============================] - 0s 2ms/step - loss: 0.8725 - accuracy: 0.5738 - val_loss: 0.8972 - val_accuracy: 0.5692
...
Epoch 498/500
61/61 [==============================] - 0s 2ms/step - loss: 0.1825 - accuracy: 0.9264 - val_loss: 2.1725 - val_accuracy: 0.6385
Epoch 499/500
61/61 [==============================] - 0s 2ms/step - loss: 0.1903 - accuracy: 0.9238 - val_loss: 2.0847 - val_accuracy: 0.6392
Epoch 500/500
61/61 [==============================] - 0s 2ms/step - loss: 0.1991 - accuracy: 0.9230 - val_loss: 2.1122 - val_accuracy: 0.6223
마지막 레이어의 뉴런 수가 3인 것 외에는 같습니다. 학습 과정을 시각화 해보겠습니다.
다항 분류 모델 학습 결과 시각화
다항 분류 모델 평가
41/41 [==============================] - 0s 1ms/step - loss: 2.0278 - accuracy: 0.6346
[2.0278103351593018, 0.6346153616905212]
학습이 진행될수록 loss는 줄어들지만 validation loss는 초반에는 감소했다가 그 후 오히려 증가하고 있습니다. 전형적인 과적합 사례입니다. accuracy 또한 training은 지속해서 증가하는 반면 검증 데이터의 accuracy는 그다지 증가하지 않습니다. 이 이상으로 좋은 성과를 내기 위해서는 네트워크의 크기를 조정하거나 학습률을 조정하는 등의 노력이 필요합니다. 이러한 방법들은 앞으로 계속해서 살펴볼 것입니다.
Fashion MNIST
Fashion MNIST는 유명한 데이터셋으로 10개의 범주로 되어있는 28x28 크기의 흑백 의류 이미지 데이터입니다. 이번에는 이 데이터셋으로 분류를 해보겠습니다. keras에 기본적으로 탑재되어 있습니다.
Fashion MNIST 데이터세트 불러오기
Downloading data from [https://storage.googleapis.com/tensorflow/tf-keras-datasets/train-labels-idx1-ubyte.gz](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](https://storage.googleapis.com/tensorflow/tf-keras-datasets/train-images-idx3-ubyte.gz)
26427392/26421880 [==============================] - 1s 0us/step
26435584/26421880 [==============================] - 1s 0us/step
Downloading data from [https://storage.googleapis.com/tensorflow/tf-keras-datasets/t10k-labels-idx1-ubyte.gz](https://storage.googleapis.com/tensorflow/tf-keras-datasets/t10k-labels-idx1-ubyte.gz)
16384/5148 [===============================================================================================] - 0s 0s/step
Downloading data from [https://storage.googleapis.com/tensorflow/tf-keras-datasets/t10k-images-idx3-ubyte.gz](https://storage.googleapis.com/tensorflow/tf-keras-datasets/t10k-images-idx3-ubyte.gz)
4423680/4422102 [==============================] - 0s 0us/step
4431872/4422102 [==============================] - 0s 0us/step
60000 10000
훈련 데이터는 60,000장, 테스트 데이터는 10,000장을 포함하고 있습니다. 이제 이 데이터가 어떻게 생겼는지 확인해보겠습니다.
데이터 확인
9
matplotlib.pyplot의 imshow() 로 이미지를 표시할 수 있습니다. 데이터의 이미지가 0에서 255까지의 값을 가지는 28x28 픽셀 크기의 2차원 이미지라는 것을 확인할 수 있습니다. 또한 데이터의 라벨로 9가 표시되었습니다. 이제 이 데이터를 정규화 하겠습니다.
데이터 정규화
[[0. 0. 0. 0. 0. 0.
0. 0. 0. 0. 0. 0.
1. 0. 0. 0. 0. 0.
2. 0. 0. 0. 0. 0.
3. 0. 0. 0. ]
...
[0. 0. 0. 0. 0. 0.
0. 0. 0. 0. 0. 0.
0. 0. 0. 0. 0. 0.
0. 0. 0. 0. 0. 0.
0. 0. 0. 0. ]]
전체 출력이 길기 때문에 생략했지만 결과를 보면 모든 데이터가 0에서 1의 값으로 잘 정규화 된 것을 알 수 있습니다.
이번에는 원핫 인코딩을 사용하지 않고 클래스를 정수값 그대로 받는 대신 모델 정의에서 이러한 값을 잘 처리해줄 수 있도록 해보겠습니다.
Fashion MNIST 분류 모델
Model: "sequential_2"
_________________________________________________________________
Layer (type) Output Shape Param #
=================================================================
flatten (Flatten) (None, 784) 0
_________________________________________________________________
dense_8 (Dense) (None, 128) 100480
_________________________________________________________________
dense_9 (Dense) (None, 10) 1290
=================================================================
Total params: 101,770
Trainable params: 101,770
Non-trainable params: 0
원핫 인코딩이 아닌 정답 행렬을 처리하기 위해 compile 함수의 loss에 들어가는 인수를 수정했습니다. 앞에서 썼던 categorical_crossentropy 앞에 sparse라는 말이 들어갔습니다. 희소 행렬을 의미하며, 별다른 전처리가 필요 없습니다.
또 원본이 28x28의 2차원 데이터이기 때문에 Flatten이라는 레이어를 사용해서 1차원으로 재정렬 했습니다. 마지막 레이어의 뉴런 수는 정답 클래스의 갯수와 같은 10개입니다. 학습률은 따로 설정해주지 않아 Adam의 기본 학습률인 0.001로 적용됩니다.
Fashion MNIST 분류 모델 학습
Epoch 1/25
1407/1407 [==============================] - 4s 2ms/step - loss: 0.5237 - accuracy: 0.8167 - val_loss: 0.4321 - val_accuracy: 0.8459
Epoch 2/25
1407/1407 [==============================] - 2s 2ms/step - loss: 0.3895 - accuracy: 0.8595 - val_loss: 0.4064 - val_accuracy: 0.8513
Epoch 3/25
1407/1407 [==============================] - 2s 2ms/step - loss: 0.3482 - accuracy: 0.8721 - val_loss: 0.3894 - val_accuracy: 0.8597
...
Epoch 23/25
1407/1407 [==============================] - 2s 2ms/step - loss: 0.1647 - accuracy: 0.9389 - val_loss: 0.4050 - val_accuracy: 0.8727
Epoch 24/25
1407/1407 [==============================] - 2s 2ms/step - loss: 0.1575 - accuracy: 0.9405 - val_loss: 0.3523 - val_accuracy: 0.8907
Epoch 25/25
1407/1407 [==============================] - 2s 2ms/step - loss: 0.1542 - accuracy: 0.9429 - val_loss: 0.3696 - val_accuracy: 0.8915
훈련 데이터는 크게 증가하지만 검증 데이터는 비교적 많이 증가하지 않습니다. 시각화해서 살펴보겠습니다.
Fashion MNIST 분류 모델의 학습 결과 시각화
역시 과적합 현상을 확인해볼 수 있습니다.
Fashion MNIST 분류 모델 평가
313/313 [==============================] - 0s 1ms/step - loss: 0.4074 - accuracy: 0.8831
[0.4074193239212036, 0.8830999732017517]
테스트 결과 정확도는 0.8831입니다. 괜찮은 수치지만 더 끌어올릴 수 있습니다. 다음 장에서 그 방법을 알아보겠습니다.
댓글남기기