專案源地址: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 - 新增一種準確率計算方式
目錄
- 2.1 資料集
- 2.2 配置檔案
- 2.3 驗證和拆分資料集
- 2.4 訓練模型
- 2.5 批量驗證
- 2.6 啟動WebServer
- 2.7 呼叫介面
- 2.8 部署
- 2.9 部署多個模型
- 2.10 壓力測試
1 專案介紹
1.1 關於驗證碼識別
驗證碼識別大多是爬蟲會遇到的問題,也可以作為影象識別的入門案例。目前通常使用如下幾種方法:
方法名稱 | 相關要點 |
---|---|
tesseract | 僅適合識別沒有干擾和扭曲的圖片,訓練起來很麻煩 |
其他開源識別庫 | 不夠通用,識別率未知 |
付費OCR API | 需求量大的情形成本很高 |
影象處理+機器學習分類演算法 | 涉及多種技術,學習成本高,且不通用 |
卷積神經網路 | 一定的學習成本,演算法適用於多類驗證碼 |
這裡說一下使用傳統的影象處理和機器學習演算法,涉及多種技術:
- 影象處理
- 前處理(灰度化、二值化)
- 影象分割
- 裁剪(去邊框)
- 影象濾波、降噪
- 去背景
- 顏色分離
- 旋轉
- 機器學習
- KNN
- SVM
使用這類方法對使用者的要求較高,且由於圖片的變化型別較多,處理的方法不夠通用,經常花費很多時間去調整處理步驟和相關演算法。
而使用卷積神經網路,只需要通過簡單的前處理,就可以實現大部分靜態字元型驗證碼的端到端識別,效果很好,通用性很高。
這裡列出目前常用的驗證碼生成庫:
參考:Java驗證全家桶
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 說明
- 目前沒有儲存用於tensorboard的日誌檔案
4 已知BUG
- 使用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資料夾出錯。 解決辦法:編輯執行配置,設定工作空間為專案目錄即可。
-
FileNotFoundError: [Errno 2] No such file or directory: 'xxxxxx'
目錄下有資料夾不存在,在指定目錄建立好資料夾即可。 -
api程式在執行過程中記憶體越佔越大
結果查閱資料:連結
在迭代迴圈時,不能再包含任何張量的計算表示式,否在會記憶體溢位。 將張量的計算表示式放到init初始化執行後,識別速度得到極大的提升。 -
載入多個模型報錯 原因是兩個Recognizer物件都使用了預設的Graph。 解決辦法是在建立物件的時候不使用預設Graph,新建graph,這樣每個Recognizer都使用不同的graph,就不會衝突了。
-
Flask程式的併發執行 暫缺,可以使用flask+uwsgi實現