全自动区分计算机和人类的公开图灵测试(英语:Completely Automated Public Turing test to tell Computers and Humans Apart,簡稱CAPTCHA),俗称验证码,是根据随机字符生成一幅图片,然后在图片中加入干扰象素,用户必须手动填入,防止有人利用机器人自动批量注册、灌水、发垃圾广告等等 。
验证码的作用是验证用户是真人还是机器人;设计理念是对人友好,对机器难。
如图所示,上图是比较常见的字符验证码,还有一些更加复杂的验证码这里没有讨论(比如图片,或者提问之类的)
破解验证码有几种常见的方式:
- 人力打码
- 绕过验证码的漏洞
- 字符串识别(本文重点
老规矩,参考的文章优先列出来:
http://blog.topspeedsnail.com/archives/10858
https://www.cnblogs.com/beer/p/7392397.html
其中第一个链接提供了主体算法的代码实现
第二个链接对代码进行了模块化优化,对函数功能进行了划分,使得代码更好理解,更清晰明了
本文拟采用第二个链接的代码
如果有优化的部分,会在文章中提到。
在一开始搭建TensorFlow的框架时候,有一个非常经典的demo,叫做OCR手写字符串识别,这个在吴恩达的课程里也有提到。
这个代表了字符串识别的最初发展趋势:将数字分割为单个字符,然后识别单个字符
但是这样面临了一个问题:当字符之间相互重叠,机器就不容易分割了。
得益于基于卷积神经网络CNN的人工智能技术的发展,目前基于主流的深度学习框架的直接开发出 端到端不分割 的识别方式,而且在没有经过太多trick的情况下,已经可以达到95%以上的识别率。
正如上面所说,本项目实现的方法不需要分割验证码,而是把验证码做为一个整体进行识别。
参考文献:
- Multi-digit Number Recognition from Street View Imagery using Deep CNN
- CAPTCHA Recognition with Active Deep Learning
- http://matthewearl.github.io/2016/05/06/cnn-anpr/
通过深度学习+训练数据+计算能力,可以训练出一个破解验证码的模型
这里获得训练数据的方法:
- 手动(不差钱的也可以从打码网站购买
- 破解验证码生成机制,自动生成
- 。。。。
这里采用了第一个链接里提到的,自己制作的验证码生成器,然后训练CNN模型来破解自己的验证码生成器
(多大仇+没事儿找事儿系列)
碎碎念:其实验证码机制本来就是一个悖论,它需要机器生成一个机器也无法识别的验证码。盲目提高验证码难度,根本就是在为难普通人而已,我已经被好多网站的验证码坑过了,死活登不上去=自己不是人。
验证码生成器
代码在gen-captcha.py里
感觉自己就像一个智障。。按着别人的代码重写一遍也会错。。
还认真的debug了半天:)
成功以后试着跑了一下,如下图所示:
这一步应该还挺简单的,代码难度不大,如图所示
from captcha.image
import ImageCaptcha # pip install captcha
import numpy as np
import matplotlib.pyplot as pltfrom PIL
import Imageimport random
#验证码中出现的字符,不用汉字
number = ['0','1','2','3','4','5','6','7','8','9']
alphabet = ['a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z']ALPHABET = ['A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z']
# 验证码一般都无视大小写;验证码长度4个字符
def random_captcha_text(char_set=number + alphabet + ALPHABET,captcha_size=4):
captcha_text = []
for i in range(captcha_size):
c = random.choice(char_set)
captcha_text.append(c)
return captcha_text
#生成字符对应的验证码
def gen_captcha_text_and_image():
image = ImageCaptcha()
captcha_text = random_captcha_text()
captcha_text = ''.join(captcha_text)
captcha = image.generate(captcha_text)
#image.write(captcha_text,captcha_text + '.jpg') #写到文件
captcha_image = Image.open(captcha)
captcha_image = np.array(captcha_image)
return captcha_text, captcha_image
if __name__ == '__main__':
# 测试
text, image = gen_captcha_text_and_image()
f = plt.figure()
ax = f.add_subplot(111)
ax.text(0.1, 0.9,text, ha='center', va='center', transform=ax.transAxes)
plt.imshow(image)
plt.show()
#####模型预测
这里坑还挺多的,一点点的说吧
首先是完全按照参考代码跑了一遍,我的配置是win10,在anaconda下的Python3.6虚拟环境环境。
一开始按照pip install tensorflow 安装的tensorflow cpu 版本
跑的时候报了一个错
查了一下,说是因为cpu版本的tensorflow版本可以用avx2或者sse2加速(GPU版本不用),但是pip install的默认版本是不支持这个加速的。
这里有两种解决方法:
- 忽视警报,不影响正常使用
- 卸载前面的版本,重新在官网下载安装,具体对应的版本可以看官网的readme,写的还挺详细的。链接点这里
我是选择了重新下载再次运行后不报错了。
开始提示我save model有问题
我查了一下,说是因为路径有问题,在
saver.save(sess, “crack_capcha.model”, global_step=step)
这一句的“crack_capcha.model“前加上./或者绝对路径就可以了。
(我这简直是一部智障犯错史)
在模型训练的时候,建议一开始可以放宽精度(比如0.2就可以存储,否则辛辛苦苦训练了一天,最后发现save model 有问题很容易心态爆炸┓( ´∀` )┏
训完的模型会出现这样的几个文件
其中13100是迭代的epoch次数,每个人可能不同
这里在后面的调用中,因为模型每次训练都会覆盖上一次的模型数据,所以为了避免总是修改名字,使用了tf.train.latest_checkpoint(’.’)
这个函数来是调用最新的checkpoint文件,避免了反复修改文件的麻烦
训练完模型以后就可以使用testmodel.py来进行调用了
预测效果还不错(毕竟我为了节省时间,降低了训练的期待精度)
在这里我还犯了一个坑爹的错误
在写trainmodel的时候,因为没有写完整工程的经验,所以没有把运行trainmodel的命令写在main函数里,也没有把整个文件写成一个类
这样,在直接执行这个文件的时候,是没问题的
但是,在testmodel调用这个文件的时候,训练就要被重新开始一次
是真的蠢本人了。。。
在文章的最后,第二个链接的作者探讨了在训练模型的时候,激活函数选择sigmoid和softmax的原因以及对比
说到这个就说点闲话吧,在微软面试的时候,面试官问了我,为什么LR的损失函数是现在的样子,而不是mse或者其他同样很经典的算法
当时就卡壳的很尴尬
都说现在算法领域,工业和理论有着很大的差距,工业上的很多使用,都很难得到理论的支持或者证明,说难听点就是“瞎几把调”,很多函数或者模型的使用,都是因为“大家都这么用”,或者“大佬说这么用”,而别没有深入思考过原因
现在看很多资料很费事儿。觉得也算是偿还自己当年不认真学数学的债了。作为一个数学系毕业的学生,还是想要努力发挥自己在这方面的优势,不能总是做一个知其然不知其所以然的“调参工程师”。
偷偷许一个2019年的愿望吧,希望我能在未来的某一天,能提出一个优秀的算法,让很多很多的人调用我的库~