はじめてのDeepLearning入門(Chainer) 日本語文字認識 3章 [モデルを利用した文字認識]


こんにちはリヒトです。こちらに続いてDeepLearningチュートリアル3章
モデルを利用した文字認識について説明します。

準備

2章のDeepLearningの機械学習によって生成されたmodel16を使います。
まずは平仮名の画像を用意しましょう。本当は学習に使ったデータに無いフォントの物が良いですが探すのも難しいのでテキトウに準備します。

探すのが面倒な人は下の画像をダウンロードして使って下さい。(OneNoteで「あ」と書いて画像で切り出した)

認識

先ほどの画像「あ」(a.png)をmodel16とと同じディレクトリに置いて、

ターミナルから以下のコマンドを入力します

python hiraganaNN_predictor.py --img a.png --model model16

すると出力に

候補 ニューロン番号:1, Unicode:3042, ひらがな:あ
候補 ニューロン番号:1, Unicode:3042, ひらがな:あ
候補 ニューロン番号:1, Unicode:3042, ひらがな:あ
候補 ニューロン番号:1, Unicode:3042, ひらがな:あ
候補 ニューロン番号:1, Unicode:3042, ひらがな:あ
候補 ニューロン番号:1, Unicode:3042, ひらがな:あ
候補 ニューロン番号:1, Unicode:3042, ひらがな:あ
候補 ニューロン番号:1, Unicode:3042, ひらがな:あ
候補 ニューロン番号:1, Unicode:3042, ひらがな:あ
候補 ニューロン番号:1, Unicode:3042, ひらがな:あ
候補 ニューロン番号:10, Unicode:304b, ひらがな:か
候補 ニューロン番号:1, Unicode:3042, ひらがな:あ
候補 ニューロン番号:78, Unicode:308f, ひらがな:わ
候補 ニューロン番号:1, Unicode:3042, ひらがな:あ
**最終判断 ニューロン番号:1, Unicode:3042, ひらがな:あ**

と予測結果が出て来ました。
候補の中に「か」とか「わ」があるものの、最終判断の出力は「あ」なので認識成功です。
この候補というのは1枚の画像を複数(14枚)に拡大した時の各々の認識結果です。
最終判断というのは各々の認識結果を平均したものです。

これは1枚の画像から判断するのでは無く、色んな角度から判断する事で認識精度を上げるというTTA(Test Time Argumentation)
という手法と同じコンセプトです。
他の認識も試してみます。

a2.png

認識

python hiraganaNN_predictor.py --img a2.png --model model16

結果

**最終判断 ニューロン番号:1, Unicode:3042, ひらがな:あ**

a3.png

認識

python hiraganaNN_predictor.py --img a2.png --model model16

結果

**最終判断 ニューロン番号:1, Unicode:3042, ひらがな:あ**

OKです。

手書きひらがな文字認識

認識正解という事でもう少し難しい認識に挑戦してみましょう。
手書きの「あ」を認識してみましょう。

正直無茶振りですが、まぁやるだけやってみましょう。

python hiraganaNN_predictor.py --img a_tegaki.png --model model16

で認識開始

候補 ニューロン番号:71, Unicode:3088, ひらがな:よ
候補 ニューロン番号:30, Unicode:305f, ひらがな:た
候補 ニューロン番号:71, Unicode:3088, ひらがな:よ
候補 ニューロン番号:1, Unicode:3042, ひらがな:あ
候補 ニューロン番号:24, Unicode:3059, ひらがな:す
候補 ニューロン番号:71, Unicode:3088, ひらがな:よ
候補 ニューロン番号:30, Unicode:305f, ひらがな:た
候補 ニューロン番号:24, Unicode:3059, ひらがな:す
候補 ニューロン番号:32, Unicode:3061, ひらがな:ち
候補 ニューロン番号:32, Unicode:3061, ひらがな:ち
候補 ニューロン番号:24, Unicode:3059, ひらがな:す
候補 ニューロン番号:30, Unicode:305f, ひらがな:た
候補 ニューロン番号:1, Unicode:3042, ひらがな:あ
候補 ニューロン番号:1, Unicode:3042, ひらがな:あ
**最終判断 ニューロン番号:32, Unicode:3061, ひらがな:ち**

失敗です!笑
候補にはちょいちょい「あ」がありますが、最終判断「ち」なのでダメでした。
まぁmodel16はlossが0.526だし、活字で学習したのでこんなもんです。
ただこの後の章の改善策を幾つか施してlossを0.237まで下げたモデルで試すと

python hiraganaNN_predictor.py --img a2.png --model loss237model
候補 ニューロン番号:30, Unicode:305f, ひらがな:た
候補 ニューロン番号:52, Unicode:3075, ひらがな:ふ
候補 ニューロン番号:52, Unicode:3075, ひらがな:ふ
候補 ニューロン番号:1, Unicode:3042, ひらがな:あ
候補 ニューロン番号:1, Unicode:3042, ひらがな:あ
候補 ニューロン番号:32, Unicode:3061, ひらがな:ち
候補 ニューロン番号:71, Unicode:3088, ひらがな:よ
候補 ニューロン番号:52, Unicode:3075, ひらがな:ふ
候補 ニューロン番号:9, Unicode:304a, ひらがな:お
候補 ニューロン番号:52, Unicode:3075, ひらがな:ふ
候補 ニューロン番号:9, Unicode:304a, ひらがな:お
候補 ニューロン番号:1, Unicode:3042, ひらがな:あ
候補 ニューロン番号:9, Unicode:304a, ひらがな:お
候補 ニューロン番号:10, Unicode:304b, ひらがな:か
**最終判断 ニューロン番号:1, Unicode:3042, ひらがな:あ**

(ぎりぎりですが)正しく認識出来てます!
基本的には活字を学習データにして機械学習して来ましたが、手書きも少しは認識できるモデルになっています。
候補がちょいちょい間違えて認識しているのでやはりTTAの有用性が伺えます。

ちなみに
候補に「あ」が3つで「ふ」が4つなのに何で最終判断「あ」なの?と思った方、するどい!
ただここでは単純な多数決をしている訳ではありません。より確信を持って判断している結果を考慮しています。
つまり4つの「ふ」は全部あやふやで予測していたのに対して3つの「あ」は確信を持っていたという事で、最終判断は「あ」と判断出来ている訳です。

ソースコード概要

最後にソースコードの概要説明です。大部分は機械学習に使ったhiraganaNN.pyと同じです。

def forward(x_data, train=False):
    x = chainer.Variable(x_data, volatile=not train)
    h = F.max_pooling_2d(F.relu(model.bn1(model.conv1(x))), 2)
    h = F.max_pooling_2d(F.relu(model.bn2(model.conv2(h))), 2)
    h = F.max_pooling_2d(F.relu(model.conv3(h)), 2)
    h = F.dropout(F.relu(model.fl4(h)), train=train)
    y = model.fl5(h)
    return y.data

ここはDeepLearningのニューラルネット構造でhiraganaNN.pyのfowardと同じ構造をしている必要があります。

src = cv2.imread(args.img, 0)
src = cv2.copyMakeBorder(
    src, 20, 20, 20, 20, cv2.BORDER_CONSTANT, value=255)
src = cv2.resize(src, (IMGSIZE, IMGSIZE))

入力画像の読み込みと64*64の画像サイズにリサイズ。
また画像に20ピクセルずつ余白をつけています。今のままのモデルでは余白が適切な量でないと上手く認識出来ません。

for x in xrange(0, 14):
    dst = dargs.argumentation([2, 3])
    ret, dst = cv2.threshold(dst,
                             23,
                             255,
                             cv2.THRESH_BINARY)
    #画像確認用
    #cv2.imshow('ARGUMENTATED', dst)
    #cv2.waitKey(0)
    #cv2.destroyAllWindows()

    xtest = np.array(dst).astype(np.float32).reshape(
        (1, 1, IMGSIZE, IMGSIZE)) / 255
    if result is None:
        result = forward(xtest)
    else:
        result = result + forward(xtest)

入力画像をデータ拡大して、2値化してから、画素値を0-1に正規化し、foward関数で予測しています。

tmp = np.argmax(forward(xtest))
for strunicode, number in unicode2number.iteritems():
    if number == tmp:
        hiragana = unichr(int(strunicode, 16))
        print '候補 ニューロン番号:{0}, Unicode:{1}, ひらがな:{2}'.format(number, strunicode, hiragana.encode('utf_8'))

ニューラルネットの認識結果(0-82の番号)をunicodeに変換し、ひらがなとして出力しています。
3章はここで終了です。
4章では1枚の画像を3500枚に拡大して精度を見てみます。

タイトル
1章 chainerをベースにしたDeepLearning環境の構築
2章 機械学習によるDeepLearning予測モデルの作成
3章 モデルを利用した文字認識
4章 データ拡大による認識精度の改善
5章 ニューラルネット入門とソースコードの解説
6章 Optimizerの選択による学習効率の改善
7章 TTA, BatchNormalizationによる学習効率の改善