基于Xception的腾讯验证码识别(样本+代码)
By 苏剑林 | 2017-07-24 | 93554位读者 |去年的时候,有幸得到网友提供的一批腾讯验证码样本,因此也研究了一下,过程记录在《端到端的腾讯验证码识别(46%正确率)》中。
后来,这篇文章引起了不少读者的兴趣,有求样本的,有求模型的,有一起讨论的,让我比较意外。事实上,原来的模型做得比较粗糙,尤其是准确率难登大雅之台,参考价值不大。这几天重新折腾了一下,弄了个准确率高一点的模型,同时也把样本公开给大家。
模型的思路跟《端到端的腾讯验证码识别(46%正确率)》是一样的,只不过把CNN部分换成了现成的Xception结构,当然,读者也可以换VGG、Resnet50等玩玩,事实上对验证码识别来说,这些模型都能够胜任。我挑选Xception,是因为它层数不多,模型权重也较小,我比较喜欢而已。
代码 #
Github:https://github.com/bojone/n2n-ocr-for-qqcaptcha/
import glob
samples = glob.glob('sample/*.jpg')
import numpy as np
np.random.shuffle(samples) #打乱训练样本
nb_train = 90000 #共有10万样本,9万用于训练,1万用于测试
train_samples = samples[:nb_train]
test_samples = samples[nb_train:]
from keras.applications.xception import Xception,preprocess_input
from keras.layers import Input,Dense,Dropout
from keras.models import Model
img_size = (50, 120) #全体图片都resize成这个尺寸
input_image = Input(shape=(img_size[0],img_size[1],3))
base_model = Xception(input_tensor=input_image, weights='imagenet', include_top=False, pooling='avg')
predicts = [Dense(26, activation='softmax')(Dropout(0.5)(base_model.output)) for i in range(4)]
model = Model(inputs=input_image, outputs=predicts)
model.compile(optimizer='adam', loss='sparse_categorical_crossentropy', metrics=['accuracy'])
model.summary()
from scipy import misc
def data_generator(data, batch_size): #样本生成器,节省内存
while True:
batch = np.random.choice(data, batch_size)
x,y = [],[]
for img in batch:
x.append(misc.imresize(misc.imread(img), img_size))
y.append([ord(i)-ord('a') for i in img[-8:-4]])
x = preprocess_input(np.array(x).astype(float))
y = np.array(y)
yield x,[y[:,i] for i in range(4)]
#训练过程终会显示逐标签的准确率
model.fit_generator(data_generator(train_samples, 100), steps_per_epoch=1000, epochs=10, validation_data=data_generator(test_samples, 100), validation_steps=100)
#评价模型的全对率
from tqdm import tqdm
total = 0.
right = 0.
step = 0
for x,y in tqdm(data_generator(test_samples, 100)):
_ = model.predict(x)
_ = np.array([i.argmax(axis=1) for i in _]).T
y = np.array(y).T
total += len(x)
right += ((_ == y).sum(axis=1) == 4).sum()
if step < 100:
step += 1
else:
break
print u'模型全对率:%s'%(right/total)
要注意的是:Xception的预训练权重是imagenet图片分类任务的,显然不适用于验证码识别,因此这里将所有层都放开训练了,没有像一般分类任务那样固定大部分权重。
结果 #
经过上述代码训练后,模型在测试集的识别率(四个全对才算对)可以达到85%以上。更精细的调参(可以考虑调整学习率、增减迭代次数、调整模型结构等等)可以达到90%以上。
另外,还可以参考杨培文大神的杰作,最后分类使用CTC来做:《使用深度学习来破解 captcha 验证码》。
资源 #
10万验证码样本公开如下:
链接: https://pan.baidu.com/s/1mhO1sG4 密码: j2rj
转载到请包括本文地址:https://kexue.fm/archives/4503
更详细的转载事宜请参考:《科学空间FAQ》
如果您还有什么疑惑或建议,欢迎在下方评论区继续讨论。
如果您觉得本文还不错,欢迎分享/打赏本文。打赏并非要从中获得收益,而是希望知道科学空间获得了多少读者的真心关注。当然,如果你无视它,也不会影响你的阅读。再次表示欢迎和感谢!
如果您需要引用本文,请参考:
苏剑林. (Jul. 24, 2017). 《基于Xception的腾讯验证码识别(样本+代码) 》[Blog post]. Retrieved from https://kexue.fm/archives/4503
@online{kexuefm-4503,
title={基于Xception的腾讯验证码识别(样本+代码)},
author={苏剑林},
year={2017},
month={Jul},
url={\url{https://kexue.fm/archives/4503}},
}
August 25th, 2017
这里弱弱问一句,我将数据切成了3份——训练、测试、验证集。最终在训练、测试集中轻松90%准确率。但是在不参与训练过程的验证集里,只有30%准确率。怎么提升这个模型的泛化能力呢。
难道你的测试集是参与训练的?
October 19th, 2017
有现在的识别系统可以识别 ,能做到95%以上的识别率,并且是开源程序,可做服务端,本地等服务,支持各类平台调用。
March 21st, 2018
你的验证集和测试集重叠了,结果肯定是过拟合的。
并没有呀,分开了train_samples和test_samples
March 22nd, 2018
我是做机器学习不是做深度学习的,对深度学习还在熟悉中,很多东西可能不太了解。但是在机器学习中,为什么有些时候一定需要验证集呢,因为有些超参数只用训练集训练不出来,例如做stacking的时候各个子模型的权重,这种情况就需要验证集来得到这些参数,这种情况下,测试集和验证集是不能有重叠的,model.fit_generator(data_generator(train_samples, 100), steps_per_epoch=1000, epochs=10, validation_data=data_generator(test_samples, 100), validation_steps=100) 你的验证集是test_samples中取的,也就是说你模型的部分参数是用验证集得到的,你再拿和验证集重叠的测试集去看结果,【for x,y in tqdm(data_generator(test_samples, 100))】结果一定是很好的。
你那是模型融合时需要交叉验证罢了。我这里直接分为两部分,训练集和测试集,训练集直接用来训练预先搭建好的模型,测试集用来测试,没有涉及到用测试集来调超参的问题。
放心吧,各个集的作用我还是分得清的~(https://kexue.fm/archives/4638)
那请教validation_data从test_samples中取,没有问题吗?还是说深度学习中验证集的在训练过程中发挥的作用不一样
其实跟深度学习没关系。
因为我这里的验证集只用来决定迭代次数,并没有用来调其它超参数,所以可以认为验证集并没有参与训练,也没有参与调解超参数,因此这里这样做是合理的。
如果我们还根据验证集反复调节了模型的超参数(网络层数、节点数等),这时候验证集的结果就不够说服力了,需要新的测试集。
明白了 谢谢你耐心的解释
May 11th, 2019
[...]keras 2.0中文文档 https://www.jiqizhixin.com/articles/2017-12-14-2 https://spaces.ac.cn/archives/4503 https://zhuanlan.zhihu.com/p/26078299[...]
February 15th, 2020
老哥,请教下,yield x,[y[:,i] for i in range(4)]中为什么label以[y[:,i] for i in range(4)]的形式返回。。。
因为这是一个四输出模型。