前回、日本の古文書で機械学習を試す(1)で、『日本古典籍字形データセット』のサンプルプログラムを用いて作ったモデルを使って、自分で手書きした文字を認識させてみる。
学習用データ、テスト用データの中身を見る
手書きした文字を認識させるには、手書きデータを学習用データと同じ形に変換する必要がある。そのため、学習用データがどうなっているのか確認してみた。
サンプルプログラム中の train_test_file_list.h5 が学習用データとテスト用データがセットになったデータセットだ。HDF5というファイル形式のようなので、中身を見るためにこちらから、HDFViewというツールをダウンロードして中身を確認した。(HDFViewのインストールで結構ハマったのだが、本筋とは関係ないので、この投稿の最後に追記する。)
HDFViewでtrain_test_file_list.h5を開くと、中身はこうなっている。画面上に表示されるのは3514x28のデータだが、下の方にデータ型が3514x28x28と書いてあるので、3次元のデータらしい。最後の次元の28個は、画面の上の方の黄色い矢印で順番に表示させることができる。
test_xは、28ピクセル x 28ピクセルの画像が3514個入っている。各セルの数字は0~255の画素値っぽい。データの性格からすると、画面上に28x28を表示して、黄色い矢印で3514個を送れるようにしてくれたほうが分かりやすいのだが、汎用ツールなので仕方がない。
test_yのほうはこんな感じ。3514個の画像がどの文字を表しているかが、0~9の数字で入っている。
サンプルプログラム中のconfig.pyにUTF-8の文字コードが設定されていて、各数字と文字の対応は以下の通り。
0 U+3057 = し
1 U+306B = に
2 U+306E = の
3 U+3066 = て
4 U+308A = り
5 U+3092 = を
6 U+304B = か
7 U+304F = く
8 U+304D = き
9 U+3082 = も
train_x, train_yは画像の数が19909個に増えているだけで、データ形式はtest_x, test_yと同じだった。
学習用データはどんな画像なのかチェック
画素値だけでは、どんな画像なのかよくわからないので、読み込んだデータを画像として表示させてみた。ついでなので、勉強のため、サンプルプログラム中に入っていたライブラリconfig.py load.pyを使わずに、データファイルを読み込んでみた。
# 学習用&テスト用データファイル読み込み
import numpy as np
import h5py
train_test_file_path = 'train_test_file_list.h5'
with h5py.File(train_test_file_path,'r') as hf:
train_x = np.array(hf.get('train_x'))
train_y = np.array(hf.get('train_y'))
test_x = np.array(hf.get('test_x'))
test_y = np.array(hf.get('test_y'))
# 学習用データを画像として見てみる
import matplotlib.pyplot as plt
%matplotlib inline
fig = plt.figure(figsize=(8,5)) # 全体の表示領域のサイズ(横, 縦)
fig.subplots_adjust(left=0, right=1, bottom=0, top=1, hspace=0.5, wspace=0.05)
# ↑サブ画像余白(左と下は0、右と上は1で余白なし) サブ画像間隔 縦,横
for i in range(40):
ax = fig.add_subplot(5, 8, i + 1, xticks=[], yticks=[]) # 縦分割数、横分割数、何番目か、目盛り表示なし
ax.title.set_text("[" + str(i) + "]") # グラフタイトルとして番号を表示
ax.imshow(train_x[i].reshape((28, 28)), cmap='gray')
19909個の学習用データ中の最初の40個を表示させてみた結果。まだまだ古文書初心者なので読めない字も多い(汗)
自分で書いた文字を認識させる
ここからやっと本題。Windows標準のペイントで文字を書いて、前回作ったモデルで認識させてみる。
まずはメモ帳で、ひらがなを書いてJPEGで保存する。28x28ピクセルだと手書き文字を書くのが難しいので、280x280で作成して、あとでサイズ変換する。
サンプルプログラム中のload.py中に画像ファイルを読み込んでHDF5ファイルを作る関数があったので、それを参考に、CV2を使って作成したJPEGを読み込み、28x28ピクセルにサイズ変換する。
import cv2
x = np.zeros((28,28))
img = cv2.imread('paint_mo.jpg', 1) # ペイントで作成した画像ファイル名。画像はpythonと同じフォルダに置く
grey_image = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
x = cv2.resize(grey_image, (28,28), interpolation = cv2.INTER_LINEAR )
サンプルプログラム本体run.pyを見ると、学習用データとテストデータのxデータ(画像データ)は、[データの個数] x 28 x 28 x 1 の行列なので、作成したデータも (28,28)から(1,28,28,1)の行列に変換する。さらに、学習用データ、テストデータに合わせて、0~255のデータを、255で割って0~1の少数データにする。
x = np.expand_dims(x, axis=0)
x = np.expand_dims(x, axis=3)
x = x.astype('float32')
x /= 255
これでやっと準備が完了したので、前回作成したモデルを読み込んで、認識させてみる。
# モデル読み込み
from keras.models import load_model
model = load_model('model_komonjo_mnist.h5')
# どの文字かを判別する
preds = model.predict_classes(x)
pred = preds.tolist()
print("予測結果:" + str(pred))
# 各文字である確率
prob = model.predict_proba(x)
prlist = prob.tolist()
print(prlist)
予測結果:[9]
[[0.29332467913627625, 0.012350857257843018, 2.0295312424423173e-05, 0.0023382618092000484, 0.003347291611135006, 0.001386790070682764, 0.0007454972364939749, 0.002765553304925561, 1.101136376746581e-06, 0.6837196946144104]]
予測結果はこうなった。番号9は「も」なので、ちゃんと正解できている!
各文字の確率は、9(も)が一番高くて0.68、次が0(し)で0.29、その次になると1(に)で0.012とかなり確率が低くなる。直感的にも「も」に一番似ているのは「し」かなぁと思うので、まぁ妥当な結果だ。学習させたのは古文書のくずし字なので、現代人が書くひらがなとはちょっと違うのかもしれないが…
調子乗って下の「く」と「の」でも試してみたが、これもちゃんと認識できた。「く」は何と確率99%以上で「く」と判定された。「の」は約70%だった。
おまけ、その1 Kerasのload_img
CV2ではなく、Kerasのload_imgを使った画像読み込みコードも書いてみた。
from keras.preprocessing.image import load_img, img_to_array
my_img = load_img('paint_no.jpg', True, target_size=(28,28)) # 2つめの引数はグレースケール
x = my_img.convert('L') # ローパスフィルタ=薄いグレーを白にする?
x = img_to_array(x)
x = np.expand_dims(x, axis=0) # (28,28,1)の行列から(1,28,28,1)の行列に変換
x = x.astype('float32')
x /= 255
load_imgを呼び出そうとすると
ImportError: Could not import PIL.Image. The use of `array_to_img` requires PIL.
というエラーが出た。調べてみるとPillowが必要らしいので、プロンプトからインストールした。
> conda install Pillow
このとき、Jupyter Notebookを起ち上げっぱなしにしていたら、Pillowをインストールしてもエラーが消えなかったが、一度Jupyter Notebookを終了して再起動したらちゃんと動いた。ライブラリをインストールした後は、Jupyter Notebookの再起動が必要らしい。
おまけ、その2 HDFViewでハマった話
こちらから、HDFViewをダウンロードしてインストールした後、train_test_file_list.h5を開こうと思ったら、HDFViewは起動するものの、
Failed to open file c:\Users ......
java.io.IOException: Unsupported fileformat -C:\Users ...
というメッセージが出てファイルが開けない。ネットで調べたら64bit版と32bit版の違いとか、色々書いてあったので、最初にインストールした64bit版をアンインストールして32bit版をインストールしてもダメ、バージョン2.14から、最新の3.0にアップしてもダメ。(ちなみに、2.14はすぐにダウンロードできたが、3.0のコンパイル済みバイナリをダウンロードするにはこちらから会員登録する必要があった)
面倒だけどソースからコンパイルするしかないか~と思ったが、ふと気づいて、ファイルのパスに日本語を含まない場所(C:\直下とか)に置いたら開けた!! 日本語のフォルダ名をつけていたのがエラーの原因だったらしい。(ちなみに、動いたのはバージョン2.14のWindows 32bit版。他のバージョンでも動くのかもしれないが、試す気力がなかった。)