0x000 前言

最近看到一篇关于 Burp Suite插件的推送
【reCAPTCHA】一款识别图形验证码的Burp Suite插件
仔细看了一下。作者再使用插件需要一个额外的第三方的验证码网站。
对于验证码识别,其实现在用深度学习已经对普通的验证码有了效率码有了非常高的识别率 。
这篇文章主要来编写如何使用tensorflow来编写自己的验证码识别器。

0x001 深度学习基础

由于本文只是简单做一下验证码的介绍 并不会过多深入讲述深度学习。只是简单概括一下 深度学习需要做的事情。总体来讲,深度学习的4个步骤

  • 采样,制作样本文件
  • 根据样本文件类型创建识别模型
  • 对样本文件分为训练样本和测试样本来训练识别模型
  • 保存识别模型和验证
  • 下面按照上面四个历程来尝试 编写自用的验证码模型。
  • 本文大多数代码来自于 腾讯开发者实验室

0x002 采样,制作样本文件

样本文件的来源有2种。

  • 有生产验证码的代码 ,可以自己生成,例如众多的开源软件
  • 人工采集,自行打码(最少最少需要200-300张左右)

为了快速验证结果,先直接使用ImageCaptcha 来生成验证码图案来识别 。
需要安装 captcha 库
sudo pip install captcha

#!/usr/bin/python
        # -*- coding: utf-8 -*

        from captcha.image import ImageCaptcha
        from PIL import Image
        import numpy as np
        import random
        import string

        class generateCaptcha():
            def __init__(self,
                         width = 160,#验证码图片的宽
                         height = 60,#验证码图片的高
                         char_num = 4,#验证码字符个数
                         characters = string.digits + string.ascii_uppercase + string.ascii_lowercase):#验证码组成,数字+大写字母+小写字母
                self.width = width
                self.height = height
                self.char_num = char_num
                self.characters = characters
                self.classes = len(characters)

            def gen_captcha(self,batch_size = 50):
                X = np.zeros([batch_size,self.height,self.width,1])
                img = np.zeros((self.height,self.width),dtype=np.uint8)
                Y = np.zeros([batch_size,self.char_num,self.classes])
                image = ImageCaptcha(width = self.width,height = self.height)

                while True:
                    for i in range(batch_size):
                        captcha_str = ''.join(random.sample(self.characters,self.char_num))
                        img = image.generate_image(captcha_str).convert('L')
                        img = np.array(img.getdata())
                        X[i] = np.reshape(img,[self.height,self.width,1])/255.0
                        for j,ch in enumerate(captcha_str):
                            Y[i,j,self.characters.find(ch)] = 1
                    Y = np.reshape(Y,(batch_size,self.char_num*self.classes))
                    yield X,Y

            def decode_captcha(self,y):
                y = np.reshape(y,(len(y),self.char_num,self.classes))
                return ''.join(self.characters[x] for x in np.argmax(y,axis = 2)[0,:])

            def get_parameter(self):
                return self.width,self.height,self.char_num,self.characters,self.classes

            def gen_test_captcha(self):
                image = ImageCaptcha(width = self.width,height = self.height)
                captcha_str = ''.join(random.sample(self.characters,self.char_num))
                img = image.generate_image(captcha_str)
                img.save(captcha_str + '.jpg')
if __name__ == '__main__':
    g = generateCaptcha()
    g.gen_test_captcha()

保存为 generate_captcha.py
进到该目录 运行 python generate_captcha.py
你会看到该目录下会生成图片文件

自此 样本的工作完成了

0x003 创建识别模型

模型使用了卷积神经网络(CNN)。(CNN是深度学习一个特殊示例,它在计算机视觉有非常重要的影响。) 这里使用了 3 层隐藏层、2 层全连接层,对每层都进行 dropout。

  • dropout是用来防止过拟合
  • 过拟合 简单的理解就是 对于训练模型识别率过高,但是真正的识别率过低。打个比喻就是你考试前背题背的很熟悉,结果考试出的不是你背的,结果很差

  • 层的计算公式

    但是一般我们都是边调整 边测试已达到效率最优

模型代码 :

#!/usr/bin/python
# -*- coding: utf-8 -*

import tensorflow as tf
import math

class captchaModel():
    def __init__(self,
                 width = 160,
                 height = 60,
                 char_num = 4,
                 classes = 62):
        self.width = width
        self.height = height
        self.char_num = char_num
        self.classes = classes

    def conv2d(self,x, W):
        return tf.nn.conv2d(x, W, strides=[1, 1, 1, 1], padding='SAME')

    def max_pool_2x2(self,x):
        return tf.nn.max_pool(x, ksize=[1, 2, 2, 1],
                              strides=[1, 2, 2, 1], padding='SAME')

    def weight_variable(self,shape):
        initial = tf.truncated_normal(shape, stddev=0.1)
        return tf.Variable(initial)

    def bias_variable(self,shape):
        initial = tf.constant(0.1, shape=shape)
        return tf.Variable(initial)

    def create_model(self,x_images,keep_prob):
        #first layer
        w_conv1 = self.weight_variable([5, 5, 1, 32])
        b_conv1 = self.bias_variable([32])
        h_conv1 = tf.nn.relu(tf.nn.bias_add(self.conv2d(x_images, w_conv1), b_conv1))
        h_pool1 = self.max_pool_2x2(h_conv1)
        h_dropout1 = tf.nn.dropout(h_pool1,keep_prob)
        conv_width = math.ceil(self.width/2)
        conv_height = math.ceil(self.height/2)

        #second layer
        w_conv2 = self.weight_variable([5, 5, 32, 64])
        b_conv2 = self.bias_variable([64])
        h_conv2 = tf.nn.relu(tf.nn.bias_add(self.conv2d(h_dropout1, w_conv2), b_conv2))
        h_pool2 = self.max_pool_2x2(h_conv2)
        h_dropout2 = tf.nn.dropout(h_pool2,keep_prob)
        conv_width = math.ceil(conv_width/2)
        conv_height = math.ceil(conv_height/2)

        #third layer
        w_conv3 = self.weight_variable([5, 5, 64, 64])
        b_conv3 = self.bias_variable([64])
        h_conv3 = tf.nn.relu(tf.nn.bias_add(self.conv2d(h_dropout2, w_conv3), b_conv3))
        h_pool3 = self.max_pool_2x2(h_conv3)
        h_dropout3 = tf.nn.dropout(h_pool3,keep_prob)
        conv_width = math.ceil(conv_width/2)
        conv_height = math.ceil(conv_height/2)

        #first fully layer
        conv_width = int(conv_width)
        conv_height = int(conv_height)
        w_fc1 = self.weight_variable([64*conv_width*conv_height,1024])
        b_fc1 = self.bias_variable([1024])
        h_dropout3_flat = tf.reshape(h_dropout3,[-1,64*conv_width*conv_height])
        h_fc1 = tf.nn.relu(tf.nn.bias_add(tf.matmul(h_dropout3_flat, w_fc1), b_fc1))
        h_fc1_drop = tf.nn.dropout(h_fc1, keep_prob)

        #second fully layer
        w_fc2 = self.weight_variable([1024,self.char_num*self.classes])
        b_fc2 = self.bias_variable([self.char_num*self.classes])
        y_conv = tf.add(tf.matmul(h_fc1_drop, w_fc2), b_fc2)

        return y_conv

保存为captcha_model.py

0x004 训练识别模型

有了样本和模型以后 我们开始训练模型

#!/usr/bin/python
import tensorflow as tf
import numpy as np
import string
import generate_captcha
import captcha_model

if __name__ == '__main__':
    captcha = generate_captcha.generateCaptcha()
    width,height,char_num,characters,classes = captcha.get_parameter()

    x = tf.placeholder(tf.float32, [None, height,width,1])
    y_ = tf.placeholder(tf.float32, [None, char_num*classes])
    keep_prob = tf.placeholder(tf.float32)

    model = captcha_model.captchaModel(width,height,char_num,classes)
    y_conv = model.create_model(x,keep_prob)
    cross_entropy = tf.reduce_mean(tf.nn.sigmoid_cross_entropy_with_logits(labels=y_,logits=y_conv))
    train_step = tf.train.AdamOptimizer(1e-4).minimize(cross_entropy)

    predict = tf.reshape(y_conv, [-1,char_num, classes])
    real = tf.reshape(y_,[-1,char_num, classes])
    correct_prediction = tf.equal(tf.argmax(predict,2), tf.argmax(real,2))
    correct_prediction = tf.cast(correct_prediction, tf.float32)
    accuracy = tf.reduce_mean(correct_prediction)

    saver = tf.train.Saver()
    with tf.Session() as sess:
        sess.run(tf.global_variables_initializer())
        step = 0
        while True:
            batch_x,batch_y = next(captcha.gen_captcha(64))
            _,loss = sess.run([train_step,cross_entropy],feed_dict={x: batch_x, y_: batch_y, keep_prob: 0.75})
            print ('step:%d,loss:%f' % (step,loss))
            if step % 100 == 0:
                batch_x_test,batch_y_test = next(captcha.gen_captcha(100))
                acc = sess.run(accuracy, feed_dict={x: batch_x_test, y_: batch_y_test, keep_prob: 1.})
                print ('###############################################step:%d,accuracy:%f' % (step,acc))
                if acc > 0.99:
                    saver.save(sess,"capcha_model.ckpt")
                    break
            step += 1

保存为 train_captcha.py
执行 python train_captcha.py

  • 其中 39行号 acc 代表准确率 此时需要准确率大于99%才保存
    各位执行的时候可以设置成 0.01 先实验一下效果
    等训练完成后 你会看得到 目录下保存了 这几个文件

0x004 验证

验证比较简单 只要加载刚才保存的模型
然后 生成一张图识别即可 。

!/usr/bin/python

from PIL import Image, ImageFilter
import tensorflow as tf
import numpy as np
import string
import sys
import generate_captcha
import captcha_model

if __name__ == '__main__':
    captcha = generate_captcha.generateCaptcha()
    width,height,char_num,characters,classes = captcha.get_parameter()

    gray_image = Image.open(sys.argv[1]).convert('L')
    img = np.array(gray_image.getdata())
    test_x = np.reshape(img,[height,width,1])/255.0
    x = tf.placeholder(tf.float32, [None, height,width,1])
    keep_prob = tf.placeholder(tf.float32)

    model = captcha_model.captchaModel(width,height,char_num,classes)
    y_conv = model.create_model(x,keep_prob)
    predict = tf.argmax(tf.reshape(y_conv, [-1,char_num, classes]),2)
    init_op = tf.global_variables_initializer()
    saver = tf.train.Saver()
    gpu_options = tf.GPUOptions(per_process_gpu_memory_fraction=0.95)
    with tf.Session(config=tf.ConfigProto(log_device_placement=False,gpu_options=gpu_options)) as sess:
        sess.run(init_op)
        saver.restore(sess, "capcha_model.ckpt")
        pre_list =  sess.run(predict,feed_dict={x: [test_x], keep_prob: 1})
        for i in pre_list:
            s = ''
            for j in i:
                s += characters[j]
            print s

保存为 predict_captcha.py
执行 python predict_captcha.py Mlzv.jpg
即可

0x005 总结

以上便是简单的一个验证码识别模型的生成。
下一篇将利用本次生成模型对一些常见的开源系统以及一些线上系统进行验证码的识别测试

点击收藏 | 1 关注 | 0
追加 于 2017年11月30日 15:56

追加 于 2017年12月21日 09:27

追加 于 2017年12月26日 11:14

硬件补充

  • CPU E5
  • 内存 16G
  • SSD 硬盘 256G
  • 显卡 GTX 970 (有钱直接买新 1060,1080)

软件

  • centos 6.5
  • cuda 5.2(记得要按照显卡的型号去比对) 查看地址

训练时间

但是时间并不是特别确定。因为得看你每次生产的验证码的图片是否足够好
可以让他学习到足够的特征
如果觉得算法有问题 可以先调低预测的概率 比如先10% 再 50% 这样子先校验一下算法
是否有效,如果无效,可以通过修改参数,padding图片 修改卷尺层大小等 去提高识别效率
达到比较低的识别率,才开始训练。
我们这种比较比较简单的验证码一般2-3天就准确率比较高的结果 。


登录 后跟帖