文章目录
  1. 1. 准备工作
  2. 2. 问题分析
  3. 3. 实践
    1. 3.1. 图片采集
    2. 3.2. 图片预处理以及图片裁剪
    3. 3.3. 图片预分类
    4. 3.4. 特征提取
    5. 3.5. SVM模型分类
    6. 3.6. BP神经网络模型分类
  4. 4. 总结

关于验证码识别的文章网上很多图像识别的大神教程也比较多,不过大多数专业性太强了,对非专业人士读起来简直是天书,不过随着机器学习的普及,一大批机器学习的开源工具出现了,这也算对大多数像我一样的学渣的福音,由于最近项目中牵扯到了一些机器学习相关的东西,所以自己最近也一直在学习机器相关的东西,这篇验证码的识别也算是练手了,本文也算是学习中的笔记,所以文章中难免有一些错误,欢迎各路大神指点。

由于本人不是相关专业的,对于文中相关算法就不会具体去讨论了,主要以实战为目的。

准备工作

主要是用到了一些机器学习开源的框架以及一些辅助工具。

  • Scikit-Learn 比较有名的Python机器学习模块,主要是操作简单。
  • Pybrain Python机器学习模块,主要以神经网络为核心,所有的训练方法都以神经网络为一个实例。
  • pytesseract 图像识别小工具,本文主要是用来预处理训练样本的。
  • PIL Python图像处理库。

问题分析

首先在进行具体工作之前,我们得看看我们需要解决的是什么问题,那么对于验证码识别来说,可以看作一个分类问题,对于数字的图片验证码来说的话,其实就是0-9数字分类的问题,验证码识别最难的部分在于怎么去将验证码进行切割成单个字符图片,当然对于图片裁剪也就是特征提取有很多办法,例如垂直投影法,等距切割法等等,其中等距切割也是比较简单的,但是对于稍微复杂一点的验证码识别时准确率非常低,因为等距切割时将验证码按照相同的宽度进行裁剪,对于那些字符宽度大小不一的,就算裁剪出来也不能很好的表示字符的特征,所以有时候需要先对图片进行一系列的预处理,例如字符矫正等等,然后再用垂直投影法在x轴和y轴上按照投影的大小进行裁剪。

对于垂直投影法来说的话,最后我们还得考虑训练集在维度上都同意,由于是非等级切割,所以每个图片的像素肯定不一样,所以为了维度统一还得进行填充,总之稍微麻烦一点。

这里主要是以等距切割为例子,因为在操作起来比较简单,那么掩码也是选用0-9的纯数字验证码来进行识别,验证码如下


/attach/AuthCode.jpg

这样的图片看起来的话间距基本上都差不多大,所以在分割时也比较容易,将图片切成四块后,就可以拿每一块去进行训练识别了。

使用机器学习来进行训练和识别的话,我们就得考虑特征选取了,一般验证码识别有一套标准的流程,图片来自于http://www.jianshu.com/p/41127bf90ca9

/attach/machineLearn-serias.png

对于验证码识别来说我们关注的不是验证码的颜色,而是字符代表的含义,所以在图片处理时进行灰度化和二值化以及去噪,比如说去掉干扰线,那么去噪也有相应的算法来实现,这里不做具体讨论,二值化其实就是将图片呈现出两种颜色,即非黑即白,这样的好处是在特征处理时可以使用0和1来代表黑色和白色,0和1代表什么颜色取决于个人喜好。

这样的话将二值化和其它步骤处理后的图片进行特征提取,将黑色像素点标记成1,白色像素点标记成0,这样就可以得到图片的数值表示,那么特征维度就等于图片像素的大小,最终将图片按照X轴或者Y轴表示,即将像素的所标记的值合并到一行,例如


1111100000000000010

1110000000000000000

表示成11111000000000000101110000000000000000,这样每张图片就可以使用一行0和1的数值来表示。

进行特征提取之后,我们得到了图片在数学上的表示,那么下一步就需要进行模型训练了,由于如上文所述,图片识别是一个分类问题,所以在机器学习中,我主要采用了两种模型来进行训练,SVM支持向量机BP神经网络来进行模型训练,SVM使用scikit-learn机器学习包里面的实现来做,神经网络使用Pybrain来进行实现。

有关SVM和BP神经网络的算法部分,大家最好还是去网上搜下相关的Paper,这样你才能知道什么算法能解决什么问题,以及它大概的原理是什么样子的,有能力的同学可以去对推导下这两个算法。

实践

在问题分析部分我们已经对验证码识别的大概思路有了一个了解,那么这部分则主要正对上面所述部分进行具体实现。

首先,我们应该明白SVM和神经网络模型算法是属于有监督学习,即需要对样本进行标注,也就是标记每张图片表示的是那个数字,但是实际遇到的问题是,如果数据量小的话,我们可以进行人工标注,那么在数据量比较大的情况下,人工标注可能就不太现实了,所以对于图片来说的话也一样,你进行切割完成之后你必须得标注这个数字是几,所以我们需要对切割的图片进行预处理,也就是打标记,我比较懒,所以我也不会一个个去打标签,所以这里使用ocr来对切割的图片进行预分类,ocr在单文字识别上的效果正确率还是可以的,在ocr进行预分类之后,我们只需要去纠正那些分类错误的图片即可,这样就能大大的减少工作量。

这里实现主要有以下几个步骤:

  1. 图片采集
  2. 图片预处理(包括图片切割,二值化以及图像增强)
  3. 图片的预分类标注以及手动纠错标注
  4. 特征提取
  5. 模型训练以及预测

图片采集

图片采集就比较简单,不过多的阐述,如下图代码所示


/attach/machineLearn-downloadfiles.png

将下载到了图片按照时间戳存到指定位置

/attach/machineLearn-pics_sava.png

图片预处理以及图片裁剪

对图片进行预处理后采用等距切割法对图片进行切割


/attach/machineLearn-cutpics.png

裁剪后的图片如下


/attach/machineLearn-cutpicssave.png

图片预分类

图片预分类采用pytesseract来对分割的图片进行预分类,减轻工作量。
具体代码如下


/attach/machineLearn-ocr_category.png

ocr的分类效果正确率应该在50%以上,剩下的就是对预分类的图片进行人工纠错了。

ocr的分类效果图


/attach/machineLearn-ocr_categoryed.png

人工纠错和标记后的结果

/attach/machineLearn-correct_category.png

每个目录表示一个类别标签。

特征提取

特征提取的具体内容请参考问题分析中,里面有详细的说明。
关键代码如下


/attach/machineLearn-featureProcess.png

最终图片的数学上表示会以记录在/Users/iswin/Downloads/yzm/traindata/train_data.txt中,数据的格式如下图所示

/attach/machineLearn-train_data.png

红色线框表示一张图片数值上的表示,最后一个数字0表示该图片的类型,我是为了方便把标签追加到最后一行。

SVM模型分类

这里svm的实现使用了scikit-learn来实现,关于scikit-learn的使用去官网看Tutorial就好了,这里需要说一下SVM关于参数选择的问题,我们都知道SVM支持多个核函数,例如高斯核、线性核、poly以及sgmoid核函数,但是选择那么核函数一开始对于不太熟悉的同学怎么选择的确是个问题,所以这里使用了scikit-learn的GridSearchCV来对参数进行最优化选择,经过参数寻优,这里高斯核的最终效果还是不错的,所以训练的时候直接使用高斯核来进行训练。

为了方便预测时的使用,这里对训练结果使用了joblib模块来进行持久化。为了简单对评价模型进行,这里使用了5折交叉验证来对结果进行检验。

最终结果的准确率在:Accuracy: 0.96 (+/- 0.09)

具体代码如下:


/attach/machineLearn-svm_1.png


/attach/machineLearn-svm_2.png

举个预测的例子,看看效果


/attach/machineLearn-svm_predict.png

BP神经网络模型分类

BP神经网络也称负反馈神经网络,即按误差逆传播算法训练的多层前馈网络,是目前应用最广泛的神经网络模型之一,在BP神经网络之后,又出现了在深度学习中应用最广泛的CNN即卷积神经网络,这几天也正在学习。

本文使用了三层BP神经网络来对训练集进行训练,即输入层+2层隐含层+输出层,关于BP神经网络本身这里需要注意的是激活函数的选择以及对于多分类问题输出层函数选择的问题,激活函数主要有sigmod、tanh以及relu,关于怎么选取激活函数,这块没有进行深入了解,一般都是每个激活函数都跑一次,看最终效果。

这里的神经网络模型分类主要是对Pybrain用法的学习以及BP神经网络的基本认识,输入层使用了LinearLayer即线性输入层,隐含层使用了SigmoidLayer即激活函数为sigmod的隐含层,输出层由于是多分类问题,所以使用了SoftmaxLayer,最终在神经网络计算的结果中选取数值最大的那个索引位置就是预测的验证码类别,也就是0-9之间的数值。

关于Pybrain的资料除了官方文档不是特别多,关于构建神经网络的方式提供了两种方式,一种是buildNetwork函数来进行构建,另外一种就是使用FeedForwardNetwork函数来进行构建,这里需要注意的是如果使用FeedForwardNetwork来进行构建的话,注意要手动给各层加上Bias偏置项,否则结果可能可能非常差,当时我实验时没加,半天计算结果不对,最后看了下buildNetwork函数的源代码才发现没加Bias项,还有就是需要注意迭代至收敛的步数即函数中的*maxEpochs=500,这个根据情况调整,Pybrain有自己的数据集格式,所以在使用时必须按照它的格式来进行数据的初始化。

这里除了输入层的维度(即验证码的训练集维度)和输出是固定的之外,其中隐含层的神经元个数也是可以调整的,具体的感兴趣的同学自己去调然后再看下结果。

对模型使用10折交叉验证进行了简单评估,错误率在Total error: 0.062左右,效果比SVM的差一点,应该通参数调优应该可以提高准确率,不过重在学习。

训练集样本:/Users/iswin/Downloads/yzm/traindata/train_data_uniq.txt

主要代码如下:


/attach/machineLearn-network-1.png


/attach/machineLearn-network-2.png


/attach/machineLearn-network-3.png

举个例子,来看看预测效果


/attach/machineLearn-network-predict.png

总结

通过这个小实验,至少让我对机器学习和相关算法大致有了一个了解,同时作为安全人员来说至少知道了如何使用开源的机器学习框架来构架自己的模型,笔记中难免会有错误之处,欢迎大家提出意见。

文章目录
  1. 1. 准备工作
  2. 2. 问题分析
  3. 3. 实践
    1. 3.1. 图片采集
    2. 3.2. 图片预处理以及图片裁剪
    3. 3.3. 图片预分类
    4. 3.4. 特征提取
    5. 3.5. SVM模型分类
    6. 3.6. BP神经网络模型分类
  4. 4. 总结