验证码是怎么被机器识别的?Keras+CNN模型验证码识别详解

一、项目简介

  • 本项目将利用Keras建立CNN模型对验证码数字进行识别,展示数字验证码识别的基本流程。
  • 此项目不采用OpenCV工具包的数字识别功能,而是将rgb数字图像转化为gray灰度图,计算图片的像素值进行模型训练和识别。
  • 验证码的训练集和测试集事先已生成,可直接调用。
    部分测试集数据测试集数据
    部分训练集数据训练集数据

二、数据预处理

1、引入第三方工具包
from PIL import Image 
from keras import backend as K
from keras.models import * 
from keras.layers import * 
import tensorflow as tf
import glob,pickle
import numpy as np 
import tensorflow.gfile as gfile 
import matplotlib.pyplot as mp
2、定义超参数和字符集
NUMBER = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9']

CAPTCHA_CHARSET = NUMBER   # 使用数字字符集生成验证码
CAPTCHA_LEN = 4            # 验证码长度
CAPTCHA_HEIGHT = 60        # 验证码高度
CAPTCHA_WIDTH = 160       # 验证码长度

TRAIN_DATA_DIR = './train_data/' # 验证码训练数据集路径
TEST_DATA_DIR = './test_data/'

BATCH_SIZE = 100    # 每个批次训练样本的数量
EPOCHS = 10        # 模型训练的轮数
OPT = 'adam'        # 采用adam算法进行模型优化
LOSS = 'binary_crossentropy' # 采用二进制交叉熵损失函数,向量的各分量相互独立

# 模型文件存储路径和文件格式
MODEL_DIR = './model/train_demo/'
MODEL_FORMAT = '.h5'
# 训练记录文件存储路径和文件格式
HISTORY_DIR = './history/train_demo/'
HISTORY_FORMAT = '.history'

# 训练日志内容格式
filename_str = "{} captcha_{}_{}_bs_{}_epochs_{}{}"

# # 模型网络结构文件
# MODEL_VIS_FILE = 'captcha_classification.png'
# 模型文件
MODEL_FILE = filename_str.format(MODEL_DIR, OPT, LOSS, str(BATCH_SIZE),
                                str(EPOCHS),MODEL_FORMAT)
# 训练记录文件
HISTORY_FILE = filename_str.format(HISTORY_DIR, OPT, LOSS, str(BATCH_SIZE),
                                str(EPOCHS),HISTORY_FORMAT)
3、将RGB图像转化为灰度图
def rgb2gray(image):
    return np.dot(image[...,:3], [0.299,0.587,0.114])
4、对验证码进行one-hot编码
def text2vec(text, length=CAPTCHA_LEN, charset=CAPTCHA_CHARSET):
    text_len = len(text)
    # 验证码长度校验
    if text_len != length:
        raise ValueError(
            "输入字符长度为{},与所需验证码长度{}不相符".format(text_len,length))
    vec = np.zeros(length*len(charset))
    for i in range(length):
        vec[charset.index(text[i])+i*len(charset)] = 1
    return vec
5、将验证码向量解码为对应字符
def vec2text(vector):
    if not isinstance(vector, np.ndarray):
        vector = np.asarray(vector)
    vector = np.reshape(vector, [CAPTCHA_LEN, -1])
    text = ''
    for item in vector:
        text += CAPTCHA_CHARSET[np.argmax(item)]
    return text
6、适配Keras图像数据格式通道
def fit_keras_channels(batch, rows=CAPTCHA_HEIGHT, cols=CAPTCHA_WIDTH):
    if K.image_data_format() == 'channel first':
        batch = batch.reshape(batch.shape[0],1,rows,cols)
        input_shape = (1,rows,cols)
    else:
        batch = batch.reshape(batch.shape[0],rows,cols,1)
        input_shape = (rows,cols,1)
    return batch,input_shape
7、读取训练集数据
X_train, Y_train = [],[]
# glob.glob遍历读取'.png'文件
for filename in glob.glob(TRAIN_DATA_DIR + '*.png'):
    X_train.append(np.array(Image.open(filename)))
    Y_train.append(filename.lstrip(TRAIN_DATA_DIR+'\\').rstrip('.png'))
8、处理训练集图像
# 将X_train格式转为rgb的np.float32型的numpy数组格式
X_train = np.array(X_train, dtype=np.float32)
# 将数据由rgb图转为gray灰度图
X_train = rgb2gray(X_train)
# 数据归一化
X_train = X_train / 255
# 适配Keras数据通道
X_train, input_shape = fit_keras_channels(X_train)

print(X_train.shape, type(X_train))
print(input_shape)
9、处理训练集标签
Y_train = list(Y_train)
for i in range(len(Y_train)):
#     print(Y_train[i])
    Y_train[i] = text2vec(Y_train[i])
Y_train = np.asarray(Y_train)

print(Y_train.shape, type(Y_train))
10、读取测试集数据,处理图像和标签
X_test,Y_test = [],[]
# 读取测试集数据
for filename in glob.glob(TEST_DATA_DIR + '*.png'):
    X_test.append(np.array(Image.open(filename)))
    Y_test.append(filename.lstrip(TEST_DATA_DIR+'\\').rstrip('.png'))
# 处理图像
X_test = np.array(X_test, dtype=np.float32)
X_test = rgb2gray(X_test) / 255
X_test,_ = fit_keras_channels(X_test)
# 处理标签
Y_test = list(Y_test)
for i in range(len(Y_test)):
    Y_test[i] = text2vec(Y_test[i])
Y_test = np.asarray(Y_test)

print(X_test.shape)
print(Y_test.shape)

三、CNN验证码识别模型

1、创建CNN模型
  • 构建三层卷积层,后加4个并列的全连接层
# 创建输入层
with tf.name_scope('inputs'):
    inputs = Input(shape=input_shape, name='inputs')
#     inputs = Input(shape=input_shape, name='inputs')

# 第一层卷积
with tf.name_scope('conv1'):
    conv1 = Conv2D(32, (3,3), name='conv1')(inputs)
    relu1 = Activation('relu', name='relu1')(conv1)

# 第二层卷积
with tf.name_scope('conv2'):
    conv2 = Conv2D(32, (3,3), name='conv2')(relu1)
    relu2 = Activation('relu', name='relu2')(conv2)
    pool2 = MaxPooling2D(pool_size=(2,2), padding='same', name='pool2')(relu2)

# 第三层卷积
with tf.name_scope('conv3'):
    conv3 = Conv2D(64, (3,3), name='conv3')(pool2)
    relu3 = Activation('relu', name='relu3')(conv3)
    pool3 = MaxPooling2D(pool_size=(2,2), padding='same', name='pool3')(relu3)

# 全连接层
with tf.name_scope('dense'):
    # 将池化后的数据摊平后输入全连接网络
    x = Flatten()(pool3)
    # Dropout
    x = Dropout(0.25)(x)
    # 创建4个全连接层,区分10类,分别识别4个字符
    x = [Dense(10, activation='softmax', name='func%d'%(i+1))(x) for i in range(4)]

# 输出层
with tf.name_scope('outputs'):
    # 将生成的4个字符拼接输出
    outs = Concatenate()(x)
# 定义模型的输入和输出    
model = Model(inputs=inputs, outputs=outs)
model.compile(optimizer=OPT, loss=LOSS, metrics=['accuracy'])
2、模型摘要信息
  • 观察模型各层的基本结构(下图展示模型训练所需的基本参数信息图示)
    模型摘要信息
3、训练模型
  • 为使模型训练简化,此处选择训练10轮
# 模型训练的过程函数赋值给history
history = model.fit(X_train,Y_train,
                   batch_size=BATCH_SIZE,
                   epochs=EPOCHS,verbose=2,
                   validation_data=(X_test,Y_test))

10轮模型训练效果

  • 经过10轮训练,模型在训练集上的loss值为0.0616,准确率为0.9792;
  • 测试集的预测损失值为0.2336,预测准确度为0.9245
4、预测样例
  • 此项目自动生成的验证码利用了反图灵测试,目的就是降低识别的准确率。
  • 同时受限于训练集的大小和训练的批次,实际预测中,4个字符都能识别正确的概率还是很低。
print(vec2text(Y_test[22]))
  • 真实值:0260
yy = model.predict(X_test[22].reshape(1, 60, 160, 1))
print(vec2text(yy))
  • 预测值:1260
5、保存模型
if not gfile.Exists(MODEL_DIR):
    gfile.MakeDirs(MODEL_DIR)

model.save(MODEL_FILE)
print(MODEL_FILE)
  • ./model/train_demo/ captcha_adam_binary_crossentropy_bs_100_epochs_10.h5
6、保存模型历史记录
if not gfile.Exists(HISTORY_DIR):
    gfile.MakeDirs(HISTORY_DIR)

with open(HISTORY_FILE, 'wb') as f:
    pickle.dump(history.history, f)
print(HISTORY_FILE)
  • ./history/train_demo/ captcha_adam_binary_crossentropy_bs_100_epochs_10.history

四、模型的分析评估

  • 对模型进行一次100轮的训练,将训练数据记录保存到history_file,进行可视化分析。
  • 训练方式与10轮相同,此处不再打印过程日志。
1、加载训练过程记录
history_file = './pre-trained/history/optimizer/binary_ce/captcha_adam_binary_crossentropy_bs_100_epochs_100.history'
# 加载模型记录数据
with open(history_file, 'rb') as f:
    history = pickle.load(f)
2、可视化训练过程数据
mp.figure()
# 绘制模型的准确率Accuracy曲线
mp.subplot(2,1,1)
mp.xlabel('epoch')
mp.ylabel('accuracy')
mp.title('Model accuracy')
mp.plot(history['acc'])
mp.plot(history['val_acc'])
mp.legend(['train','test'], loc='lower right')

# 绘制模型的损失值Loss曲线
mp.subplot(2,1,2)
mp.xlabel('epoch')
mp.ylabel('loss')
mp.title('Model Loss')
mp.plot(history['loss'])
mp.plot(history['val_loss'])
mp.legend(['train','test'], loc='lower right')
mp.tight_layout()

mp.show()

训练集、测试集参数变化曲线

小结
  • 训练集的准确率accuracy和损失值loss表现都非常好;而测试集上的accuracy和loss表现非常糟糕,说明模型在10轮训练集后出现了过拟合现象。实际应用选择10轮训练的模型即可。

github源代码地址:
https://github.com/Willsgao/Personal-projects/tree/master/验证码识别项目

  • 0
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值