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

本文介绍了从零开始搭建深度学习验证码识别模型的过程,包括CNN模型与图像识别、数据集生成、模型搭建和训练,以及模型分析。通过生成EasyCaptcha和Kaptcha两种类型的验证码,使用EasyNet和KCapNet模型进行训练,最终达到高识别准确率。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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

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

验证码示例:
easycaptcha

EasyCaptch项目主页

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

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

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

验证码示例:
kaptchas

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
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值