使用tensorflow和cnn(卷積神經網路)識別驗證碼並構建API

Nick本尊發表於2019-03-24

專案源地址:github.com/nickliqian/… 歡迎大家star和fork~

cnn_captcha

use CNN recognize captcha by tensorflow.
本專案針對字元型圖片驗證碼,使用tensorflow實現卷積神經網路,進行驗證碼識別。
專案封裝了比較通用的校驗、訓練、驗證、識別、API模組,極大的減少了識別字元型驗證碼花費的時間和精力。

專案已經幫助很多同學高效完成了驗證碼識別任務。 如果你在使用過程中出現了bug和做了良好的改進,歡迎提出issue和PR,作者會盡快回復,希望能和你共同完善專案。

如果你需要識別點選、拖拽類驗證碼,或者有目標檢測需求,也可以參考這個專案nickliqian/darknet_captcha

時間表

2018.11.12 - 初版Readme.md
2018.11.21 - 加入關於驗證碼識別的一些說明
2018.11.24 - 優化校驗資料集圖片的規則
2018.11.26 - 新增train_model_v2.py檔案,訓練過程中同時輸出訓練集和驗證集的準確率
2018.12.06 - 新增多模型部署支援,修復若干bug
2018.12.08 - 優化模型識別速度,支援api壓力測試和統計耗時
2018.02.19 - 新增一種準確率計算方式

目錄

1 專案介紹

2 如何使用

3 說明

4 已知BUG

1 專案介紹

1.1 關於驗證碼識別

驗證碼識別大多是爬蟲會遇到的問題,也可以作為影象識別的入門案例。目前通常使用如下幾種方法:

方法名稱 相關要點
tesseract 僅適合識別沒有干擾和扭曲的圖片,訓練起來很麻煩
其他開源識別庫 不夠通用,識別率未知
付費OCR API 需求量大的情形成本很高
影象處理+機器學習分類演算法 涉及多種技術,學習成本高,且不通用
卷積神經網路 一定的學習成本,演算法適用於多類驗證碼

這裡說一下使用傳統的影象處理和機器學習演算法,涉及多種技術:

  1. 影象處理
  • 前處理(灰度化、二值化)
  • 影象分割
  • 裁剪(去邊框)
  • 影象濾波、降噪
  • 去背景
  • 顏色分離
  • 旋轉
  1. 機器學習
  • KNN
  • SVM

使用這類方法對使用者的要求較高,且由於圖片的變化型別較多,處理的方法不夠通用,經常花費很多時間去調整處理步驟和相關演算法。
而使用卷積神經網路,只需要通過簡單的前處理,就可以實現大部分靜態字元型驗證碼的端到端識別,效果很好,通用性很高。

這裡列出目前常用的驗證碼生成庫:

參考:Java驗證全家桶

使用tensorflow和cnn(卷積神經網路)識別驗證碼並構建API
在這裡插入圖片描述

1.2 目錄結構

1.2.1 基本配置

序號 檔名稱 說明
1 sample.py 配置檔案
2 sample資料夾 存放資料集
3 model資料夾 存放模型檔案

1.2.2 訓練模型

序號 檔名稱 說明
1 verify_and_split_data.py 驗證資料集和拆分資料為訓練集和測試集
2 train_model.py 訓練模型
3 train_model_v2.py 訓練模型,訓練過程中同時輸出訓練集和驗證集的準確率,推薦使用此種方式訓練
4 test_batch.py 批量驗證
5 gen_image/gen_sample_by_captcha.py 生成驗證碼的指令碼
6 gen_image/collect_labels.py 用於統計驗證碼標籤(常用於中文驗證碼)

1.2.3 web介面

序號 檔名稱 說明
1 recognition_object.py 封裝好的識別類
2 recognize_api.py 使用flask寫的提供線上識別功能的介面
3 recognize_online.py 使用介面識別的例子
4 recognize_local.py 測試本地圖片的例子
5 recognize_time_test.py 壓力測試識別耗時和請求響應耗時

1.3 依賴

pip3 install tensorflow==1.7.0 flask==1.0.2 requests==2.19.1 Pillow==4.3.0 matplotlib==2.1.0 easydict==1.8
複製程式碼

1.4 模型結構

序號 層級
輸入 input
1 卷積層 + 池化層 + 降取樣層 + ReLU
2 卷積層 + 池化層 + 降取樣層 + ReLU
3 卷積層 + 池化層 + 降取樣層 + ReLU
4 全連線 + 降取樣層 + Relu
5 全連線 + softmax
輸出 output

2 如何使用

2.1 資料集

原始資料集可以存放在./sample/origin目錄中
為了便於處理,圖片最好以2e8j_17322d3d4226f0b5c5a71d797d2ba7f7.jpg格式命名(標籤_序列號.字尾)

如果你沒有訓練集,你可以使用gen_sample_by_captcha.py檔案生成訓練集檔案。 生成之前你需要修改相關配置(路徑、檔案字尾、字符集等)。

2.2 配置檔案

建立一個新專案前,需要自行修改相關配置檔案

圖片資料夾
sample_conf.origin_image_dir = "./sample/origin/"  # 原始檔案
sample_conf.train_image_dir = "./sample/train/"   # 訓練集
sample_conf.test_image_dir = "./sample/test/"   # 測試集
sample_conf.api_image_dir = "./sample/api/"   # api接收的圖片儲存路徑
sample_conf.online_image_dir = "./sample/online/"  # 從驗證碼url獲取的圖片的儲存路徑

# 模型資料夾
sample_conf.model_save_dir = "./model/"  # 訓練好的模型儲存路徑

# 圖片相關引數
sample_conf.image_width = 80  # 圖片寬度
sample_conf.image_height = 40  # 圖片高度
sample_conf.max_captcha = 4  # 驗證碼字元個數
sample_conf.image_suffix = "jpg"  # 圖片檔案字尾

# 驗證碼字元相關引數
# 驗證碼識別結果類別
sample_conf.char_set = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i',
                        'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z']

# 驗證碼遠端連結
sample_conf.remote_url = "https://www.xxxxx.com/getImg"
複製程式碼

具體配置的作用會在使用相關指令碼的過程中提到 關於驗證碼識別結果類別,假設你的樣本是中文驗證碼,你可以使用gen_image/collect_labels.py指令碼進行標籤的統計。 會生成檔案gen_image/labels.json存放所有標籤,在配置檔案中設定use_labels_json_file = True開啟讀取labels.json內容作為結果類別

2.3 驗證和拆分資料集

此功能會校驗原始圖片集的尺寸和測試圖片是否能開啟,並按照19:1的比例拆分出訓練集和測試集。
所以需要分別建立和指定三個資料夾:origin,train,test用於存放相關檔案。

也可以修改為不同的目錄,但是最好修改為絕對路徑。
資料夾建立好之後,執行以下命令即可:

python3 verify_and_split_data.py
複製程式碼

一般會有類似下面的提示

Total image count: 10094
====以下4張圖片有異常====
[第2123張圖片] [325.txt] [檔案字尾不正確]
[第3515張圖片] [_15355300508855503.gif] [圖片標籤異常]
[第6413張圖片] [qwer_15355300721958663.gif] [圖片尺寸異常為:(50, 50)]
[第9437張圖片] [abcd_15355300466073782.gif] [圖片無法正常開啟]
========end
開始分離原始圖片集為:測試集(5%)和訓練集(95%)
共分配10090張圖片到訓練集和測試集,其中4張為異常留在原始目錄
測試集數量為:504
訓練集數量為:9586
複製程式碼

2.4 訓練模型

建立好訓練集和測試集之後,就可以開始訓練模型了。
訓練的過程中會輸出日誌,日誌展示當前的訓練輪數、準確率和loss。
此時的準確率是訓練集圖片的準確率,代表訓練集的圖片識別情況
例如:

第10次訓練 >>> 準確率為 1.0 >>> loss 0.0019966468680649996
複製程式碼

這裡不具體介紹tensorflow安裝相關問題,直奔主題。
確保圖片相關引數和目錄設定正確後,執行以下命令開始訓練:

python3 train_model.py
複製程式碼

也可以呼叫類開始訓練或執行一次簡單的識別演示

from train_model import TrainModel
from sample import sample_conf

# 匯入配置
train_image_dir = sample_conf["train_image_dir"]
char_set = sample_conf["char_set"]
model_save_dir = sample_conf["model_save_dir"]

# verify引數預設為False,當verify=True則會在訓練前校驗所有圖片格式時候為指定的字尾
tm = TrainModel(train_image_dir, char_set, model_save_dir, verify=False)

tm.train_cnn()  # 執行訓練

tm.recognize_captcha()  # 識別演示

複製程式碼

2018.11.26 新增train_model_v2.py檔案
同樣是訓練模型的指令碼,在訓練過程中增加了識別測試集的並輸出準確率的過程,例如:

第480次訓練 >>> [訓練集] 準確率為 1.0 >>> loss 0.0017373242881149054
            >>> [驗證集] 準確率為 0.9500000095367432 >>> loss 0.0017373242881149054
驗證集準確率達到99%,儲存模型成功
複製程式碼

由於訓練集中常常不包含所有的樣本特徵,所以會出現訓練集準確率是100%而測試集準確率不足100%的情況,此時提升準確率的一個解決方案是增加正確標記後的負樣本。

2.5 批量驗證

使用測試集的圖片進行驗證,輸出準確率。

python3 test_batch.py
複製程式碼

也可以呼叫類進行驗證

from test_batch import TestBatch
from sample import sample_conf

# 匯入配置
test_image_dir = sample_conf["test_image_dir"]
model_save_dir = sample_conf["model_save_dir"]
char_set = sample_conf["char_set"]
total = 100  # 驗證的圖片總量

tb = TestBatch(test_image_dir, char_set, model_save_dir, total)
tb.test_batch()  # 開始驗證

複製程式碼

2.6 啟動WebServer

專案已經封裝好載入模型和識別圖片的類,啟動web server後呼叫介面就可以使用識別服務。
啟動web server

python3 recognize_api.py
複製程式碼

介面url為http://127.0.0.1:6000/b

2.7 呼叫介面

使用requests呼叫介面:

url = "http://127.0.0.1:6000/b"
files = {'image_file': (image_file_name, open('captcha.jpg', 'rb'), 'application')}
r = requests.post(url=url, files=files)
複製程式碼

返回的結果是一個json:

{
    'time': '1542017705.9152594',
    'value': 'jsp1',
}
複製程式碼

檔案recognize_online.py是使用介面線上識別的例子

2.8 部署

部署的時候,把recognize_api.py檔案的最後一行修改為如下內容:

app.run(host='0.0.0.0',port=5000,debug=False)
複製程式碼

然後開啟埠訪問許可權,就可以通過外網訪問了。
另外為了開啟多程式處理請求,可以使用uwsgi+nginx組合進行部署。
這部分可以參考:Flask部署選擇

2.9 部署多個模型

部署多個模型: 在recognize_api.py檔案彙總,新建一個Recognizer物件;
並參照原有up_image函式編寫的路由和識別邏輯。

Q = Recognizer(image_height, image_width, max_captcha, char_set, model_save_dir)
複製程式碼

注意修改這一行:

value = Q.rec_image(img)
複製程式碼

2.10 壓力測試和統計資料

提供了一個簡易的壓力測試指令碼,可以統計api執行過程中識別耗時和請求耗時的相關資料,不過圖需要自己用Excel拉出來。
開啟檔案recognize_time_test.py,修改main函式下的test_file路徑,這裡會重複使用一張圖片來訪問是被介面。
最後資料會儲存在test.csv檔案中。
使用如下命令執行:

python3 recognize_time_test.py
----輸出如下
2938,5150,13:30:25,總耗時:29ms,識別:15ms,請求:14ms
2939,5150,13:30:25,總耗時:41ms,識別:21ms,請求:20ms
2940,5150,13:30:25,總耗時:47ms,識別:16ms,請求:31ms
複製程式碼

這裡對一個模型進行了兩萬次測試後,一組資料test.csv。 把test.csv使用箱線圖進行分析後可以看到:

在這裡插入圖片描述

  • 單次請求API總耗時(平均值):27ms
  • 單次識別耗時(平均值):12ms
  • 每次請求耗時(平均值):15ms
    其中有:請求API總耗時 = 識別耗時 + 請求耗時

3 說明

  1. 目前沒有儲存用於tensorboard的日誌檔案

4 已知BUG

  1. 使用pycharm啟動recognize_api.py檔案報錯
2018-12-01 00:35:15.106333: W T:\src\github\tensorflow\tensorflow\core\framework\op_kernel.cc:1273] OP_REQUIRES failed at save_restore_tensor.cc:170 : Invalid argument: Unsuccessful TensorSliceReader constructor: Failed to get matching files on ./model/: Not found: FindFirstFile failed for: ./model : ϵͳ�Ҳ���ָ����·����
; No such process
......
tensorflow.python.framework.errors_impl.InvalidArgumentError: Unsuccessful TensorSliceReader constructor: Failed to get matching files on ./model/: Not found: FindFirstFile failed for: ./model : ϵͳ\udcd5Ҳ\udcbb\udcb5\udcbdָ\udcb6\udca8\udcb5\udcc4·\udcbe\udcb6\udca1\udca3
; No such process
	 [[Node: save/RestoreV2 = RestoreV2[dtypes=[DT_FLOAT, DT_FLOAT, DT_FLOAT, DT_FLOAT, DT_FLOAT, DT_FLOAT, DT_FLOAT, DT_FLOAT, DT_FLOAT, DT_FLOAT], _device="/job:localhost/replica:0/task:0/device:CPU:0"](_arg_save/Const_0_0, save/RestoreV2/tensor_names, save/RestoreV2/shape_and_slices)]]
複製程式碼

由pycharm預設設定了工作空間,導致讀取相對路徑的model資料夾出錯。 解決辦法:編輯執行配置,設定工作空間為專案目錄即可。

在這裡插入圖片描述

  1. FileNotFoundError: [Errno 2] No such file or directory: 'xxxxxx'
    目錄下有資料夾不存在,在指定目錄建立好資料夾即可。

  2. api程式在執行過程中記憶體越佔越大
    結果查閱資料:連結
    在迭代迴圈時,不能再包含任何張量的計算表示式,否在會記憶體溢位。 將張量的計算表示式放到init初始化執行後,識別速度得到極大的提升。

  3. 載入多個模型報錯 原因是兩個Recognizer物件都使用了預設的Graph。 解決辦法是在建立物件的時候不使用預設Graph,新建graph,這樣每個Recognizer都使用不同的graph,就不會衝突了。

  4. Flask程式的併發執行 暫缺,可以使用flask+uwsgi實現

相關文章