驗證碼識別技術——CNN+BLSTM+CTC從訓練到部署

項目地址:https://github.com/kerlomz/captcha_trainernode

1. 前言

本項目適用於Python3.6,GPU>=NVIDIA GTX1050Ti,原master分支已經正式切換爲CNN+LSTM+CTC的版本了,是時候寫一篇新的文章了。python

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

最新更新(2019/01/21)
若是對於DLL調用感興趣或是其餘語言的TensorFlow API感興趣的移步如下兩個項目:git

  1. https://github.com/kerlomz/captcha_library_c
  2. https://github.com/kerlomz/captcha_demo_csharp

筆者選用的時下最爲流行的CNN+BLSTM+CTC進行端到端的不定長驗證碼識別,代碼中預留了DenseNet+BLSTM+CTC的選項,能夠在配置中直接選用。首先,介紹個大概吧。github

網格結構 predict-CPU predict-GPU 模型大小
CNN5+Bi-LSTM+H64+CTC 15ms 28ms 2mb
CNN5+Bi-LSTM+H16+CTC 8ms 28ms 1.5mb
DenseNet+Bi-LSTM+H64+CTC 60ms 60ms 6.5mb

H16/H64指的是Bi-LSTM的隱藏神經元個數num_units,這裏注意,你沒有看錯,也沒有寫反,LSTM有時序依賴,tf.contrib.rnn.LSTMCell的實現沒能很充分的利用GPU的計算資源,底層kernel函數之間的間隙很是大,不利於充分的利用 GPU 的並行性來進行計算。因此本項目使用GPU訓練,使用CPU進行預測。預測服務部署項目源碼請移步此處:https://github.com/kerlomz/captcha_platform正則表達式

2.環境依賴:

關於CUDA和cuDNN版本的問題,很多人很糾結,這裏就列出官方經過pip安裝的TensorFlow的版本對應表:算法

Linux

Version Python version Compiler Build tools cuDNN CUDA
tensorflow_gpu-1.12.0 2.7, 3.3-3.6 GCC 4.8 Bazel 0.15.0 7 9
tensorflow_gpu-1.11.0 2.7, 3.3-3.6 GCC 4.8 Bazel 0.15.0 7 9
tensorflow_gpu-1.10.0 2.7, 3.3-3.6 GCC 4.8 Bazel 0.15.0 7 9
tensorflow_gpu-1.9.0 2.7, 3.3-3.6 GCC 4.8 Bazel 0.11.0 7 9
tensorflow_gpu-1.8.0 2.7, 3.3-3.6 GCC 4.8 Bazel 0.10.0 7 9
tensorflow_gpu-1.7.0 2.7, 3.3-3.6 GCC 4.8 Bazel 0.9.0 7 9
tensorflow_gpu-1.6.0 2.7, 3.3-3.6 GCC 4.8 Bazel 0.9.0 7 9

Windows

Version Python version Compiler Build tools cuDNN CUDA
tensorflow_gpu-1.12.0 3.5-3.6 MSVC 2015 update 3 Bazel 0.15.0 7 9
tensorflow_gpu-1.11.0 3.5-3.6 MSVC 2015 update 3 Bazel 0.15.0 7 9
tensorflow_gpu-1.10.0 3.5-3.6 MSVC 2015 update 3 Cmake v3.6.3 7 9
tensorflow_gpu-1.9.0 3.5-3.6 MSVC 2015 update 3 Cmake v3.6.3 7 9
tensorflow_gpu-1.8.0 3.5-3.6 MSVC 2015 update 3 Cmake v3.6.3 7 9
tensorflow_gpu-1.7.0 3.5-3.6 MSVC 2015 update 3 Cmake v3.6.3 7 9
tensorflow_gpu-1.6.0 3.5-3.6 MSVC 2015 update 3 Cmake v3.6.3 7 9

若是但願使用上面對應以外的搭配的CUDA和cuDNN,能夠自行編譯TensorFlow,或者去Github上搜索TensorFlow Wheel找到第三方編譯的對應版本的whl安裝包。提早預警,如果本身編譯將會苦難重重,坑不少,這裏就不展開了。sql

2.1 本項目環境依賴

目前在如下主流操做系統平臺均測試經過:json

操做系統 最低支持版本
Ubuntu 16.04
Windows 7 SP1
MacOS N/A

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

依賴 最低支持版本
Python 3.6
TensorFlow-GPU 1.6.0
Opencv-Python 3.3.0.10
Numpy 1.14.1
Pillow 4.3.0
PyYaml 3.13

2.1.1 Ubuntu 16.04 下的 Python 3.6

1)先安裝Python環境

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.6.6/Python-3.6.6.tgz
tar -vxf Python-3.6.6.tar.xz
cd Python-3.6.6
./configure --prefix=/usr/local  --enable-shared
make -j8
sudo make install -j8

通過上面指令就安裝好Python3.6環境了,若是提示找不到libpython3.6m.so.1.0就到/usr/local/lib路徑下將該文件複製一份到/usr/lib和/usr/lib64路徑下。
2)安裝相關依賴(這一步Windows和Linux通用)
能夠直接在項目路徑下執行pip3 install -r requirements.txt安裝全部依賴,注意這一步是安裝在全局Python環境下的,強烈建議使用虛擬環境進行項目間的環境隔離,如VirtualenvAnaconda等等。
我通常使用的是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
  1. 執行安裝程序,按指示無腦繼續就行了,若是提示是否安裝驅動選不安裝。
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 使用

入手的第一步環境搭建好了,那就是準備跑代碼了,仍是有幾個必要的條件,巧婦難爲無米之炊,首先,既然是訓練,要先有訓練集,有一個新手嚐鮮的訓練集,是mnist手寫識別的例子,能夠在騰訊雲下載:https://share.weiyun.com/5pzGF4V,如今萬事俱備,只欠東風。

3.1 定義一個模型

本項目採用的是參數化配置,不須要改動任何代碼,能夠訓練幾乎任何字符型圖片驗證碼,下面從兩個配置文件提及:
config.yaml # 系統配置

# - requirement.txt  -  GPU: tensorflow-gpu, CPU: tensorflow
# - If you use the GPU version, you need to install some additional applications.
# TrainRegex and TestRegex: Default matching apple_20181010121212.jpg file.
# - The Default is .*?(?=_.*\.)
# TrainsPath and TestPath: The local absolute path of your training and testing set.
# TestSetNum: This is an optional parameter that is used when you want to extract some of the test set
# - from the training set when you are not preparing the test set separately.
System:
  DeviceUsage: 0.7
  TrainsPath: 'E:\Task\Trains\YourModelName\'
  TrainRegex: '.*?(?=_)'
  TestPath: 'E:\Task\TestGroup\YourModelName\'
  TestRegex: '.*?(?=_)'
  TestSetNum: 1000

# CNNNetwork: [CNN5, DenseNet]
# RecurrentNetwork: [BLSTM, LSTM]
# - The recommended configuration is CNN5+BLSTM / DenseNet+BLSTM
# HiddenNum: [64, 128, 256]
# - This parameter indicates the number of nodes used to remember and store past states.
NeuralNet:
  CNNNetwork: CNN5
  RecurrentNetwork: BLSTM
  HiddenNum: 64
  KeepProb: 0.98

# SavedSteps: A Session.run() execution is called a Steps,
# - Used to save training progress, Default value is 100.
# ValidationSteps: Used to calculate accuracy, Default value is 100.
# TestNum: The number of samples for each test batch.
# - A test for every saved steps.
# EndAcc: Finish the training when the accuracy reaches [EndAcc*100]%.
# EndEpochs: Finish the training when the epoch is greater than the defined epoch.
Trains:
  SavedSteps: 100
  ValidationSteps: 500
  EndAcc: 0.975
  EndEpochs: 1
  BatchSize: 64
  TestBatchSize: 400
  LearningRate: 0.01
  DecayRate: 0.98
  DecaySteps: 10000

上面看起來好多好多參數,其實大部分能夠不用改動,你須要修改的僅僅是訓練集路徑就能夠了,注意:若是訓練集的命名格式和我提供的新手訓練集不同,請根據實際狀況修改TrainRegex和TestRegex的正則表達式。,TrainsPath和TestPath路徑支持list參數,容許多個路徑,這種操做適用於須要將多種樣本訓練爲一個模型,或者但願訓練一套通用模型的人。爲了加快訓練速度,提升訓練集讀取效率,特別提供了make_dataset.py來支持將訓練集打包爲tfrecords格式輸入,通過make_dataset.py打包以後的訓練集將輸出到本項目的dataset路徑下,只需修改TrainsPath鍵的配置以下便可

TrainsPath: './dataset/xxx.tfrecords'

TestPath是容許爲空的,若是TestPath爲空將會使用TestSetNum參數自動劃分出對應個數的測試集。若是使用自動劃分機制,那麼TestSetNum測試集總數參數必須大於等於TestBatchSize測試集每次讀取的批次大小。
神經網絡這塊能夠講一講,默認提供的組合是CNN5(CNN5層模型)+BLSTM(Bidirectional LSTM)+CTC,親測收斂最快,可是訓練集太小,實際圖片變化很大特徵不少的狀況下容易發生過擬合。DenseNet能夠碰運氣在樣本量很小的狀況下很好的訓練出高精度的模型,爲何是碰運氣呢,由於收斂快不快隨機的初始權重很重要,運氣好前500步可能對測試集就有40-60%準確率,運氣很差2000步以後仍是0,收斂快慢是有必定的運氣成分的。

NeuralNet:
  CNNNetwork: CNN5
  RecurrentNetwork: BLSTM
  HiddenNum: 64
  KeepProb: 0.99

隱藏層HiddenNum筆者嘗試過8~64,都能控制在很小的模型大小以內,若是想使用DenseNet代替CNN5直接修改如上配置中的CNNNetwork參數替換爲:

NeuralNet:
  CNNNetwork: DenseNet
  ......

model.yaml # 模型配置

# ModelName: Corresponding to the model file in the model directory,
# - such as YourModelName.pb, fill in YourModelName here.
# CharSet: Provides a default optional built-in solution:
# - [ALPHANUMERIC, ALPHANUMERIC_LOWER, ALPHANUMERIC_UPPER,
# -- NUMERIC, ALPHABET_LOWER, ALPHABET_UPPER, ALPHABET]
# - Or you can use your own customized character set like: ['a', '1', '2'].
# CharExclude: CharExclude should be a list, like: ['a', '1', '2']
# - which is convenient for users to freely combine character sets.
# - If you don't want to manually define the character set manually,
# - you can choose a built-in character set
# - and set the characters to be excluded by CharExclude parameter.
Model:
  Sites: []
  ModelName: YourModelName-CNN5-H64-150x50
  ModelType: 150x50
  CharSet: ALPHANUMERIC_LOWER
  CharExclude: []
  CharReplace: {}
  ImageWidth: 150
  ImageHeight: 50

# Binaryzation: [-1: Off, >0 and < 255: On].
# Smoothing: [-1: Off, >0: On].
# Blur: [-1: Off, >0: On].
# Resize: [WIDTH, HEIGHT]  
# - If the image size is too small, the training effect will be poor and you need to zoom in.
# - ctc_loss error "No valid path found." happened
Pretreatment:
  Binaryzation: -1
  Smoothing: -1
  Blur: -1

上述的配置只要關注
ModelName、CharSet、ImageWidth、ImageHeight
首先給模型取一個好名字是成功的第一步,字符集CharSet其實大多數狀況下不須要修改,通常的圖形驗證碼離不開數字和英文,並且通常來講是大小寫不敏感的,不區分大小寫,由於打碼平臺收集的訓練集質量良莠不齊,有些大寫有些小寫,不如所有統一爲小寫,默認ALPHANUMERIC_LOWER則會自動將大寫的轉爲小寫,字符集可定製化很靈活,除了配置備註上提供的幾種類型,還能夠訓練中文,自定義字符集用list表示,示例以下:

CharSet: ['常', '世', '寧', '慢', '南', '制', '根', '難']

能夠本身根據收集訓練集的實際字符集使用率來定義,也能夠無腦網上找3500經常使用字來訓練,注意:中文字符集通常比數字英文大不少,剛開始收斂比較慢,須要更久的訓練時間,也須要更多的樣本量,請量力而行

QQ截圖20181204150924.png


形如上圖的圖片能輕鬆訓練到95%以上的識別率。
ImageWidth、ImageHeight只要和當前圖片尺寸匹配便可,其實這裏的配置主要是爲了方便後面的部署智能策略。
其餘的如Pretreatment之下的參數是用來作圖片預處理的,由於筆者致力於作一套通用模型,模型只使用了灰度作預處理。其中可選的二值化、均值濾波、高斯模糊均未開啓,即便不進行那些預處理該框架已經可以達到很理想的識別效果了,筆者自用的大多數模型都是98%以上的識別率。

 

3.2 開始訓練

按照上面的介紹,配置只要修改極少數的參數對應的值,就能夠開啓正式的訓練之旅了,具體操做以下:
能夠直接使用PyCharm的Run,執行trains.py,也能夠在激活Virtualenv下使用終端亦或在安裝依賴的全局環境下執行

python3 trains.py

剩下的就是等了,看過程,等結果。
正常開始訓練的模樣應該是這樣的:

 

QQ截圖20181204152301.png

 

訓練結束會在項目的out路徑下生成一個pb和yaml文件,下面該到部署環節了。

3.3 部署

真的頗有必要認真的介紹一下部署項目,比起訓練,這個部署項目傾注了筆者更多的心血,爲何呢?
項目地址:https://github.com/kerlomz/captcha_platform

真的值得了解的幾點

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

首先筆者重寫了Tensor Flow的Graph會話管理,設計會話池,容許同時管理多模型,實現多模型動態部署方案。
1)訓練好的pb模型只要放在部署項目的graph路徑下,yaml模型配置文件放在model,便可被服務發現並加載,
2)若是須要卸載一個正在服務的模型,只須要在model中刪除該模型的yaml配置文件,在graph中刪除對應的pb模型便可。
3)若是須要更新一個已經服務中的模型,只需修改新版的模型yaml配置文件的版本號高於原模型的版本號,按先放pb後放yaml的順序,服務便會自動發現新版的模型並加載使用,舊的模型將因版本低於新版模型不會被調用,能夠按照上述的卸載方法卸載已被棄用的模型釋放內存。
上面的操做中無需重啓服務,徹底的無縫切換

其次,一套服務想要服務於各式各樣的圖像識別需求,能夠定義一套策略,訓練時將全部尺寸同樣的圖片訓練成一個模型,服務根據圖片尺寸自動選擇使用哪一個模型,這樣的設計使定製化和通用性共存,等積累到必定多樣的訓練集時能夠將全部的訓練集合到一塊兒訓練一個通用模型,亦能夠彼此獨立,每一個模型的疊加僅僅增長了少許的內存或顯存,網上的方案大可能是不一樣的模型單獨部署一套服務,每一個進程加載了一整套TensorFlow框架勢必是過於龐大和多餘的。

用到批量識別需求的人相對少不少這裏就不展開介紹了。識別項目提供了多套可選的服務有:gRPC,Flask,Tornado,Sanic,其中Flask和Tornado提供了加密接口,相似於微信公衆號開發接口的SecretKey和AccessKey接口,感興趣的能夠在demo.py中閱讀調用源碼瞭解。

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

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

Windows:
Windows平臺下都是經過python3 xxx_server.py啓動對應的服務,注意,Tornado、Flask、Sanic的性能在Windows平臺都大打折扣,gRPC是Google開源的RPC服務,有較爲優越的性能。

3.4 調用/測試

1. Flask服務:

請求地址 Content-Type 參數形式 請求方法
http://localhost:19951/captcha/v1 application/json JSON POST

具體參數:

參數名 必選 類型 說明
image Yes String Base64 編碼
model_site No String 網站名,yaml配置中可綁定
model_type No String 類別,yaml配置中可綁定

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

返回結果:

參數名 類型 說明
message String 識別結果或錯誤消息
code String 狀態碼
success String 是否請求成功

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

2. Tornado服務:

請求地址 Content-Type 參數形式 請求方法
http://localhost:19952/captcha/v1 application/json JSON POST

請求參數和返回格式同上

3. Sanic服務:

請求地址 Content-Type 參數形式 請求方法
http://localhost:19953/captcha/v1 application/json JSON POST

請求參數和返回格式同上

4. gRPC服務:
須要安裝依賴,grpcio、grpcio_tools和對應的grpc.proto文件,能夠直接從項目中的示例代碼demo.py中提取。

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 奇技淫巧

該項目還能夠直接用於識別帶顏色的圖片,本質是不一樣的顏色分別訓練,調用的時候經過傳參區分,若是但願得到圖片中紅色的文字,就直接經過參數定位到訓練紅色的模型,但願獲取圖片中藍色的圖片就經過參數定位到藍色模型,如:

 

藍色.png

紅色.png

不過這種操做對樣本量要求較高,且效率不高,當顏色參數愈來愈多時就不適用,能夠採用顏色提取的方式,這樣所須要的樣本量將大大減小,但對於顏色提取算法效果要求高了。還有一種方案是同時預測驗證碼和每一個字符對應的顏色,不過這須要修改現有的神經網絡進行支持,在最後一層修改成雙輸出,一個輸出顏色,一個輸出對應字符,這對於樣本標註的要求較高,也提升的成本,因此若是能用無限生成樣本,那問題就迎刃而解了,好比上圖,筆者就寫了樣本生成代碼,感興趣的能夠移步:
https://www.jianshu.com/p/da1b972e24f2
其實還有不少不少技巧,例如,用生成的樣本代替訓練集,其實網上的圖片驗證碼大可能是採用開源的,稍做修改而已,大多數狀況都能被近似生成出來,上述展現的驗證碼圖片不表明任何實際的網站,若有雷同,純屬巧合,該項目只能用於學習和交流用途,不得用於非法用途。

後記

若是文章描述不夠詳盡或須要技術支持的,能夠加羣857149419諮詢,或在開源項目中提issue,很榮幸能爲開源社區貢獻綿薄之力。

相關文章
相關標籤/搜索