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

こんにちはリヒトです。[こちら](http://qiita.com/LichtLabo/items/d78b50bbf0826b8f599a)に続いてDeepLearningチュートリアル3章
モデルを利用した文字認識について説明します。

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

探すのが面倒な人は下の画像をダウンロードして使って下さい。(OneNoteで「あ」と書いて画像で切り出した)
<img width="59" alt="a.png" src="https://qiita-image-store.s3.amazonaws.com/0/110732/088675ef-ed0a-91e9-8b16-b30d94356cc0.png">


#認識
先ほどの画像「あ」(a.png)をmodel16とと同じディレクトリに置いて、
<img width="205" alt="lets_recognize.png" src="https://qiita-image-store.s3.amazonaws.com/0/110732/aec8bdc9-1a2d-ea90-01b3-5d77e4396b07.png">

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


```
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枚)に拡大した時の各々の認識結果です。
最終判断というのは各々の認識結果を平均したものです。

<img width="371" alt="arg_aaaa.png" src="https://qiita-image-store.s3.amazonaws.com/0/110732/24e7817c-6864-4a88-023d-478905ebd117.png">

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

a2.png
<img width="60" alt="a2.png" src="https://qiita-image-store.s3.amazonaws.com/0/110732/b90fc74a-79d5-d003-7aa9-19bb04f016a3.png">
認識

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

結果

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

a3.png
<img width="60" alt="a3.png" src="https://qiita-image-store.s3.amazonaws.com/0/110732/8ce9540a-b76b-a53c-ac33-460ef166e121.png">
認識

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

結果

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

OKです。

##手書きひらがな文字認識

認識正解という事でもう少し難しい認識に挑戦してみましょう。
手書きの「あ」を認識してみましょう。
<img width="80" alt="a_tegaki.png" src="https://qiita-image-store.s3.amazonaws.com/0/110732/7d9b8a62-fac1-ee07-486e-4f9689c037d0.png">

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

```
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の有用性が伺えます。


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


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

```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と同じ構造をしている必要があります。

```py
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ピクセルずつ余白をつけています。今のままのモデルでは余白が適切な量でないと上手く認識出来ません。


```py
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関数で予測しています。

```py
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環境の構築](http://qiita.com/LichtLabo/items/94063ee9d66263d1911a)|
|2章  |[機械学習によるDeepLearning予測モデルの作成](http://qiita.com/LichtLabo/items/d78b50bbf0826b8f599a)|
|3章  |[モデルを利用した文字認識](http://qiita.com/LichtLabo/items/c987cd93928ab70f46e1)|
|4章  |[データ拡大による認識精度の改善](http://qiita.com/LichtLabo/items/c4b33432a46cb7dd6b7d)|
|5章|ニューラルネット入門とソースコードの解説|
|6章|Optimizerの選択による学習効率の改善|
|7章|TTA, BatchNormalizationによる学習効率の改善|