多任务验证码识别

Posted 瓜大三哥

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了多任务验证码识别相关的知识,希望对你有一定的参考价值。

使用Alexnet网络进行训练,多任务学习:验证码是根据随机字符生成一幅图片,然后在图片中加入干扰象素,用户必须手动填入,防止有人利用机器人自动批量注册、灌水、发垃圾广告等等

Tensorflow是目前最流行的深度学习框架,我们可以用它来搭建自己的卷积神经网络并训练自己的分类器,本文介绍怎样使用Tensorflow构建自己的CNN,怎样训练用于简单的验证码识别的分类器。

1.网络结构

2012年,Imagenet比赛冠军的model——Alexnet [2](以第一作者alex命名)。模型结构见下图,别看只有寥寥八层(不算input层),但是它有60M以上的参数总量,事实上在参数量上比后面的网络都大。

多任务验证码识别

这个图有点点特殊的地方是卷积部分都是画成上下两块,意思是说吧这一层计算出来的feature map分开,但是前一层用到的数据要看连接的虚线,如图中input层之后的第一层第二层之间的虚线是分开的,是说二层上面的128map是由一层上面的48map计算的,下面同理;而第三层前面的虚线是完全交叉的,就是说每一个192map都是由前面的128+128=256map同时计算得到的。

多任务验证码识别

前后几层(对应位置的点)对中间这一层做一下平滑约束,计算方法是:

多任务验证码识别

具体打开Alexnet的每一阶段(含一次卷积主要计算)来看:

1con - relu - pooling - LRN 

多任务验证码识别

验证具体计算都在图里面写了,要注意的是input层是227*227,而不是paper里面的224*224,这里可以算一下,主要是227可以整除后面的conv1计算,224不整除。如果一定要用224可以通过自动补边实现,不过在input就补边感觉没有意义,补得也是0

2conv - relu - pool - LRN

多任务验证码识别

和上面基本一样,唯独需要注意的是group=2,这个属性强行把前面结果的feature map分开,卷积部分分成两部分做。

3conv - relu

多任务验证码识别

4conv-relu

多任务验证码识别

5conv - relu - pool

多任务验证码识别

(6)fc - relu - dropout

多任务验证码识别

这里有一层特殊的dropout层,在alexnet中是说在训练的以1/2概率使得隐藏层的某些neuron的输出为0,这样就丢到了一半节点的输出,BP的时候也不更新这些节点。 

(7) fc - relu - dropout 

多任务验证码识别



(8)fc - softmax 

多任务验证码识别

多任务主要体验在去掉了这个全连接层,然后分别使用验证码的个位、十位、百位和千位进行训练

 net0 = slim.conv2d(net, num_classes, [1, 1],

                          activation_fn=None,

                          normalizer_fn=None,

                          biases_initializer=tf.zeros_initializer(),

                          scope='fc8_0')

        net1 = slim.conv2d(net, num_classes, [1, 1],

                          activation_fn=None,

                          normalizer_fn=None,

                          biases_initializer=tf.zeros_initializer(),

                          scope='fc8_1')

        net2 = slim.conv2d(net, num_classes, [1, 1],

                          activation_fn=None,

                          normalizer_fn=None,

                          biases_initializer=tf.zeros_initializer(),

                          scope='fc8_2')

        net3 = slim.conv2d(net, num_classes, [1, 1],

                          activation_fn=None,

                          normalizer_fn=None,

                          biases_initializer=tf.zeros_initializer(),

                          scope='fc8_3')

 

2.验证码生成

from captcha.image import ImageCaptcha  # pip install captcha

import numpy as np

from PIL import Image

import random

import sys

 

number = ['0','1','2','3','4','5','6','7','8','9']

 

def random_captcha_text(char_set=number, captcha_size=4):

    # 验证码列表

    captcha_text = []

    for i in range(captcha_size):

        #随机选择

        c = random.choice(char_set)

        #加入验证码列表

        captcha_text.append(c)

    return captcha_text

 

# 生成字符对应的验证码

def gen_captcha_text_and_image():

    image = ImageCaptcha()

    #获得随机生成的验证码

    captcha_text = random_captcha_text()

    #把验证码列表转为字符串

    captcha_text = ''.join(captcha_text)

    #生成验证码

    captcha = image.generate(captcha_text)

    image.write(captcha_text, 'D:/ten/images/' + captcha_text + '.jpg')  # 写到文件

 

#数量少于10000,因为重名

num = 10000

if __name__ == '__main__':

    for i in range(num):

        gen_captcha_text_and_image()

        sys.stdout.write('\r>> Creating image %d/%d' % (i+1, num))

        sys.stdout.flush()

    sys.stdout.write('\n')

    sys.stdout.flush()

                        

    print("生成完毕")

3.验证码识别

import os

import tensorflow as tf

from PIL import Image

from nets import nets_factory

import numpy as np

# 不同字符数量

CHAR_SET_LEN = 10

# 图片高度

IMAGE_HEIGHT = 60 

# 图片宽度

IMAGE_WIDTH = 160  

# 批次

BATCH_SIZE = 25

filename='captcha_test_iter.txt'

filepath='d:/ten/'

file=open(filepath+filename,'w')

# tfrecord文件存放路径

TFRECORD_FILE="D:/ten/train.tfrecords"

# placeholder

x = tf.placeholder(tf.float32, [None, 224, 224])  

y0 = tf.placeholder(tf.float32, [None]) 

y1 = tf.placeholder(tf.float32, [None]) 

y2 = tf.placeholder(tf.float32, [None]) 

y3 = tf.placeholder(tf.float32, [None])

 

# 学习率

lr = tf.Variable(0.003, dtype=tf.float32)

 

# 从tfrecord读出数据

def read_and_decode(filename):

    # 根据文件名生成一个队列

    filename_queue = tf.train.string_input_producer([filename])

    reader = tf.TFRecordReader()

    # 返回文件名和文件

    _, serialized_example = reader.read(filename_queue)   

    features = tf.parse_single_example(serialized_example,

                                       features={

                                           'image' : tf.FixedLenFeature([], tf.string),

                                           'label0': tf.FixedLenFeature([], tf.int64),

                                           'label1': tf.FixedLenFeature([], tf.int64),

                                           'label2': tf.FixedLenFeature([], tf.int64),

                                           'label3': tf.FixedLenFeature([], tf.int64),

                                       })

    # 获取图片数据

    image = tf.decode_raw(features['image'], tf.uint8)

    # tf.train.shuffle_batch必须确定shape

    image = tf.reshape(image, [224, 224])

    # 图片预处理

    image = tf.cast(image, tf.float32) / 255.0

    image = tf.subtract(image, 0.5)

    image = tf.multiply(image, 2.0)

    # 获取label

    label0 = tf.cast(features['label0'], tf.int32)

    label1 = tf.cast(features['label1'], tf.int32)

    label2 = tf.cast(features['label2'], tf.int32)

    label3 = tf.cast(features['label3'], tf.int32)

 

    return image, label0, label1, label2, label3

 

 

# In[3]:

 

# 获取图片数据和标签

image, label0, label1, label2, label3 = read_and_decode(TFRECORD_FILE)

 

#使用shuffle_batch可以随机打乱

image_batch, label_batch0, label_batch1, label_batch2, label_batch3 = tf.train.shuffle_batch(

        [image, label0, label1, label2, label3], batch_size = BATCH_SIZE,

        capacity = 50000, min_after_dequeue=10000, num_threads=1)

 

#定义网络结构

train_network_fn = nets_factory.get_network_fn(

    'alexnet_v2',

    num_classes=CHAR_SET_LEN,

    weight_decay=0.0005,

    is_training=True)

 

    

with tf.Session() as sess:

    # inputs: a tensor of size [batch_size, height, width, channels]

    X = tf.reshape(x, [BATCH_SIZE, 224, 224, 1])

    # 数据输入网络得到输出值

    logits0,logits1,logits2,logits3,end_points = train_network_fn(X)

    

    # 把标签转成one_hot的形式

    one_hot_labels0 = tf.one_hot(indices=tf.cast(y0, tf.int32), depth=CHAR_SET_LEN)

    one_hot_labels1 = tf.one_hot(indices=tf.cast(y1, tf.int32), depth=CHAR_SET_LEN)

    one_hot_labels2 = tf.one_hot(indices=tf.cast(y2, tf.int32), depth=CHAR_SET_LEN)

    one_hot_labels3 = tf.one_hot(indices=tf.cast(y3, tf.int32), depth=CHAR_SET_LEN)

    

    # 计算loss

    loss0 = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(logits=logits0,labels=one_hot_labels0)) 

    loss1 = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(logits=logits1,labels=one_hot_labels1)) 

    loss2 = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(logits=logits2,labels=one_hot_labels2)) 

    loss3 = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(logits=logits3,labels=one_hot_labels3)) 

    # 计算总的loss

    total_loss = (loss0+loss1+loss2+loss3)/4.0

    # 优化total_loss

    optimizer = tf.train.AdamOptimizer(learning_rate=lr).minimize(total_loss) 

    

    # 计算准确率

    correct_prediction0 = tf.equal(tf.argmax(one_hot_labels0,1),tf.argmax(logits0,1))

    accuracy0 = tf.reduce_mean(tf.cast(correct_prediction0,tf.float32))

    

    correct_prediction1 = tf.equal(tf.argmax(one_hot_labels1,1),tf.argmax(logits1,1))

    accuracy1 = tf.reduce_mean(tf.cast(correct_prediction1,tf.float32))

    

    correct_prediction2 = tf.equal(tf.argmax(one_hot_labels2,1),tf.argmax(logits2,1))

    accuracy2 = tf.reduce_mean(tf.cast(correct_prediction2,tf.float32))

    

    correct_prediction3 = tf.equal(tf.argmax(one_hot_labels3,1),tf.argmax(logits3,1))

    accuracy3 = tf.reduce_mean(tf.cast(correct_prediction3,tf.float32)) 

    

    # 用于保存模型

    saver = tf.train.Saver()

    # 初始化

    sess.run(tf.global_variables_initializer())

    

    # 创建一个协调器,管理线程

    coord = tf.train.Coordinator()

    # 启动QueueRunner, 此时文件名队列已经进队

    threads = tf.train.start_queue_runners(sess=sess, coord=coord)

 

    for i in range(6001):

        # 获取一个批次的数据和标签

        b_image, b_label0, b_label1 ,b_label2 ,b_label3 = sess.run([image_batch, label_batch0, label_batch1, label_batch2, label_batch3])

        # 优化模型

        sess.run(optimizer, feed_dict={x: b_image, y0:b_label0, y1: b_label1, y2: b_label2, y3: b_label3})

        

        

 

        # 每迭代20次计算一次loss和准确率  

        if i % 20 == 0:  

            # 每迭代2000次降低一次学习率

            if i%2000 == 0:

                sess.run(tf.assign(lr, lr/3))

            acc0,acc1,acc2,acc3,loss_ = sess.run([accuracy0,accuracy1,accuracy2,accuracy3,total_loss],feed_dict={x: b_image,

                                                                                                                y0: b_label0,

                                                                                                                y1: b_label1,

                                                                                                                y2: b_label2,

                                                                                                                y3: b_label3})      

            learning_rate = sess.run(lr)

            print ("Iter:%d  Loss:%.3f  Accuracy:%.2f  %.2f  %.2f  %.2f  Learning_rate:%.4f" % (i,loss_,acc0,acc1,acc2,acc3,learning_rate))

            file.write("Iter:%d  Loss:%.3f  Accuracy:%.2f  %.2f  %.2f  %.2f  Learning_rate:%.4f\n" % (i,loss_,acc0,acc1,acc2,acc3,learning_rate))

            if acc0>0.9 and acc1 >0.9 and acc2>0.9 and acc3>0.9 or i==6000:

                saver.save(sess, "D:/ten/model/crack_captcha.model", global_step=i)

                file.close()

                break 

                

    # 通知其他线程关闭

    coord.request_stop()

    # 其他所有线程关闭之后,这一函数才能返回

    coord.join(threads)

4.验证结果

多任务验证码识别


5.验证码测试

import os

import tensorflow as tf

from PIL import Image

from nets import nets_factory

import numpy as np

import matplotlib.pyplot as plt  

# 不同字符数量

CHAR_SET_LEN = 10

# 图片高度

IMAGE_HEIGHT = 60 

# 图片宽度

IMAGE_WIDTH = 160  

# 批次

BATCH_SIZE = 1

# tfrecord文件存放路径

TFRECORD_FILE = "D:/ten/test.tfrecords"

 

#测试数据个数

N_TEST=200

#错误数据个数

 

 

# placeholder

x = tf.placeholder(tf.float32, [None, 224, 224])  

 

# 从tfrecord读出数据

def read_and_decode(filename):

    # 根据文件名生成一个队列

    filename_queue = tf.train.string_input_producer([filename])

    reader = tf.TFRecordReader()

    # 返回文件名和文件

    _, serialized_example = reader.read(filename_queue)   

    features = tf.parse_single_example(serialized_example,

                                       features={

                                           'image' : tf.FixedLenFeature([], tf.string),

                                           'label0': tf.FixedLenFeature([], tf.int64),

                                           'label1': tf.FixedLenFeature([], tf.int64),

                                           'label2': tf.FixedLenFeature([], tf.int64),

                                           'label3': tf.FixedLenFeature([], tf.int64),

                                       })

    # 获取图片数据

    image = tf.decode_raw(features['image'], tf.uint8)

    # 没有经过预处理的灰度图

    image_raw = tf.reshape(image, [224, 224])

    # tf.train.shuffle_batch必须确定shape

    image = tf.reshape(image, [224, 224])

    # 图片预处理

    image = tf.cast(image, tf.float32) / 255.0

    image = tf.subtract(image, 0.5)

    image = tf.multiply(image, 2.0)

    # 获取label

    label0 = tf.cast(features['label0'], tf.int32)

    label1 = tf.cast(features['label1'], tf.int32)

    label2 = tf.cast(features['label2'], tf.int32)

    label3 = tf.cast(features['label3'], tf.int32)

 

    return image, image_raw, label0, label1, label2, label3

 

 

 

# 获取图片数据和标签

image, image_raw, label0, label1, label2, label3 = read_and_decode(TFRECORD_FILE)

 

#使用shuffle_batch可以随机打乱

image_batch, image_raw_batch, label_batch0, label_batch1, label_batch2, label_batch3 = tf.train.shuffle_batch(

        [image, image_raw, label0, label1, label2, label3], batch_size = BATCH_SIZE,

        capacity = 50000, min_after_dequeue=10000, num_threads=1)

 

#定义网络结构

train_network_fn = nets_factory.get_network_fn(

    'alexnet_v2',

    num_classes=CHAR_SET_LEN,

    weight_decay=0.0005,

    is_training=False)

 

with tf.Session() as sess:

    # inputs: a tensor of size [batch_size, height, width, channels]

    X = tf.reshape(x, [BATCH_SIZE, 224, 224, 1])

    # 数据输入网络得到输出值

    logits0,logits1,logits2,logits3,end_points = train_network_fn(X)

    

    # 预测值

    predict0 = tf.reshape(logits0, [-1, CHAR_SET_LEN])  

    predict0 = tf.argmax(predict0, 1)  

 

    predict1 = tf.reshape(logits1, [-1, CHAR_SET_LEN])  

    predict1 = tf.argmax(predict1, 1)  

 

    predict2 = tf.reshape(logits2, [-1, CHAR_SET_LEN])  

    predict2 = tf.argmax(predict2, 1)  

 

    predict3 = tf.reshape(logits3, [-1, CHAR_SET_LEN])  

    predict3 = tf.argmax(predict3, 1)  

 

    # 初始化

    sess.run(tf.global_variables_initializer())

    # 载入训练好的模型

    saver = tf.train.Saver()

    saver.restore(sess,'D:/ten/model/crack_captcha.model-2320')

 

    # 创建一个协调器,管理线程

    coord = tf.train.Coordinator()

    # 启动QueueRunner, 此时文件名队列已经进队

    threads = tf.train.start_queue_runners(sess=sess, coord=coord)

    for j in range(100,600,100):

        ERROR_COUNT = 0

        for i in range(j):

            # 获取一个批次的数据和标签

            b_image, b_image_raw, b_label0, b_label1 ,b_label2 ,b_label3 = sess.run([image_batch, 

                                                                        image_raw_batch, 

                                                                        label_batch0, 

                                                                        label_batch1, 

                                                                        label_batch2, 

                                                                        label_batch3])

            # 显示图片

            img=Image.fromarray(b_image_raw[0],'L')

            plt.imshow(img)

            plt.axis('off')

            plt.show()

            # 打印标签

            print('label:',b_label0, b_label1 ,b_label2 ,b_label3)

            # 预测

            label0,label1,label2,label3 = sess.run([predict0,predict1,predict2,predict3], feed_dict={x: b_image})

            # 打印预测值

            print('predict:',label0,label1,label2,label3)

            if b_label0!=label0 or b_label1!=label1 or b_label2!=label2 or b_label3!=label3:

                ERROR_COUNT = ERROR_COUNT +1

        accuracy_rate=1-ERROR_COUNT/N_TEST

        print('Test_Number: %d accuracy_rate=%.2f%%\n' % (j,accuracy_rate))

    #     print('accuracy_rate=%.2f\n',%(accuracy_rate))            

        # 通知其他线程关闭

    coord.request_stop()

    # 其他所有线程关闭之后,这一函数才能返回

    coord.join(threads)

6.识别结果

多任务验证码识别Test_Number: 100 accuracy_rate=88.00%

Test_Number: 200 accuracy_rate=76.50%

Test_Number: 300 accuracy_rate=63.00%

Test_Number: 400 accuracy_rate=51.50%

Test_Number: 500 accuracy_rate=37.50%

测试数据个数

准确率

100

88%

200

76.5%

300

63%

400

51.5%

500

37.5%

参考文献

Multi-digit Number Recognition from Street View Imagery using Deep CNN

CAPTCHA Recognition with Active Deep Learning

http://matthewearl.github.io/2016/05/06/cnn-anpr/

http://blog.csdn.net/xbinworld/article/details/45619685

网络运行图

多任务验证码识别

准确度


损失函数


以上是关于多任务验证码识别的主要内容,如果未能解决你的问题,请参考以下文章

验证码识别多输出模型

Python 爬虫高阶

[图像]验证码图片内容识别(付费参加)

01 | 图形验证码的识别

自动化过程中验证码识别方法--纯技术

图片验证码识别算法