Tensorflow2.0으로 Fashion MNIST 분류하기

Updated:

이번 시간에는 tensorflow 2.0으로 Fashion MNIST 데이터를 분류하는 모델을 만들어 보도록 하겠습니다. 많은 분들이 알고 계시는 MNIST는 0~9까지의 숫자(digit)를 분류하는 문제에 사용되는 데이터셋입니다.

Fashion MNIST는 28x28의 크기를 가지는 이미지로서 10가지 종류의 드레스, 코트, 부츠 등을 분류하는 문제에 사용됩니다. 정확한 label은 fashion mnist github에서 확인할 수 있습니다. 학습 데이터 6만장, 테스트 데이터 1만장을 갖추고 있습니다.

아래는 많은 부분tensorflow tutorial classification에 있는 내용을 참고하였습니다. 다른 점은 예제는 단순 Dense layer만을 사용했는데(약 89%의 테스트 정확도) 성능 향상을 위해서 이를 Conv layer로 바꾼 것입니다. 먼저 실행하기 위해 필요한 것들을 설치해 보겠습니다.

1. 설치

Anaconda 3이 설치되었다는 가정에서 Anaconda prompt를 실행하고 Tensor라는 이름으로 파이썬 3.7버전conda 가상환경을 만들어 주고 가상환경을 활성화하겠습니다.

conda create -n Tensor python=3.7
conda activate Tensor

그리고 tensorflow-gpu 2.0.0 버전을 포함해 필요한 라이브러리들을 설치해 주겠습니다. 참고로 Anaconda prompt에서 코드를 실행하지 않으면 tensorflow-gpu를 인식 못해 에러가 발생할 수도 있습니다.

(정확한 원인은 아직 모르겠습니다. Pycharm에서 conda 가상환경을 가져와 실행했을 때 cudart64_100.dll not found와 같은 에러가 발생했습니다. tensorflow-gpu를 설치할 때 함께 설치된 cudnn 등을 인식 못하는 것 같습니다.)

conda install tensorflow-gpu==2.0.0 numpy matplotlib

저 같은 경우에는

  • tensorflow-gpu 2.0.0
  • numpy 1.18.1
  • matplotlib 3.1.3

이 설치되었습니다.

2. Fashion Mnist 데이터 가져오기

데이터를 가져오는 것은 tensorflow 라이브러리에서 함수를 불러오면 train 이미지, train 라벨, test 이미지, test 라벨 순서대로 가져올 수 있습니다. 정답 라벨은 미리 class_names로 변수를 만들어 주겠습니다.

from tensorflow import keras
import numpy as np
import matplotlib.pyplot as plt
from fashion_mnist_plot import plot_image

(train_images, train_labels), (test_images, test_labels) = keras.datasets.fashion_mnist.load_data()

class_names = ['T-shirt/top', 'Trouser', 'Pullover', 'Dress', 'Coat',
               'Sandal', 'Shirt', 'Sneaker', 'Bag', 'Ankle boot']

그리고 0 ~ 255 사이에 분포한 픽셀 값을 0 ~ 1 사이의 값을 가지게 하기 위해 255로 나누어 정규화를 실시합니다.

train_images = train_images / 255.0
test_images = test_images / 255.0

Conv layer를 갖는 모델을 만들기 이전에 train 이미지, train 라벨, test 이미지, test 라벨의 차원을 1 증가 시키도록 하겠습니다. 모델 처음에 생성된 Conv layer는 input이 4차원일 것이라 기대하는데 현재 이미지는 3차원이기 때문입니다.

train_images = np.expand_dims(train_images, -1)
train_labels = np.expand_dims(train_labels, -1)
test_images = np.expand_dims(test_images, -1)
test_labels = np.expand_dims(test_labels, -1)

numpyexpand_dims함수의 경우에 첫 번째 인자에 numpy array를 넣고, 두 번째 인자를 차원을 증가시킬 axis를 설정할 수 있습니다. 이에 대한 내용은 axis를 인자로 받는 numpy 함수들을 예로 들어 나중에 더 자세히 정리해보도록 하겠습니다.

3. 분류 모델 구성

분류 모델은 기존 예제에서 Dense layer만을 사용했던 것에 Conv layer를 추가하였습니다. Conv - MaxPooling - Dropout를 3번 반복하는 단순한 구조이고, 마지막에 Dense layer를 사용해 완전연결계층(FC layer)를 구성했습니다.

model = keras.Sequential([
    keras.layers.Conv2D(32, (3, 3), padding='same', activation='relu', input_shape=(28, 28, 1)),
    keras.layers.MaxPooling2D(pool_size=2),
    keras.layers.Dropout(0.3),
    keras.layers.Conv2D(64, (3, 3), padding='same', activation='relu'),
    keras.layers.MaxPooling2D(pool_size=2),
    keras.layers.Dropout(0.3),
    keras.layers.Conv2D(128, (3, 3), padding='same', activation='relu'),
    keras.layers.MaxPooling2D(pool_size=2),
    keras.layers.Dropout(0.3),
    keras.layers.Flatten(),
    keras.layers.Dense(256, activation='relu'),
    keras.layers.Dropout(0.5),
    keras.layers.Dense(10, activation='softmax'),
])

model.compile(optimizer='adam', loss='sparse_categorical_crossentropy', metrics=['accuracy'])

활성화 함수에 relu를 사용하고 conv layer의 채널 개수를 32 - 64 - 128로 순차적으로 증가시켜 나갔습니다. optimizer는 adam를, loss function에는sparse_categorigcal_crossentropy를 사용하였습니다.

4. 학습 및 평가

epochs에는 총 30회, 배치 사이즈를 64개로 설정하여 실험을 진행했습니다.

model.fit(train_images, train_labels, epochs=30, batch_size=64)

test_loss, test_acc = model.evaluate(test_images, test_labels, verbose=2)
print('test accuracy: ', test_acc)

그 결과, 약 92.7%로 분류 성능이 향상되었습니다.

.

5. 분류 결과 시각화

약 92.7% 성능을 갖는 fashion mnist 분류 모델에 10개의 이미지를 시각화해서 그 결과를 살펴보겠습니다.

# fashion_mnist_plot.py
import numpy as np
import matplotlib.pyplot as plt


def plot_image(i, predictions_array, true_label, img, class_names):
    predictions_array, true_label, img = predictions_array[i], true_label[i], img[i]
    plt.grid(False)
    plt.xticks([])
    plt.yticks([])

    plt.imshow(img, cmap=plt.cm.binary)

    predicted_label = np.argmax(predictions_array)
    if predicted_label == true_label:
        color = 'blue'
    else:
        color = 'red'

    plt.xlabel("pred:{} {:2.0f}%, label:{}".format(class_names[predicted_label],
                                         100 * np.max(predictions_array),
                                         class_names[true_label]), color=color)
from fashion_mnist_plot import plot_image

predictions = model.predict(test_images)

train_images = np.squeeze(train_images, -1)
train_labels = np.squeeze(train_labels, -1)
test_images = np.squeeze(test_images, -1)
test_labels = np.squeeze(test_labels, -1)

num_rows = 5
num_cols = 2
num_images = num_rows*num_cols
plt.figure(figsize=(2 * 2 * num_cols, 2 * num_rows))
for i in range(num_images):
    plt.subplot(num_rows, 2 * num_cols, 2 * i +1)
    plot_image(i, predictions, test_labels, test_images, class_names)
plt.show()

원활한 시각화를 위해서 이전에 expand_dims 함수로 차원을 증대시킨 것을 squeeze 함수를 사용해서 원래대로 돌려 놓도록 합니다. 결과를 살펴보면 비교적 잘 물체를 분류할 수 있음을 알 수 있습니다.

.

Updated:

Leave a comment