LoginSignup
6
8

More than 5 years have passed since last update.

日本の古文書で機械学習を試す(2) 作成したモデルを使って文字を認識させる

Last updated at Posted at 2018-11-14

前回、日本の古文書で機械学習を試す(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個を送れるようにしてくれたほうが分かりやすいのだが、汎用ツールなので仕方がない。
FDFView_test_x.jpg

test_yのほうはこんな感じ。3514個の画像がどの文字を表しているかが、0~9の数字で入っている。
FDFView_test_y.jpg

サンプルプログラム中の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個を表示させてみた結果。まだまだ古文書初心者なので読めない字も多い(汗)
学習用画像.png

自分で書いた文字を認識させる

ここからやっと本題。Windows標準のペイントで文字を書いて、前回作ったモデルで認識させてみる。

まずはメモ帳で、ひらがなを書いてJPEGで保存する。28x28ピクセルだと手書き文字を書くのが難しいので、280x280で作成して、あとでサイズ変換する。
paint_mo.jpg

サンプルプログラム中の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%だった。
paint_ku_small.jpg  paint_no_small.jpg

おまけ、その1 Kerasのload_img

CV2ではなく、Kerasのload_imgを使った画像読み込みコードも書いてみた。

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版。他のバージョンでも動くのかもしれないが、試す気力がなかった。)

6
8
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
6
8