이번 장에서는 드디어 딥러닝을 처음 배우기 시작했습니다. 가장 간단한 딥러닝 모델은 로지스틱 회귀와 같은 과정을 거쳐 계산하므로 먼저 로지스틱 회귀로 다중 분류를 해보고, 같은 데이터셋으로 keras와 tensorflow를 이용하여 인공 신경망으로 다중 분류를 해보았습니다.
1. fashion mnist 데이터 셋
mnist는 0 ~ 9의 숫자로 이루어진 데이터 셋으로 저도 알고 있을 만큼 무척 유명한 데이터 셋입니다. 이 장에서 사용한 fashion mnist는 숫자 대신 옷의 사진이 담겨 있습니다.
1
2
3
from tensorflow import keras
(train_input, train_target), (test_input, test_target) = keras.datasets.fashion_mnist.load_data() # 패션 MNIST를 불러오는 load_data 함수는 데이터셋을 train과 test로 나누어 불러옴
keras에 내장되어 있는 fashion mnist를 load_data 메서드로 부르면 60000개의 사진이 있는 train 셋과 10000개의 사진이 있는 test 셋으로 나누어 전달해 줍니다.
1
2
3
4
5
6
7
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')
plt.show()
직접 사진을 출력해 보면 위와 같은 작은 이미지들로 구성되어 있음을 확인할 수 있습니다. 타겟 값은 0 ~ 9까지로, 0 티셔츠 1 바지 2 스웨터 3 드레스 4 코드 5 샌달 6 셔츠 7 스니커즈 8 가방 9 앵클 부츠입니다. 다음의 코드를 통해 각 클래스별로 6000개씩의 데이터가 있음을 알 수 있습니다.
1
2
3
4
import numpy as np
print(np.unique(train_target, return_counts=True)) # 각 Class별로 6000개씩의 데이터
=>(array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9], dtype=uint8), array([6000, 6000, 6000, 6000, 6000, 6000, 6000, 6000, 6000, 6000]))
2. 로지스틱 회귀
로지스틱 회귀로 fashion mnist 데이터 셋을 분류해 봅시다.
1
2
3
4
5
6
7
8
9
10
11
12
13
train_scaled = train_input / 255.0 # 특성 스케일을 위해 0 ~ 255 사이의 정수 값을 가지는 각 픽셀을 255로 나누어 줌 -> 0~1 사이의 값으로 정규화
train_scaled = train_scaled.reshape(-1, 28*28) # 28 x 28 이미지를 1차원 배열로
from sklearn.model_selection import cross_validate
from sklearn.linear_model import SGDClassifier
# 사이킷런 1.1.0 버전 이하일 경우 'log_loss'를 'log'로 바꾸어 주세요.
sc = SGDClassifier(loss='log', max_iter=5, random_state=42)
scores = cross_validate(sc, train_scaled, train_target, n_jobs=-1)
print(np.mean(scores['test_score']))
=>0.8195666666666668
로지스틱 회귀는 특성 스케일에 민감하므로 0 ~ 255 값을 갖는 각 픽셀을 255로 나누어 0 ~ 1 사이의 값으로 정규화 해줍니다. 1차원 배열이 되도록 reshape하고 로지스틱 회귀 모델에 넣어 coss validate로 교차 검증해주면 0.8195 정도의 score를 얻습니다.
3. 인공 신경망
앞에서 언급했듯, 가장 단순한 인공 신경망 모델은 로지스틱 회귀와 같습니다. 각 픽셀들의 데이터를 입력받고, 훈련하여 가중치(절편과 기울기)를 결정한 뒤 타깃 별로 출력층에 방정식을 만듭니다. 여기까지의 작업은 다음과 같습니다.
1
2
3
4
5
# 실행마다 동일한 결과를 얻기 위해 케라스에 랜덤 시드를 사용하고 텐서플로 연산을 결정적으로 만듭니다.
import tensorflow as tf
tf.keras.utils.set_random_seed(42)
tf.config.experimental.enable_op_determinism()
위의 코드는 실행마다 동일한 결과를 얻기 위한 코드입니다. 자세히 알고 싶다면 여기를 참고하면 됩니다.
1
2
3
4
5
6
7
8
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) # 딥러닝에서는 대부분의 경우 데이터 셋이 충분히 크므로 검증 세트를 따로 떼어내어 사용
dense = keras.layers.Dense(10, activation='softmax', input_shape=(784,)) # 밀집층(뉴런 개수, 뉴런의 출력에 적용할 함수, 입력의 크기), 다중분류이므로 softmax를 활성화 함수로 사용함
model = keras.Sequential(dense) # 위에서 만든 밀집층을 가진 신경망 모델
딥러닝에서는 보통 데이터가 많은 상태에서 모델을 만들기 때문에 교차 검증 하지 않고 검증 세트를 따로 떼어 사용합니다. 그 다음으로는 밀집층을 만들텐데, 이 밀집층은 784(28 * 28)개의 픽셀들로 구성된 입력층과 10개의 타깃값들로 구성된 출력층, 출력층에서 나오는 값을 확률로 바꾸기 위한 활성화 함수로 구성되어 있습니다. 저희가 사용할 것은 이 밀집층이 전부이므로 모델 안에 만든 밀집층을 넣어주면 됩니다.
이제 만든 모델로 분류를 해봅시다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
model.compile(loss='sparse_categorical_crossentropy', metrics='accuracy') # 케라스 모델은 훈련하기 전에 꼭 compile하는 단계를 거쳐야 함, categorical은 다중 분류, sparse은 음성 클래스의 타깃값을 1로 하여 계산함을 뜻함
model.fit(train_scaled, train_target, epochs=5)
=>Epoch 1/5
1500/1500 [==============================] - 4s 2ms/step - loss: 0.6058 - accuracy: 0.7932
Epoch 2/5
1500/1500 [==============================] - 3s 2ms/step - loss: 0.4785 - accuracy: 0.8385
Epoch 3/5
1500/1500 [==============================] - 3s 2ms/step - loss: 0.4564 - accuracy: 0.8471
Epoch 4/5
1500/1500 [==============================] - 3s 2ms/step - loss: 0.4435 - accuracy: 0.8539
Epoch 5/5
1500/1500 [==============================] - 3s 2ms/step - loss: 0.4358 - accuracy: 0.8551
<keras.callbacks.History at 0x7fbab43b7810>
model.evaluate(val_scaled, val_target) # evaluate : 성능 평가
=>375/375 [==============================] - 1s 2ms/step - loss: 0.4579 - accuracy: 0.8483
[0.4579426348209381, 0.8483333587646484]
keras 모델은 훈련 전에 반드시 compile을 해야 합니다. loss에는 손실함수를, metrics에는 추가로 표시할 것을 적는데 score를 표시하도록 하였습니다. 이렇게 하면 각 epoch마다 손실과 score의 변동을 볼 수 있습니다.
모델의 평가에는 evaluate를 사용하는데 검증 세트로 확인해봐도 표시된 것과 비슷한 loss와 accuracy를 얻었음을 알 수 있습니다.
공부한 내용의 전체 코드는 github에 작성해 두었습니다.