从零开始搭建深度学习验证码识别模型(代码片段)

ZipZou ZipZou     2022-12-03     254

关键词:

文章目录

从零开始搭建深度学习验证码识别模型

CNN模型与图像识别

CNN模型是图像问题的基本深度学习算法,使用CNN算法不用人工从图片中提取特征,更加end2end,符合表示学习的特征,避免繁琐、低效的特征工程。CNN算法目前在CV领域已经成为基本方法之一。

CNN模型的核心在于,利用卷积核在特征图上进行运算,从中提取到充足的特征。在CNN的研究发现,在浅层网络,CNN模型可以提取到部分简单的特征,如轮廓等,而深层CNN则将基础特征进行整合,提取到更加复杂的特征,从而能够对图片中的内容进行特征提取。

CNN的核心在于卷积运算规则,其大致为:

C N N ( m a p ) = ∑ i = 0 f h ∑ j = 0 f w f i l t e r h , w ⋅ s l i c e ( m a p ) h , w CNN(map)=\\sum\\limits_i=0^f_h \\sum\\limits_j=0^f_w filter_h,w \\cdot slice(map)_h,w CNN(map)=i=0fhj=0fwfilterh,wslice(map)h,w

其中, s l i c e ( ⋅ ) slice(\\cdot) slice()为特征图切片运算,如当 f i l t e r filter filter 3 3 3时,则 m a p map map将会根据步长,从中进行切片,并与卷积核进行element-wise乘积运算后并求和。

在CV诸多任务中,卷积层往往被用来做特征提取,而到具体的任务时,需要拼接更多的网络,如拼接全连接网络进行分类任务。

验证码识别,本质上也为一个图像识别任务。在对象识别模型中,通常需要从图像中尽可能识别多的对象,并以框的形式对其位置进行标记。验证码识别也可采用该方式实现,该方法为multi-stage方法,即:通常使用对象识别模型,识别图片中的文字,并用框标记出文字所在位置,再利用CNN和FCN的结构对所识别的文字进行分类。并且,若为文档OCR识别,输出层还可能借助LSTM等RNN结构网络。

考虑到验证码通常位数有限,即4位、5位较为常见,因此该模型采用end2end multi-task方法也可满足需求,且模型复杂度并不高。multi-task任务可简单的理解为,有多个输出层负责不同任务的输出,其弊端在于扩展性低,如只能识别固定数量的对象。

验证码数据集介绍

所有训练数据均以验证码图片内容为名称命名,如2ANF.jpg,因此可以保证训练数据没有重复项,根据文件名即可获取样本label。

数据集下载:Dataset-Google Drive for Easy Captcha

数据规格:48,320张验证码图片,全由Easy Captcha框架生成,大小为 120 × 80 120 \\times 80 120×80

验证码示例:

EasyCaptch项目主页

EasyCaptcha验证码特点在于可以构造Gif动态验证码,而其他验证码则显得相对简单,主要在于该验证码间隔较开,易于区分,因此识别较为简单。根据对上例中的验证码分析可知,验证码由不定位置的1-2个圆圈与曲线构成噪音,对文本加以干扰,文字颜色可变。从布局来看,文字的布局位置相对固定,且间隔也相对固定,这无疑也简化了识别过程。

数据集下载:Dataset-Google Drive for Kaptcha

数据规格:52,794张验证码图片,全由Kaptcha生成,大小为 200 × 50 200 \\times 50 200×50

验证码示例:

Kaptcha项目主页

相对而言,Kaptcha验证码相对而言文本排布默认更加紧凑,但是文字间距再kaptcha中是一个可以调节的超参数。Kaptcha较难识别的主要原因在于其文本存在可能的扭曲形变,并且形变状态不定,因此模型需要能够克服该形变,方可较为准确的识别,因此Kaptcha识别较captcha困难,并且准确度指标会有所下降。

注:在直接使用模型时需要严格注意验证码规格,这主要在于图片过小会导致CNN过程异常。若对图片进行分辨率调整,长宽比不一,将导致严重形变,导致识别精度下降。

生成数据集

基于上述两个验证码框架,可以使用其提供的开源库进行验证码生成。

生成EasyCaptcha

如下代码所示,主要是从配置中获取验证码的配置,并使用给定的框架进行验证码生成,并最终输出到文件中。

public boolean generate() 
    String outputFolder = config.get(ConfigConstants.OUT_DIR);
    int width = Integer.parseInt(config.get(ConfigConstants.WIDTH, "120"));
    int height = Integer.parseInt(config.get(ConfigConstants.HEIGHT, "80"));
    int len = Integer.parseInt(config.get(ConfigConstants.LENGTH));
    SpecCaptcha captcha = new SpecCaptcha(width, height, len);
    captcha.setCharType(Captcha.TYPE_DEFAULT);
    try 
      captcha.setFont(Captcha.FONT_3);
     catch (IOException | FontFormatException e1) 
      e1.printStackTrace();
      return false;
    
    String codes = captcha.text();
    if (LOG.isInfoEnabled()) 
      LOG.info("Generating " + codes + "...");
    
    return ImageOutputUtil.writeToFile(captcha, outputFolder, codes);
  

为提升图片生成的效率,我们使用多线程的方式,同时生成:

public class CaptchaTaskRunner implements Runnable 

  private static final Logger LOG = Logger.getLogger(CaptchaTaskRunner.class);

  private CaptchaGenerator generator;

  @Override
  public void run() 
    boolean success = generator.generate();
    if (success) 
      if (LOG.isInfoEnabled()) 
        LOG.info("Complete!");
      
     else 
      if (LOG.isInfoEnabled()) 
        LOG.info("Failed!");
      
    
  

  /**
   * @return CaptchaGenerator return the generator
   */
  public CaptchaGenerator getGenerator() 
    return generator;
  

  /**
   * @param generator the generator to set
   */
  public void setGenerator(CaptchaGenerator generator) 
    this.generator = generator;
  

代码中CaptchaGenerator即为generate()方法的接口类,在线程池中提交若干任务,最终都由CaptchaTaskRunner实例进行生成。

生成Kcaptcha

相较于EasyCaptcha,Kcaptcha的配置项更多,因此其识别更加困难,为增强最终模型的可信度与拟合能力,可随机地产生若干配置,来生成验证码:

public KaptchaGeneratorWorker(me.zouzhipeng.config.Config config) 
    Properties prop = new Properties();
    prop.put(Constants.KAPTCHA_BORDER, true);
    prop.put(Constants.KAPTCHA_BORDER_COLOR,
        String.join(",", rand.nextInt(256) + "", rand.nextInt(256) + "", rand.nextInt(256) + ""));
    prop.put(Constants.KAPTCHA_IMAGE_WIDTH, config.get(ConfigConstants.WIDTH, "200"));
    prop.put(Constants.KAPTCHA_IMAGE_HEIGHT, config.get(ConfigConstants.HEIGHT, "50"));
    String textColor = config.get(ConfigConstants.TEXT_COLOR);
    if (null == textColor) 
      textColor = String.join(",", rand.nextInt(256) + "", rand.nextInt(256) + "", rand.nextInt(256) + "");
    
    prop.put(Constants.KAPTCHA_TEXTPRODUCER_FONT_COLOR,
        textColor);
    prop.put(Constants.KAPTCHA_TEXTPRODUCER_CHAR_LENGTH, config.get(ConfigConstants.LENGTH, "4"));
    prop.put(Constants.KAPTCHA_TEXTPRODUCER_FONT_NAMES, "彩云,宋体,楷体,微软雅黑,Arial,SimHei,SimKai,SimSum");
    if (Boolean.parseBoolean(config.get(ConfigConstants.NOISE_SAME_TEXT_COLOR, "true"))) 
      prop.put(Constants.KAPTCHA_NOISE_COLOR, textColor);
     else 
      prop.put(Constants.KAPTCHA_NOISE_COLOR,
        String.join(",", rand.nextInt(256) + "", rand.nextInt(256) + "", rand.nextInt(256) + ""));
    
    prop.put(Constants.KAPTCHA_TEXTPRODUCER_CHAR_STRING, "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz012345679");
    this.output = config.get(ConfigConstants.OUT_DIR);
    Config kaptchaConfig = new Config(prop);
    producer = kaptchaConfig.getProducerImpl();
  

如上述构造函数,针对Kaptcha进行了所需配置项的配置。

而生成部分,同EasyCaptcha相似,如下:

public boolean generate(String folder) 
    String text = producer.createText();
    BufferedImage imageBuffered = producer.createImage(text);
    if (LOG.isInfoEnabled()) 
      LOG.info("Generating " + text + "...");
    
    return ImageOutputUtil.writeToFile(imageBuffered, folder, text, "jpg");
  

同样地,采用多线程对图片进行生成,以得到大量验证码训练图片。

搭建模型

针对两个不同的数据集,本项目设计了两个不同的模型,但是总体上都是基于CNN和FCN结构的分类任务。在诸多OCR任务中,通常会使用multi-stage方法设计模型,即:通常使用对象识别模型,识别图片中的文字,并用框标记出文字所在位置,再利用CNN和FCN的结构对所识别的文字进行分类。并且,若为文档OCR识别,输出层还可能借助LSTM等RNN结构网络。

考虑到验证码通常位数有限,即4位、5位较为常见,因此该模型采用end2end multi-task方法也可满足需求,且模型复杂度并不高。

针对EasyCaptcha验证码,其产生的验证码较容易区分,字符分隔较开,且变形选项较少,因此使用很简单的模型即可达到较高的精度,在本项目的模型中,验证集准确度可达到 98 − 99 98-99% 9899左右。

而对于Kaptcha验证码,其存在较多可选的配置项,并且会在验证码中间添加噪音扰动,因此识别较为困难,使用EasyCaptcha的模型,精度仅能达到70%左右,准确度较低,Kaptcha模型适当地加大了CNN网络的深度,并增加了一层全连接隐藏层,在验证集上达到93-94%的准确度。

在训练过程中,采用长度为4的验证码,其中验证码中可选字符为:a-zA-Z0-9,共62个可能字符。

下面为两个模型:EasyNet, KCapNet的详细介绍。

EasyNet模型

EasyNet模型由2层卷积层和4个输出层构成,该模型结构细节如下:

  1. 第一层卷积,卷积核大小为 5 × 5 5 \\times 5 5×5,步长为1,通道为16,参数量为 3 × 5 × 5 × 16 3 \\times 5 \\times 5 \\times 16 3×5×5×16,得到大小 76 × 116 76\\times 116 76×116的特征图共16个;
  2. 第二层为最大池化层,无参数,池化核大小为 5 × 5 5\\times 5 5×5,步长为5,得到特征图大小为 15 × 23 15 \\times 23 15×23
  3. 第三层为批归一化层,避免过拟合并加速模型收敛。根据效果,同时还可尝试使用shortcut方法。归一化后,采用RReLu激活函数;
  4. 第四层为卷积层,卷积核大小为 5 × 5 5 \\times 5 5×5,步长为1,通道数为32,得到特征图大小为 11 × 19 11 \\times 19 11×19,参数量为 16 × 5 × 5 × 32 16 \\times 5 \\times 5 \\times 32 16×5×5×32
  5. 第五层为最大池化层,无参数,池化核大小为 5 × 5 5 \\times 5 5×5,步长为5,得到特征图大小为 2 × 4 2 \\times 4 2×4,无参数;
  6. 第六层仍为批归一化层,并采用RReLu函数激活;
  7. 第七层为Dropout层,经过第二个卷积后,将得到特征图展开,得到特征向量维度为256维,对256的特征向量进行Dropout处理,避免过拟合,采用的失效概率为 p = 0.3 p=0.3 p=0.3
  8. 第八层为输出层,用于得到验证码序列,由于模型为multi-task,因此输出层有4个(根据验证码中字符长度确定),使用softmax激活,参数量为 4 × 256 × 62 4\\times 256 \\times 62 4×256×62

KCapNet模型

KCapNet共由3个卷积层,1个全连接层,4个输出层组成,以下为模型具体细节:

  1. 第一层为卷积层,卷积核大小为 5 × 5 5 \\times 5 5×5,步长为1,通道数为16,输入图片大小为 50 × 200 50 \\times 200 50×200,因此可得到16个大小为 46 × 196 46 \\times 196 46×196的特征图,参数量为 3 × 5 × 5 × 16 3\\times 5 \\times 5 \\times 16 3×5×5×16
  2. 第二层为最大池化层,无参数,池化核大小为 3 × 3 3 \\times 3 3×3,步长为3,得到特征图大小为 15 × 64 15 \\times 64 15×64
  3. 第三层为批归一化层,在归一化结束后,使用RReLu激活函数激活;
  4. 第四层为第二个卷积层,卷积核大小为 3 × 3 3 \\times 3 3×3,通道数为32,可得到大小为 13 × 62 13 \\times 62 13×查看详情

    从零开始搭建深度学习环境(代码片段)

    开始深度学习调参之路不能没有一个趁手的环境安装基础环境我使用的机器环境是:Ubuntu18.04安装显卡驱动1.2.按照cuda所需的版本安装3.sudoubuntu-driversautoinstall安装pipsudoapt-getinstallpython-pip下载anaconda3地址:https://www.anaconda.co... 查看详情

    深度学习基于卷积神经网络的验证码识别(代码片段)

    活动地址:CSDN21天学习挑战赛目录前言了解captcha数据集下载weather_photos数据集采用CPU训练还是GPU训练区别使用CPU训练使用GPU训练支持中文导入数据查看数据量显示部分图片预处理手动设置标签灰度化处理平均值法加权平均法c... 查看详情

    keras深度学习实战(15)——从零开始实现yolo目标检测(代码片段)

    Keras深度学习实战(15)——从零开始实现YOLO目标检测0.前言1.YOLO目标检测模型1.1锚框(anchorboxes)1.2YOLO目标检测模型原理2.从零开始实现YOLO目标检测2.1加载数据集2.2计算锚框尺寸2.3创建训练数据集2.4实现YOLO目标检测模型2.5... 查看详情

    论文笔记旋转图像验证码角度识别-深度学习(代码片段)

    ...法)发挥作用,需要被不断训练。在训练过程的开始,权重是随机初始化的,所以网络会做出随机预测。在训练过程中,网络被输入大量的标记数据,并使用预测误差(预测输出和真实输出之间的误差&... 查看详情

    深度学习--tensorflow(项目)验证码生成与识别(多任务学习)(代码片段)

    目录基础理论一、生成验证码数据集1、生成验证码训练集1-0、判断文件夹是否为空1-1、创建字符集(数字、大小写英文字母)1-2、随机生成验证码(1000个,长度为4)2、生成验证码测试集代码二、获取数据... 查看详情

    python机器学习从零开始选择模型(代码片段)

    目录1.数据分离与验证1.1分离训练数据集和评估数据集1.2K折交叉验证分离1.3弃一交叉验证分离1.4重复随机分离评估数据集与训练数据集2.算法评估2.1分类算法评估2.2回归算法评估总结1.数据分离与验证要知道算法模型对未知的数... 查看详情

    深度学习入门比赛——街景字符识别(代码片段)

    这是比赛的第四阶段,模型的相关训练与验证选好模型之后,需要建立训练集与验证集进行模型的效果验证,保证模型的预测结果正确符合,以及不过拟合训练与验证主要有以下几种方法:交叉验证法交叉验证法的作用就是尝试... 查看详情

    深度学习与爬虫实例教学--深度学习模型构建和训练

    深度学习与爬虫实例教学声明:该教程不会直接贴代码,以免凌乱,你需要先下载项目代码(第一章)并结合来看,教程中会告诉你具体代码放在什么位置,以及作用,用法深度学习实现验证码自动识别,爬虫自动认证防ban我们... 查看详情

    《动手学深度学习》线性回归从零开始(linear-regression-scratch)(代码片段)

    线性回归的从零开始实现前言1.生成数据集2.读取数据3.初始化模型参数4.定义模型5.定义损失函数6.定义优化算法7.训练模型8.小结前言在了解了线性回归的背景知识之后,现在我们可以动手实现它了。尽管强大的深度学习框架... 查看详情

    《动手学深度学习》线性回归从零开始(linear-regression-scratch)(代码片段)

    线性回归的从零开始实现前言1.生成数据集2.读取数据3.初始化模型参数4.定义模型5.定义损失函数6.定义优化算法7.训练模型8.小结前言在了解了线性回归的背景知识之后,现在我们可以动手实现它了。尽管强大的深度学习框架... 查看详情

    字符识别--模型的训练与验证(代码片段)

    一个成熟合格的深度学习训练过程至少具备以下功能:在训练集上训练,并在验证集上进行验证模型可以保存最优的权重,并读取权重记录训练集和验证集的精度,便于调参本节将构建验证集、模型训练和验证、模型保存与加载... 查看详情

    linux下nvidia显卡驱动+cuda+anaconda安装配置全流程(记录深度学习服务器环境从零开始搭建)(代码片段)

    ...安装四结语与后续〇目的以及初始条件本帖旨在记录一次从零开始搭建深度学习Linux服务器所需 查看详情

    基于cnn卷积神经网络的tensorflow+keras深度学习的人脸识别(代码片段)

    ...失值和优化器设置训练检查点设置训练batch设置训练循环开始训 查看详情

    从零开始,搭建一个简单的uvm验证平台(代码片段)

    ...08;一)4.UVM基础-TLM通信机制(二)...还在更新从零搭建一个UVM验证平台:从零开始,搭建一个简单的UVM验证平台(一)从零开始,搭建一个简单的UVM验证平台(二)从零开始,搭建一个... 查看详情

    使用tensorflow深度学习识别验证码

    除了传统的PIL包处理图片,然后用pytessert+OCR识别意外,还可以使用tessorflow训练来识别验证码。此篇代码大部分是转载的,只改了很少地方。代码是运行在linux环境,tessorflow没有支持windows的python2.7。 gen_captcha.py代码。#coding=utf-8f... 查看详情

    keras深度学习实战——卷积神经网络详解与实现(代码片段)

    ...陷1.1构建传统神经网络1.2传统神经网络的缺陷2.使用Python从零开始构建CNN2.1卷积神经网络的基本概念2.2卷积和池化相比全连接网络的优势3.使用Keras构建卷积神经网络3.1CNN使用示例3.2验证CNN输出4.构建CNN模型识别MNIST手写数字4.1任... 查看详情

    字符识别--模型集成(代码片段)

    ...同时这些集成学习方法于具体验证集划分联系密切。由于深度学习模型一般需要较长的训练周期,如果硬件设备不允许,建议采用留出法;如果需要追求精度可以使用交叉验证的方法交叉验证法假设构建10折交叉验证,训练得到1... 查看详情