ocrd-train(Tesseract)の学習で辞書が見つからない（Failed to load any lstm-specific dictionaries for lang XXX!!）

#経緯
タイトルの通りなのですが、先日kmnistを使ってTesseractにLSTMの再学習をさせたのですが（[ここ](https://qiita.com/mimuro_syunya/items/7eaea638530f7034a29f)）、最後の認識で警告が出ているんですよね。

```
$ tesseract -l kuzusiPlus kuzusi.0.tif stdout
Failed to load any lstm-specific dictionaries for lang kuzusiPlus!!
Page 1
Warning: Invalid resolution 0 dpi. Using 70 instead.
Estimating resolution as 272
お れ お ま お は れ は は お を や き す す き す き つ や れ き な つ れ つ や き れ は き ま れ を き お お つ つ ま
```
コマンドを打った次の行！

>Failed to load any lstm-specific dictionaries for lang kuzusiPlus!!

もちろん、学習前のjpn_bestではこんなの出てません。。。
今回はこの警告について調べたのであとで忘れてもいいように記事にまとめておきます。

注意）私がTesseractについて詳しくないので、現在（2019年12月22日時点）で間違った認識もあるかもしれないです。あくまでヒント程度で見て下さい。それと、間違いがあったら気軽にご指摘してくださるとありがたいです！

解決だけしたい方は[ここ](https://qiita.com/mimuro_syunya/items/c99a7ad37b3c1b98cfd0#%E8%A7%A3%E6%B1%BA%E7%AD%96%E4%BB%AE)

#原因調査（警告を消したいだけなら読む必要はないです）
今回はTesseractの学習データであるXXX.traineddataについて調べてみて、警告が言っている事について調べてみました。

##XXX.traineddataの構成についての調査
まず、(MODEL_NAME)Plus.traineddata（というか.traineddata全部）は、いくつかのファイルが統合されたものなんですね。
Tesseractのwikiを見てみると、、、

[Understanding the Various Files Used During Training](https://github.com/tesseract-ocr/tesseract/wiki/TrainingTesseract-4.00#understanding-the-various-files-used-during-training)より
>As with base Tesseract, the completed LSTM model and everything else it needs is collected in the traineddata file. Unlike base Tesseract, a starter traineddata file is given during training, and has to be setup in advance. It can contain:

>　・Config file providing control parameters.
　・__Unicharset__ defining the character set.
　・__Unicharcompress__, aka the recoder, which maps the unicharset further to the codes actually used by the neural network recognizer.
　・Punctuation pattern dawg, with patterns of punctuation allowed around words.
　・Word dawg. The system word-list language model.
　・Number dawg, with patterns of numbers that are allowed.

>Bold elements __must__ be provided. Others are optional, but if any of the dawgs are provided, the punctuation dawg must also be provided. A new tool: combine_lang_model is provided to make a starter traineddata from a unicharset and optional wordlists.

ふーんって感じですね。要するにtraineddataにはUnicharsetとUnicharcompressが必ず必要で、それ以外はオプションらしいですね。
具体的にどれ？？ってことなんですが、次の３つのwikiの記事を見ると分かってきます。

まず、話は少し逸れますが、バージョン3.0.0or3.0.2での学習方法の記事を見ます。
Tesseractのwiki : [Data file required](https://github.com/tesseract-ocr/tesseract/wiki/Training-Tesseract-3.00%E2%80%933.02#data-files-required)

>To train for another language, you have to create some data files in the tessdata subdirectory, and then crunch these together into a single file, using combine_tessdata. The naming convention is languagecode.file_name Language codes for released files follow the ISO 639-3 standard, but any string can be used. The files used for English (3.00) are:

>　・tessdata/eng.config
　・tessdata/eng.unicharset
　・tessdata/eng.unicharambigs
　・tessdata/eng.inttemp
　・tessdata/eng.pffmtable
　・tessdata/eng.normproto
　・tessdata/eng.punc-dawg
　・tessdata/eng.word-dawg
　・tessdata/eng.number-dawg
　・tessdata/eng.freq-dawg

>... and the final crunched file is:

>　・tessdata/eng.traineddata

なるほど、中間にリストアップされているeng.***を作ってcombine_tessdataを使うと、eng.treineddataの完成って事ですね。
これは4.0.0以降も大体同じことが言えるんです。

それじゃ逆に完成されているeng.traineddataを分解した例を見てみましょう。
Tesseractのwiki : [Pre 4.0.0 format from Nov 2016 (with both LSTM and Legacy models)](https://github.com/tesseract-ocr/tesseract/wiki/Data-Files#pre-400-format-from-nov-2016-with-both-lstm-and-legacy-models)

>combine_tessdata -u eng.traineddata eng.
Extracting tessdata components from eng.traineddata
Wrote <font color='LimeGreen'>eng.unicharset</font color>
Wrote <font color='LimeGreen'>eng.unicharambigs</font color>
Wrote <font color='LimeGreen'>eng.inttemp</font color>
Wrote <font color='LimeGreen'>eng.pffmtable</font color>
Wrote <font color='LimeGreen'>eng.normproto</font color>
Wrote <font color='LimeGreen'>eng.punc-dawg</font color>
Wrote <font color='LimeGreen'>eng.word-dawg</font color>
Wrote <font color='LimeGreen'>eng.number-dawg</font color>
Wrote <font color='LimeGreen'>eng.freq-dawg</font color>
Wrote <font color="CadetBlue">eng.cube-unicharset</font color>
Wrote <font color="CadetBlue">eng.cube-word-dawg</font color>
Wrote <font color="CadetBlue">eng.shapetable</font color>
Wrote <font color="CadetBlue">eng.bigram-dawg</font color>
Wrote <font color='MediumTurquoise'>eng.lstm</font>
Wrote <font color='MediumTurquoise'>eng.lstm-punc-dawg</font>
Wrote <font color='MediumTurquoise'>eng.lstm-word-dawg</font>
Wrote <font color='MediumTurquoise'>eng.lstm-number-dawg</font>
Wrote eng.version
Version string:Pre-4.0.0

これは3.0.0のOCRエンジン（LSTMの前）とLSTMの２つのモードができる.traineddataを分解した例なのですが、<font color='LimeGreen'>緑色</font color>がさっき引用したバージョン3.0.0or3.0.2で必要なデータに対応していますね。そして<font color='MediumTurquoise'>青色</font>の部分にlstmと入っています。なるほど、こんな感じなファイルの塊が.traineddataになるわけですね。（<font color='CadetBlue'>この色</font color>については次説明します）
ちなみにこれは最新のやつじゃなくて、最新のLSTM onlyは
Tesseractのwiki : [4.00.00alpha LSTM only format](https://github.com/tesseract-ocr/tesseract/wiki/Data-Files#40000alpha-lstm-only-format)

>combine_tessdata -u eng.traineddata eng.
Extracting tessdata components from eng.traineddata
Wrote <font color='MediumTurquoise'>eng.lstm</font>
Wrote <font color='MediumTurquoise'>eng.lstm-punc-dawg</font>
Wrote <font color='MediumTurquoise'>eng.lstm-word-dawg</font>
Wrote <font color='MediumTurquoise'>eng.lstm-number-dawg</font>
Wrote <font color='CadetBlue'>eng.lstm-unicharset</font>
Wrote <font color='CadetBlue'>eng.lstm-recoder</font>
Wrote eng.version
Version string:4.00.00alpha:eng:synth20170629:[1,36,0,1Ct3,3,16Mp3,3Lfys64Lfx96Lrx96Lfx512O1c1]

ほほう。最新のやつでは全部にlstmが付いてますね。単純に考えると、<font color='CadetBlue'>この色</font color>のlstm-unicharsetとlstm-recoderが上記の引用の同じ色のやつに対応してる訳ですね。

やっと話を戻そうかと思います（あくまで私のメモなので必要ない事も書きました。くどい説明で申し訳ないですm(_ _)m）。
さっき「要するにtraineddataにはUnicharsetとUnicharcompressが必ず必要」と言いましたが、それはどれかというと、

>Wrote <font color='MediumTurquoise'>eng.lstm</font>
Wrote <font color='CadetBlue'>eng.lstm-unicharset</font>
Wrote <font color='CadetBlue'>eng.lstm-recoder</font>

の３つです。Unicharsetは<font color='CadetBlue'>eng.lstm-unicharset</font>の事です。
Unicharcompressは、引用の説明を読んでみると「Unicharcompress（別名recoder）は、unicharsetをニューラルネットワーク認識エンジンが実際に使用するコードにさらにマッピングします。」とあります。つまり、Neural Networkがある前提（まあlstm使うから当たり前）でrecorderが必要って事ですね。
結論、
<font color='MediumTurquoise'>eng.lstm</font>がlstm本体で、<font color='CadetBlue'>eng.lstm-recoder</font>がそのrecoderらしいです。

##警告についての調査
それじゃ！必要なその３つだけにしちゃえ！！って事でcombine_tessdataを使ってやってみますね。
（combine_tessdataの使い方も後でまとめようかと思います）

・追記、[combine_tessdataのまとめ](https://qiita.com/mimuro_syunya/items/ec8349ed099c1f6ebe23)にまとめました。（12月25日時点）

実験するだけなので、今まで作った.traineddataに影響が出ないように適当な場所（今回はデスクトップ）にeng.traineddataを置きます。
ここから[eng.traineddata](https://github.com/tesseract-ocr/tessdata/blob/master/eng.traineddata)をダウンロードしてデスクトップに置いて下さい。(DownLoadを押すだけで取得できます）

そして端末を開いてデスクトップに行って作業しましょう！（ここではcombine_tessdataの説明は省きます）

```
# デスクトップにさっきのeng.traineddataを置いて下さい
$ cd デスクトップ

# eng.traineddataを分解する
$ combine_tessdata -u eng.traineddata eng_unpack.
```
さあ、デスクトップに大量にeng_unpack.***が発生しましたね。これらがeng.traineddataのパーツとなります。それじゃ、

・eng_unpack.lstm
・eng_unpack.lstm-unicharset
・eng_unpack.lstm-recoder

以外を消してcombine_tessdataで.traineddataを作ってみましょう。（手動でやってもいいですが一応コマンドを乗っけときます）

```
# 必須なパーツ以外全て消す
$ rm eng_unpack.unicharset
$ rm eng_unpack.unicharambigs
$ rm eng_unpack.inttemp
$ rm eng_unpack.pffmtable
$ rm eng_unpack.normproto
$ rm eng_unpack.punc-dawg
$ rm eng_unpack.word-dawg
$ rm eng_unpack.number-dawg
$ rm eng_unpack.freq-dawg
$ rm eng_unpack.shapetable
$ rm eng_unpack.bigram-dawg
$ rm eng_unpack.lstm-punc-dawg
$ rm eng_unpack.lstm-word-dawg
$ rm eng_unpack.lstm-number-dawg
$ rm eng_unpack.version
```

よーし、これで準備は整いましたね。combine_tessdataで残ったパーツを統合しましょう。

```
# パーツの結合
$ combine_tessdata eng_unpack.
```
すると、デスクトップ上にeng_unpack.traineddataができてますね！
最低限のパーツで構成されたデータは大丈夫なのか？試してみましょう。
このeng_unpack.traineddataを$TESSDATA_PREFIX（tesseractの学習データが置いてあるフォルダ、[参考](https://qiita.com/mimuro_syunya/items/7eaea638530f7034a29f#1-tesseract%E3%82%92%E3%82%A4%E3%83%B3%E3%82%B9%E3%83%88%E3%83%BC%E3%83%AB%E3%81%99%E3%82%8B)）にコピーさせましょう。

```
$ sudo cp eng_unpack.traineddata $TESSDATA_PREFIX/
```

そして、適当な英語の画像をデスクトップに置いて下さい。
（適当な英語）
![sample.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/554112/686a41ba-4c1a-808d-a069-8b10a0f9393f.png)
これを認識にかけてみると、

```
$ tesseract -l eng_unpack sample.png stdout
Failed to load any lstm-specific dictionaries for lang eng_unpack!!
| am a student of National College of Technology/
```
おお！出ました「Failed to load any lstm-specific dictionaries for lang eng_unpack!!」最低限しかないとこの警告が出るんですね。
それじゃ何が足りないんだ！とそろそろ説明が長すぎて罵倒されそうですが、まずdictionaryについて調べてみました。

ここにありました。
tesseractのwiki : [Overview of Training Process](https://github.com/tesseract-ocr/tesseract/wiki/TrainingTesseract-4.00#overview-of-training-process)
ここの4.Make a starter traineddata from the unicharset and optional dictionary data.をクリックすると以下の記事に飛ばされて、

>lstmtraining takes a traineddata file on its command-line, to obtain all the information it needs on the language to be learned. The traineddata must contain at least an lstm-unicharset and lstm-recoder component, and may also contain the three dawg files: <font color='red'>lstm-punc-dawg lstm-word-dawg lstm-number-dawg</font color> A <font color='red'>config file</font color> is also optional. The other components, if present, will be ignored and unused.

なるほど、何やらdictionaryはlstm-punc-dawg、lstm-word-dawg、lstm-number-dawg、config fileのどれかっぽいですね。
それじゃ3.0.0 or 3.0.2ではdictionaryは何だったのか。

tesseractのwiki : [Dictionary Data (Optional)](https://github.com/tesseract-ocr/tesseract/wiki/Training-Tesseract-3.00%E2%80%933.02#dictionary-data-optional)
>Seven of the files are coded as a Directed Acyclic Word Graph (DAWG), and the other is a plain UTF-8 text file:

DAWGが辞書って扱いになってますね。（DAWGとは文字列から単語を検索するためのデータ構造です、詳しくは[ここ](https://tesseract-ocr.repairfaq.org/allaboutdawg.html)）
という事で、lstm-punc-dawg lstm-word-dawg lstm-number-dawg の３つが辞書ファイルに相当する訳です。

じゃあeng.traineddataからその３つのファイルを取り出してeng_unpack.traineddataに組み込もうじゃないか。

```
# lstm-punc-dawgを取り出し、組み込む
$ combine_tessdata -e eng.traineddata eng.lstm-punc-dawg
$ combine_tessdata -o eng_unpack.traineddata eng.lstm-punc-dawg

# lstm-number-dawgを取り出し、組み込む
$ combine_tessdata -e eng.traineddata eng.lstm-number-dawg
$ combine_tessdata -o eng_unpack.traineddata eng.lstm-number-dawg

# lstm-word-dawgを取り出し、組み込む
$ combine_tessdata -e eng.traineddata eng.lstm-word-dawg
$ combine_tessdata -o eng_unpack.traineddata eng.lstm-word-dawg

# 新しくできたeng_unpack.traineddataを$TESSDATA_PREFIXに移す
$ sudo cp eng_unpack.traineddata $TESSDATA_PREFIX/
```
できたファイルを移しました。これで辞書は組み込めたはず！それじゃあさっきやった実行をやってみます。

```
$ tesseract -l eng_unpack sample.png stdout
| am a student of National College of Technology/
```
やった！「Failed to load any lstm-specific dictionaries for lang eng_unpack!!」が出ません。
つまりこの警告は
・lstm-punc-dawg
・lstm-word-dawg
・lstm-number-dawg 
がないために出てしまうんですね。

しかし、今回はおんなじデータを分解して、一部抜いて、組み直しただけです。
元データから一部取り出して、他のデータに組み込むのとは話が違います。
解決策（仮）はjpn_best.traineddataから辞書のみを取り出して、kuzusiPlus.traineddataに組み込んでいます。
なので精度にどのような影響が出るか正確に分かっている訳では無いのですが、自分なりに調べて結論が出たのでリンクを貼っておきます。
長いですが、良かったら読んで下さい。
・[Tesseractの辞書ファイルの有無の影響にてついて（Failed to load any specific lstm-dictionaries for lang XXX!）](https://qiita.com/mimuro_syunya/items/41d7d61501dea161a214)


#解決策（仮）
とりあえず、こうすると警告が出なります。（仮）なのはこれが正しいかどうか分かっていないので仮です。
まず、前回作ったkuzusiPlus.traineddataとjpn_best.traineddataを適当な場所（今回はデスクトップ）に置きましょう。

```
$ cd デスクトップ
$ cp $TESSDATA_PREFIX/kuzusiPlus.traineddata .
$ cp $TESSDATA_PREFIX/jpn_best.traineddata .
```
そして、jpn_best.traineddataから辞書ファイルを取り出し、kuzusiPlus.traineddataに組み込みます。

```
# 辞書ファイルをjpn_best.traineddataから引き抜く
$ combine_tessdata -e jpn_best.traineddata jpn_best.lstm-punc-dawg
$ combine_tessdata -e jpn_best.traineddata jpn_best.lstm-number-dawg
$ combine_tessdata -e jpn_best.traineddata jpn_best.lstm-word-dawg

# 辞書ファイルをkuzusiPlus.traineddataに組み込む
$ combine_tessdata -o kuzusiPlus.traineddata jpn_best.lstm-punc-dawg
$ combine_tessdata -o kuzusiPlus.traineddata jpn_best.lstm-number-dawg
$ combine_tessdata -o kuzusiPlus.traineddata jpn_best.lstm-word-dawg
```
これで警告は出なくなるはずです。それではこれを$TESSDATA_PREFIXに移動させて、試しに以下の画像をデスクトップに置いて試してみましょう。
![sample.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/554112/2c01e77c-3beb-d040-4500-00e582374373.jpeg)


```
# 編集したkuzusiPlus.traineddataを TESSDATA_PREFIX　に移す
$ sudo cp kuzusiPlus.traineddata $TESSDATA_PREFIX/

#　sample.jpegをkuzusiPlusで認識してみる
$ tesseract -l kuzusiPlus sample.jpeg stdout
お れ お ま お は れ は は お を や きす すき すき つや れき な つれ つや きれ は きま れ を き お お つつ ま
```
警告が消えてますね^^　おめでとうございます！

注意）今回の方法は私が試して解決した方法であり、根本的な解決先ではない可能性が高いです。
　　　なので、これで精度に影響が出てしまっても責任は取れません。
　　　あくまで私の為のメモとして書きました。