天天看點

Python驗證碼識别 | 源碼+通用識别模型

項目位址: https://github.com/kerlomz/captcha_trainer 編譯版下載下傳位址: https://github.com/kerlomz/captcha_trainer/releases/tag/v1.0 注意:若使用雲伺服器 (Windows Server版) 遇到閃退,請按照步驟:我的電腦——屬性——管理——添加角色和功能——勾選桌面體驗,點選安裝,安裝之後重新開機即可。

2020/06/01編外:

想必各位隻是偶然間搜到這篇文章,網上文章參差不齊,标題黨很多,能跑起來的開源代碼很少,對于能跑起來的代碼,也經常遇到以下問題如:記憶體洩漏,網絡參數寫死導緻更換訓練集報錯,網絡跑其他樣本識别率低,沒有調用示例等等。

再往下看之前,我可以向你們保證,它絕對會是你所見過的所有驗證碼有關的文章中最實用,最接近生産水準的。

  1. 對小白: 你可以不需要動手寫任何一行代碼。
  2. 對小企業: 它的可用性和穩定性是經得起考驗的,在性能上也是同行領先的,可以放心入坑。

你們要的通用識别模型:

可能你們想要的是一行pip就搞定環境的,是以今天給你們安排了麻瓜OCR(MuggleOCR)。

https://pypi.org/project/muggle-ocr

它整合了簡單驗證碼識别通用模型+印刷文字通用識别,并且支援調用本文架構訓練的模型。調用隻需要三行核心代碼:

import time
# STEP 1
import muggle_ocr
import os
# STEP 2
sdk = muggle_ocr.SDK(model_type=muggle_ocr.ModelType.OCR)
root_dir = r"./imgs"
for i in os.listdir(root_dir):
    n = os.path.join(root_dir, i)
    with open(n, "rb") as f:
        b = f.read()
    st = time.time()
    # STEP 3
    text = sdk.predict(image_bytes=b)
    print(i, text, time.time() - st)           

這真的很簡單,應付一般的文字識别和驗證碼都足夠了。(文字識别過幾天會更新一下新模型,畢竟0601模型就跑了半天。

1. 前言

本項目适用于Python3.7,GPU>=NVIDIA GTX1050Ti,原master分支新增了GUI配置界面以及編譯版本了,是時候寫一篇新的文章了。

長話短說,開門見山,網絡上現有的代碼以教學研究為主,本項目是為實用主義者定制的,隻要基本的環境安裝常識,便可很好的訓練出期望的模型,重定義幾個簡單的參數任何人都能使用深度學習技術訓練一個商業化成品。

筆者選用的時下最為流行的CNN+BLSTM+CTC(CRNN)進行端到端的不定長驗證碼識别,代碼中預留了CNNX(搜不到因為是小編自己拼湊的)/MobileNet/DenseNet121/ResNet50等選項,可以在配置界面中直接選用。首先,介紹個大概吧。

Python驗證碼識别 | 源碼+通用識别模型
網格結構 predict-CPU predict-GPU 模型大小
CNN5+Bi-LSTM+H64+CTC 15ms 8ms 2mb
CNN5+CrossEntropy 2ms 1.5mb

H16/H64指的是Bi-LSTM的隐藏神經元個數UnitsNum,是以本項目使用GPU訓練,使用CPU進行預測。

預測服務部署項目源碼請移步此處:

https://github.com/kerlomz/captcha_platform

部署項目的編譯版下載下傳位址:

https://github.com/kerlomz/captcha_platform/releases

2.環境依賴:

花了超長篇幅介紹了訓練環境的基本搭建,主要是給尚未入門的讀者看的,老鳥們随便跳過,若不希望在環境上面浪費時間的,歡迎使用編譯版,可在文章開頭找到下載下傳位址。

關于CUDA和cuDNN版本的問題,不少人很糾結,這裡就列出官方通過pip安裝的TensorFlow的版本對應表:

Linux

Version Python version Compiler Build tools cuDNN CUDA
tensorflow_gpu-1.14.0 3.7 GCC 4.8 Bazel 0.15.0 7.6 9

Windows

MSVC 2015 update 3 10

如果希望使用上面對應之外的搭配的CUDA和cuDNN,可以自行編譯TensorFlow,或者去Github上搜尋

TensorFlow Wheel

找到第三方編譯的對應版本的whl安裝包。提前預警,若是自己編譯将會苦難重重,坑很多,這裡就不展開了。

2.1 本項目環境依賴

目前在以下主流作業系統平台均測試通過:

作業系統 最低支援版本
Ubuntu 16.04
7 SP1
MacOS N/A

本訓練項目主要的環境依賴清單如下

依賴
Python
TensorFlow-GPU 1.14.0
Opencv-Python 4.1.2.30
Numpy 1.16.0
Pillow 4.3.0
PyYaml 3.13
tqdm

2.1.1 Ubuntu 16.04 下的 Python 3.7

1)先安裝Python環境(有Python 3.7環境的可以忽略)

sudo apt-get install openssl  
sudo apt-get install libssl-dev
sudo apt-get install libc6-dev gcc  
sudo apt-get install -y make build-essential zlib1g-dev libbz2-dev libreadline-dev $ libsqlite3-dev wget curl llvm tk-dev 
wget https://www.python.org/ftp/python/3.7.6/Python-3.7.6.tgz
tar -vxf Python-3.7.6.tar.xz
cd Python-3.7.6
./configure --prefix=/usr/local  --enable-shared
make -j8
sudo make install -j8           

經過上面指令就安裝好Python3.7環境了,如果提示找不到

libpython3.7m.so.1.0

就到/usr/local/lib路徑下将該檔案複制一份到/usr/lib和/usr/lib64路徑下。

2)安裝相關依賴(這一步Windows和Linux通用)

可以直接在項目路徑下執行

pip3 install -r requirements.txt

安裝所有依賴,注意這一步是安裝在全局Python環境下的,強烈建議使用虛拟環境進行項目間的環境隔離,如Virtualenv或Anaconda等等。

我一般使用的是Virtualenv,有修改代碼需要的,建議安裝PyCharm作為Python IDE

virtualenv -p /usr/bin/python3 venv # venv is the name of the virtual environment.
cd venv/ # venv is the name of the virtual environment.
source bin/activate # to activate the current virtual environment.
cd captcha_trainer # captcha_trainer is the project path.
pip3 install -r requirements.txt           

2.1.2 Ubuntu 16.04 下的 CUDA/cuDNN

網上看到過很多教程,我自己也部署過很多次,Ubuntu 16.04遇到的坑還是比較少的。14.04支援就沒那麼好,如果主機闆不支援關閉SecureBoot的話千萬不要安裝Desktop版,因為安裝好之後一定會無限循環在登陸界面無法進入桌面。

網上教程說要加驅動黑名單什麼的我直接跳過了,親測沒那個必要。就簡單的幾步:

1. 下載下傳好安裝包

注意下載下傳runfile類型的安裝包,deb安裝會自動安裝預設驅動,極有可能導緻登陸循環

NVIDIA 驅動下載下傳:

https://www.geforce.cn/drivers

CUDA 下載下傳位址:

https://developer.nvidia.com/cuda-downloads

cuDNN 下載下傳位址:

https://developer.nvidia.com/cudnn

(需要注冊NVIDIA賬号且登陸,下載下傳deb安裝包)

2. 關閉圖形界面

Ctrl+alt+F1進入字元界面,關閉圖形界面

sudo service lightdm stop           

3. 安裝Nvidia Driver

指令中的版本自己對應下載下傳的版本改,在上面的下載下傳位址根據自己的顯示卡型号下載下傳最新版,切記是runfile格式的安裝包。

sudo chmod a+x NVIDIA-Linux-x86_64-384.90.run //擷取執行權限
sudo ./NVIDIA-Linux-x86_64-384.90.run –no-x-check –no-nouveau-check –no-opengl-files //安裝驅動           

安裝成功以後使用以下指令驗證,如果顯示顯示卡資訊則表示安裝成功

nvidia-smi           

4. 安裝CUDA

1)先安裝一些系統依賴庫

sudo apt-get install freeglut3-dev build-essential libx11-dev libxmu-dev libxi-dev libgl1-mesa-glx libglu1-mesa libglu1-mesa-dev           

2) 執行安裝程式,按訓示無腦繼續就好了,如果提示是否安裝驅動選不安裝。

sudo sh cuda_9.0.176_384.81_linux.run           

安裝完如果環境變量沒配上去,就寫到 ~/.bashrc 檔案的尾部

export PATH=/usr/local/cuda-9.0/bin${PATH:+:${PATH}}
export LD_LIBRARY_PATH=/usr/local/cuda-9.0/lib64${LD_LIBRARY_PATH:+:${LD_LIBRARY_PATH}}           

然後在終端執行

sudo ldconfig

更新,安裝完畢就可以重新開機機器重新開機圖形界面了。

sudo service lightdm start           

2.1.3 Windows 系統

在Windows其實簡單很多,隻要到官網下載下傳安裝包無腦安裝就可以了,下載下傳連接配接同Ubuntu,先安裝Python,顯示卡驅動,CUDA,然後下載下傳對應的cuDNN替換到對應路徑即可。

3 使用

在訓練之前,有不少群友經常問我“訓練4位數英文數字需要多少樣本?”諸如此類的問題,我這裡統一做個回複,樣本數量主要是看樣本的特征複雜度而定。

這裡可以提供幾個參考依據: 是否變形?是否旋轉?是否有複雜背景幹擾?是否多種字型?字元集(分類數)多大?位數(标簽數)多少?

  1. 一般簡單的幾百個樣本(需要自行調整 驗證集大小 和 驗證批次大小 )即可。
  2. 稍微複雜的幾千個樣本一般都能搞定。
  3. 特别複雜的幾萬樣本起。
  4. 中文這種幾千個分類的一般十萬起。

注:隻準備一百個不到樣本的親們,千萬不要嘗試訓練測試,因為根本跑不起來。

入手的第一步環境搭建好了,那就是準備跑代碼了,還是有幾個必要的條件,巧婦難為無米之炊,首先,既然是訓練,要先有訓練集,有一個新手嘗鮮的訓練集,是mnist手寫識别的例子,可以在騰訊雲下載下傳:

https://share.weiyun.com/5pzGF4V

,現在萬事俱備,隻欠東風。

3.1 定義一個模型

本項目基于參數化配置,不需要改動任何代碼,可以通過可視化界面操作訓練幾乎任何字元型圖檔驗證碼。訓練架構界面可以大緻劃分為幾個部分:

  1. Neural Network - 神經網絡區
    ![image](https://yqfile.alicdn.com/11991b433a38e6662663b7d7646d2a951c65f550.png)
               
  2. Project Configuration - 項目配置區
    ![1.png](https://upload-images.jianshu.io/upload_images/10905998-b2a7c8309c8190c3?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)           
  3. Sample Source - 樣本源配置區
    ![image](https://yqfile.alicdn.com/a3fbb29232110ba2a0ac626a25cb79c311497a6c.png)
               
  4. Training Configuration - 訓練配置區
    ![image](https://yqfile.alicdn.com/45b11b40ef6956d01f4b8a9038b55fa5f803e262.png)
               
  5. Buttons - 功能控制區
    ![image](https://yqfile.alicdn.com/feb636d1f54f4bfec7534f1d4d18b3bf7cec8e39.png)
               

依此類推的訓練配置的步驟如下:

  1. 神經網絡區 的配置項看起來很多,對于新手來說,可以直接使用預設的配置:CNNX+GRU+CTC+C1組合(CNN前置網絡+GRU+CTC+單通道)。
  2. 項目配置區 的配置項在網絡選好之後配置項目名,按回車或者點選空白處确認。
  3. 樣本源配置區 的配置項用來配置樣本源的路徑,訓練樣本是根據此路徑進行打包成TFRecords格式,驗證樣本可以不指定,使用[Validation Set Num]參數随機從訓練集總抽樣成驗證集。
  4. 訓練配置區 的配置項負責定義訓練完成的條件如:結束準确率,結束COST,結束Epochs,批次大小
  5. 功能控制區 的配置項,設定完上面步驟,先點選[Make Dataset] 打包樣本,再點選[Start Training]開始訓練。

以下部分有基礎的讀者們可以了解一下:

如若使用CrossEntropy作為解碼器需要注意标簽數LabelNum和圖檔尺寸需要滿足的關系,因為網絡為多标簽而設計(一般的多标簽采用直接連接配接多個分類器),卷積層的輸出 outputs 經過了以下變換:

Reshape([label_num, int(outputs_shape[1] / label_num)])           

為了保證運算 int(outputs_shape[1] / label_num) 能夠取得正整數,也意味着他們之間存在某種關系,對于CNN5+Cross Entropy的網絡結構,Conv2D層的步長皆為1,那麼需要保證以下關系成立:

$$

mod(\frac{輸入寬度\times輸入高度\times輸出層參數}{池化步長^{池化層數}\times标簽數})= 0

是以有時候需要Resize網絡輸入的Shape

網絡 池化步長^池化層數 輸出層參數
CNN5 16 64
CNNX 8
ResNet50 1024
DenseNet 32 2048

例如使用CNN5+CrossEntropy組合,則輸入寬度與輸入高度需要滿足:

mod(\frac{輸入寬度\times輸入高度\times64}{16\times标簽數})= 0

同理如果CNN5+RNN+CTC,卷積層之後的輸出經過以下變換:

Reshape([-1, outputs_shape[2] * outputs_shape[3]])           

原輸出(batch_size, outputs_shape[1], outputs_shape[2], outputs_shape[3]),RNN層的輸入輸出要求為(batch, timesteps, num_classes),為了接入RNN經過以上操作,那麼又引出一個Time Step的概念,是以timesteps的值也是 outputs_shape[1],而CTC Loss要求的輸入為 [batch_size, frames, num_labels],若是 timesteps 小于标簽數則無法計算損失,也就無法找損失函數中找到極小值,梯度何以下降。timesteps 最合理的值一般是标簽數的2倍,為了達到目的,也可以通過Resize網絡輸入的Shape解決,一般情況timesteps直接關聯于圖檔寬度,大多情況隻要按比例放大寬度即可。

ExtractRegex 參數:

注意:如果訓練集的命名格式和我提供的新手訓練集不一樣,請根據實際情況修改ExtractRegex的正規表達式。目前隻支援在yaml配置檔案中直接修改,尚未提供GUI界面修改的支援。 DatasetPath 和SourcePath參數允許多個路徑,這種操作适用于需要将多種樣本訓練為一個模型,或者希望訓練一套通用泛化模型的人。

字元集Category其實大多數情況下不需要修改,一般的圖形驗證碼離不開數字和英文,而且一般來說是大小寫不敏感的,不區分大小寫,因為打碼平台收集的訓練集品質參差不齊,有些大寫有些小寫,不如全部統一為小寫,預設ALPHANUMERIC_LOWER則會自動将大寫的轉為小寫,字元集可定制化很靈活,除了配置備注上提供的幾種類型,還可以訓練中文,自定義字元集用list表示,示例如下:

Category: ['常', '世', '甯', '慢', '南', '制', '根', '難']           

如果是單标簽分類,可以配合LabelNum=1,例如:

Category: ["航母", "雨靴", "毛線", "安全帽", "調色闆", "海鷗", "月曆", "網球拍", ......]           

其檔案名示例:航母_1231290424123.png

如果是多标簽分類,可以配合LabelSplit=&,例如:

Category: ["航母", "雨靴", "毛線", "安全帽", "調色闆", "海鷗", "月曆", "網球拍", ......]           

其檔案名示例:航母&雨靴&毛線_1231290424123.png

可以自己根據收集訓練集的實際字元集使用率來定義,也可以無腦網上找3500常用字來訓練,注意:中文字元集一般比數字英文大很多,剛開始收斂比較慢,需要更久的訓練時間,也需要更多的樣本量,請量力而行

Python驗證碼識别 | 源碼+通用識别模型

形如上圖的圖檔能輕松訓練到95%以上的識别率。

ImageWidth、ImageHeight隻要和目前圖檔尺寸比對即可,其實這裡的配置主要是為了友善後面的部署智能政策。

Pretreatment參數:

該參數是用來做圖檔預處理的,例如形如以下的GIF動圖,

Python驗證碼識别 | 源碼+通用識别模型

可以使用ConcatFrames參數選取幀對兩幀進行水準拼接,适用于處理滾動型GIF,而閃爍型GIF可以使用BlendFrames參數進行融合。

3.2 開始訓練

  1. 經過 采集标注樣本形如 xxx_随機數.png
    Python驗證碼識别 | 源碼+通用識别模型
  1. 打包樣本

    通過GUI界面的 [Make Dataset] 或者 make_dataset.py 直接打包。

注意:使用源碼運作本項目的功能子產品需要具備一定的語言基礎,參數修改的部分和示例已預留好,盡量不修改核心類或函數的代碼以免出現錯誤。

按照上面的介紹,配置隻要修改極少數的參數對應的值,就可以開啟正式的訓練之旅了,具體操作如下:

可以直接使用 PyCharm 的 Run,執行 trains.py,也可以在激活Virtualenv下使用終端亦或在安裝依賴的全局環境下執行,但本文建議全程使用GUI界面進行操作,使用GUI僅需啟動 app.py 即可。

python3 trains.py           

剩下的就是等了,看過程,等結果。

正常開始訓練的模樣應該是這樣的:

訓練結束會在項目的out路徑下生成一個包含pb檔案的graph目錄和包含yaml檔案的model目錄,下面該到部署環節了。

3.3 部署

真的很有必要認真的介紹一下部署項目,比起訓練,這個部署項目傾注了筆者更多的心血,為什麼呢?

如希望将本系統內建于自己的項目中的可以參考python-sdk的使用:

https://pypi.org/project/muggle-ocr/

該項目的核心基于 captcha_platform/sdk/pb/sdk.py 可以根據需要自行修改,抑或直接使用MuggleOCR 調用訓練架構生産的模型。(具體調用方法可點選上面連結有對應的文檔介紹)

編譯版:

,使用編譯版無需安裝Python和TensorFlow環境。

真的值得了解的幾點

  1. 同時管理多個模型,支援模型熱拔插
  2. 靈活的版本控制
  3. 支援批量識别
  4. 服務智能路由政策

首先筆者重寫了TensorFlow的Graph會話管理,設計會話池,允許同時管理多模型,實作多模型動态部署方案。

1) 訓練好的 pb模型隻要放在部署項目的graph路徑下,yaml模型配置檔案放在model, 即可被服務發現并加載。(用SDK調用時,兩者置于同一目錄下)

2) 如果需要解除安裝一個正在服務的模型,隻需要在model中删除該模型的yaml配置檔案,在graph中删除對應的pb模型即可。

3) 如果需要更新一個已經服務中的模型,隻需修改新版的模型yaml配置檔案的版本号高于原模型的版本号,按先放pb後放yaml的順序,服務便會自動發現新版的模型并加載使用,舊的模型将因版本低于新版模型不會被調用,可以按照上述的解除安裝方法解除安裝已被棄用的模型釋放記憶體。

上面的操作中無需重新開機服務,完全的無縫切換

其次,一套服務想要服務于各式各樣的圖像識别需求,可以定義一套政策,訓練時将所有尺寸一樣的圖檔訓練成一個模型,服務根據圖檔尺寸自動選擇使用哪個模型,這樣的設計使定制化和通用性共存,等積累到一定多樣的訓練集時可以将所有的訓練集合到一起訓練一個通用模型,亦可以彼此獨立,每個模型的疊加僅僅增加了少量的記憶體或顯存,網上的方案大多是不同的模型單獨部署一套服務,每個程序加載了一整套TensorFlow架構勢必是過于龐大和多餘的。

用到批量識别需求的人相對少很多這裡就不展開介紹了。但是這裡給出一個12306的例子:

FieldParam:
  CorpParams: [
    {
      "start_pos": [118, 0],
      "interval_size": [0, 0],
      "corp_num": [1, 1],
      "corp_size": [60, 30]
    },
    {
      "start_pos": [5, 40],
      "interval_size": [5, 5],
      "corp_num": [4, 2],
      "corp_size": [66, 66]
    }
  ]
  OutputCoord: True           

該參數可以用于大圖的裁剪組成一批小圖作為一個批次的輸入,改用法可以避免多次調用。

但是識别項目提供了多套可選的服務有:gRPC,Flask,Tornado,Sanic,其中Flask和Tornado提供了加密接口,類似于微信公衆号開發接口的SecretKey和AccessKey接口,感興趣的可以在demo.py中閱讀調用源碼了解。

部署的使用可以經過package.py編譯為可執行檔案,這樣可以免去更換機器環境安裝的煩惱,部署項目安裝流程同訓練項目,項目中提供的requirements.txt已經将所需的依賴都列清楚了,強烈建議部署項目安裝cpu版TensorFlow。

本項目部署推薦使用Tornado版,功能最齊全,性能最為穩定。

Linux:

  1. Tornado:
# 端口 19952
python3 tornado_server.py           
  1. Flask
# 方案1,裸啟動, 端口 19951
python flask_server.py 
# 方案2,使用gunicorn,端口 5000
pip install gunicorn 
gunicorn -c deploy.conf.py flask_server:app           
  1. Sanic:
# 端口 19953
python3 sanic_server.py           
  1. gRPC:
# 端口 50054
python3 grpc_server.py           
  1. 編譯版(基于Tornado)
# 前台運作
./captcha_platform_tornado
#背景運作
nohup ./captcha_platform_tornado &           

Windows:

Windows平台下都是通過

python3 xxx_server.py

啟動對應的服務,注意,Tornado、Flask、Sanic的性能在Windows平台都大打折扣,gRPC是Google開源的RPC服務,有較為優越的性能。

編譯版直接運作編譯後的exe可執行檔案即可。

3.4 調用/測試

1. Tornado服務:

請求位址 Content-Type 參數形式 請求方法
http://[ 服務IP]:19952/captcha/v1 application/json JSON POST

具體參數:

參數名 必選 類型 說明
image Yes String Base64 編碼
model_name No 模型名,yaml配置中可綁定
need_color 顔色過濾,black/red/blue/yellow/green/white
output_split 多标簽分割字元

請求為JSON格式,形如:{"image": "base64編碼後的圖像二進制流"}

傳回結果:

message 識别結果或錯誤消息
code 狀态碼
success 是否請求成功

該傳回為JSON格式,形如:{"message": "xxxx", "code": 0, "success": true}

2. Flask服務:

服務IP]:19951/captcha/v1

請求參數和傳回格式同上

3. Sanic服務:

服務IP]:19953/captcha/v1

4. gRPC服務:

需要安裝依賴,grpcio、grpcio_tools和對應的grpc.proto檔案,可以直接從項目中的示例代碼demo.py中提取。

python -m grpc_tools.protoc -I. --python_out=. --grpc_python_out=. ./grpc.proto           

grpcio、grpcio_tools 是根據 grpc.proto 使用上述指令生成的。

class GoogleRPC(object):

    def __init__(self, host: str):
        self._url = '{}:50054'.format(host)
        self.true_count = 0
        self.total_count = 0

    def request(self, image, model_type=None, model_site=None):

        import grpc
        import grpc_pb2
        import grpc_pb2_grpc
        channel = grpc.insecure_channel(self._url)
        stub = grpc_pb2_grpc.PredictStub(channel)
        response = stub.predict(grpc_pb2.PredictRequest(
            image=image, split_char=',', model_type=model_type, model_site=model_site
        ))
        return {"message": response.result, "code": response.code, "success": response.success}

if __name__ == '__main__':
    result = GoogleRPC().request("base64編碼後的圖檔二進制流")
    print(result)           

3.5 奇技淫巧

該項目還可以直接用于識别帶顔色的驗證碼,部署項目middleware/impl/color_extractor.py基于k-means實作了顔色分離子產品,可用于處理如下形式的驗證碼:

Python驗證碼識别 | 源碼+通用識别模型

還有一種方案是同時預測驗證碼和每個字元對應的顔色,不過這需要修改現有的神經網絡進行支援,在最後一層修改為雙輸出,一個輸出顔色,一個輸出對應字元,這對于樣本标注的要求較高,也提高的成本,是以如果能用無限生成樣本,那問題就迎刃而解了。

其實還有很多很多技巧,例如,用生成的樣本代替訓練集,其實網上的圖檔驗證碼大多是采用開源的,稍作修改而已,大多數情況都能被近似生成出來,上述展示的驗證碼圖檔不代表任何實際的網站,如有雷同,純屬巧合,該項目隻能用于學習和交流用途,不得用于非法用途。

後記

如果文章描述不夠詳盡或需要技術支援的,可以加群 857149419 咨詢,或在開源項目中提issue,很榮幸能為開源社群貢獻綿薄之力。

繼續閱讀