1. 程式人生 > >移動裝置上的多位數字識別

移動裝置上的多位數字識別

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

前言

將紙質文件轉換為數字文件有著巨大的需求,因為數字文件更容易檢索。經過多年的探索和研究,OCR(Optical Character Recognition,光學字元識別)技術日趨成熟,OCR技術在印刷、列印行業應用廣泛,可以快速的將紙質資料轉換為電子資料。而近些年來,卷積神經網路(CNN)快速發展,是最先進的影象識別技術,其應用範圍不僅僅侷限於轉化文件,在人臉識別、號碼識別、自動駕駛等領域得到廣泛應用。

先前的研究表明,使用CNN,單個數字識別可以實現低於1%的錯誤率。對於多位數字識別,也有人進行門牌號碼、車輛VIN(Vehicle Identification Number,車輛識別碼)識別之類的研究。但是,據我們所知,在移動裝置上使用CNN進行多位數字識別尚未得到很好的研究。

移動解決方案具有許多優點:便攜、便宜且擁有便捷的互動介面。但是,移動平臺有其自身的約束,例如實時響應速度、有限的記憶體資源。特別是,在移動裝置上執行CNN是一個具有挑戰性的問題,因為傳統的CNN通常需要大量的記憶體。雖然在服務端有一個功能強大的伺服器來處理和識別影象,可以大大減少客戶端的計算,但該解決方案需要可靠的網路連線,存在額外的網路延遲。

因此,在大多數情況下,我們仍然優選基於客戶端的解決方案。為了達到移動客戶端的效能要求,我們從以下幾個方面優化了系統:

  • 分割影象 為了減少識別過程中的計算量,對原始影象進行預處理,並分割出數字,輸入給CNN的是影象分割塊。

  • 簡單的CNN架構 與那些為識別複雜的物件而設計的最先進的深度CNN相比,我們構建了一個神經元較少的淺層網路,並只針對手寫數字識別進行訓練。簡單的CNN只需少量的記憶體,並能在移動裝置上快速執行,實驗結果表明它仍然可以達到不錯的準確度 - 錯誤率低於1%。

  • 批量處理全連線層 批量化處理全連線層,更多的引數得到重用,區域性快取更有效。

測試結果表明,雖然使用了相對較淺的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-4、正規化率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