本文章將記錄我利用深度學習方法實現身份證圖像的信息識別系統的實現過程,及學習到的心得與體會。本次實踐是我投身AI的初次系統化的付諸實踐,意義重大,讓本身成長許多。終於有空閒的時間,將其記錄,只爲更好的分享與學習。html
一、本人的主要工做python
二、關鍵技術git
三、模型訓練github
四、系統設計及實現web
五、總結算法
深度學習技術與傳統模式識別技術相比,免去人工提取特徵,識別率更高。我基於深度學習的技術背景,主要的研究內容以下:數據庫
1)身份證圖像涉及我的隱私,很難獲取其數據訓練集。針對此問題,我採用獲取身份證上印刷體漢字和數字的數據訓練集的方法,利用Python圖像庫(PIL)將13類漢字印刷體字體轉換成6492個類別,創建了較大的字符訓練集;數組
2)如何獲取身份證圖片上的字符是在設計中一個重要問題。我採用水平和垂直投影技術,首先對身份證圖像進行預處理,而後對圖片在水平和垂直方向上像素求和,區分字符與空白區域,完成了身份證圖像中字符定位與分割工做,有很好的切分效果;服務器
3)在模型訓練中模型的選擇與設計是一個重要的環節,本文選擇Lenet模型,發現模型層次太淺,而後增長卷積層和池化層,設計出了改進的深層Lenet模型,而後採用Caffe深度學習工具對模型進行訓練,並在訓練好的模型上進行測試,實驗代表,模型的測試精度達到96.2%。網絡
基於上述研究,本文設計並實現了身份證圖像自動識別系統,該系統先從移動端拍照獲取身份證圖片,而後在Flask輕量級web服務器上將身份證圖像輸入到模型中進行識別,並返回識別結果。設計的系統能準確識別出身份證上文字信息,具備較高的準確率,有必定的實用價值。
深度學習技術被提出後,發展迅速,在人工智能領域取得了很好的成績,愈來愈多優秀的神經網絡也應運而生。深度學習經過創建多個隱層的深層次網絡結構,好比卷積神經網絡,能夠用來研究並處理目前計算機視覺領域的一些熱門的問題,如圖像識別和圖像檢索。
深度學習創建從輸入數據層到高層輸出層語義的映射關係,免去了人工提取特徵的步驟,創建了相似人腦神經網的分層模型結構。深度學習的示意圖如圖所示
圖 有多個隱層的深度學習示意圖
深度學習的學習過程會分層進行,深度學習通過多層的學習,最終能夠很好的表示數據特徵。
卷積神經網絡有了不少年的發展,CNN在對圖像的識別上有很好的表現,利用權值共享的方式,下降了網絡的維度,也加快了訓練網絡的效率。
CNN具備多層的神經網絡,有卷積層、池化層和線性整流層等,具體CNN網絡結構示意圖如圖所示:
圖 卷積神經網絡示意圖
如上圖所示,輸入數據圖像Input分別與三個濾波器作卷積運算。卷積完成後, 函數獲得的特徵映射圖,而後再將特徵映射圖輸入到 層。S2層處理完後到達C3層,和 C1 的處理步驟同樣,以此類推,最終輸出結果。
卷積神經網絡的網絡結構以下:
1)卷積層(Convolutional layer)
卷積層由卷積單元組成,卷積單元進行着卷積運算,目的是提取出不一樣的形色各異的特徵。
2)線性整流層(ReLU layer)
線性整流層使用線性整流(Rectified Linear Units, ReLU)做爲這一層神經的激活函數(Activation function)。它能夠加強斷定函數和整個神經網絡的非線性特性,而自己並不會改變卷積層。ReLU能夠將神經網絡的訓練速度提高數倍,而並不會對模型的泛化準確度形成產生顯著影響。
3)池化層(Pooling Layer)
池化(Pooling)是卷積神經網絡中另外一個重要的概念。Pooling是一種向下採樣過程,它將劃定的區域輸出最大值。Pooling層的做用是經過減少數據參數數量有效的控制過擬合。在平時的使用中,Convolutional層之間都會按照必定的週期來插入Pooling層。
圖 池化層下采樣過程圖
池化層一般會分別做用於每一個輸入的特徵並減少其大小。目前最經常使用形式的池化層是每隔2個元素從圖像劃分出2x2的區塊,而後對每一個區塊中的4個數取最大值。這將會減小75%的數據量。過去,平均池化的使用曾經較爲普遍,可是最近因爲最大池化在實踐中的表現更好,平均池化已經不太經常使用。
由於池化層太快地減少了數據量的大小,對與數據特徵的學習不利,目前的趨勢是使用較小的池化濾鏡,甚至再也不使用池化層。
4)損失函數層(loss layer)
損失函數層一般是網絡的最後一層,用於表示預測與測試結果之間的差異。在網絡中有不一樣類型的損失函數。
1)Lenet模型
Lenet由Yan Le Cun等人於1998年提出,在手寫數字的識別上有極高的準確率。 輸入層大小爲128×128,第一個卷積層窗口大小爲 5×5,有 20 個過濾器共生成 20 張特徵圖,第二個卷積層窗口大小爲 5×5,50 個過濾器共生成 50 張特徵圖,兩個池化層窗口大小均爲 2,第一個全鏈接層共有 500 個神經元,如圖所示。
圖 Lennet模型示意圖
2)Alexnet模型
Alexnet比Lenet的網絡結構更深,如圖 Alexnet示意圖所示。
圖 Alexnet網絡模型示意圖
在Alexnet網絡模型中輸入的圖像規格是224*224三通道的RGB顏色圖像,特徵的提取採用5*5的卷積核。在使用中Alexnet會輸入227*227三通道圖片,而不是它規格中的大小。在提取特徵圖時採用公式,以下所示:
[img_size - filter_size]/stride +1 = new_feture_size
其中[ ]表示向下取整,圖片而且是RGB通道的。
Caffe是目前深度學習領域主流的一個開源庫,採用C++和CUDA實現,支持MATLAB和Python接口,優勢是速度快、開放性好、易於模塊化拓展。Caffe能夠普遍應用在多個領域,典型的如圖像分類、語音識別等。
Caffe框架能夠從三方面來理解:1)數據存儲:Caffe經過四維數組(blobs)來保存模型計算上的數據,大型數據存儲在LevelDB數據庫中。2)層結構: Caffe支持完整的層類型,按照分層模型,能夠在分層模型中保證了數據參數傳遞的準確性; 3)網絡和運行方式:一個深度神經網絡起始於數據層,數據層加載數據,中間每一層計算出梯度,最後一層完成分類重建,這樣一整套運行流程使得Caffe有很好的健壯性。
在對輸入的身份證圖片進行識別前,要進行必要的圖片預處理。在本文的系統中是基於單個字符的數據集訓練獲得的模型,系統中輸入身份證圖片後要對文字區域進行定位與切割,以此來獲取身份證 圖片上的每個字符。
本文的字符定位與切割採用水平和垂直投影分割法,具體流程以下:1)首先對圖像進行二值化處理,將圖像轉變成黑白的灰度圖;2)在字符區域圖像中呈現出白色,空隙區域爲黑色,經過水平投影的方式將字符區域與空隙區域分割開,以此來肯定字符所在行。3)使用垂直投影分割法,將字符從二值化圖像的每一行中分割出來,如此通過垂直與水平的切割,能夠將每一個字都分割出來。
本系統中,要獲取的身份證上的字體爲印刷體,目前有不少開源的手寫漢字數據庫,好比中科院的CASIA手寫中文字庫]中的 HWDB1.1 脫機字庫子集。若是利用手寫漢字庫進行本系統的模型訓練,會致使模型精度不高,並且適用情景也有誤差。因此在本系統中我將用Python生成印刷體漢字圖片字庫。
在平常使用中,共有3755個經常使用漢字,如何獲取這些數據訓練集成爲一個要解決的問題。在Python圖像庫(PIL)是很好的工具,有很優秀的圖像處理功能,本文利用PIL對數據訓練集進行處理。PIL實現文字轉換成圖片的過程核心代碼以下所示:
find_image_bbox = FindImageBBox() img = Image.new("RGB", (self.width, self.height), "black") draw = ImageDraw.Draw(img)
PIL將文字繪製到img上,以後把圖片保存成jpg格式。
從項目的font_path目錄下選擇字體格式文件,有ttf、ttc、otf等格式的文件,都經過ImageFont的truetype()方法設置選擇哪一種字體文件
而後將文字繪製到圖片中,輸出文字圖片的列表:
draw.text((0, 0), char, (255, 255, 255),font=font)
data = list(img.getdata())
通過實驗,本文收集到一級經常使用和次經常使用的漢字共有6492個,將每一個漢字寫入腳本的數據文件中,而後使用上述PIL方法依次讀取數據文件中的漢字並生成字體圖片輸出,並將字體圖片按順序分類存放,漢字的腳本見下面的連接:腳本連接GitHub
字體文件下載地址:連接
這裏生成的字體圖片如圖所示,最後會組合類別:
這裏附上執行獲取數據集的腳本代碼:
本文采用Caffe框架在分割好的字符集上進行訓練。在訓練網絡前首先要準備好數據訓練集。在Caffe中直接使用的是lmdb或者leveldb文件,因此咱們須要將圖片文件轉化成db文件,讓Caffe可識別。在Caffe的工程目錄下的tools裏不少工具類文件,其中的convert_imageset.cpp的做用就是將圖片數據訓練集轉換成db文件,讓Caffe可以識別,在對Caffe編譯以後產生可執行文件,這裏convert_imageset的使用格式以下:
convert_imageset [FLAGS] ROOTFOLDER/ LISTFILE DB_NAME
如上所示,convert_imageset方法須要帶四個參數,分別指定圖片參數、圖片存放的路徑、圖片清單和生成的db文件存放目錄。
以後建立一個sh腳本文件將各個函數與輸出進行整合,最終的目的是在相應目錄下生成train_lmdb和test_lmdb文件,以下爲在腳本文件中生成train_lmdb的示例代碼:
GLOG_logtostderr=1 $TOOLS/convert_imageset \ --resize_height=$RESIZE_HEIGHT \ --resize_width=$RESIZE_WIDTH --shuffle --gray \ $TRAIN_DATA_ROOT $DATA/train.txt $EXAMPLE/train_lmdb
最後在終端下運行這個腳本文件,生成了訓練用和測試用的lmdb文件,如圖所示:
圖 lmdb文件目錄
同時會生成圖片清單文件列表,本文截取部份內容,如圖3.5所示:
這裏附上數據集轉換的完整代碼:
#!/usr/bin/env sh # Create the imagenet lmdb inputs # N.B. set the path to the imagenet train + val data dirs EXAMPLE=/workspace/caffe_dataset_lower_eng mkdir -p $EXAMPLE rm -rf $EXAMPLE/train_lmdb rm -rf $EXAMPLE/val_lmdb DATA=/workspace/caffe_dataset_lower_eng TOOLS=$CAFFE_ROOT/build/tools TRAIN_DATA_ROOT=${DATA}/ VAL_DATA_ROOT=${DATA}/ # Set RESIZE=true to resize the images to 256x256. Leave as false if images have # already been resized using another tool. RESIZE=true if $RESIZE; then RESIZE_HEIGHT=28 RESIZE_WIDTH=28 else RESIZE_HEIGHT=0 RESIZE_WIDTH=0 fi if [ ! -d "$TRAIN_DATA_ROOT" ]; then echo "Error: TRAIN_DATA_ROOT is not a path to a directory: $TRAIN_DATA_ROOT" echo "Set the TRAIN_DATA_ROOT variable in create_imagenet.sh to the path" \ "where the ImageNet training data is stored." exit 1 fi if [ ! -d "$VAL_DATA_ROOT" ]; then echo "Error: VAL_DATA_ROOT is not a path to a directory: $VAL_DATA_ROOT" echo "Set the VAL_DATA_ROOT variable in create_imagenet.sh to the path" \ "where the ImageNet validation data is stored." exit 1 fi echo "Creating train lmdb..." GLOG_logtostderr=1 $TOOLS/convert_imageset \ --resize_height=$RESIZE_HEIGHT \ --resize_width=$RESIZE_WIDTH \ --shuffle \ --gray \ $TRAIN_DATA_ROOT \ $DATA/train.txt \ $EXAMPLE/train_lmdb GLOG_logtostderr=1 $TOOLS/convert_imageset \ --resize_height=$RESIZE_HEIGHT \ --resize_width=$RESIZE_WIDTH \ --shuffle \ --gray \ $VAL_DATA_ROOT \ $DATA/test.txt \ $EXAMPLE/val_lmdb echo "Done."
在進行網絡訓練前另外一項關鍵的任務是模型的選擇與配置,由於要保證模型的精度,要選一個適合本文身份證信息識別的網絡模型。
首先由於漢字識別至關於一個類別不少的圖片分類系統,因此先考慮深層的網絡模型,優先採用Alexnet網絡模型,對於漢字識別這種千分類的問題很合適,可是在具體實施時發現本文獲取到的數據訓練集每張圖片都是64*64大小的一通道的灰度圖,而Alexnet的輸入規格是224*224三通道的RGB圖像,在輸入上不匹配,而且Alexnet在處理像素較高的圖片時效果好,用在本文的訓練中顯然不合適。
其次是Lenet模型,沒有改進的Lenet是一個淺層網絡模型,現在利用這個模型對手寫數字識別精度達到99%以上,效果很好,在實驗時我利用在Caffe下的draw_net.py腳本而且用到pydot庫來繪製Lenet的網絡模型圖,實驗中繪製的原始Lenet網絡模型圖如圖所示,圖中有兩個卷積層和兩個池化層,網絡層次比較淺。
圖 原始的Lenet網絡模型結構圖
前期實驗中將數據訓練集放入未改進的Lenet模型中進行訓練,訓練效果不理想,accurary值不是穩定上升,而是時常波動,而且出現了過擬合的現象,這裏截取了一張網絡迭代四千次的ROC曲線圖如圖所示:
圖 迭代四千次的ROC曲線圖
圖中出現明顯的波動,而且出現accuracy=1的時候的過擬合現象,由於網絡層次比較淺,訓練效果不理想。這是由於在漢字識別系統上,漢字分類數量巨大,有幾千個分類,簡單的Lenet不足夠支持這麼多的分類。本文嘗試使用改進的Lenet網絡模型用在本文的訓練上,因此本文在訓練網絡前期實驗的基礎上,在Lenet-5的基礎上增長隱含層,包括卷積層和池化層,最後網絡結構達到了11層,修改完成後,爲了查看網絡的結構組成,一樣使用draw_net.py腳本對改進的網絡模型進行繪製,具體結構如圖所示:
圖 改進的Lenet網絡模型結構圖
在圖中能夠明顯看出和原始的Lenet模型相比,改進後的模型層次明顯加深,多了一倍的卷積層和池化層。通過前期訓練實驗得出:深層網絡能更好的支持漢字的分類。
首先編輯solver.prototxt文件:
# The train/test net protocol buffer definition net: "data/lenet_train_test.prototxt" # test_iter specifies how many forward passes the test should carry out. # In the case of MNIST, we have test batch size 100 and 100 test iterations, # covering the full 10,000 testing images. test_iter: 100 # Carry out testing every 500 training iterations. test_interval: 500 # The base learning rate, momentum and the weight decay of the network. base_lr: 0.01 momentum: 0.9 weight_decay: 0.0005 # The learning rate policy lr_policy: "inv" gamma: 0.0001 power: 0.75 # Display every 100 iterations display: 100 # The maximum number of iterations max_iter: 50000 # snapshot intermediate results snapshot: 5000 snapshot_prefix: "data/lenet" # solver mode: CPU or GPU solver_mode: GPU
在Caffe的build文件夾下有tools文件夾,這個裏面有個可執行文件caffe,根據上文生成的Solver文件,能夠在終端直接輸入命令開始訓練網絡:
build/tools/caffe train -solver data/deepocr/solver.prototxt --gpu=1
這裏在命令行的後面添加--gpu=1,表示選擇Tesla K40上GPU進行模型的訓練。由於數據量和迭代次數較大,儘管在GPU環境下訓練並利用cuDNN加速,但訓練模型仍是花費了大約8個小時的時間。最後的訓練精度達到0.962,loss值較小,如圖所示,圖中達到預期的效果,訓練成功。
圖 訓練並測試的結果圖
爲了更加詳細的瞭解訓練的過程,本文使用Python的接口對訓練過程進行可視化,方便得出實驗的結論,因此本文使用jupyter notebook來繪製loss值和accuracy值變化的ROC曲線圖,在網頁的輸入框中配置相關信息便可繪製曲線圖,本文使用SGDSolver,即隨機梯度降低算法對曲線進行繪製:
solver = caffe.SGDSolver('data/deepocr/solver.prototxt')
而後設置迭代次數和手機數據的間隔等信息:
niter = 20000 display= 100
這裏迭代次數設置爲20000次是由於訓練時發現迭代次數超過兩萬次後accuracy的值會趨於穩定,浮動很小,爲了使用對訓練過程進行可視化,沒法利用服務器GPU進行繪製,只能在本地CPU環境下繪製,因此耗時會很是長。
迭代並獲取loss和accuracy值後,初始化曲線圖,將值傳入,即可繪製出ROC曲線圖:
ax1.set_xlabel('iteration') ax1.set_ylabel('train loss') ax2.set_ylabel('test accuracy')
loss和accuracy曲線圖如圖所示:
圖 loss和accuracy曲線圖
在圖中能夠看出,在迭代兩萬次後,loss值逐漸減少並 趨於穩定,accuracy精度值逐漸穩定達到0.96左右,
最後訓練完模型後配置生成deploy.prototxt文件,這個文件會在模型的使用時用到。和訓練時的網絡模型文件不一樣,它沒有數據層,而且將最後的層替換成爲機率層。deploy文件經過手動配置完成。
最後附上修改後的Lenet網絡:連接
其他代碼見GitHub:https://github.com/still-wait/deepLearning_OCR
因爲篇幅緣由,本文其他內容詳見 :
深度學習實踐系列之--身份證上漢字及數字識別系統的實現(下)
本人原創,轉載請註明:http://www.cnblogs.com/ygh1229/p/7224940.html
by still、