版权声明:本文为博主原创文章,未经博主允许不得转载。
狭义上讲验证码识别是将验证码图像转化成字符串值,传统方法常常先对验证码图像进行字符分割,再对验证码进行识别,劣势在于字符分割方法往往针对不同风格的验证码需要做修改,某些验证码加入噪声或线条,字符位置不固定及粘连时,字符分割效果不好,也会影响后续字符识别。除了只包含字母和数字的验证码,国内还有一些识别汉字的验证码,还有你以为考你认字符串实际上考你加减乘除的验证码,还有图像匹配和图像分类的验证码,种类和花样很多。所以验证码识别这种东西不可沉迷,毕竟道高一尺魔高一丈,道再高一尺。你破解了别人的验证码,维护人员立马弄出了新花样,换了一批新的验证码,你之前的方法就不好使了,于是你又破解了他的,他又改了新的…看谁能耗的久…不过博主作为菜鸟,做做验证码识别还是很有意义的,如果你也是搞深度学习的新手,不妨接着看下去,能够把数据集的准备和制作、深度网络模型的设计、训练和测试的流程走一遍。
博主是用深度学习做多任务分类的思路来识别验证码。多任务学习是针对数据给出多个监督信息(标签)进行学习,例如识别一张图像中的脸是否是人脸、脸部表情、性别、年龄等,识别图像中车的颜色、车型、姿态等,都属于多任务分类。项目用深度学习做多标签分类,是用深度神经网络对整张验证码图片进行多标签学习,来完成多任务分类,端到端的识别出验证码中的所有字符。这种思路同样可以用于车牌识别中。
用深度学习来做验证码识别,优势在于,只需要找一个合适的网络模型稍加修改,再给网络送入足够的有标签样本进行训练,就能达到很好的识别效果,无论验证码里面是白的黑的正的歪的、何种字体、粘不粘连都可以,也没必要做去噪、二值化、纠正和调各种阈值等各种处理(不然验证码风格稍微一变,你的处理方法就得改),直接端到端识别验证码的效果完全不比先做字符分割再做识别差。但其劣势也很明显,对不同手段生成的不同风格的验证码,都需要收集、爬取或模仿其验证码风格自己写代码生成大量样本,以维持较高的识别率。
为了满足多任务分类要求,对caffe源码进行针对性的修改满足多标签的输入和训练;然后在2012年ImageNet大赛冠军AlexNet网络模型的基础上进行了修改,作为多任务分类的模型;在windows平台下(ubuntu下当然也可以)利用caffe对两个不同的数据集进行了训练和测试;最后利用caffe的matlab接口,修改classfication_demo.m来做批量验证码图片识别,对caffe自带的classification.cpp做修改,也可以满足多任务分类。如果还没有编译caffe(gpu版本)及matlab接口,请先参照我的博客Windows下编译caffe
第一个验证码数据集条件比较理想,在生成时做了添加随机噪声点、字符变形、旋转缩放、随机赋色等处理。训练集64536张,验证集9096张(验证模型好坏),另外有714张用于做测试(测试模型效果),图片大小为88x28,共有数字0~9和大写字母A~Z共36类,每张验证码图像中包含四个字符,数据集下载。经过训练,该验证集图像上四个字符单个字符的识别准确率都达到了99.5%。而用于测试的714张验证码中,有15个识别出错,验证码整体识别结果准确率接近98%,主要是对0和O比较容易搞混。
第二个验证码数据集比较常见,更具有挑战性,生成验证码图像时使用了6种字体并加入随机线条、字体变形、旋转、随机赋色等处理。训练集63926张,验证集11914张,另有4112张用于测试,图片大小为150x40,共有数字0~10和小写字母a~z共36类(实际上该数据集中没有数字0和小写字母o,但并不影响我们用36类做分类),每张验证码图像中包含五个字符,数据集下载,其验证码生成程序来源github链接。经过训练,验证集验证码图像上五个字符单个字符的识别准确率平均达到了88%,最后对4112张图片进行测试,验证码整体识别结果准确率超过63%,虽然效果不是特别理想,但仔细观察下图,会发现随机线条的加入使得很多字符人眼都难以确认。
有了数据集之后,下面介绍利用深度学习进行验证码识别的具体步骤,在两种数据集上采用的方法是基本一致的,但第二个数据集似乎更有趣一些,以此为例。
1 数据准备
下载得到的数据集中的前三个文件夹,包括captcha_train、captcha_val和test_images,分别是验证码图像训练集、验证集和用于测试的图片文件夹。
captcha_train.txt和captcha_val.txt分别是captcha_train和captcha_val数据集对应的多标签文本文件,可以由matlab实现,生成captcha_train.txt和captcha_val.txt的create_captcha_train_val_txt.m代码如下。
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
然后在caffe的data\目录下新建captcha文件夹,并将这些文件/文件夹都拷贝到captcha目录下。
2 修改caffe源码使其支持多标签输入和训练
caffe源码对hdf5支持多标签,但lmdb格式只支持单标签输入和训练,但lmdb读取效率更高。对caffe源码做以下修改使得caffe对lmdb格式满足多标签输入和训练。这里提供修改后的相应文件下载 多标签修改
打开caffe目录下src\caffe\proto\caffe.proto,找到Datum如下可以发现label是不可重复(单个)int32型变量,这里不要注释掉它,在后面新增可重复(多个)的float型变量labels,然后在caffe目录下找到并删除src\caffe\proto\caffe.pb.cc和include\caffe\proto\caffe.pb.h这两个文件,再重新编译caffe。注:红框表示需要注意或待修改的部分,绿框为新增或修改后的内容。
然后对convert_imageset.cpp(项目convert_imageset中)进行如下修改,修改后可以读取五个标签值。不过修改后也导致后面的ReadImageToDatum函数也要进行修改,转到ReadImageToDatum函数定义处,即在io.hpp(项目libcaffe中include\util\下)中,重载函数ReadImageToDatum并添加新的ReadFileLabelsToDatum函数,然后在io.cpp(项目libcaffe中src\util\下)给出上述两个函数的实现代码,分别参考原来的ReadImageToDatum和ReadFileToDatum进行修改。
然后对data_layer.cpp(libcaffe项目中src\layers下)进行如下两处修改,然后重新编译libcaffe,再右键convert_imageset项目生成,这样就完成了caffe源码的修改,能够支持多标签的输入和训练了。
3 数据格式转换
利用修改后的caffe编译出的convert_imageset将图像数据及标签转换成具有更高读取效率的lmdb格式文件。在caffe目录下新建create_captcha_lmdb.txt文件,在文件中写入如下,其中图像统一缩放到227x227,shuffle表示乱序处理。保存后修改后缀为.bat文件,执行之,开始读取图像和标签并转换成lmdb文件,等待几分钟后,转换生成完毕,可以在data\captcha\下看到生成的captcha_train_lmdb和captcha_val_lmdb文件夹,文件夹比较大,大约接近16G和2G,要预留好足够的内存空间。需要注意的是,我们这里并没有用compute_mean.exe来生成均值文件,是因为我们后面统一采用了ImageNet数据集的均值作为样本均值,ImageNet数据集的均值具有统计特性,而我们的样本又是随机生成的,所以这是合理的做法。
- 1
- 2
- 3
- 4
- 5
- 1
- 2
- 3
- 4
- 5
4 设计多任务训练深度网络
在models文件夹下新建captcha文件夹,将models\bvlc_alexnet下的deploy.prototxt、solver.prototxt、train_val.prototxt拷贝到captcha文件夹下,然后分别对这三个文件进行修改。需要注意的是,train_val.prototxt和deploy.prototxt都是网络结构的描述文件,但train_val.prototxt还指出了训练集和验证集样本数据及标签,用于训练和验证;训练好的模型和deploy.prototxt可以用于测试单张或多张图片。
用caffe的Python接口提供的draw_net.py可以实现caffe网络结构可视化,也有在线可视化网页 netscope 提供网络结构可视化功能,将train_val.prototxt(或者deploy.prototxt)描述的网络结构画出来。
使用draw_net.py画出的alexnet的网络结构的最后几层如图所示,alexnet是单标签分类网络,后面接了三个全连接层fc6、fc7、fc8,其中全连接层fc6和前面的卷积层相连,fc8同label一起与accuracy层(test模式下)和loss层相连。
而我们现在进行验证码识别的思路是多标签分类,输入一张验证码图像输出多路分类结果。为此我们需要对train_val.prototxt和deploy.prototxt(批量测试时会用到deploy.prototxt)做一定修改。
train_val.prototxt的具体修改方法是对fc6及前面的层保持不变,从fc6后展开多路分类网络,例如第二个数据集验证码中有5个字符,那我们在fc6后连接5个全连接层fc7_1、fc7_2、fc7_3、fc7_4、fc7_5(原先只有1个全连接层fc7),这些全连接层同样经过relu层和dropout层;然后分别与全连接层fc8_1、fc8_2、fc8_3、fc8_4、fc8_5相连,节点个数由1000改成36(0~9、a~z共36类);再由Slice层分割的多标签label_1、label_2、label_3、label_4、label_5、分别和fc8_1、fc8_2、fc8_3、fc8_4、fc8_5一起接上各自对应的accuracy层(test模式下)和loss层。
修改好train_val.prototxt后,就可以据此自行对deploy.prototxt进行修改了,最后在solver.prototxt中修改网络训练参数,这里提供修改后的deploy.prototxt、solver.prototxt、train_val.prototxt,网络模型文件下载
5 训练和测试深度网络
在做好上述准备的基础上,在caffe目录下新建train_captcha.txt,在里面写入如下语句,保存后修改后缀名为.bat文件,双击执行开始训练。博主在NVIDIA GTX1060 6G单gpu上对第二个数据集训练迭代15000次,耗时约两个半小时,之后accuracy和loss趋于稳定,但loss还是有点大。虽然模型在第一个数据集上字符准确率达到99.5%以上,但在第二个数据集上的效果并不是特别理想,如图第1、2、3、4、5个字符在验证集上的准确率约为95.76%、90.61%、88.09%、91.50%、96.21%
- 1
- 2
- 3
- 1
- 2
- 3
最后利用caffe的matlab接口调用caffe来对验证码图片进行批量测试,在caffe下的matlab\demo下新建test_captcha.m对验证码测试图片进行批量测试,参考classification.demo进行修改得到test_captcha.m,生成的测试结果输出到test_captcha_result.txt中,其matlab代码如下,测试要用到deploy.prototxt和训练好的.caffemodel网络模型,这里提供训练好的迭代了15000次的captcha_iter_15000.caffemodel 模型文件下载。测试结果显示,4112张验证码有1220个识别出错,识别准确率为70.33%,在GTX1060 6G单gpu上平均每张验证码识别耗时10ms。
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
- 99
- 100
- 101
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
- 99
- 100
- 101
也可以对caffe的example\cpp_classification\下的classification.cpp进行修改来满足多任务分类,用于验证码识别,这里提供修改好的classification.cpp 测试代码下载 ,不过这里还只做了单张图片测试,果然c++相比matlab还是要麻烦点>_<
因为第一个验证码数据集条件比较理想,模型表现很完美,但第二个数据集的表现就没那么优秀了,出现过拟合后尝试增加训练的batchsize、适当调整学习率、增加dropout来降低loss,还可以考虑用更深的网络进一步提高识别的准确率。不过博主认为单纯的刷准确率意义不大,最理想的情况是在网络模型只做很小修改的情况下,对采取不同风格和方法生成的验证码都能比较好的识别效果。
至此,完成了验证码识别的整个流程,你有收获吗?