19
15

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

自分の手書きデータをTensorFlowで予測する

Last updated at Posted at 2018-06-11

はじめに

TensorFlowのチュートリアルで、MNISTを使って学習すると90%以上の精度がでます。そこで誰しも思うのが、「自分で書いた手書きデータも当然予測できるよね?」だと思います。
私も以前、同じことを思って自分の手書きデータを予測させてみたのですが、精度は50%程度だったのを覚えてます。90%以上の精度がでるはずなのに、なぜ!?
今回はようやく解決できたので記事にしました。キーとなるのは前処理でした。

MNISTの前処理

MNISTは、THE MNIST DATABASE of handwritten digitsで公開されている手書きの数字データです。公開サイトを見ると、ちゃんと前処理について書いてあります。

The original black and white (bilevel) images from NIST were size normalized to fit in a 20x20 pixel box while preserving their aspect ratio. The resulting images contain grey levels as a result of the anti-aliasing technique used by the normalization algorithm. the images were centered in a 28x28 image by computing the center of mass of the pixels, and translating the image so as to position this point at the center of the 28x28 field.

MNISTは20×20ピクセルに変換された画像(ただしアスペクト比は保ったまま)の重心を、28×28ピクセルの画像の中心に合わせた画像となってます。
当初、私の予測させるための画像は、28×28ピクセルに数字を書いただけでした。
では実際に前処理の有無で精度がどう変わるか見ていきましょう。

使用するモデル

TensorFlowで以下のネットワークを組みました。KerasのAPIを使っています。
200エポック回して、MNISTのテストデータで精度が97%程度でした。
(※コードは抜粋です。今後も基本的にコードは抜粋です。)

learn.py
# Define model
model = tf.keras.Sequential([
    tf.keras.layers.Dense(100, activation="relu", input_shape=(28 * 28,)),
    tf.keras.layers.Dense(100, activation="relu"),
    tf.keras.layers.Dense(10, activation="softmax")
])

model.compile(optimizer='sgd',
              loss='categorical_crossentropy',
              metrics=['accuracy'])

前処理前のデータで予測

最初は、28×28ピクセルの画像に数字を書いた前処理前のデータで予想しました。
以下の画像になります。

0.png 1.png 2.png 3.png 4.png 5.png 6.png 7.png 8.png 9.png

結果は下記の通りで、60%の精度でした。
(0から順に9まで予測した結果です。)

predict.py
# Predict raw data
images = []

for i in range(10):
    im = Image.open('data/{}.png'.format(i)).convert('L')
    im = np.array(im)
    im = im.reshape((28 * 28))
    im = np.true_divide(im, 255)
    images.append(im)
pred = model.predict_classes(np.array(images))
score = metrics.accuracy_score([0, 1, 2, 3, 4, 5, 6, 7, 8, 9], pred)

print(pred)
print('Accuracy is {}%'.format(score * 100))


[3 1 2 3 4 5 5 3 3 9]
Accuracy is 60.0%

前処理後のデータで予測

次は前処理を行います。以下のコードで20×20ピクセルにリサイズしてから、重心を28×28ピクセルの画像の中心に貼り付けます。

predict.py
# Define preprocess
def preprocess(image):
    # Resize to 20 * 20
    im = image.resize((20, 20))

    # Pasete to 28 * 28
    im_ret = Image.new('L', (28, 28))
    im_ret.paste(im, (4, 4))

    # Compute the center of mass and translate the image
    # to the point at the center of 28 * 28
    y_center, x_center = center_of_mass(np.array(im_ret))
    x_move = x_center - 14
    y_move = y_center - 14
    im_ret = im_ret.transform(size=(28, 28), method=Image.AFFINE,
                              data=(1, 0, x_move, 0, 1, y_move))
    return im_ret

前処理を行った画像は以下になります。

conv0.png conv1.png conv2.png conv3.png conv4.png conv5.png conv6.png conv7.png conv8.png conv9.png

結果は下記の通り、90%の精度になりました。
(0から順に9まで予測した結果です。)

predict.py
# Predict proprocessed data
images = []

for i in range(10):
    im = Image.open('data/{}.png'.format(i)).convert('L')
    im = preprocess(im)
    im.save('data/conv{}.png'.format(i))
    im = np.array(im)
    im = im.reshape((28 * 28))
    im = np.true_divide(im, 255)
    images.append(im)
pred = model.predict_classes(np.array(images))
score = metrics.accuracy_score([0, 1, 2, 3, 4, 5, 6, 7, 8, 9], pred)

print(pred)
print('Accuracy is {}%'.format(score * 100))

[0 1 2 3 4 5 6 7 2 9]
Accuracy is 90.0%

位置をずらして予測

追加の実験で数字の7で、位置を上下左右にずらして予測させてみました。予測に使った画像は以下の通りになります。

conv7t.png conv7b.png conv7l.png conv7r.png

結果は下記の通り、ずらした画像は4つのうち1つしか当たりませんでした。
(比較のため、ずらしていない画像の予測も行っています。一番最初の結果がずらしていない画像になります。そのあとは上下左右の順に予測しています。)

predict.py
# Predict shifted data
images = []

for i in ['', 't', 'b', 'l', 'r']:
    im = Image.open('data/conv7{}.png'.format(i)).convert('L')
    im = np.array(im)
    im = im.reshape((28 * 28))
    im = np.true_divide(im, 255)
    images.append(im)
pred = model.predict_classes(np.array(images))
score = metrics.accuracy_score([7, 7, 7, 7, 7], pred)

print(pred)
print('Accuracy is {}%'.format(score * 100))

[7 3 4 7 5]
Accuracy is 40.0%

まとめ

今回はMNISTで学習したモデルで自分の手書きデータを予測してみました。0から9の数字のデータなので、簡単に予測できるのかと思いきや、前処理の有無で結果がずいぶんと違っていました。
データ分析の本等を読むとデータの前処理の重要性が必ずと言っていいほど書かれています。今回はそれを実感しました。

今回使ったソースコードはGitHubに公開していますので、処理の詳細を確認したい方は参照して下さい。

ちなみに初めてTensorFlowを触ったときに書いた記事でも、手書きデータでの予測をやっていました。読み返すと50%や60%の精度となっています。こちらの記事も更新しておきました。

Cloud9でTensorFlowのチュートリアル(初心者のためのMNIST)をやってみた~手書き画像の分類~
Cloud9でTensorFlowのチュートリアルで畳み込みニューラルネットワーク(CNN)をやってみた~手書き画像の分類~

19
15
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
19
15

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?