多任务验证码识别

时间:2022-04-28
本文章向大家介绍多任务验证码识别,主要内容包括1.网络结构、4.验证结果、5.验证码测试、6.识别结果、参考文献、基本概念、基础应用、原理机制和需要注意的事项等,并结合实例形式分析了其使用技巧,希望通过本文能帮助到大家理解应用这部分内容。

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

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

1.网络结构

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

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

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

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

(1)con - relu - pooling - LRN

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

(2)conv - relu - pool - LRN

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

(3)conv - relu

(4)conv-relu

(5)conv - 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:%.4fn" % (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=%.2fn',%(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

网络运行图

准确度

损失函数