目錄
原文鏈接:http://www.juzicode.com/image-ocr-tesseract-ocr5-train
Tesseract除了可以使用官方提供的語言包(traineddata文件),還可以自己訓練模型,特別適用於某些官方語言包識別效果不佳的場景下。我們今天就以手寫數字mnist數據集為例,來看下Tesseract-OCR5.0如何訓練自己的模型,以及如何提高准確率、提升訓練效率。
1、准備工作
- 安裝tesseract5.0版本,可以參考這里:Tesseract-OCR5.0軟件安裝和語言包安裝(Windows系統)。
- 安裝python3(從mnist文件生成訓練用的tif和box文件,以及編寫自動訓練、測試腳本)。
- 從github倉下載traineddata_best類型的traineddata文件,可以選擇eng.traineddata,用來初次訓練字體;下載地址:https://github.com/tesseract-ocr/tessdata_best,下載后保存在當前工作目錄下,另外拷貝一份到系統變量“TESSDATA_PREFIX”路徑下,也即tesseract安裝路徑的tessdata\\文件夾下。
- mnist數據集文件,包含60000個訓練圖片和標簽、10000個測試圖片和標簽;用來訓練和測試;下載地址:http://yann.lecun.com/exdb/mnist/,下載的4個文件接下后保存在當前工作目錄的mnist\\文件夾下。
- 在當前工作目錄下新建一個out_mnist\\文件夾,用來存放所有輸出的文件。
准備工作完成后當前工作目錄下是這樣的:
2、訓練步驟
在討論步驟前,先對一些配置作出約定:
- 輸出文件都存放在out_mnist\\路徑下;
- 中間文件的文件名一般使用LANG.FONT.EXPn.xxx的形式,其中設置LANG=arabnum,FONT=mnist,EXPn=exp0,這樣tif文件名稱為arabnum.mnist.exp0.tif,box文件為arabnum.mnist.exp0.box,以此類推。
2.1、生成訓練用tif和box文件
首先生成訓練用的tif圖片,將60000個訓練圖片合成到一個tif文件里,直接從mnist訓練圖片文件中提取數據,合成tif圖片,注意按照順序提取,這樣后面的標簽文件也按照順序提取,就可以做到一一對應。mnist文件的讀取方法可以參考 mnist數據集的獲取、訪問、使用例子。
接着生成訓練用的box文件,因為mnist數據集里面包含了標簽數據,所以不需要自己使用工具來標注圖片,這樣描述圖片的box文件就可以直接根據訓練標簽文件里的序號和標簽值來生成。box文件里的一行數據由下列幾個元素組成:
標簽值 起始位置x坐標 起始作為y坐標 圖片寬度 圖片高度 在tif文件中的編號(從0開始編號)
mnist的圖片是28×28像素的,所以可以設置圖片寬度為28,圖片高度為28,起始點的坐標x和y都設置為1。標簽值是從訓練標簽文件中按照順序提取的標簽值,最后一列數據從0開始編號即可,最后編號到59999,box文件是這個樣子的:
2.2、生成lstm文件
從當前工作目錄下的訓練用traineddata文件中抽取lstm文件,這里用當前目錄下的eng.traineddata文件生成eng.lstm文件:
combine_tessdata -e eng.traineddata out_mnist\\eng.lstm
打印輸出:
Extracting tessdata components from eng.traineddata
Wrote out_mnist\eng.lstm
Version string:4.00.00alpha:eng:synth20170629:[1,36,0,1Ct3,3,16Mp3,3Lfys64Lfx96Lrx96Lfx512O1c1]
17:lstm:size=11689099, offset=192
18:lstm-punc-dawg:size=4322, offset=11689291
19:lstm-word-dawg:size=3694794, offset=11693613
20:lstm-number-dawg:size=4738, offset=15388407
21:lstm-unicharset:size=6360, offset=15393145
22:lstm-recoder:size=1012, offset=15399505
23:version:size=80, offset=15400517
2.3、生成lstmf文件
這一步利用tif圖片文件生成lstmf文件,如下命令會生成arabnum.mnist.exp0.lstmf文件,這個是典型tesseract命令:
tesseract out_mnist\arabnum.mnist.exp0.tif out_mnist\arabnum.mnist.exp0 -l eng --psm 13 lstm.train
- out_mnist\arabnum.mnist.exp0.tif 輸入圖像
- out_mnist\arabnum.mnist.exp0 輸出的lstmf文件名稱
- -l eng 使用的字體,使用tesseract安裝目錄tessdata\\文件夾下的eng.traineddata
- –psm 13 分割模式
- lstm.train 配置文件,指明進行lstm訓練,使用tesseract安裝目錄tessdata\\config文件夾下的配置文件
打印輸出:
..........
Warning: Invalid resolution 1 dpi. Using 70 instead.
Loaded 6/6 lines (1-6) of document out_mnist\arabnum.mnist.exp0.lstmf
Page 8
Warning: Invalid resolution 1 dpi. Using 70 instead.
Loaded 7/7 lines (1-7) of document out_mnist\arabnum.mnist.exp0.lstmf
Page 9
Warning: Invalid resolution 1 dpi. Using 70 instead.
Loaded 8/8 lines (1-8) of document out_mnist\arabnum.mnist.exp0.lstmf
Page 10
Warning: Invalid resolution 1 dpi. Using 70 instead.
Loaded 9/9 lines (1-9) of document out_mnist\arabnum.mnist.exp0.lstmf
..........
mnist的60000個訓練圖片,該步驟會耗時4-5小時。
2.4、生成lstmf清單文件
手動新建一個arabnum.mnist.exp0.list.txt文本文件,輸入上一步驟生成的lstmf文件的路徑和文件名,可以使用相對路徑,輸入“out_mnist\arabnum.mnist.exp0.lstmf”,文件內容是這個樣子的:
2.5、開始訓練
使用lstmtraining命令開始訓練,這一步生成mod_out_checkpoint文件:
lstmtraining --debug_interval -5 --max_iterations 9000 --target_error_rate 0.01 --continue_from=out_mnist\eng.lstm --model_output=out_mnist\mod_out --train_listfile=out_mnist\arabnum.mnist.exp0.list.txt --traineddata=eng.traineddata
- –debug_interval -5 調試打印等級
- –max_iterations 9000 最大迭代次數
- –target_error_rate 0.01 期望錯誤率
- –continue_from=out_mnist\eng.lstm 基於eng字體的lstm文件(步驟2.2)
- –model_output=out_mnist\mod_out 輸出checkpoint文件的名稱
- –train_listfile=out_mnist\arabnum.mnist.exp0.list.txt 訓練清單文件名稱(步驟2.3和2.4)
- –traineddata=eng.traineddata 訓練使用的字體,當前工作目錄下
輸出打印:
Loaded file out_mnist\eng.lstm, unpacking...
Warning: LSTMTrainer deserialized an LSTMRecognizer!
Continuing from out_mnist\eng.lstm
Loaded 10/10 lines (1-10) of document out_mnist\arabnum.mnist.exp0.lstmf
Iteration 0: GROUND TRUTH : 2
Iteration 0: ALIGNED TRUTH : 22
Iteration 0: BEST OCR TEXT : ps
File out_mnist\arabnum.mnist.exp0.lstmf line 5 :
Mean rms=4.213%, delta=16.667%, train=300%(100%), skip ratio=0%
Iteration 1: GROUND TRUTH : 5
Iteration 1: ALIGNED TRUTH : 55
Iteration 1: BEST OCR TEXT : by
File out_mnist\arabnum.mnist.exp0.lstmf line 0 :
Mean rms=4.146%, delta=16.667%, train=300%(100%), skip ratio=0%
..........
..........
Mean rms=0.015%, delta=0%, train=0%(0%), skip ratio=0%
Iteration 2299: GROUND TRUTH : 4
File out_mnist\arabnum.mnist.exp0.lstmf line 9 (Perfect):
Mean rms=0.015%, delta=0%, train=0%(0%), skip ratio=0%
2 Percent improvement time=2, best error was 10.3 @ 60
At iteration 62/2300/2300, Mean rms=0.015000%, delta=0.000000%, char train=0.000000%, word train=0.000000%, skip ratio=0.000000%, New best char error = 0.000000 wrote best model:out_mnist\mod_out_0.000000_62_2300.checkpoint wrote checkpoint.
Finished! Error rate = 0
2.6、生成traineddata文件
這一步生成mnist.traineddata文件:
lstmtraining --stop_training --traineddata=eng.traineddata --continue_from=out_mnist\mod_out_checkpoint --model_output=mnist.traineddata
- –stop_training 停止訓練
- –traineddata=eng.traineddata 訓練使用的字體,當前工作目錄下
- –continue_from=out_mnist\mod_out_checkpoint 中間文件名稱(步驟2.5)
- –model_output=mnist.traineddata 生成字體文件的traineddata文件名稱
輸出打印:
Loaded file out_mnist\mod_out_checkpoint, unpacking...
2.7、安裝字體
最后將生成的mnist.traineddata文件拷貝到tesseract安裝目錄的tessdata\\路徑下,即完成字體的安裝。
3、驗證與測試
接下來就是利用tesseract命令行或者pytesseract對測試集的圖片進行識別,然后和測試集標簽比對識別是否正確並統計其准確率,可以用python實現自動測試和比對,核心代碼如下:
right_count=0
with open(test_image_file,'rb') as pf_image, open(test_label_file,'rb') as pf_label:
for __ind in range(sample_count):
ind = __ind + offset
img = mnist.read_image_p(pf_image, image_head_len, ind)
label= mnist.read_label_p(pf_label, label_head_len, ind)
text = ts.image_to_string(img,lang,config=config).strip('\x0c').strip('\n')
logger.info('ind:%d, label:%d, text:%s',ind,label,str(bytes(text,encoding='utf8')))
if text == str(label):
right_count += 1
打開test_image_file表示的測試圖片文件和test_label_file表示的測試標簽文件,每次讀出1張圖片和1個標簽,讀出的圖片傳入到pytesseract的image_to_string()進行識別,然后將結果和讀出的標簽數據進行比對,如果結果一樣就將right_count的數值加1,最后統計出准確率。
因為tesseract有13種分割模式,為了減少測試時間,這里先用小樣本數據1000個圖片對不同的分割模式進行了實驗:
('--psm 0', 'exception')
('--psm 1', '0', '0.0')
('--psm 2', 'exception')
('--psm 3', '0', '0.0')
('--psm 4', '2', '0.002')
('--psm 5', '107', '0.107')
('--psm 6', '879', '0.879')
('--psm 7', '890', '0.89')
('--psm 8', '927', '0.927')
('--psm 9', '912', '0.912')
('--psm 10', '890', '0.89')
('--psm 11', '3', '0.003')
('--psm 12', '2', '0.002')
('--psm 13', '932', '0.932')
其中psm=0和2時發生異常識別錯誤,psm=1,3,4,5,11,12的准確率或者為0,或者只有0.1,慘不忍睹,psm=6,7,10正確率不到0.9,psm=8,9,13時的准確率稍高在0.91-0.93。
接下來在3種准確率較高的psm=8,9,13分割模式下,用所有的10000個圖片進行測試:
config=--psm 13 right_count=9445 right_ratio=0.944500
config=--psm 9 right_count=9261 right_ratio=0.926100
config=--psm 8 right_count=9422 right_ratio=0.942200
最高的模式仍然是psm=13時,正確率為0.9445,這個准確率還有很大的提升空間的。
4、提高准確率
從前面的訓練步驟可以看到,開始訓練時需要用到一個已經存在的字體eng.traineddata,比如第2.2步抽取它的lstm文件、第2.5步的訓練等。既然這個原始字體訓練出來的新字體識別的准確率不高,很可能跟選擇的這個初始字體有關,那是不是可以用我們訓練好的新字體替代eng.traineddata再訓練一次呢,這樣產生的第2代的新字體是不是可以有更好地表現?如此迭代多次之后,得到第3代、第4代、第5代字體呢?
回到步驟2.1步~步驟2.7,將eng.traineddata相關的內容替換為mnist.traineddata,新生成的第2代字體取名為mnist2nd,依次替代命令行里面的參數訓練第2代字體,類似的方法再訓練出第3、第4、第5代字體,將每一代字體都用測試集進行測試,得到了下面的測試結果:
mnist1st 13 9445 0.944500
mnist1st 8 9422 0.942200
mnist1st 9 9261 0.926100
mnist2nd 13 9707 0.970700
mnist2nd 8 9685 0.968500
mnist2nd 9 9493 0.949300
mnist3rd 13 9733 0.973300
mnist3rd 8 9710 0.971000
mnist3rd 9 9527 0.952700
mnist4th 13 9682 0.968200
mnist4th 8 9659 0.965900
mnist4th 9 9471 0.947100
mnist5th 13 9743 0.974300
mnist5th 8 9720 0.972000
mnist5th 9 9494 0.949400
畫出來的曲線圖是這樣的:
從這里可以看到,第2代字體較第1代有明顯提升,綠色代表的psm=13,提升了近3個百分點,達到0.973300,再往后第3代稍有提升,第4代反而下降了一點點,到了第5代又升到0.9743。
桔子菌用單步方式沒有再往后測試了,因為訓練到第5代字體斷斷續續用了快2天的時間,接下來有必要提升下訓練的效率了。
5、提升訓練效率
要提升訓練的效率先要找到最耗時的步驟:其中步驟2.3提取lstmf文件耗時最長,桔子菌的電腦耗時4-5小時,其他的步驟都比較少,多的才10分鍾左右。找到了阻塞點,接下來就是如何提高效率。在步驟2.3中可以將樣本進行拆分,比如將60000個樣本分成10組,每組6000條數據丟到一個線程里去提取lstmf文件,理論上會降低到原來的1/10右。經過實際測試,拆分lstmf文件后,單代字體的訓練在步驟2.3上只用了15-20分鍾左右,最后訓練出20代字體耗時9小時左右。
測試環節為了對比每代的准確率,需要將每一代生成的字體對所有樣本進行測試,所以可以通過拆分樣本和拆分字體的方法,丟到多個線程里去執行。桔子菌用psm13的方式測試完20代字體耗時6小時左右,最后得到的准確率是這樣的:
lang=mnist0–config=–psm13–right_count=9521–right_ratio=0.952100
lang=mnist1–config=–psm13–right_count=9471–right_ratio=0.947100
lang=mnist2–config=–psm13–right_count=9779–right_ratio=0.977900
lang=mnist3–config=–psm13–right_count=9762–right_ratio=0.976200
lang=mnist4–config=–psm13–right_count=9797–right_ratio=0.979700
lang=mnist5–config=–psm13–right_count=9755–right_ratio=0.975500
lang=mnist6–config=–psm13–right_count=9836–right_ratio=0.983600
lang=mnist7–config=–psm13–right_count=9843–right_ratio=0.984300
lang=mnist8–config=–psm13–right_count=9817–right_ratio=0.981700
lang=mnist9–config=–psm13–right_count=9845–right_ratio=0.984500
lang=mnist10–config=–psm13–right_count=9836–right_ratio=0.983600
lang=mnist11–config=–psm13–right_count=9832–right_ratio=0.983200
lang=mnist12–config=–psm13–right_count=9778–right_ratio=0.977800
lang=mnist13–config=–psm13–right_count=9832–right_ratio=0.983200
lang=mnist14–config=–psm13–right_count=9862–right_ratio=0.986200
lang=mnist15–config=–psm13–right_count=9831–right_ratio=0.983100
lang=mnist16–config=–psm13–right_count=9848–right_ratio=0.984800
lang=mnist17–config=–psm13–right_count=9842–right_ratio=0.984200
lang=mnist18–config=–psm13–right_count=9859–right_ratio=0.985900
lang=mnist19–config=–psm13–right_count=9882–right_ratio=0.988200
從最終的測試結果看,第3-6代較第1-2代提升了3個百分點,這點和前面單步訓練看到的效果差不多,到了第7代(mnist6)之后提升效果非常緩慢了,最好的情況是在pms=13、第20代字體時,准確確率為0.988200。
如果你想試試更多的迭代次數,獲取更好的訓練效果,桔子菌已經將數據集、訓練腳本、測試腳本都打包好了,可以戳這里獲取:tesseract訓練mnist-by-juzicode.com-vx桔子code
6、避坑指南
1、注意Tesseract 4.0和5.0版本的訓練方式和3.0相差甚遠,3.0方式的訓練不再適用4.0和5.0的LSTM訓練。
2、生成tif文件時用訓練集的單張圖片作為tif圖片的一頁即可,這樣box文件也更簡單。
3、步驟2.2生成eng.lstm文件時,選用的初始traineddata文件必須是從traindata_best中下載的文件,否則會報錯out_mnist\eng.lstm is an integer (fast) model, cannot continue training:
Loaded file out_mnist\eng.lstm, unpacking...
Warning: LSTMTrainer deserialized an LSTMRecognizer!
Error, out_mnist\eng.lstm is an integer (fast) model, cannot continue training
Failed to continue from: out_mnist\eng.lstm
4、步驟2.4生成文件清單arabnum.mnist.exp0.list.txt時,如果文件內容像下面這樣有多余的換行符\r(CR):
在步驟2.5則會報錯Load of page 0 failed:
Loaded file out_mnist\eng.lstm, unpacking...
Warning: LSTMTrainer deserialized an LSTMRecognizer!
Continuing from out_mnist\eng.lstm
Deserialize header failed: out_mnist\arabnum.mnist.exp0.lstmf
Load of page 0 failed!
Load of images failed!!
最簡單的解決辦法是去掉2個換行符\r\n(CR LF)。
當使用多個lstmf文件訓練時,每個文件名之間必須要保留一個換行符,則必須去掉\r(CR)符號,只保留\n(LF),如下圖所示:
推薦閱讀:
Tesseract-OCR5.0軟件安裝和語言包安裝(Windows系統)
新鮮上架的Python3.10,來個match-case嘗嘗鮮