今回は機械学習を用いて数字の分類問題について皆様にご紹介させていただきます。
前回の記事をご覧になっていない方はこちらから確認していただけると幸いです。
OpenCVと機械学習-画像,動画入門
文字認識-手書き数字を判定しよう
画像を扱う機械学習で、もっともよく見かけるサンプルが「手書き数字の判定」です。手書き数字の判定に挑戦してみましょう。
手書き数字の光学認識デニタセッドを使ってみよう
初に scikit-learn に標準で付属している手書き数字データセットを使ってみましょう。8×8ピクセルの手書き数字データが5620個ほど用意されています。この元データは、UCI Machine Learning Repository で公開されているものです。
Optical Recognition of Handwritten Digits Data Set
(URL) https://archive.ics.uci.edu/ml/datasets/optical+recognitiontoshandwritten+digits
scikit-learn に収録されている手書き数字データを読み込むには、以下のように記述します。
from sklearn import datasets
digits=datasets.load_digits()
読み出したデータですが、この digits は辞書型 (dict)になっており、次のような仕組みとなっています。
digits.images:画像データの配列
digits.target:データがどの数字を表すかのラベルデータ
つまり、digits.images には、8×8ピクセルの二次元の画像データが入っており、digits.target には、digits.images の画像にどの数字が描かれているのかという情報が入っています。
データを確認してみよう
どんなデータなのか視覚的に確認してみましょう。Jupyter Notebook に以下のプログラムを記入して実行してみましょう。以下は、15個の手書き数字を出力するプログラムです。
import matplotlib.pyplot as plt
# 手書きデータを読み込む
from sklearn import datasets
digits= datasets.load_digits()
# 15個連続で出力する
for i in range (15):
plt.subplot(3, 5, i+1)
plt.axis (“off”)
plt.title(str(digits.target [i]))
plt.imshow (digits.images [i], cmap=”gray”)
plt.show ()
▲手書き数宇のデータは8×8ピクセルの画像データ
ちなみに pyplot の subplot() は、複数のデータをプロットするのに利用します。行数×列数個の図を準備し、どの図に対して書き込みを行うのかを指定します。以下の書式で利用します。
【書式]
pit.subplot(行数,列数,何番目に出力するか)
上記の数字を15個出力するプログラムでは、3行×5列の図を出力するよう指定し、for 文を利用して、合計15個の手書き数字の画像を出力するように指定しています。
画像のフォーマットについて
さらに詳しく見てみましょう。手書き数字は8×8ピクセルであり、各ピクセルは0から 16までの値で表されます。0が透明(背景色で黒色)で、16が線のある部分(白色)を表しています。
d0 = digits.images [0]
plt.imshow(d0, cmap=”gray”)
plt.show ()
print (d0)
画像を機械学習しよう
それでは、手書き数字の画像データを機械学習に与えてみましょう。そもそも、画像データと言えど、連続した値を持つデータです。8×8ピクセルであれば、64個で1つのデータです。そこで、画像の画素データを学習データとして機械学習に与えて、判定できるのか確認してみましょう。以下のプログラムは、手書き数字データを読み出して、データの8割を学習用に、2割をテスト用に振り分けます。そして、8割のデータを用いて学習を行い、残り2割のテストデータをどれほど正確に分類できるかを調べるものです。
from sklearn.model_selection import train_test_split
from sklearn import datasets, svm, metrics
from sklearn.metrics import accuracy_score
#データを読み込む( * 1)
digits = datasets.load_digits()
x =digits.images
y = digits.target
x= X.reshape((-1, 64))# 二次元配列を一次元配列に変換(* 2)
# データを学習用とテスト用に分割する( * 3)
x_train, x_test, y_train, y_test = train test_split(x, y, test_size=0.2)
# データを学習( * 4)
clf = Sym.SVC()
clf.fit (x train, y_train)
# 予測して精度を確認する( * 5)
y-pred= clf.predict(x_test)
print (accuracy_score(y_test, y_pred))
このプログラムでは、ランダムに学習データとテストデータを分割するので、実行結果の精度は多
少ばらつきがありますが、0.97(97%)から 0.99(99%)くらいの精度が出ています。
プログラムを確認してみましょう。プログラムの(※1)の部分では、手書き数字の画像データをそして、(※2)では、二次元の画像データの配列を一次元の配列に変換します。reshape) メソッドを(※4)では、学習用のデータを用いて学習し、(※5)ではテストデータを用いて精度を確認します。み込みます。使うと、手軽に配列の次元を変更することができるので便利です。プログラムの(※3)では、画像データを学習用 (8割)とテスト用(2割)に分割します。その際、画像の各ピクセルをデータとして与えているという点だけが異なります。
学習済みデータを保存しよう
さて、ここまでの部分で作成した学習済みのデータを、データファイルに保存してみましょう。学習済みのデータを保存するには、pickle モジュールを使います。先ほどのプログラムをJupyrerNotebook で実行した後で、以下のプログラムを実行してみましょう。
# 学習済みデータを保存
import pickle
with open (“digits.pkl”, “wb”) as fp:
pickle.dump(clf, fp)
なお、保存したデータを読み込むには、以下のように記述します。
# 学習済みデータを読み込み
import pickle
with open (“digits.pkl”, “rb”) as fp:
clf =pickle.load(fp)
自分で用意した画像を判定させてみよう
さて、先ほどのプログラムで最大0.99(99%) ほどの高い判定精度を出すことができましたが、数字が表示されるだけでは、手書き数字が判定できていることが実感できないのではないでしょうか。そこで、自分で用意した画像を判定させて、精度の高さを確かめてみましょう。Windows なら標準のペイント、macOS/Linux なら GIMP などのペイントソフトを使って、手書き数字のデータを用意しましょう。筆者は、FireAlpaca というオープンソースのペイントソフトを使って手書き数字のデータを作りました。
フリーのペイントソフト
FireAlpaca (Windows/macOS 対応)
URL: https://firealpaca.com/ja/
GIMP (Windows/Linux/macos 対応)
URL: https://uww.gimp.org/
ここでは、以下のような「my2 png」と「mriy4 prg」「rrry9png」という3つの画像を用意しました。これらの画像を機械学習で判定してみましょう。画像サイズは自動でリサイズを行うので、とくに気にせず、正方形の画像で作ってください。また、書きやすさの点から、背景を白色にし、黒のペンで数字を書いてください。そして、保存するときは、透過PNG ではない 24bit-PNGの形式で保存してください。
用意した画像を判定してみよう
画像が用意できたら、画像に何が書かれているのか判定させてみましょう。以下のプログラムをApyter Notebook で実行しましょう。
1mport cv2
import pickle
def predict_digit(filename):
# 学習済みデータを読み込む
with open(“digits.pkl”, “rb”) as fp:
clf = pickle.load (fp)
#自分で用意した手書きの画像ファイルを読み込む
my_img = cv2. imread (filename)
#画像データを学習済みデータに合わせる
my_img = cv2.cvtColor (my img, cv2.COLOR_BGR2GRAY)
my_img = cv2.resize(my_img, (8, 8))
my_img =15 – my img // 16
# 二次元を一次元に変換
my_img = my_img.reshape ((-1, 64))
# データ予測する
res =clf.predict (my_img)
return res [0]
# 画像ファイルを指定して実行
predict_digit(“my2.png”)
print(“my2.png =”+ str(n))
predict_digit(“ny4.png”)
print (“my4.png= ” + str(n))
predict_digit(“my9.png”)
print (“my9.png =” + str(n))
すると、以下の結果が表示されます。それほど上手に書いた数字ではありませんが、この2つの画像は正しく判定できました。
my2.png = 2
my4. png = 4
my9.png = 9
ただし、実際にやってみるとわかりますが、いろいろな画像で試してみると、文字が左右に寄っていたり、ペンが細すぎたりすると正しく判定できないことがありました。
画像を対象とする機械学習
ここまでのプログラムを見ると、画像を対象とする機械学習も、これまで見てきた機械学習とそれほど違いがないということがわかるのではないでしょうか。ところで、画像を機械学習にかける点で、いくつか注意すべきポイントがあります。まず、画像のサイズを一定のサイズに合わせること、そして画像の色空間を合わせることが大切です。機械学習に与えるデータ、つまり、入力する画像を前処理する必要があるのです。
改良のヒント
今回、手書き数字のデータセットには、UCIが公開している簡単な画像セットを利用しました。実際、自分で用意した画像を判定させてみるとわかりますが、それほど正確な結果が出ないと思った方もいることでしょう。そこで、改良のヒントを見てみましょう。機械学習は良質なデータが多いほど、高い精度を出すことができます。手書き数字のデータセットとしては、MNIST というデータセットが有名です。7万点の手書き数字データセットをダウンロードできます。
この節のまとめ
・手書き数字データセットを学習することで、数字の文字認識を行うことができる
・画像の画素データは数字の連続なので、機械学習を行うことができる
・画像データを機械学習にかける場合には、画像サイズや色空間など形式を統一する必要がある
次回の数字分類問題の続きはこちらからご覧ください。