はじめての機械学習、自分で書いて数字が正しく識別できるか試してみる

はじめての機械学習、自分で書いて数字が正しく識別できるか試してみる

機械学習チュートリアルの定番ですが、数字認識をやってみました。今回は自作した数字画像を読み込ませてキチンと識別できるか試しています。うまく識別してくれるとチョットだけ嬉しいものです。

Scikit learnのdigitsデータで数字を識別


テスト環境

機械学習ライブラリーにscikit-learn、学習データ数字は付属データセットのdigitsを利用しています。digitsはMNISTのスモール版です。画像はかなり粗めですが初心者が扱うにはお手頃なデータです。

digitsデータセット

データは0~9の数字がそれぞれ180個前後、計1797個入っています。一つのデータサイズは8×8で明暗0〜16のデータとなっています。以下はdigitsデータをMatplotlibで可視化したものです。画像は各数字データを平均化して値で出力しています。
In [2]:
import numpy as np
from sklearn.datasets import load_digits
import matplotlib.pyplot as plt

digits = load_digits() # digitsデータ読み込み
data_train = digits.images # (1797, 8, 8) 8×8サイズの数字データ1797点
label_train = digits.target # (1797,) 正解ラベル

# 各数字データの平均化した値を画像で出力
mean_images = np.zeros((10,8,8))
fig = plt.figure(figsize=(10,5))
for i in range(10):
    mean_images[i] = data_train[label_train==i].mean(axis=0)
    ax = fig.add_subplot(2, 5, i+1)
    ax.axis('off')
    ax.set_title('train.{0} (n={1})'.format(i, len(data_train[label_train==i])))
    ax.imshow(mean_images[i],cmap=plt.cm.gray, interpolation='none')
plt.show()

検証テストに使う自作の数字画像

0~9の10個の数値画像はペイントとマウスで作成しています。機械学習に使う際はdigitsのデータ形式に変換してから利用しますので、画像は縦横の比率がおおよそ同じであれば特に支障ないようです。だだし、線の太さや濃淡などは識別結果に大きく影響するため注意です。

以下はMatplotlibでpng画像のデータをそままま出力したものです。画像ファイルの読み込みはPILライブラーを利用しています。
In [1]:
import numpy as np
import matplotlib.pyplot as plt
from PIL import Image

# 自作数字の読み込み
pil_images = []
for i in range(10):
    # mytest_数値.pngでローカルに保存したファイル
    pil_image = Image.open('mytest_' + str(i) + '.png').convert('L') # 8bit(256階調) gray
    pil_images.append(pil_image)

# 画像出力(オリジナル)
fig = plt.figure(figsize=(10,5))
for i in range(10):
    ax = fig.add_subplot(2, 5, i+1)
    ax.axis('off')
    ax.set_title('MyDraw.{}'.format(i))
    ax.imshow(pil_images[i],cmap=plt.cm.gray, interpolation='none')
plt.show()

自作画像データの形式変換

読み込んだ自作画像データはトレーニングデータと形式を合わせます。
  • データを8×8にリサイズする
  • 明暗を反転する(文字を明るく)
  • 明暗を16階調に変換する(0~255→0~16)
  • numpy.ndarray型に変換する
In [1]:
import numpy as np
import matplotlib.pyplot as plt
from PIL import Image

# 自作数字の読み込み
pil_images = []
for i in range(10):
    # mytest_数値.pngでローカルに保存したファイル
    pil_image = Image.open('mytest_' + str(i) + '.png').convert('L') # 8bit(256階調) gray
    pil_image_resize = pil_image.resize((8, 8), Image.LANCZOS)
    pil_images.append(pil_image)

# pil画像データ変換(サイズ、階調をトレーニングデータ形式あわせる)
test_data = np.empty((10, 8, 8), dtype=float)
for i in range(10):
    pil_image_resize = pil_images[i].resize((8, 8), Image.LANCZOS) # 8×8にリサイズ
    test_data[i] = np.array(pil_image_resize, dtype=float) # ndarray型に変換
    test_data[i] = 16 - np.floor(17 * test_data[i] / 256) # 0-255 -> 0-16&明暗反転

# 画像出力(digits形式に変換後)
fig = plt.figure(figsize=(10,5))
for i in range(10):
    ax = fig.add_subplot(2, 5, i+1)
    ax.axis('off')
    ax.set_title('MyTest.{}'.format(i))
    ax.imshow(test_data[i],cmap=plt.cm.gray, interpolation='none')
plt.show()

機械学習で手書き文字を識別

学習と検証テストを行った結果を下に貼付します。数字識別用の分類器にはSVM(SVC)を利用しています。学習とテストの処理はわずか数行のコードで実現できます。大まかな処理の流れは以下の通りです。
  • トレーニングデータを準備する
  • テスト用データを準備する(今回は自作の数字)
  • トレーニングデータで学習を行い解析モデルを得る
  • 解析モデルでテストデータを識別する
  • 識別された結果を検証する
In [1]:
import numpy as np
from sklearn import datasets, svm, metrics
from PIL import Image

# ■トレーニングデータの準備■
digits = datasets.load_digits() #ldigitsデータ読み込み (1797, 8, 8)
# リサイズデータ(1797, 8, 8) -> (1797, 64) digits.dataでも同じ
data_train = digits.images.reshape((digits.images.shape[0],-1)) 
label_train = digits.target # 正解ラベル(1797,)

# ■自作数値データの準備■
# pilで画像読み込み
pil_images = []
for i in range(10):
    # mytest_数値.pngでローカルに保存したファイル
    pil_image = Image.open('mytest_' + str(i) + '.png').convert('L') # 8bit(256階調) gray
    pil_image_resize = pil_image.resize((8, 8), Image.LANCZOS)
    pil_images.append(pil_image)
# pil画像のデータ変換
test_data = np.empty((10, 8, 8), dtype=float)
for i in range(10):
    pil_image_resize = pil_images[i].resize((8, 8), Image.LANCZOS) # 8×8にリサイズ
    test_data[i] = np.array(pil_image_resize, dtype=float) # ndarray型に変換
    test_data[i] = 16 - np.floor(17 * test_data[i] / 256) # 0-255 -> 0-16&明暗反転
test_data = test_data.reshape((test_data.shape[0],-1)) #リサイズデータ(10, 8, 8) -> (10, 64)
# テストデータの正解ラベル(10,)
label_test = np.array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]) 

# ■トレーニングデータで機械学習SVM(SVC)■
clf = svm.SVC(gamma=0.001, C=10.0)
clf.fit(data_train, label_train)

# ■自作数値データで識別テスト■
predict = clf.predict(test_data)
# ■結果検証
print('テストラベル(正解の数字)') 
print(label_test)
print('解析結果(識別した数字)')
print(predict)
ac_score = metrics.accuracy_score(label_test, predict)
print("正解率{}%".format(ac_score*100))
テストラベル(正解の数字)
[0 1 2 3 4 5 6 7 8 9]
解析結果(識別した数字)
[0 1 2 3 4 5 6 7 8 9]
正解率100.0%

自分で書いた数字を無事に識別することができました。正解率100%と表示されていますが手書き文字は何度か修正しております。

変換後の画像が8×8と粗いため適当に数字を書くと正解率は7割程度の結果でした。(うまく正解してもらうには分類器の気持ち?になって書く必要がありそうです)
練習を積んで、次の機会にはMNISTを使って試してみたいと思います。

スポンサーリンク

0 件のコメント :

コメントを投稿