1. 程式人生 > >[純C#實現]基於BP神經網路的中文手寫識別演算法

[純C#實現]基於BP神經網路的中文手寫識別演算法

效果展示

這不是OCR,有些人可能會覺得這東西會和OCR一樣,直接進行整個字的識別就行,然而並不是.
OCR是2維畫素矩陣的畫素資料.而手寫識別不一樣,手寫可以把使用者寫字的筆畫時間順序,抽象成一個維度.這樣識別的就是3維的資料了.識別起來簡單很多.

最近需要做一箇中文手寫識別演算法.搜尋了網上的一些前人作品,發現都是隻講了理論,不講實際開發.於是打算自己開發一個,並記錄開發過程.

由於程式碼量比較多,這裡不會全部貼上來講解,程式碼已經放到了gitee,部分地方需對照程式碼進行觀看,下面有URL.

思路

網上關於中文手寫識別的文章不多,不過數字OCR方案確有很多.
雖然中文手寫識別並不等於OCR,但總歸有點關聯性.
我發現數字的OCR大概是這麼個套路:

神經網路的輸出層每一個節點對應一個數字的相似度.而中文不能這麼做.因為中文有上萬字.
不過這是手寫識別,我們有使用者寫字的時候每一筆的資料,可以先識別筆畫.然後再根據筆畫,去識別字.

資源獲取與資料模型設定

首先我們需要一個字典,用於提供所有中文漢字的筆畫順序,這玩意在百度搜索"字典 mdb"能得到很多(我會放到原始碼裡)

通過檢視字典的"筆順"欄位,我們可以看到,字典中的字,筆順分為了: 橫,豎,撇,捺,其它 這5個型別

橫豎撇捺好弄,不過這個"其它"有點特別,通過查詢.中文的筆畫有30多種.
我按照長相,將筆畫大體分成了這7種:

ID 筆畫 名稱
0
1
2
3
4 ㇕㇖⺄ 橫折
5 ㇗㇙㇞㇟ㄣ㇂ ㇛㇜ 豎折
6 ㇡ ㇌ 橫折折折

也就是說,我這裡是分成7種來識別的,後續使用的時候,是再轉換為5種筆畫.

我們將使用者輸入的筆畫順序識別出來後,經過字串相似度演算法,識別出使用者輸入的筆畫,與字典中每個字的筆畫的相似度,然後進行排序.
關於字串相似度,這裡採用的是

levenshtein演算法,相關程式碼可在我的原始碼中找到.

開發採集工具&採集一些資料

首先我需要採集一些筆畫資料,然後交給神經網路,訓練神經網路識別能力.

這裡開發了一個採集工具,用來採集一些用於訓練的資料:
原始碼>>

使用方法如下:

儲存後會得到一個json檔案,裡面是採集到的筆畫資料:

每個筆畫採集30次之後儲存,在儲存後,請將這個檔案改名,然後再重新開啟一次軟體,採集下一個筆畫

把上面表格中的7個筆畫每一個採集30次左右(次數不需要完全一樣)每個筆畫單獨採集到一個檔案

再額外採集一個用於測試的資料:

訓練過程

這裡選擇BP網路的原因是因為網路上有直接複製即可用的C#程式碼,畢竟我是用C#開發,基於C#的神經網路程式碼很少.大部分是基於C或者python的.
我對我找到的BP網路的部分程式碼進行了修改,訓練完後可以把訓練結果儲存為單個json檔案.也可以讀取json檔案接著訓練,或著運用裡面的訓練結果進行識別.

把上面採集的7個筆畫樣本放入神經網路訓練:

如你所見,我另外開發了一個訓練工具,讀取前面步驟採集到的筆畫資料生成矩陣,給BP網路,進行訓練.

矩陣的格式:
**注:我用來訓練的矩陣的大小是固定的16*16,以下只是為了說明而做的一個縮小版:**

\ 第0列 第1列 第2列 第3列 第4列 第5列 更多列
第0行 0.2 0.0 0.0 0.0 0.0 0.0 .
第1行 0.0 0.4 0.0 0.0 0.0 0.0 .
第2行 0.0 0.0 0.6 0.0 0.0 0.0 .
第3行 0.0 0.0 0.0 0.8 0.0 0.0 .
第4行 0.0 0.0 0.0 0.0 1.0 0.0 .
第5行 0.0 0.0 0.0 0.0 0.0 0.0 .
更多行 . . . . . . .

注意:我在矩陣中使用0~1之間的浮點數標識出了哪個畫素是先畫出來的,哪個畫素是後畫出來的.
不過神經網路輸入的矩陣是1維的,所以在程式碼中可以看到,我寫了個GetDim1Matrix方法,將這裡面的資料,全部連線到了一起.
在程式碼中,有一個MatrixData類,這個類用於存放訓練或者識別用的資料並進行矩陣的輸出,可以在這裡面找到生成矩陣的演算法.

訓練完成後,使用訓練結果,對測試資料進行了測試.並生成了訓練結果檔案:

訓練工具原始碼:
原始碼>>

實際使用

識別功能和採集工具做在一起了,將神經網路訓練出來的結果"GData.json"檔案放進採集工具工程裡.執行工程即可.

在實際使用中效果沒有想象中的好,筆畫相似度高的字比較多,得把字寫得比較工整才能識別到,想要獲取更好的結果,還需要對方案進行更多的優化才行.

改進計劃

目前我比較傾向於這兩個方案:

  1. 在測試中有個現象,筆畫識別錯誤率有點高,可能需要修改筆畫識別的方式,嘗試用別的方式去識別筆畫
  2. 我找到的字典有問題,字元雖然很全,但是筆畫分類才5種,只分為"橫,豎,撇,捺,其它",這個"其它"比較礙事,可以嘗試找筆畫分類更細的字典來解決這個問題.

如果對這個專案感興趣或者有更好優化的思路,可以給我留言或者到Q群:801522252與我討論.