TensorFlow 验证码识别
• 准备模型开发环境
第三方依赖包
Pillow (PIL Fork)
PIL(Python Imaging Library) 为 Python 解释器添加了图像处理功能。但是,在 2009 年发布
1.1.7 版本后,社区便停止更新和维护。
Pillow 是由 Alex Clark 及社区贡献者 一起开发和维护的一款分叉自 PIL 的图像工具库。
至今,社区依然非常活跃,Pillow 仍在快速迭代。
Pillow提供广泛的文件格式支持,高效的内部表示和相当强大的图像处理功能。
核心图像库旨在快速访问以几种基本像素格式存储的数据, 它应该为一般的图像处理工
具提供坚实的基础。
captcha
Catpcha 是一个生成图像和音频验证码的开源工具库。
from captcha.image importImageCaptchafrom captcha.audio importAudioCaptcha
image= ImageCaptcha(fonts=[‘/path/A.ttf‘, ‘/path/B.ttf’])
data = image.generate(‘1234’)
image.write(‘1234‘, ‘out.png’)
audio = AudioCaptcha(voicedir=‘/path/to/voices’)
data = audio.generate(‘1234’)
audio.write(‘1234‘, ‘out.wav’)
pydot
pydot 是用纯 Python 实现的 GraphViz 接口,支持使用 GraphViz 解析和存储 DOT语言
(graph description language)。其主要依赖 pyparsing 和 GraphViz 这两个工具库。
pyparsing:仅用于加载DOT文件,在 pydot 安装期间自动安装。
GraphViz:将图形渲染为PDF,PNG,SVG等格式文件,需独立安装。
flask
flask 是一个基于 Werkzeug 和 jinja2 开发的 Python Web 应用程序框架,遵从 BSD 开源协
议。它以一种简约的方式实现了框架核心,又保留了扩展性。
• 生成验证码数据集
验证码(CAPTCHA)简介
全自动区分计算机和人类的公开图灵测试(英语:Completely Automated Public Turing test
to tell Computers and Humans Apart,简称CAPTCHA),俗称验证码,是一种区分用户是
计算机或人的公共全自动程序。在CAPTCHA测试中,作为服务器的计算机会自动生成一
个问题由用户来解答。这个问题可以由计算机生成并评判,但是必须只有人类才能解答。
由于计算机无法解答CAPTCHA的问题,所以回答出问题的用户就可以被认为是人类。
一种常用的CAPTCHA测试是让用户输入一个扭曲变形的图片上所显示的文字或数字,扭
曲变形是为了避免被光学字符识别(OCR, Optical Character Recognition)之类的计算机程
序自动识别出图片上的文数字而失去效果。由于这个测试是由计算机来考人类,而不是
标准图灵测试中那样由人类来考计算机,人们有时称CAPTCHA是一种反向图灵测试。
验证码(CAPTCHA)破解
一些曾经或者正在使用中的验证码系统已被破解。
这包括Yahoo验证码的一个早期版本 EZ-Gimpy,PayPal使用的验证码,LiveJournal、
phpBB使用的验证码,很多金融机构(主要是银行)使用的网银验证码以及很多其他网站
使用的验证码。
俄罗斯的一个黑客组织使用一个自动识别软件在2006年破解了Yahoo的CAPTCHA。准确
率大概是15%,但是攻击者可以每天尝试10万次,相对来说成本很低。而在2008年,
Google的CAPTCHA也被俄罗斯黑客所破解。攻击者使用两台不同的计算机来调整破解进
程,可能是用第二台计算机学习第一台对CAPTCHA的破解,或者是对成效进行监视。
验证码(CAPTCHA)演进
验证码(CAPTCHA)生成
使用 Pillow(PIL Fork) 和 captcha 库生成验证码图像:
PIL.Image.open(fp, mode=‘r’) - 打开和识别输入的图像(文件)
captcha.image.ImageCaptcha(width, height,) – 创建 ImageCaptcha 实例
captcha.image.ImageCaptcha.write(‘1234’, ‘out.png’) – 生成验证码并保存
captcha.image.ImageCaptcha.generate(‘1234’) – 生成验证码图像
代码实现:
创建验证码数据集
引入第三方包from captcha.image importImageCaptchaimportrandomimportnumpy as npimporttensorflow.gfile as gfileimportmatplotlib.pyplot as pltimportPIL.Image as Image
定义常量和字符集
NUMBER= [‘0‘, ‘1‘, ‘2‘, ‘3‘, ‘4‘, ‘5‘, ‘6‘, ‘7‘, ‘8‘, ‘9‘]
LOWERCASE= [‘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‘]
UPPERCASE= [‘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‘]
CAPTCHA_CHARSET= NUMBER #验证码字符集
CAPTCHA_LEN = 4 #验证码长度
CAPTCHA_HEIGHT = 60 #验证码高度
CAPTCHA_WIDTH = 160 #验证码宽度
TRAIN_DATASET_SIZE= 5000 #验证码数据集大小
TEST_DATASET_SIZE = 1000TRAIN_DATA_DIR= ‘./train-data/‘ #验证码数据集目录
TEST_DATA_DIR = ‘./test-data/‘生成随机字符的方法def gen_random_text(charset=CAPTCHA_CHARSET, length=CAPTCHA_LEN):
text= [random.choice(charset) for _ inrange(length)]return ‘‘.join(text)
创建并保存验证码数据集的方法def create_captcha_dataset(size=100,
data_dir=‘./data/‘,
height=60,
width=160,
image_format=‘.png‘):#如果保存验证码图像,先清空 data_dir 目录
ifgfile.Exists(data_dir):
gfile.DeleteRecursively(data_dir)
gfile.MakeDirs(data_dir)#创建 ImageCaptcha 实例 captcha
captcha = ImageCaptcha(width=width, height=height)for _ inrange(size):#生成随机的验证码字符
text =gen_random_text(CAPTCHA_CHARSET, CAPTCHA_LEN)
captcha.write(text, data_dir+ text +image_format)returnNone
创建并保存训练集
create_captcha_dataset(TRAIN_DATASET_SIZE, TRAIN_DATA_DIR)
创建并保存测试集
create_captcha_dataset(TEST_DATASET_SIZE, TEST_DATA_DIR)
生成并返回验证码数据集的方法def gen_captcha_dataset(size=100,
height=60,
width=160,
image_format=‘.png‘):#创建 ImageCaptcha 实例 captcha
captcha = ImageCaptcha(width=width, height=height)#创建图像和文本数组
images, texts = [None]*size, [None]*sizefor i inrange(size):#生成随机的验证码字符
texts[i] =gen_random_text(CAPTCHA_CHARSET, CAPTCHA_LEN)#使用 PIL.Image.open() 识别新生成的验证码图像
#然后,将图像转换为形如(CAPTCHA_WIDTH, CAPTCHA_HEIGHT, 3) 的 Numpy 数组
images[i] =np.array(Image.open(captcha.generate(texts[i])))returnimages, texts
生成100张验证码图像和字符
images, texts=gen_captcha_dataset()
plt.figure()for i in range(20):
plt.subplot(5,4,i+1) #绘制前20个验证码,以5行4列子图形式展示
plt.tight_layout() #自动适配子图尺寸
plt.imshow(images[i])
plt.title("Label: {}".format(texts[i])) #设置标签为子图标题
plt.xticks([]) #删除x轴标记
plt.yticks([]) #删除y轴标记
plt.show()
• 输入与输出数据处理
输入数据处理
图像处理:RGB图 -> 灰度图 -> 规范化数据
输入数据处理
适配 Keras 图像数据格式:“channels_frist” 或 “channels_last”
输出数据处理
One-hot 编码:验证码转向量
解码:模型输出向量转验证码
• 模型结构设计
分类问题
图像分类模型 AlexNet
使用卷积进行特征提取
图像分类模型 VGG-16
验证码识别模型结构
验证码识别模型实现
• 模型损失函数设计
交叉熵(Cross-Entropy, CE)
我们使用交叉熵作为该模型的损失函数。
虽然 Categorical / Binary CE 是更常用的损失函数,不过他们都是 CE 的变体。
CE 定义如下:
对于二分类问题 (C‘=2) ,CE 定义如下:
Categorical CE Loss(Softmax Loss)
常用于输出为 One-hot 向量的多类别分类(Multi-Class Classification)模型。
Binary CE Loss(Sigmoid CE Loss)
与 Softmax Loss 不同,Binary CE Loss 对于每个向量分量(class)都是独立
的,这意味着每个向量分量计算的损失不受其他分量的影响。
因此,它常被用于多标签分类(Multi-label classification)模型。
• 模型训练过程分析
模型训练过程
学习率(Learning rate)
学习率与损失值变化(模型收敛速度)直接相关。
何时加大学习率
• 训练初期,损失值一直没什么波动
何时减小学习率
• 训练初期,损失值直接爆炸或者 NAN
• 损失值先开始速降,后平稳多时
• 训练后期,损失值反复上下波动
优化器介绍:SGD(Stochastic Gradient Descent)
优化器介绍:SGD-M(Momentum)
SGD 在遇到沟壑时容易陷入震荡。为此,可以为其引入动量(Momentum),加速 SGD
在正确方向的下降并抑制震荡。
优化器介绍:Adagrad – RMSprop – Adam
优化器对比:鞍点
优化器对比: 验证码识别模型
• 模型部署与效果演示
数据-模型-服务流水线
使用 Flask 快速搭建 验证码识别服务
使用 Flask 启动 验证码识别服务
访问 验证码识别服务