티스토리 뷰

빅데이터

MNIST 인식

한 혜윰 2017. 8. 21. 23:08


MNIST Database 인식


사진 출처: corochanneNote


안녕하세요. 벌써 마지막 작성 글로부터 20일이 됬네요. 그동안 여러가지를 배우고 또 블로그 작성을 위해 MNIST를 복습하느라 정신없이 여러 날을 보냈습니다. 오늘은 머신러닝의 기본인 MNIST 데이터 인식에 대해 배워보도록 하겠습니다. MNIST는 Modified National Institute of Standards and Technology의 줄임말인데요 이 데이터를 이해할려면 그 전 단계인 NIST, 즉 National Institute of Standards and Technology 데이터베이스를 알아야 합니다. NIST 데이터는 미국 인구조사국에서 트레이닝 데이터(필기된 숫자)를, 그리고 미국 고등학생에게서 테스트 데이터(필기된 숫자)를 받았습니다. 하지만 머신러닝에서(통계적으로) 제대로된 인식(예측)을 할려면 궁극적으로 트레이닝과 테스트 데이터는 같아야 하기에 새로운 데이터베이스 MNIST는 트레이닝과 테스트 데이터를 NIST의 트레이닝과 테스트 데이터를 반반씩 가져왔습니다. 뿐만 아니라 기계 친화적이게 28x28 픽셀로 정규화 된 데이터베이스 입니다. MNIST 데이터베이스는 총 60,000개의 트레이닝 데이터와 10,000개의 테스트 데이터로 이루어져 있습니다. 


MNIST 데이터베이스를 다운받는 방법에는 크게 두가지가 있습니다. 첫번째는 파일을 직접 홈페이지에 가서 다운받는 방법과 처음부터 다운로드 코드를 짜는 것이죠. 저희는 목표가 코딩을 배우는 것이기 때문에 모든 과정을 코드화 하겠습니다. 


In [21]:
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function

import tensorflow as tf
tf.reset_default_graph()
from tensorflow.examples.tutorials.mnist import input_data

mnist = input_data.read_data_sets('/Users/yoon/Desktop/87_105_110_107/TP/MNIST/data', one_hot=True)
#directory, one-hot: 숫자 대신 영과 일만 
Extracting /Users/yoon/Desktop/87_105_110_107/TP/MNIST/data/train-images-idx3-ubyte.gz
Extracting /Users/yoon/Desktop/87_105_110_107/TP/MNIST/data/train-labels-idx1-ubyte.gz
Extracting /Users/yoon/Desktop/87_105_110_107/TP/MNIST/data/t10k-images-idx3-ubyte.gz
Extracting /Users/yoon/Desktop/87_105_110_107/TP/MNIST/data/t10k-labels-idx1-ubyte.gz


맨 위의 from __future__ 부분은 파이썬 버젼 2와 3의 호환성 문제를 해결하기 위해 삽입됬습니다. 자세한 설명은 ssut님의 블로그를 읽어보세요. 호환성을 해결 한 뒤 바로 밑 코드는 텐서플로를 불러냅니다. 그리고 저는 실험하던게 있어서 텐서플로의 그래프를 초기화 하기 위해 tf.reset_default_graph()를 삽입했습니다. 나중에 다룰 예정인데 텐서플로의 그래프를 텐서보드로 출력시 저렇게 그래프를 리셋하지 않으면 텐서보드용 폴더를 새로 생성하지 않는 이상 그래프가 중복되어서 나오더군요. 그리고나서 MNIST데이터를 불러냅니다.  반갑게도 텐서플로에서 MNIST 데이터를 받는 코드가 있습니다. input_data.read_data_sets가 바로 그 코드입니다. 제 코딩에서 첫번째 입력 변수는 파일을 저장할 경로고 두번째 매개 변수는 one-hot 벡터에 관한겁니다. 혹시 모르시는 분들을 위해 첨언합니다. 각 데이터는 숫자를 나타내는데 사진 정보와 그 사진이 무슨 숫자를 의미하는 레이블 데이터가 필요합니다. one-hot 벡터는 레이블을 숫자, 예를들어 사진이 7을 나타내면 레이블은 [7]을 표현하는게 아니라 [0,0,0,0,0,0,0,1,0,0] 처럼 오직 영과 일만으로 표현합니다. 일이 표시하는게 의미있는 부분이기에 one-hot 벡터입니다. 


사진 출처: KDnuggets


이 one-hot 벡터를 이용하면 알고리즘의 정확도가 올라갑니다. one-hot 벡터를 이용한다고 모든 데이터에 대한 알고리즘 정확성이 올라가는게 아니라 이런 범주형(categorical) 데이터에 대해 정확도가 올라갑니다. 이렇게 범주에 따라 따로 데이터를 분류하는데 one-hot 벡터로 레이블을 표시하는게 아니라 7은 7로 표현하면 레이블에 대해 숫자적 상관관계도 인식하기 때문에 인식 정확도가 낮아집니다. 극단적인 예를 들자면 한국과 미국, 프랑스를 분류하려고 할 때 한국은 1, 미국은 2, 그리고 프랑스는 3으로 레이블을 적었다고 합시다. 여러 데이터를 분석하지만 한국 X 2 = 미국이라는 상관관계가 성립하지 않기에 충분한 데이터 분석을 하더라도 정확도엔 한계가 있습니다. 대신 one-hot 벡터를 이용해 한국은 [1, 0, 0], 미국은 [0, 1, 0], 프랑스는 [0, 0, 1]로 표현할 수 있고 이 방식이 알고리즘의 나라 분석 정확도에 도움을 줍니다. one-hot 벡터에 대해 어느정도 알았으니 input_data.read_data_sets 코드를 자세히 살펴봅시다.


def read_data_sets(train_dir,
                   fake_data=False,
                   one_hot=False,
                   dtype=dtypes.float32,
                   reshape=True,
                   validation_size=5000,
                   seed=None):
  if fake_data:

.
.
.
  
  train = DataSet(train_images, train_labels, **options)
  validation = DataSet(validation_images, validation_labels, **options)
  test = DataSet(test_images, test_labels, **options)
  
  return base.Datasets(train=train, validation=validation, test=test)


이 코드는 트레이닝, 밸리데이션 그리고 테스트 데이트를 Datasets 객체로 출력하네요. 여기서 DataSet은 클래스입니다. 동일한 코드 페이지 안에 DataSet 클래스에 대한 코드가 있습니다. 보시다시피 객체에 대한 특성으로 images, labels, num_examples, 그리고 epochs_completed가 있네요.


class DataSet(object):

  def __init__(self,
               images,
               labels,
               fake_data=False,
               one_hot=False,
               dtype=dtypes.float32,
               reshape=True,
               seed=None):

.
.
.

    self._images = images
    self._labels = labels
    self._epochs_completed = 0
    self._index_in_epoch = 0

  @property
  def images(self):
    return self._images

  @property
  def labels(self):
    return self._labels

  @property
  def num_examples(self):
    return self._num_examples

  @property
  def epochs_completed(self):
    return self._epochs_completed


images와 num_examples 특성을 활용해 데이터를 살펴보도록 하겠습니다.


In [22]:
print("MNIST: ", mnist)
print("MNIST 트레인 데이터 사이즈:", mnist.train.num_examples)
print("MNIST 밸리데이션 데이터 사이즈:", mnist.validation.num_examples)
print("MNIST 테스트 데이터 사이즈:", mnist.test.num_examples)
MNIST:  Datasets(train=<tensorflow.contrib.learn.python.learn.datasets.mnist.DataSet object at 0x1132e75c0>, validation=<tensorflow.contrib.learn.python.learn.datasets.mnist.DataSet object at 0x111b8d4e0>, test=<tensorflow.contrib.learn.python.learn.datasets.mnist.DataSet object at 0x111b8db70>)
MNIST 트레인 데이터 사이즈: 55000
MNIST 밸리데이션 데이터 사이즈: 5000
MNIST 테스트 데이터 사이즈: 10000


mnist 데이터베이스는 파이선 collections 모듈에 있는 tuple 서브클래스 입니다. 쉽게 말해 지칭어가 있는 tuple이라 생각하시면 됩니다. 출력란 첫번째 줄에서 확인할 수 있다시피 mnisttensorflow.contrib.learn.python.learn.datasets.mnist.DataSet 클래스의 객체임을 볼 수 있습니다. 이 데이터베이스는 밸리데이션(입증)을 위해 5000개의 데이터를 따로 떼어놨습니다. 


왜 트레이닝/테스트의 2단계가 아니라 트레이닝/밸리데이션/테스트의 3단계로 데이터베이스를 구분해 놨는지 헷갈려 하시는 분들을 위해 첨언하겠습니다. 큰 그림을 보자면 이와같은 지도 학습(supervised learning)방식에는 2가지 데이터, 트레이닝과 테스트 데이터가 필요합니다. 실직적으로는 머신러닝에 여러가지 알고리즘이 있기에 각 알고리즘을 비교 분석하기 위해 밸리데이션을 데이터베이스를 따로 떼어놨습니다. 밸리데이션 데이터 베이스를 통해 기록이 가장 좋은 알고리듬을 찾아내고 다시 그걸 테스트 데이터 베이스를 통해 확인할 수 있습니다. 저는 이번에 한가지의 알고리듬만을 갖고 진행할 거기에 트레인과 테스트 데이터만 사용하겠습니다. 


In [23]:
mnist.train.images[0]
Out[23]:
array([ 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. ,  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. ,  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. ,  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. ,  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. ,  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. ,  0. ,  0. ,  0. ,  0. ,  0. ,  0. ,
        0. ,  0. ,  0. ,  0. ,  0. ,  0. ,  0. ,  0. ,  0. ,  0.4,  0.4,
        0.3,  0.5,  0.2,  0. ,  0. ,  0. ,  0. ,  0. ,  0. ,  0. ,  0. ,
        0. ,  0. ,  0. ,  0. ,  0. ,  0. ,  0. ,  0.4,  0.5,  0.9,  0.9,
        0.9,  0.9,  0.9,  0.9,  1. ,  1. ,  1. ,  1. ,  1. ,  0.9,  0.7,
        0.1,  0. ,  0. ,  0. ,  0. ,  0. ,  0. ,  0. ,  0. ,  0. ,  0. ,
        0. ,  0.5,  1. ,  1. ,  1. ,  1. ,  1. ,  1. ,  1. ,  1. ,  1. ,
        1. ,  1. ,  1. ,  1. ,  1. ,  1. ,  0.7,  0.1,  0. ,  0. ,  0. ,
        0. ,  0. ,  0. ,  0. ,  0. ,  0. ,  0. ,  0.9,  1. ,  0.8,  0.8,
        0.8,  0.8,  0.8,  0.5,  0.2,  0.2,  0.2,  0.2,  0.2,  0.5,  0.9,
        1. ,  1. ,  0.7,  0.1,  0. ,  0. ,  0. ,  0. ,  0. ,  0. ,  0. ,
        0. ,  0. ,  0.1,  0.3,  0.1,  0. ,  0. ,  0. ,  0. ,  0. ,  0. ,
        0. ,  0. ,  0. ,  0. ,  0. ,  0.1,  0.8,  1. ,  1. ,  0.5,  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.3,  1. ,  1. ,  0.9,  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.3,  1. ,  1. ,  0.9,
        0. ,  0. ,  0. ,  0. ,  0. ,  0. ,  0. ,  0. ,  0. ,  0. ,  0. ,
        0. ,  0. ,  0. ,  0. ,  0. ,  0. ,  0. ,  0. ,  0. ,  0. ,  0. ,
        0. ,  0.4,  0.6,  1. ,  1. ,  1. ,  0.2,  0. ,  0. ,  0. ,  0. ,
        0. ,  0. ,  0. ,  0. ,  0. ,  0. ,  0. ,  0. ,  0. ,  0. ,  0. ,
        0. ,  0. ,  0.1,  0.5,  0.9,  0.9,  0.9,  1. ,  1. ,  1. ,  1. ,
        1. ,  0.9,  0. ,  0. ,  0. ,  0. ,  0. ,  0. ,  0. ,  0. ,  0. ,
        0. ,  0. ,  0. ,  0. ,  0. ,  0. ,  0.3,  0.5,  0.9,  1. ,  1. ,
        1. ,  1. ,  1. ,  1. ,  1. ,  1. ,  1. ,  0.6,  0. ,  0. ,  0. ,
        0. ,  0. ,  0. ,  0. ,  0. ,  0. ,  0. ,  0. ,  0. ,  0. ,  0.1,
        0.7,  1. ,  1. ,  1. ,  1. ,  0.9,  0.8,  0.8,  0.3,  0.3,  0.8,
        1. ,  1. ,  0.5,  0. ,  0. ,  0. ,  0. ,  0. ,  0. ,  0. ,  0. ,
        0. ,  0. ,  0. ,  0. ,  0.4,  0.9,  1. ,  0.9,  0.9,  0.5,  0.3,
        0.1,  0. ,  0. ,  0. ,  0. ,  0.8,  1. ,  0.9,  0.2,  0. ,  0. ,
        0. ,  0. ,  0. ,  0. ,  0. ,  0. ,  0. ,  0. ,  0. ,  0. ,  0.7,
        1. ,  0.7,  0.2,  0. ,  0. ,  0. ,  0. ,  0. ,  0. ,  0. ,  0.2,
        0.9,  1. ,  0.9,  0. ,  0. ,  0. ,  0. ,  0. ,  0. ,  0. ,  0. ,
        0. ,  0. ,  0. ,  0. ,  0. ,  0.1,  0.5,  0. ,  0. ,  0. ,  0. ,
        0. ,  0. ,  0. ,  0. ,  0. ,  0.3,  1. ,  1. ,  0.7,  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.5,  1. ,  0.9,  0.2,  0. ,  0. ,  0. ,  0. ,  0. ,  0. ,  0. ,
        0. ,  0. ,  0. ,  0. ,  0. ,  0. ,  0. ,  0. ,  0. ,  0. ,  0. ,
        0. ,  0. ,  0. ,  0. ,  0. ,  0.8,  1. ,  1. ,  0.7,  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.9,  1. ,  0.9,  0.2,  0. ,  0. ,  0. ,  0. ,  0. ,  0. ,  0. ,
        0. ,  0. ,  0. ,  0. ,  0. ,  0. ,  0. ,  0. ,  0. ,  0. ,  0. ,
        0. ,  0. ,  0. ,  0. ,  0. ,  0.3,  1. ,  0.9,  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.8,  1. ,  0.6,  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.5,  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. ,  0. ,  0. ,  0. ,  0. ,
        0. ,  0. ,  0. ], dtype=float32)


이렇게 숫자만을 나타내니까 정확히 어떤 숫자를 나타내는지 모르겠네요. 총 데이터 크기는 얼마나 되는지 보겠습니다.


In [24]:
print(len(mnist.train.images[0]))
784


총 784개 입니다. MNIST 데이터는 앞서 설명했다시피 본시 28x28의 데이터로로 이루어진 2-D 구조 였습니다만 효율적인 데이터 집적을 위해 1차원 데이터, 즉 28 x 28 = 784의 1-D 구조로 변경되어 있습니다. 그러면 이제 시각화를 해보겠습니다. 파이선을 하시는 분들은 많이 들어봤을 matplotlib을 이용하겠습니다.


In [25]:
%matplotlib inline
import matplotlib #그래프화 하기 좋은 모듈
import matplotlib.pyplot as plt

some_digit = mnist.train.images[1] #숫자 하나
some_digit_image = some_digit.reshape(28, 28) #2차원으로
plt.imshow(some_digit_image, cmap = matplotlib.cm.binary,
           interpolation="bessel")
plt.axis("off")
plt.show()


%matplotlib inline은 jupyter의 magic function 중 하나입니다. magic function이라는게 퍼센트를 시작으로 커맨드 라인처럼 한줄만 입력해서 인식합니다. 아까 말했다시피 images는 784개의 1차원 데이터로 이루어져 있으니 2차원으로 치환해 plt.imshow 함수를 이용해 시각화 합니다. 저는 데이터 표현 방식을 matplotlib.cm.binary로 했고 interpolation(부드러운 시각화를 위해 각 데이터 값 사이를 수학 공식을 이용해 채우는 방식)은 bessel로 하겠습니다. 숫자 하나하나를 이렇게 입력해 표현하기에는 귀찮으므로 함수화를 하겠습니다.


In [26]:
def plot_digit(data):
    image = data.reshape(28, 28)
    plt.imshow(image, cmap = matplotlib.cm.binary,
              interpolation="neareast")
    plt.axis("off")


이렇게 숫자를 하나하나 표현하기보다 여러개를 동시에 표현할 수 있습니다.


In [27]:

import numpy as np def plot_digits(instances, images_per_row=10, **options): size = 28 images_per_row = min(len(instances), images_per_row) images = [instance.reshape(size, size) for instance in instances] n_rows = (len(instances) - 1) // images_per_row + 1 #floor division. Do write it down on a paper and see it row_images = [] n_empty = n_rows * images_per_row - len(instances) images.append(np.zeros((size, size * n_empty))) #make a empty space for row in range(n_rows): rimages = images[row * images_per_row : (row + 1) * images_per_row] row_images.append(np.concatenate(rimages, axis=1)) #axis=1 는 오른쪽으로 (adding new columns) image = np.concatenate(row_images, axis=0) plt.imshow(image, cmap = matplotlib.cm.binary, **options) plt.axis("off")


함수가 길기에 나눠서 설명하겠습니다.


size = 28
images_per_row = min(len(instances), images_per_row)
images = [instance.reshape(size, size) for instance in instances]


각 줄 마다 몇개의 이미지를 놀 지 정하고 images라는 리스트 데이터를 만듭니다.


n_rows = (len(instances) - 1) // images_per_row + 1
row_images = []
n_empty = n_rows * images_per_row - len(instances)
images.append(np.zeros((size, size * n_empty)))


총 몇개의 줄이 필요한지 정하고 각 줄에 들어갈 이미지 리스트 데이터를 만듭니다. 마지막 줄에 몇개의 공백 데이터가 필요한지 정하고 images 리스트 데이터에 공백 데이터를 추가합니다.


for now in range(n_rows):
    rimages = images[row * images_per_row : (row + 1) * images_per_row]
    row_images.append(np.concatenate(rimages, axis=1))
image = np.concatenate(row_images, axis=0)


rimages를 만들어 images 리스트 데이터에서 필요한 숫자만큼의 이미지를 복사하고 row_images에 추가합니다. 하지만 row_images또한 결국 리스트 데이터이기 때문에 image라는 numpy 데이터를 만들어 row_images의 데이터를 추가하고 시각화 하는게 위에 보여드린 plot_digits 함수입니다.


사용법은 간단합니다. mnist.train.images 데이터 여러개를 입력하면 됩니다. 


examples = np.r_[mnist.train>images[:12000:600], mnist.train.images[13000:40000:600]]
plot_digits(examples)


여기까지 시각화를 마치겠습니다. 한 포스트에 텐서플로를 이용한 분석과 텐서보드를 이용한 시각화까지 진행하고 싶었지만 포스트가 너무 길어져 나머지는 나눠서 올리겠습니다. 



참조

www.tensorflow.org

https://github.com/ageron/handson-ml

Stanford CS20 SI

'빅데이터' 카테고리의 다른 글

MNIST 데이터베이스 시각화  (0) 2017.11.06
MNIST 분석  (0) 2017.09.05
댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/03   »
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
글 보관함