꿈이 너무 많은 한 대학생의 공간

7-1 향상된 숫자 인식 모델 제작하기 본문

튜토리얼 번역/손글씨 숫자 분류기 만들기

7-1 향상된 숫자 인식 모델 제작하기

청울울청 2020. 4. 22. 11:59

이전 과정에서는, 우리는 MNIST 데이터셋을 이용해 숫자를 인식하는 모델을 훈련시켰습니다. 우리는 98%의 정확도를 달성할 수 있었지만, 안드로이드 앱에서 테스트할때는 아마 문제점을 느꼈을 것 입니다. 비록 앱이 그려진 숫자를 인식할 수 있어도, 정확도는 아마도 98%보다 낮을 것 입니다.

 

이번 과정에서는, 정확도 하락의 문제점과 확대된 데이터를 사용해 정확도를 향상시켜 볼 것입니다.

 

준비

 

Tensorflow 와 기타 라이브러리를 설치하는 것으로 시작해 봅시다.

import tensorflow as tf
from tensorflow import keras

import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
import random

print(tf.__version__)

MNIST 데이터셋을 가져와 봅시다.

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

# 입력된 이미지의 각각의 픽셀값을 일반화해봅시다.
train_images = train_images / 255.0
test_images = test_images / 255.0

# 나중에 Keras의 확대된 데이터셋을 끌어내기 위해 train 데이터셋과 validate 데이터셋의
# 이미지들의 컬러 차원(color dimension)을 추가해봅시다.
train_images = np.expand_dims(train_images, axis=3)
test_images = np.expand_dims(test_images, axis=3)

처리 과정에서 쓰일 함수를 정의하면서 나중에 비교할때 쓰일 모델 구조를 가진 다중 모델을 빠르게 제작할 수 있습니다.

def create_model():
  model = keras.Sequential([
    keras.layers.InputLayer(input_shape=(28, 28, 1)),
    keras.layers.Conv2D(filters=32, kernel_size=(3, 3), activation=tf.nn.relu),
    keras.layers.Conv2D(filters=64, kernel_size=(3, 3), activation=tf.nn.relu),
    keras.layers.MaxPooling2D(pool_size=(2, 2)),
    keras.layers.Dropout(0.25),
    keras.layers.Flatten(),
    keras.layers.Dense(10, activation=tf.nn.softmax)
  ])
  model.compile(optimizer='adam',
                loss='sparse_categorical_crossentropy',
                metrics=['accuracy'])
  return model

우리의 모델이 MNIST 데이터셋에서는 98% 이상의 정확도를 달성하는 걸 확인합시다.

base_model = create_model()
base_model.fit(
    train_images,
    train_labels,
    epochs=5,
    validation_data=(test_images, test_labels)
)

정확도 하락의 문제점 찾기

MNIST 데이터셋의 숫자 이미지를 확인하고 우리가 아까 안드로이드 앱 개발에서 경험했던 정확도 하락의 원인을 추측해봅시다.

# Show the first 25 images in the training dataset.
plt.figure(figsize=(10,10))
for i in range(25):
  plt.subplot(5,5,i+1)
  plt.xticks([])
  plt.yticks([])
  plt.grid(False)
  plt.imshow(np.squeeze(train_images[i], axis=2), cmap=plt.cm.gray)
  plt.xlabel(train_labels[i])
plt.show()

우리는 같은 크기의 숫자 이미지 25개를 가지고 있고, 그들은 모두 이미지 정중앙에 위치해 있습니다.

이 가정이 MNIST 데이터셋에서는 성립할 지 확인해 봅시다.

# 숫자가 어디에 위치하는지 반환하는 유틸리티 함수입니다.
def digit_area(mnist_image):
  # 색을 제거합니다.
  mnist_image = np.squeeze(mnist_image, axis=2)

  # 세로로 숫자로부터의 픽셀값의 리스트를 가져옵니다.
  x_nonzero = np.nonzero(np.amax(mnist_image, 0))
  x_min = np.min(x_nonzero)
  x_max = np.max(x_nonzero)

  # 가로로 숫자로부터의 픽셀값의 리스트를 가져옵니다.
  y_nonzero = np.nonzero(np.amax(mnist_image, 1))
  y_min = np.min(y_nonzero)
  y_max = np.max(y_nonzero)

  return [x_min, x_max, y_min, y_max]

# MNIST 데이터셋으로부터의 숫자를 포함하는 위치를 계산합니다.
digit_area_rows = []
for image in train_images:
  digit_area_row = digit_area(image)
  digit_area_rows.append(digit_area_row)
digit_area_df = pd.DataFrame(
  digit_area_rows,
  columns=['x_min', 'x_max', 'y_min', 'y_max']
)
digit_area_df.hist()

아래와 같은 히스토그램을 통해, MNIST 데이터셋으로부터의 숫자는 이미지 정중앙 확실한 위치에 딱 맞는 것을 확인할 수 있습니다.

하지만, 안드로이드 앱에서 숫자를 그릴 때, 위의 데이터처럼 정확히 정중앙에 그리기 위해 집중하지는 않았을 것입니다. 머신러닝 모델은 그려진 숫자를 제대로 보지 못해 결과가 이상하게 나온 것이고, 특히 정중앙 밖에 숫자를 그렸을 때 정확도가 하락했을 것입니다.

 

이제 우리의 가정이 맞다는 것을 증명하기 위해 확대된 데이터를 MNIST 데이터셋에 추가합시다.

우리는 회전, 가로와 세로, 기울임, 확대를 추가함으로써 우리의 MNIST 데이터셋을 왜곡할 것입니다.

# 확대된 데이터를 정의합시다.
datagen = keras.preprocessing.image.ImageDataGenerator(
  rotation_range=30,
  width_shift_range=0.25,
  height_shift_range=0.25,
  shear_range=0.25,
  zoom_range=0.2
)

# MNIST 데이터셋으로부터 확대된 데이터셋을 제작합니다.
train_generator = datagen.flow(train_images, train_labels)
test_generator = datagen.flow(test_images, test_labels)

확대된 데이터셋으로 우리가 아까 훈련시켰던 모델을 평가해보고 정확도가 하락하는지 안하는지를 확인해봅시다.

base_model.evaluate(test_generator)

아마도 확대된 데이터셋에서는 정확도가 상당히 낮아져 40% 이하로 기록된 것을 볼 수 있을 것입니다.

 

확대된 데이터셋과 정확도 향상시키기

 

개발 환경에서 더 잘 작동하도록 우리의 확대된 데이터셋을 학습시켜 봅시다.

improved_model = create_model()
improved_model.fit(train_generator, epochs=5, validation_data=test_generator)

 우리는 학습시키는 중 더 왜곡된 이미지들을 보고 학습했기 떄문에, 왜곡된 test 이미지들을 학습한 모델의 정확도는 90% 이상으로 향상됨을 알 수 있습니다.

 

Tensorflow Lite로 변환하기

 

상향된 모델을 Tensorflow Lite 로 변환하고 안드로이드 앱으로 재배포 해봅시다.

# Keras 모델을 Tensorflow Lite 형식과 양자화된 형식으로 변환
converter = tf.lite.TFLiteConverter.from_keras_model(improved_model)
converter.optimizations = [tf.lite.Optimize.DEFAULT]
tflite_quantized_model = converter.convert()

# 양자화된 모델을 download 디렉토리로 저장
f = open('mnist.tflite', "wb")
f.write(tflite_quantized_model)
f.close()

# 숫자 인식 모델을 다운로드
from google.colab import files
files.download('mnist.tflite')

수고하셨습니다!

 

본 과정이 다 끝났습니다. 3번째 과정으로 돌아가 안드로이드 앱에 재배포한 뒤 정확도가 올랐는지 안올랐는지 확인해 보세요.

 

본 강의는 구글의 Codelab 강의 중 "Build a handwritten digit classifier app with Tensorflow Lite" 강의를 

번역할 것이며, 제가 직접 번역하였기에 오역과 의역이 있음을 알립니다.

Comments