移動設備上的多位數字識別

本文的主要內容來自一篇paper,題目爲:MDig: Multi-digit Recognition using Convolutional Nerual Network on Mobile,文章內容並不是對這篇paper的逐句翻譯,若是您在閱讀過程當中有什麼以爲不對、或者某些地方講的不清楚,請參考原文: web.stanford.edu/class/cs231…html

前言

將紙質文檔轉換爲數字文檔有着巨大的需求,由於數字文檔更容易檢索。通過多年的探索和研究,OCR(Optical Character Recognition,光學字符識別)技術日趨成熟,OCR技術在印刷、打印行業應用普遍,能夠快速的將紙質資料轉換爲電子資料。而近些年來,卷積神經網絡(CNN)快速發展,是最早進的圖像識別技術,其應用範圍不只僅侷限於轉化文檔,在人臉識別、號碼識別、自動駕駛等領域獲得普遍應用。java

先前的研究代表,使用CNN,單個數字識別能夠實現低於1%的錯誤率。對於多位數字識別,也有人進行門牌號碼、車輛VIN(Vehicle Identification Number,車輛識別碼)識別之類的研究。可是,據咱們所知,在移動設備上使用CNN進行多位數字識別還沒有獲得很好的研究。git

移動解決方案具備許多優勢:便攜、便宜且擁有便捷的交互界面。可是,移動平臺有其自身的約束,例如實時響應速度、有限的內存資源。特別是,在移動設備上運行CNN是一個具備挑戰性的問題,由於傳統的CNN一般須要大量的內存。雖然在服務端有一個功能強大的服務器來處理和識別圖像,能夠大大減小客戶端的計算,但該解決方案須要可靠的網絡鏈接,存在額外的網絡延遲。github

所以,在大多數狀況下,咱們仍然優選基於客戶端的解決方案。爲了達到移動客戶端的性能要求,咱們從如下幾個方面優化了系統:web

  • 分割圖像 爲了減小識別過程當中的計算量,對原始圖像進行預處理,並分割出數字,輸入給CNN的是圖像分割塊。緩存

  • 簡單的CNN架構 與那些爲識別複雜的對象而設計的最早進的深度CNN相比,咱們構建了一個神經元較少的淺層網絡,並只針對手寫數字識別進行訓練。簡單的CNN只需少許的內存,並能在移動設備上快速運行,實驗結果代表它仍然能夠達到不錯的準確度 - 錯誤率低於1%。bash

  • 批量處理全鏈接層 批量化處理全鏈接層,更多的參數獲得重用,局部緩存更有效。服務器

測試結果代表,雖然使用了相對較淺的CNN,在MNIST數據集上的單個數字識別仍能夠達到99.07%的Top 1精度。經過使用上述優化方法,咱們能夠在大約60ms內處理一個圖像幀,提取32位數字。而批量化的全鏈接層將CNN運行時間再減小12%。網絡

技術方案

流水線

如前面所述,移動應用程序受限於內存和低延遲要求,咱們採用幾種技術來克服這些限制。多位數字的識別過程包括:多線程

  • 預處理 將圖像預處理爲灰度圖像,並使用Canny邊緣檢測來定位數字、放大數字並將背景設置爲全黑以減小噪點。

  • 分割 使用輪廓查找器分割數字塊,並將其調整爲28×28,以便於識別。此外,系統還基於數字的位置來計算哪些數字位屬於同一個數。

  • 識別 使用CNN識別每一個圖像塊中的數字。CNN在主機上訓練,移動設備加載訓練好的參數。程序在全鏈接層中批量處理多個圖像,加速CNN計算。

預處理

圖1:預處理和分割步驟中的輸入和中間圖像

用戶拍攝寫在淺色紙或紙板上的手寫數字的照片。然而,在真實世界的燈光下,陰影和鏡面高光使得數字分割困難,難以直接識別數字。例如,在圖1(a)中,數字的顏色值接近陰影,所以對圖像應用全局閾值不能有效的從背景中分割出數字。爲了解決這一問題,咱們首先在拍攝的圖像上進行預處理。預處理步驟頗有用,由於它能夠消除紙張和光線帶來的噪音且只放大數字。

在預處理中,圖像上的Canny邊緣特徵計算結果被輸入到輪廓查找器中,繪製出每一個特徵的邊界框。邊界框的結果如圖1(b)所示。爲了提升預處理步驟的速度,輸入圖像一開始就調整爲640×480,而且對顏色值進行反向處理,將淺色背景轉換爲深色。

接下來,咱們使用以下等式計算閾值,對邊界框內的像素進行閾值處理:

閥值 = 均值 + 標準誤差 / 2
複製代碼

爲確保閾值與邊距(margin)大小無關,均值和標準誤差值使用邊界框內的全部像素來計算,而不是全部的圖像像素。預處理後的圖像如圖1(c)所示。

分割數字塊

即便將圖像尺寸調整爲640×480,對於圖像識別來講仍然太大。此外,用戶可能想在同一頁面上寫多個數字,一次性找出每一個數字是有用的。所以,分割步驟被引入進來,解決掉這兩個問題。

咱們分兩步對圖像進行分割,首先找到每一個數的邊界框,而後分割邊界框內的每一個數字位。在第一步中,咱們使用輪廓查找器來定位每一個數字位,並在每一個數字位周圍繪製邊界框,而後經過計算和比較數字的位置,合併屬於相同數的數字邊界框。結果如圖1(d)所示。在第二步中,咱們使用空格從左到右掃描合併的邊界框(每列之間的空列),分割出數字塊。數字塊的大小調整爲28×28,因此它與CNN的輸入大小兼容。分段的數字塊如圖1(e)所示。

使用CNN進行數字識別

進行數字分割以後,原始圖像中的每一個數字位依次縮放成28×28的圖像塊。圖像塊送入CNN進行識別。咱們使用了自定義的CNN體系結構,由兩個卷積層(C1和C3)、兩個最大池化層(S2和S4)和兩個全鏈接層(F5和F6)組成,如圖2所示。F2的輸出傳給10路softmax層,它產生10個標籤(即'0' - '9')上的機率分佈。

圖2:CNN架構

第一個卷積層(C1)用8個5×5大小的核過濾輸入的28×28灰度圖像,第二個卷積層(C3)使用16個大小爲5×5×8的核過濾下采樣後的14×14×8特徵映射。兩個卷積層都使用單位步幅。在S2和S4層處,使用2×2非重疊最大池化進行下采樣。最後,兩個全鏈接層F5和F6分別具備128和10個神經元。

整個神經網絡的尺寸(例如卷積窗口大小、層數、內核數等)和LeNet-5接近,它是手寫數字識別早期使用的CNN,但咱們減小了一個全鏈接層。不過,咱們使用了更簡單但更受歡迎的組件來構建網絡。例如,ReLU非線性函數用於卷積層和全鏈接層的輸出,比sigmoid或雙曲線更有效,在相同精度下訓練得更快。

離線訓練

咱們使用Python構建和訓練圖2所示的CNN架構,使用MNIST做爲訓練數據集。使用MATLAB進行大小端格式轉換後,每一個輸入圖像是一個28×28的數字塊,有着灰色背景和白色數字。咱們計算圖像均值,對每一個圖像減去均值,以造成最終的輸入塊。因爲輸入塊是中心只有一個對象的單通道圖像,咱們沒有對它執行任何數據擴充。爲了加速訓練過程並減小過擬合,咱們在F5和F6之間插入了一個dropout層,丟棄率爲0.5。初始學習速率設置爲0.01,並批量處理100張圖像,咱們嘗試了不一樣的學習率和正規化,發如今學習率5e-四、正規化率5e-3的時候,訓練5個週期(epoch)後,模型能夠達到99.07%的驗證準確率。

移動端實現

鑑於相對較低的CPU性能和有限的內存資源,在移動平臺上實施CNN具備必定的挑戰。在這個項目中,,咱們基於DeepBeliefSDK,一個面向移動平臺的開源CNN框架,構建了CNN。DeepBeliefSDK使用高度優化的C++代碼實現,卷積操做轉換爲通用矩陣-矩陣乘法(GEMM),支持好幾種GEMM庫。對於Android平臺來講,它使用Eigen庫實現了NEON優化的GEMM。

DeepBeliefSDK最初是爲AlexNet而構建的,但框架的模塊化容許咱們大量重用代碼。因爲咱們的CNN使用了和AlexNet相同的組件(好比卷積、全鏈接、ReLU、最大池化和softmax層),咱們調用DeepBeliefSDK中的內部函數和類方法,手動構建網絡。而後採用DeepBeliefSDK的標準文件格式保存網絡,這樣咱們的的主應用程序能夠調用DeepBeliefSDK庫API使用該框架。因爲咱們的CNN相對較淺,網絡參數(主要是32位浮點權重)沒有進行壓縮,而是將它們直接轉儲到二進制文件中,最終文件大小爲426 kB。

參考實現

源碼

項目源代碼位於 github.com/jingpu/MDig

主圖像處理流水線,包括預處理、分割和CNN使用C++實現,用Android NDK 10d 構建,應用程序UI使用Android SDK API 21和OpenCV java庫。C++代碼依賴於兩個庫,OpenCV和 咱們修改過的DeepBeliefSDK。咱們使用具備NEON優化的NDK構建帶Eigen庫的DeepBeliefSDK,關閉了Eigen庫中的多線程優化,以便得到更一致的性能分析。

UI展現

請參看如下演示視頻:

v.qq.com/x/page/n077…


本文到此結束,下一篇文章將說明如何build代碼並運行,敬請關注!

image
相關文章
相關標籤/搜索