Python項目實戰(zhàn)篇—常用驗證碼標注&識別(CNN神經網絡模型訓練/測試/部署)
大家好,我是Snowball。
一、前言
今天給大家分享的實戰(zhàn)項目是常用驗證碼標注&識別,前面三篇文章講解了文章的創(chuàng)作靈感、需求分析和實現思路、數據采集/預處理/字符圖切割等知識、高效率數據標注等知識,分別是以下文章:
- Python項目實戰(zhàn)篇——常用驗證碼標注和識別(需求分析和實現思路)
- Python項目實戰(zhàn)篇——常用驗證碼標注&識別(數據采集/預處理/字符圖切割)
- Python項目實戰(zhàn)篇——常用驗證碼標注&識別(前端+后端實現高效率數據標注)
這篇文章引入機器學習,給大家講解下基于該項目的CNN神經網絡模型訓練/測試/部署。
二、背景知識
按照學習的好習慣,先搜索網上資源,再腦洞一下,先思考啥是神經網絡,啥是卷積,CNN神經網絡為啥能提取圖片特征,這些問題筆者剛開始全部都遇到過,一臉蒙蔽有沒有。不要急,有問題有時候是好事,說明你知道自己那些不知道,等到自己了解和懂得多了,有些問題就迎刃而解。
筆者剛開始在上面的OpenCV知識學習過程中,就嘗試用過傳統(tǒng)的SIFT算法進行提取圖片特征可以進行圖片相似度匹配,但是效果都比較差,這里面用的是多維向量特征描述。而神經網絡在機器學習的領域為啥這么牛皮,這里面是有數學方面的理論支撐,也有現在計算力和數據量的支持,而卷積神經網絡專門用來處理圖片特征提取。
剛開始,筆者對這方面的理論知識了解甚少,于是充分利用搜索工具和網上資源,這里分享一下自己學習過程中的文章鏈接和視頻鏈接,可以保證讀者看完基本可以加深對神經網絡訓練的實戰(zhàn)了解,可上手進行項目功能調整。好的,讓我們開始學習(卷)起來,以下就是所有內容的鏈接,沒有基礎的朋友可以補一補,有基礎的可以直接跳過:
- **數學基礎**
- [微積分](https://www.bilibili.com/video/BV1Eb411u7Fw)
- [線性代數](https://www.bilibili.com/video/BV1aW411Q7x1)
- [概率論](https://www.bilibili.com/video/BV1ot411y7mU)
- [計算機數學基礎](https://www.bilibili.com/video/BV1AB4y1K7kM)
- **OpenCV**
- [OpenCV文章專欄](https://blog.csdn.net/yukinoai/category_9283880.html)
- [OpenCV-Python視頻](https://www.bilibili.com/video/BV1tb4y1C7j7)
- **神經網絡**
- [理解卷積意義](https://www.bilibili.com/video/BV1VV411478E)
- [前饋神經網絡](https://www.bilibili.com/video/BV1Tt411s7fK)
- [神經網絡學習理解](https://space.bilibili.com/504715181?spm_id_from=333.788.b_765f7570696e666f.1)
- **Python框架使用**
- [Numpy中文教程](https://www.runoob.com/numpy/numpy-tutorial.html)
- [PyTorch中文教程](https://pytorch.panchuang.net/SecondSection/neural_networks/)
- [PyTorch視頻](https://www.bilibili.com/video/BV1t64y1t7V8)
以上就是筆者這次項目開發(fā)幾個月來搜索的優(yōu)質學習文章和視頻資源了,有基礎的朋友可以選擇性相關知識學習,沒有基礎而時間充裕的可以惡補基礎再動手實戰(zhàn),所謂磨刀不誤砍柴工。想快速動手的小伙伴可以快速學習,把對應項目需要的知識點看明白即可。筆者建議的學習方式是確定自己的任務主線,然后邊學邊練邊思考,在項目實戰(zhàn)中學習總結是成長最快的方式。
好的,在上面前置知識學習了解的差不多后,相信大家都已經知道CNN神經網絡的理論知識了,接下來我們動手進行CNN模型的實戰(zhàn)訓練過程。
在開始,確定模型訓練基本過程
- 準備訓練數據集、測試數據集、預測數據集
- CNN模型編碼
- 模型訓練、測試
- 模型預測、部署
三、CNN神經網絡模型訓練
1.準備數據
通過實現思路第1-2步,可以得到相關圖片驗證碼字符數據,筆者這里準備訓練集500多張(這里得感謝我妹子花時間幫我在標注系統(tǒng)上手動標注的初始數據集~~),測試集30多張,預測5張。讀者在python項目拉取下來后,對應的文件夾下面已有全部數據,對應路徑如下:
src_img:訓練數據集
test_src_img:測試數據集
usage_src_img:預測數據集
在準備好圖片驗證碼數據后,本次案例需要先進行字符切割預處理(其他常用驗證碼需要讀者自己調整),對應文件image_split,以下是main方法代碼。
- if __name__ == '__main__':
- split_image_dir(SRC_IMG_DIR)
- split_test_image()
執(zhí)行字符切割后,對應的訓練集字母分類在letter_template目錄下,測試集字母分類在letter_test目錄下。
數據集類:net_data.py,下面是主要代碼。
- labels = []
- #2-9
- for i in range(8):
- labels.append(50 + i)
- #A-Z
- for i in range(26):
- labels.append(65 + i)
- class VerCodeDataset(Dataset):
- def __init__(self, image_dir="./letter_template/"):
- l = os.listdir(image_dir)
- self.data = []
- self.label = []
- for d in l:
- fs = os.listdir("{}{}".format(image_dir, d))
- for f in fs:
- fup = "{}{}/{}".format(image_dir, d, f)
- #圖片numpy轉tensor
- t = torch.from_numpy(io.imread(fup)).float() / 255
- #將二維值標準化
- norl = transforms.Normalize(t.mean(), t.std())
- self.data.append(norl(t.reshape(1, 40, 40)))
- #添加字符對應標簽序號
- self.label.append(labels.index(ord(d)))
數據集值制作描述可參考該文章鏈接:
- [數據集制作參考文章](https://zhuanlan.zhihu.com/p/358671390)
2.CNN模型編碼
本文驗證碼的識別與MNIST的識別相當類似,模型這塊采用簡單的前饋神經網絡,它接收輸入,讓輸入一個接著一個的通過一些層,最后給出輸出。下面是minst網絡結構圖:
- [PyTorch 神經網絡 - PyTorch官方教程中文版](https://link.zhihu.com/?target=http%3A//pytorch.panchuang.net/SecondSection/neural_networks/)
一個典型的神經網絡訓練過程包括以下幾點:
1.定義一個包含可訓練參數的神經網絡
2.迭代整個輸入
3.通過神經網絡處理輸入
4.計算損失(loss)
5.反向傳播梯度到神經網絡的參數
6.更新網絡的參數,典型的用一個簡單的更新方法:weight = weight - learning_rate *gradient
定義神經網絡(net_train.py):
- class Net(nn.Module):
- def __init__(self, dropout=0.1):
- super(Net, self).__init__()
- self.dropout = nn.Dropout(dropout)
- #第一層,卷積核個數從6改成10
- self.conv1 = nn.Conv2d(1, 10, 5)
- #第二層,卷積核個數從10改成25
- self.conv2 = nn.Conv2d(10, 25, 5)
- #全連接層1,40*40的字符圖經過2層卷積+2層池化變成7*7
- self.fc1 = nn.Linear(1 * 25 * 7 * 7, 120)
- #全連接層2
- self.fc2 = nn.Linear(120, 84)
- #最后全連接3層為輸出層,本案例驗證碼分類一共34類,[2-9,A-Z],改為34。
- self.fc3 = nn.Linear(84, 34)
- def forward(self, x):
- # 池化出來大小直接除2
- x = F.max_pool2d(F.relu(self.conv1(x)), (2, 2))
- #防止過擬合
- x = self.dropout(x)
- x = F.max_pool2d(F.relu(self.conv2(x)), (2, 2))
- x = self.dropout(x)
- x = x.view(-1, self.num_flat_features(x))
- #神經元relu激活函數
- x = F.relu(self.fc1(x))
- x = F.relu(self.fc2(x))
- x = self.fc3(x)
- return x
- def num_flat_features(self, x):
- size = x.size()[1:] # all dimensions except the batch dimension
- num_features = 1
- for s in size:
- num_features *= s
- return num_features
下方代碼中:
- self.fc1 = nn.Linear(1 * 25 * 7 * 7, 120)
全連接層第一個參數的大小:
[40,40]經過[5,5]卷積核->[35,35]
[35,35]經過[2,2]池化->[18,18]
[18,18]經過[5,5]卷積核->[13,13]
[13,13]經過[2,2]池化->[7,7]
上層卷積層一共25個卷積核,因此這里的大小為1(通道數)*25*7*7= 1225,至于后面全連接的84可以隨便改,和下層全連接層保持一致即可。
以上就是模型定義的代碼了,讀者有興趣的也可以自行用其他模型訓練。
四、CNN神經網絡模型測試
net_train.py文件提供的訓練代碼支持GPU訓練,在沒有NVDIA顯卡和安裝pytorch對應版本的CUDA庫,默認是使用CPU訓練,筆者對二種訓練方式都進行了嘗試,下面是訓練對比情況:
- 數據量:2286張 40*40 單通道字符圖片
- batch_size: 50
- epoch: 200
- 設備 時間
- GTX 1070TI 25s
- AMD R7 4750U PRO 4min
總結,數據量大,有條件上GTX顯卡就用顯卡訓練,訓練效率高出CPU數量級
- [cuda安裝文章鏈接](https://www.cnblogs.com/yang520ming/p/10677110.html)
這里是cuda安裝注意事項:
- 1.更新nvida顯卡驅動程序,然后看cuda版本
- 2.找pytorch對應cuda的版本安裝
train方法代碼如下:
- def train_gpu():
- use_cuda = torch.cuda.is_available()
- if(use_cuda):
- print("use gpu cuda")
- else:
- print("use cpu")
- device = torch.device("cuda:0" if use_cuda else "cpu")
- net = Net()
- net.to(device)
- #隨機梯度下降
- opt = optim.SGD(net.parameters(), lr=0.01)
- #迭代數據200次
- epoch = 200
- #單批次數據為50個
- batch_size = 50
- trainloader = data.trainloader(batch_size)
- st = datetime.datetime.now()
- loss = 0
- for e in range(epoch):
- for step, d in enumerate(trainloader):
- data_cuda = d["data"].to(device)
- label_cuda = d["label"].to(device)
- #每次反向傳播后,梯度清零
- opt.zero_grad()
- #前向傳播
- out = net(data_cuda)
- #分類問題選用交叉熵損失函數
- lf = nn.CrossEntropyLoss()
- #計算損失
- loss = lf(out, label_cuda)
- #反向傳播修改神經元參數
- loss.backward()
- opt.step()
- #每迭代50次或第一個批次步驟數據輸出損失值
- if (e % 50 == 0 or step == 1):
- print("e : {} , step : {}, loss : {}".format(e, step, loss))
- print("loss : {}".format(loss))
- #輸出訓練時間
- print("cost time:",datetime.datetime.now() - st)
- #保存模型
- saveModel(net, opt)
描述見上面代碼注釋,對概念理解有問題建議可以再看下這個up主的視頻,筆者覺得講得非常不錯:
- [神經網絡學習理解](https://space.bilibili.com/504715181?spm_id_from=333.788.b_765f7570696e666f.1)
下面給出訓練、測試過程中的效果圖:
GPU模型-訓練集訓練:
CPU模型-訓練集訓練:
可以看到迭代200次,花費4分鐘的訓練,模型趨于擬合效果,次數越往后梯度下降越慢。其實在迭代100次之后就接近穩(wěn)定來回振蕩,損失值減少越慢,最后的損失值為0.0016,擬合效果還不錯,如果增加訓練數據量、迭代次數、優(yōu)化部分字符串的切割,可以讓模型效果更好,讀者可自行實踐。
CPU模型-測試集測試:
代碼見net_test.py
可以看到152個字符,97%的準確率,部分字符切割問題會導致準確率下降,不過問題不大,基本達到個人項目可用程度,Nice~~
五、CNN神經網絡模型預測和部署
經過1,2,3步循環(huán)過程后,可以用一個相對擬合穩(wěn)定的模型進行預測集預測,因為過擬合的問題,可能有些模型在測試集表現較好,在測試時效果就不太好,這里需要對訓練數據,模型參數進行排
CPU模型-預測集測試:
代碼見net_usage.py
上圖可以看到,5張驗證碼的字符全部預測正確。
CPU模型-部署:
使用python的web框架Flask API,編寫圖片驗證碼識別POST接口,傳入文件路徑,啟動web應用,以下是通過本機文件路徑識別接口代碼,詳細代碼見net_flask.py
- @app.route('/recognize/path', methods=['POST'])
- def recognize_path():
- filePathList = request.json['filePathList']
- code = CODE_SUCCESS
- msg = MSG_SUCCESS
- data = []
- for filePath in filePathList:
- if not os.path.exists(filePath):
- # code = CODE_FAIL
- # msg = "文件不存在"
- print("文件不存在:", filePath)
- data.append("")
- continue
- else:
- labels = usage_model.usage(filePath)
- data.append(''.join(labels))
- result = {'code': code, "msg": msg, "data": data}
- return jsonify(result)
模型-Flask Web App啟動效果:
Postman接口測試效果:
Web頁面批量請求-預測:
好的,以上就是筆者圖片驗證碼識別案例中的卷積神經網絡模型訓練、測試、部署的全部內容了,總的來說,從結果看模型預測效果還是非常不錯的,首先利用標注系統(tǒng)進行人工標注初始數據集、下載數據集,然后再進行數據集的準備,接著進行模型的編碼、訓練和測試,然后利用訓練出來的模型進行數據預測,通過人工判斷修正再把加入到訓練集中,從而低時間成本、高效率增加訓練數據量。
六、總結
大家好,我是Snowball。這幾篇文章,整個過程下來,讀者就會熟悉到CNN神經網絡在圖片特征提取的魅力之處,其原理還是利用概率論、機器學習知識,在多層CNN模型下,通過多層感知機的激活函數、隨機梯度下降法、損失函數、反向傳播等機制進行復雜非線性模型參數的調節(jié),使得訓練處理的模型概率分布盡可能接近人腦中標注數據的概率模型。
當然,讀者看到這里覺得這里面還有很多疑問和問題,請不要氣餒,整個機器學習、神經網絡的知識體系是非常龐大的,從數學理論到計算機算法,再到工程框架,細節(jié)一步步被隱藏,請保持好奇心和思考,持續(xù)了解和學習,未來可能等知識積累到一定程度,那么很多問題就會明白和理解。說的東西有點多了,哈哈,總之還是,信息時代合理利用互聯(lián)網上的資源。