大家好,我是Snowball。
一、前言
今天给大家分享的实战项目是常用验证码标注&识别,前面三篇文章讲解了文章的创作灵感、需求分析和实现思路、数据采集/预处理/字符图切割等知识、高效率数据标注等知识,分别是以下文章:
- Python项目实战篇——常用验证码标注和识别(需求分析和实现思路)
- Python项目实战篇——常用验证码标注&识别(数据采集/预处理/字符图切割)
- Python项目实战篇——常用验证码标注&识别(前端+后端实现高效率数据标注)
这篇文章引入机器学习,给大家讲解下基于该项目的CNN神经网络模型训练/测试/部署。
二、背景知识
按照学习的好习惯,先搜索网上资源,再脑洞一下,先思考啥是神经网络,啥是卷积,CNN神经网络为啥能提取图片特征,这些问题笔者刚开始全部都遇到过,一脸蒙蔽有没有。不要急,有问题有时候是好事,说明你知道自己那些不知道,等到自己了解和懂得多了,有些问题就迎刃而解。
笔者刚开始在上面的OpenCV知识学习过程中,就尝试用过传统的SIFT算法进行提取图片特征可以进行图片相似度匹配,但是效果都比较差,这里面用的是多维向量特征描述。而神经网络在机器学习的领域为啥这么牛皮,这里面是有数学方面的理论支撑,也有现在计算力和数据量的支持,而卷积神经网络专门用来处理图片特征提取。
刚开始,笔者对这方面的理论知识了解甚少,于是充分利用搜索工具和网上资源,这里分享一下自己学习过程中的文章链接和视频链接,可以保证读者看完基本可以加深对神经网络训练的实战了解,可上手进行项目功能调整。好的,让我们开始学习(卷)起来,以下就是所有内容的链接,没有基础的朋友可以补一补,有基础的可以直接跳过:
- **数学基础**
- [微积分](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)
以上就是笔者这次项目开发几个月来搜索的优质学习文章和视频资源了,有基础的朋友可以选择性相关知识学习,没有基础而时间充裕的可以恶补基础再动手实战,所谓磨刀不误砍柴工。想快速动手的小伙伴可以快速学习,把对应项目需要的知识点看明白即可。笔者建议的学习方式是确定自己的任务主线,然后边学边练边思考,在项目实战中学习总结是成长最快的方式。
好的,在上面前置知识学习了解的差不多后,相信大家都已经知道CNN神经网络的理论知识了,接下来我们动手进行CNN模型的实战训练过程。
在开始,确定模型训练基本过程
- 准备训练数据集、测试数据集、预测数据集
- CNN模型编码
- 模型训练、测试
- 模型预测、部署
三、CNN神经网络模型训练
1.准备数据
通过实现思路第1-2步,可以得到相关图片验证码字符数据,笔者这里准备训练集500多张(这里得感谢我妹子花时间帮我在标注系统上手动标注的初始数据集~~),测试集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()
执行字符切割后,对应的训练集字母分类在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次之后就接近稳定来回振荡,损失值减少越慢,最后的损失值为0.0016,拟合效果还不错,如果增加训练数据量、迭代次数、优化部分字符串的切割,可以让模型效果更好,读者可自行实践。
CPU模型-测试集测试:
代码见net_test.py
可以看到152个字符,97%的准确率,部分字符切割问题会导致准确率下降,不过问题不大,基本达到个人项目可用程度,Nice~~
五、CNN神经网络模型预测和部署
经过1,2,3步循环过程后,可以用一个相对拟合稳定的模型进行预测集预测,因为过拟合的问题,可能有些模型在测试集表现较好,在测试时效果就不太好,这里需要对训练数据,模型参数进行排
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页面批量请求-预测:
好的,以上就是笔者图片验证码识别案例中的卷积神经网络模型训练、测试、部署的全部内容了,总的来说,从结果看模型预测效果还是非常不错的,首先利用标注系统进行人工标注初始数据集、下载数据集,然后再进行数据集的准备,接着进行模型的编码、训练和测试,然后利用训练出来的模型进行数据预测,通过人工判断修正再把加入到训练集中,从而低时间成本、高效率增加训练数据量。
六、总结
大家好,我是Snowball。这几篇文章,整个过程下来,读者就会熟悉到CNN神经网络在图片特征提取的魅力之处,其原理还是利用概率论、机器学习知识,在多层CNN模型下,通过多层感知机的激活函数、随机梯度下降法、损失函数、反向传播等机制进行复杂非线性模型参数的调节,使得训练处理的模型概率分布尽可能接近人脑中标注数据的概率模型。
当然,读者看到这里觉得这里面还有很多疑问和问题,请不要气馁,整个机器学习、神经网络的知识体系是非常庞大的,从数学理论到计算机算法,再到工程框架,细节一步步被隐藏,请保持好奇心和思考,持续了解和学习,未来可能等知识积累到一定程度,那么很多问题就会明白和理解。说的东西有点多了,哈哈,总之还是,信息时代合理利用互联网上的资源。