详解如何用 LSTM 自动识别验证码

这是去年博主心血来潮实现的一个小模型,现在把它总结一下。由于楼主比较懒,网上许多方法都需要切割图片,但是楼主思索了一下感觉让模型有多个输出就可以了呀,没必要一定要切割的吧?切不好还需要损失信息啊!本文比较简单,只基于传统的验证码。

Part 0 模型概览

从图片到序列实际上就是Image2text也就是seq2seq的一种。encoder是Image, decoder是验证码序列。由于keras不支持传统的在decoder部分每个cell输出需要作为下一个rnn的cell的输入(见下图),所以我们这里把decoder部分的输入用encoder(image)的最后一层复制N份作为decoder部分的每个cell的输入。

典型的seq2seq

keras可以直接实现的image2text

当然利用 recurrentshop 和 seq2seq,我们也可以实现标准的seq2seq的网络结构(后文会写)。

Part I 收集数据

网上还是有一些数据集可以用的,包括dataCastle也举办过验证码识别的比赛,都有现成的标注好了的数据集。(然而难点是各种花式验证码啊,填字的,滑动的,还有那个基于语义的reCaptcha~)。

因为我想弄出各种长度的验证码,所以我还是在github上下载了一个生成验证码的python包。

下载后,按照例子生成验证码(包含26个小写英文字母):

#!/usr/bin/env python
# -*- coding: utf-8
from captcha.image import ImageCaptcha
from random import sample

image = ImageCaptcha() #fonts=[ "font/Xenotron.ttf"]
characters =  list("abcdefghijklmnopqrstuvwxyz")

def generate_data(digits_num, output, total):
    num = 0
    while(num<total):
        cur_cap = sample(characters, digits_num)
        cur_cap =''.join(cur_cap)
        _ = image.generate(cur_cap)
        image.write(cur_cap, output+cur_cap+".png")
        num += 1

generate_data(4, "images/four_digit/", 10000)  #产生四个字符长度的验证码
generate_data(5, "images/five_digit/", 10000) #产生五个字符长度的验证码
generate_data(6, "images/six_digit/", 10000) #产生六个字符长度的验证码
generate_data(7, "images/seven_digit/",10000) # 产生七个字符长度的验证码

产生的验证码

(目测了一下生成验证码的包的代码,发现主要是在x,y轴上做一些变换,加入一些噪音)

Part II 预处理

由于生成的图片不是相同尺寸的,为了方便训练我们需要转换成相同尺寸的。另外由于验证码长度不同,我们需要在label上多加一个符号来表示这个序列的结束。

处理之后的结果就是图像size全部为Height=60, Width=250, Channel=3。label全部用字符id表示,并且末尾加上表示<EOF>的id。比如假设a-z的id为0-25,<EOF>的id为26,那么对于验证码"abdf"的label也就是[0,1,3,5,26,26,26,26],"abcdefg"的label为[0,1,2,3,4,5,6,26]。

由于我们用的是categorical_crossentropy来判断每个输出的结果,所以对label我们还需要把其变成one-hot的形式,那么用Keras现成的工具to_categorical函数对上面的label做一下处理就可以了。比如abdf的label进一步转换成:

[[1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],

[0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],

[0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],

[0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],

[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1],

[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1],

[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1],

[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1]]

Part III 构建模型

不借助外部包可以实现的模型

def create_simpleCnnRnn(image_shape, max_caption_len,vocab_size):
    image_model = Sequential()
    # image_shape : C,W,H
    # input: 100x100 images with 3 channels -> (3, 100, 100) tensors.
    # this applies 32 convolution filters of size 3x3 each.
    image_model.add(Convolution2D(32, 3, 3, border_mode='valid', input_shape=image_shape))
    image_model.add(BatchNormalization())
    image_model.add(Activation('relu'))
    image_model.add(Convolution2D(32, 3, 3))
    image_model.add(BatchNormalization())
    image_model.add(Activation('relu'))
    image_model.add(MaxPooling2D(pool_size=(2, 2)))
    image_model.add(Dropout(0.25))
    image_model.add(Convolution2D(64, 3, 3, border_mode='valid'))
    image_model.add(BatchNormalization())
    image_model.add(Activation('relu'))
    image_model.add(Convolution2D(64, 3, 3))
    image_model.add(BatchNormalization())
    image_model.add(Activation('relu'))
    image_model.add(MaxPooling2D(pool_size=(2, 2)))
    image_model.add(Dropout(0.25))
    image_model.add(Flatten())
    # Note: Keras does automatic shape inference.
    image_model.add(Dense(128))
    image_model.add(RepeatVector(max_caption_len)) # 复制8份
    image_model.add(Bidirectional(GRU(output_dim=128, return_sequences=True)))
    image_model.add(TimeDistributed(Dense(vocab_size)))
    image_model.add(Activation('softmax'))
    sgd = SGD(lr=0.002, decay=1e-6, momentum=0.9, nesterov=True)
    image_model.compile(loss='categorical_crossentropy', optimizer=sgd, metrics=['accuracy'])
    return image_model

借助recurrentshop和seq2seq可以实现的结构

def create_imgText(image_shape, max_caption_len,vocab_size):
    image_model = Sequential()
    # image_shape : C,W,H
    # input: 100x100 images with 3 channels -> (3, 100, 100) tensors.
    # this applies 32 convolution filters of size 3x3 each.
    image_model.add(Convolution2D(32, 3, 3, border_mode='valid', input_shape=image_shape))
    image_model.add(BatchNormalization())
    image_model.add(Activation('relu'))
    image_model.add(Convolution2D(32, 3, 3))
    image_model.add(BatchNormalization())
    image_model.add(Activation('relu'))
    image_model.add(MaxPooling2D(pool_size=(2, 2)))
    image_model.add(Dropout(0.25))
    image_model.add(Convolution2D(64, 3, 3, border_mode='valid'))
    image_model.add(BatchNormalization())
    image_model.add(Activation('relu'))
    image_model.add(Convolution2D(64, 3, 3))
    image_model.add(BatchNormalization())
    image_model.add(Activation('relu'))
    image_model.add(MaxPooling2D(pool_size=(2, 2)))
    image_model.add(Dropout(0.25))
    image_model.add(Flatten())
    # Note: Keras does automatic shape inference.
    image_model.add(Dense(128))
    image_model.add(RepeatVector(1)) # 为了兼容seq2seq,要多包一个[]
    #model = AttentionSeq2Seq(input_dim=128, input_length=1, hidden_dim=128, output_length=max_caption_len, output_dim=128, depth=2)
    model = Seq2Seq(input_dim=128, input_length=1, hidden_dim=128, output_length=max_caption_len,
                             output_dim=128, peek=True)
    image_model.add(model)
    image_model.add(TimeDistributed(Dense(vocab_size)))
    image_model.add(Activation('softmax'))
    image_model.compile(loss='categorical_crossentropy', optimizer='adam', metrics=['accuracy'])

    return image_model

Part IV 模型训练

之前写过固定长度的验证码的序列准确率可以达到99%,项目可以参考这里

另外,我们在用Keras训练的时候会有一个acc,这个acc是指的一个字符的准确率,并不是这一串序列的准确率。也就是说在可以预期的情况下,如果你的一个字符的准确率达到了99%,那么如果你的序列长度是5的时候,理论上你的序列准确率是0.99^5 = 0.95, 如果像我们一样序列长度是7,则为0.99^8=0.923。

所以当你要看到实际的验证集上的准确率的时候,应该自己写一个callback的类来评测,只有当序列中所有的字符都和label一样才可以算正确。

class ValidateAcc(Callback):
    def __init__(self, image_model, val_data, val_label, model_output):
        self.image_model = image_model
        self.val = val_data
        self.val_label = val_label
        self.model_output = model_output

    def on_epoch_end(self, epoch, logs={}):  # 每个epoch结束后会调用该方法
        print '\n———————————--------'
        self.image_model.load_weights(self.model_output+'weights.%02d.hdf5' % epoch)
        r = self.image_model.predict(val, verbose=0)
        y_predict = np.asarray([np.argmax(i, axis=1) for i in r])
        val_true = np.asarray([np.argmax(i, axis = 1) for i in self.val_label])
        length = len(y_predict) * 1.0
        correct = 0
        for (true,predict) in zip(val_true,y_predict):
            print true,predict
            if list(true) == list(predict):
                correct += 1
        print "Validation set acc is: ", correct/length
        print '\n———————————--------'

val_acc_check_pointer = ValidateAcc(image_model,val,val_label,model_output)

记录每个epoch的模型结果

check_pointer = ModelCheckpoint(filepath=model_output + "weights.{epoch:02d}.hdf5")

训练

image_model.fit(train, train_label,
                shuffle=True, batch_size=16, nb_epoch=20, validation_split=0.2, callbacks=[check_pointer, val_acc_check_pointer])

Part V 训练结果

在39866张生成的验证码上,27906张作为训练,11960张作为验证集。

第一种模型:

序列训练了大约80轮,在验证集上最高的准确率为0.9264, 但是很容易变化比如多跑一轮就可能变成0.7,主要原因还是因为预测的时候考虑的是整个序列而不是单个字符,只要有一个字符没有预测准确整个序列就是错误的。

第二种模型:

第二个模型也就是上面的create_imgText,验证集上的最高准确率差不多是0.9655(当然我没有很仔细的去调参,感觉调的好的话两个模型应该是差不多的,验证集达到0.96之后相对稳定)。

Part VI 其它

看起来还是觉得keras实现简单的模型会比较容易,稍微变形一点的模型就很纠结了,比较好的是基础的模型用上其他包都可以实现。keras 2.0.x开始的版本跟1.0.x还是有些差异的,而且recurrentshop现在也是支持2.0版本的。如果在建模型的时候想更flexible一点的话,还是用tensorflow会比较好,可以调整的东西也比较多,那下一篇可以写一下img2txt的tensorflow版本。

Part VII 代码

完整源代码:https://github.com/Slyne/CaptchaVariLength

Part VIII 后续

现在的这两个模型还是需要指定最大的长度,后面有时间会在训练集最多只有8个字符的情况下,利用rnn的最后一层进一步对于有9个以及以上字符的验证码效果,看看是不是可以再进一步的扩展到任意长度。(又立了一个flag~)

====================================分割线================================

本文作者:AI研习社

本文转自雷锋网禁止二次转载,原文链接

时间: 2024-09-20 06:35:44

详解如何用 LSTM 自动识别验证码的相关文章

如何用电脑自动识别验证码,而不是用人眼来识别?可行吗?

问题描述 如何用电脑自动识别验证码,而不是用人眼来识别?可行吗? 请问,我想通过编程实现机器代替人眼来识别验证码图片里的字母,或者是文字. 可以实现吗?如可行,给个思路呗!拜托! 解决方案 那你得看验证码的复杂程度了 有些验证码只有字母和数字,或者像贴吧那样的纯汉子和拼音 这种还比较好处理 如果是那种叠影处理过的验证码 要进行的图形解析就很难了 解决方案二: 如果验证码可以被机器识别,那验证码存在的意义就没有了.而增加验证码识别的难度比识别复杂验证码的程序实现要简单.在这种不对称的竞赛中,程序是

一文详解如何用 python 做中文分词

打算绘制中文词云图?那你得先学会如何做中文文本分词.跟着我们的教程,一步步用 Python 来动手实践吧.   需求 在此前发布的文章<从零开始教你用 Python 做词云>一文中,我们介绍了英文文本的词云制作方法.大家玩儿得可还高兴? 文中提过,选择英文文本作为示例,是因为处理起来最简单.但是很快就有读者尝试用中文文本做词云了.按照前文的方法,你成功了吗? 估计是不成功的.因为这里面缺了一个重要的步骤. 观察你的英文文本.你会发现英文单词之间采用空格作为强制分隔符. 例如: Yes Mini

一文详解如何用 TensorFlow 实现基于 LSTM 的文本分类(附源码)

 引言 学习一段时间的tensor flow之后,想找个项目试试手,然后想起了之前在看Theano教程中的一个文本分类的实例,这个星期就用tensorflow实现了一下,感觉和之前使用的theano还是有很大的区别,有必要总结mark一下.   模型说明 这个分类的模型其实也是很简单,主要就是一个单层的LSTM模型,当然也可以实现多层的模型,多层的模型使用Tensorflow尤其简单,下面是这个模型的图  简单解释一下这个图,每个word经过embedding之后,进入LSTM层,这里LSTM是

一文详解如何用 R 语言绘制热图

简介 本文将绘制静态与交互式热图,需要使用到以下R包和函数: ● heatmap():用于绘制简单热图的函数 ● heatmap.2():绘制增强热图的函数 ● d3heatmap:用于绘制交互式热图的R包 ● ComplexHeatmap:用于绘制.注释和排列复杂热图的R&bioconductor包(非常适用于基因组数据分析) 数据准备 使用R内置数据集 mtcars df <- as.matrix((scale(mtcars))) #归一化.矩阵化 使用基本函数绘制简单简单热图 主要是函

如何用 TensorFlow 搞定知乎验证码;深层神经网络的致命问题详解 | AI 开发者日报

利用 TensorFlow 搞定知乎验证码之<让你找中文倒转汉字> 本文来自知乎专栏,作者 ID "想飞的石头".文中用 TensorFlow 实现了一个验证码识别模型,即如何从一排汉字中挑出倒置的汉字.作者首先利用网上的开源代码实现了汉字自动生成(当然其中有一些是倒置的).然后因为所有的验证码长度都是 10,因此用 0 表示正常,1 表示倒置,构造了一个 input 到 10 个 binary classification 的网络.最后用数据对模型展开训练,经过一些调试和

Android中用Bmob实现短信验证码功能的方法详解_Android

 这篇文章主要介绍发送验证码和校验验证码的功能,用到一个第三方平台Bmob,那Bmob是什么呢?Bmob可以开发一个云存储的移动应用软件,他提供了大量的标准的API接口,根据需要接入相关服务,开发者可以更加专注于应用的开发,让产品交付更快速,验证码功能就是其中一个. 一.跟其他第三方一样,我们开发之前要做一些准备工作. 1.首先,去官网注册一个帐号:http://www.bmob.cn/: 2.然后就可以创建应用了:具体怎么做Bmob说得很清楚了(官方操作介绍),如果你不想看,我简单说一下:点击

Yii2增加验证码步骤详解_php实例

本来以为yii2框架验证码这块很全面,尝试百度google了一下,大多数教程写的零零散散不全面,想着自己写一份带有完整步骤的验证码教程. 我们假设site/login 表单登录需要增加验证码. 1.siteController控制器的actions方法增加captcha设置 public function actions() { return [ 'captcha' => [ 'class' => 'yii\captcha\CaptchaAction', 'maxLength' => 4

最新最全PHP生成制作验证码代码详解(推荐)_php实例

1.0 首先先看代码 <?php header("Content-Type:text/html;Charset=UTF-");// 设置页面的编码风格 header("Content-Type:image/jpeg");// 通知浏览器输出的是jpeg格式的图像 $img = imagecreatetruecolor(,);//创建画布并设置大小 x轴 y轴 $bgcolor = imagecolorallocate($img, mt_rand(,), mt_

利用Python破解验证码实例详解_python

一.前言 本实验将通过一个简单的例子来讲解破解验证码的原理,将学习和实践以下知识点:       Python基本知识       PIL模块的使用 二.实例详解 安装 pillow(PIL)库: $ sudo apt-get update $ sudo apt-get install python-dev $ sudo apt-get install libtiff5-dev libjpeg8-dev zlib1g-dev \ libfreetype6-dev liblcms2-dev lib