文档章节

【AI实战】手把手教你实现文字识别模型(入门篇:验证码识别)

雪饼
 雪饼
发布于 05/12 10:52
字数 2870
阅读 1183
收藏 21

文字识别在现实生活中有着非常重要的应用,主要由文字检测、内容识别两个关键步骤组成,在本博客之前的文章中已介绍了文字检测、内容识别的经典模型原理(见文章:大话文本检测经典模型:CTPN大话文本识别经典模型:CRNN),本文主要从实战的角度介绍如何实现文字识别模型。

在之前的文章中,已经介绍过了跟文字识别相关的实战内容:基于MNIST数据集识别手写数字的实战内容(见文章:训练你的第一个AI模型:MNIST手写数字识别模型),这个相对简单。今天再介绍文字识别的另一个经典应用:验证码识别,作为文字识别的实战入门篇。

 

验证码在手机APP、WEB网站中非常普遍,主要是为了防止恶意登录、刷票、灌水、爬虫等异常行为,也可能是为了缓解系统的后台压力(例如在秒杀、抢票时,强制要求输入验证码)。本文主要介绍文本型验证码的识别,文本型验证码由数字、英文大小写字母,甚至中文随机组成,再进行变形扭曲、加干扰线、加背景噪音等操作,主要是为了防止被光学字符识别(OCR)之类的程序自动识别出图片上的文字而失去效果,如下图:

由于存在着比较强的干扰信息,因此,直接使用OCR进行识别,效果很不理想,而通过AI可很好地实现这种复杂信息的识别。目前百度等AI开放平台,也提供了验证码识别的开放接口,但由于验证码可由各APP、网站根据任意自定的规则随机组合生成,因此,这些AI平台的验证码识别开放接口在某些场景下效果很好,在某些场景下可能就失灵了。针对具体的场景,我们通过自己训练验证码识别的AI模型,能很好地解决该场景下的验证码识别问题。

 

下面开始介绍使用Tensorflow构建验证码的识别模型,主要步骤如下:

  • step 1. 获取验证码图片
  • step 2. 图片标注
  • step 3. 训练模型
  • step 4. 模型应用

 

1、获取验证码图片

(1)如果是自己练习的,可直接随机生成验证码图片作为基础数据集。在python里面使用captcha库来快速生成验证码图片,通过pip install captcha进行安装,或者手动下载captcha-0.3-py3-none-any.whl文件进行安装。(注:anaconda无法通过conda install 直接安装captcha,但可使用anaconda里面的pip来安装captcha),核心代码如下:

from captcha.image import ImageCaptcha
import random

# 生成验证码的字符集
CHAR_SET = ['0','1','2','3','4','5','6','7','8','9']
CHAR_SET_LEN = len(CHAR_SET)

# 验证码长度
CAPTCHA_LEN  = 4

for i in range(CHAR_SET_LEN):
    for j in range(CHAR_SET_LEN):
        for k in range(CHAR_SET_LEN):
            for l in range(CHAR_SET_LEN):
                captcha_text = CHAR_SET[i] + CHAR_SET[j] + CHAR_SET[k] + CHAR_SET[l]
                image = ImageCaptcha()
                image.write(captcha_text, '/tmp/mydata/' + captcha_text + '.jpg')

生成的效果如下图

(2)如果是要针对某个网站的验证码进行识别的,则可使用一些工具将对应的验证码下载下来。一般网站登录的界面如下:

其中,通常可直接点击验证码图片,或旁边的“换一张”按钮,更换验证码图片。这时,可使用像“按键精灵”之类的模拟鼠标操作的软件,录制一段脚本,然后在验证码图片处模拟右键鼠标保存图片,再点击验证码图片更换新的验证码,如此反复,即可下载该网站的大量验证码图片,用于训练模型。至于这个下载验证码图片的脚本嘛,为了不教坏大家,此处省略500字,嘿嘿~

 

2、图片标注

如果第1步是自己随机生成验证码图片的,那么在保存图片时,文件名便是该验证码图片的文本内容,无须再进行标注。

如果第1步是下载了某个网站的验证码图片的,那么需要先人工对验证码图片的文本内容进行标注,以方便接下来的模型训练。可通过观察,将验证码图片的文本信息记在文件名中(重命名),通过这种方式进行图片标注,也可以单独记录在文本文件中。

 

3、训练模型

(1)标签one-hot编码

为了能够将验证码图片的文本信息输入到卷积神经网络模型里面去训练,需要将文本信息向量化编码。在这里使用“热独编码”(one-hot),即使用01编码表示文本信息。本项目的验证码文本长度为4位,验证码编码由0至9的数字组成,例如验证码文本信息为“1086”,则one-hot编码时在相应的位置标为1,其余为0,如下图

则“1086”经one-hot编码后变为[0 1 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 1 0 0 0] 。将验证码文本信息进行one-hot编码的核心代码如下:

def text2label(text):
    label = np.zeros(CAPTCHA_LEN * CHAR_SET_LEN)
    for i in range(len(text)):
        idx = i * CHAR_SET_LEN + CHAR_SET.index(text[i])
        label[idx] = 1
    return label

(2)读取图片文件

读取验证码图片、验证码文本内容(保存在文件名中),并编写获取下个批量数据的方法,主要函数如下:


# 获取验证码图片路径及文本内容
def get_image_file_name(img_path):
    img_files = []
    img_labels = []
    for root, dirs, files in os.walk(img_path):
        for file in files:
            if os.path.splitext(file)[1] == '.jpg':
                img_files.append(root+'/'+file)
                img_labels.append(text2label(os.path.splitext(file)[0]))
    return img_files,img_labels

# 批量获取数据
def get_next_batch(img_files,img_labels,batch_size):
    batch_x = np.zeros([batch_size, IMAGE_WIDTH*IMAGE_HEIGHT])
    batch_y = np.zeros([batch_size, CAPTCHA_LEN * CHAR_SET_LEN])

    for i in range(batch_size):
        idx = random.randint(0, len(img_files) - 1)
        file_path = img_files[idx]
        image = cv2.imread(file_path)
        image = cv2.resize(image, (IMAGE_WIDTH, IMAGE_HEIGHT))
        image = image.astype(np.float32)
        image = np.multiply(image, 1.0 / 255.0)
        batch_x[i, :] = image
        batch_y[i, :] = img_labels[idx]

    return batch_x,batch_y

(3)构建CNN模型

由于验证码的识别相对比较简单,借鉴LeNet的网络结构构建CNN模型,由3个卷积层和1个全连接层组成,网络结构图如下:

核心代码如下:

# 图像尺寸
IMAGE_HEIGHT = 60
IMAGE_WIDTH = 160

# 网络相关变量
X = tf.placeholder(tf.float32, [None, IMAGE_HEIGHT * IMAGE_WIDTH])
Y = tf.placeholder(tf.float32, [None, CAPTCHA_LEN * CHAR_SET_LEN])
keep_prob = tf.placeholder(tf.float32)  # dropout

# 验证码 CNN 网络
def crack_captcha_cnn_network (w_alpha=0.01, b_alpha=0.1):
    x = tf.reshape(X, shape=[-1, IMAGE_HEIGHT, IMAGE_WIDTH, 1])

    w_c1 = tf.Variable(w_alpha * tf.random_normal([3, 3, 1, 32]))
    b_c1 = tf.Variable(b_alpha * tf.random_normal([32]))
    conv1 = tf.nn.relu(tf.nn.bias_add(tf.nn.conv2d(x, w_c1, strides=[1, 1, 1, 1], padding='SAME'), b_c1))
    conv1 = tf.nn.max_pool(conv1, ksize=[1, 2, 2, 1], strides=[1, 2, 2, 1], padding='SAME')
    conv1 = tf.nn.dropout(conv1, keep_prob)

    w_c2 = tf.Variable(w_alpha * tf.random_normal([3, 3, 32, 64]))
    b_c2 = tf.Variable(b_alpha * tf.random_normal([64]))
    conv2 = tf.nn.relu(tf.nn.bias_add(tf.nn.conv2d(conv1, w_c2, strides=[1, 1, 1, 1], padding='SAME'), b_c2))
    conv2 = tf.nn.max_pool(conv2, ksize=[1, 2, 2, 1], strides=[1, 2, 2, 1], padding='SAME')
    conv2 = tf.nn.dropout(conv2, keep_prob)

    w_c3 = tf.Variable(w_alpha * tf.random_normal([3, 3, 64, 64]))
    b_c3 = tf.Variable(b_alpha * tf.random_normal([64]))
    conv3 = tf.nn.relu(tf.nn.bias_add(tf.nn.conv2d(conv2, w_c3, strides=[1, 1, 1, 1], padding='SAME'), b_c3))
    conv3 = tf.nn.max_pool(conv3, ksize=[1, 2, 2, 1], strides=[1, 2, 2, 1], padding='SAME')
    conv3 = tf.nn.dropout(conv3, keep_prob)

    w_d = tf.Variable(w_alpha * tf.random_normal([8 * 20 * 64, 1024]))
    b_d = tf.Variable(b_alpha * tf.random_normal([1024]))
    dense = tf.reshape(conv3, [-1, w_d.get_shape().as_list()[0]])
    dense = tf.nn.relu(tf.add(tf.matmul(dense, w_d), b_d))
    dense = tf.nn.dropout(dense, keep_prob)

    w_out = tf.Variable(w_alpha * tf.random_normal([1024, CAPTCHA_LEN * CHAR_SET_LEN]))
    b_out = tf.Variable(b_alpha * tf.random_normal([CAPTCHA_LEN * CHAR_SET_LEN]))
    out = tf.add(tf.matmul(dense, w_out), b_out)
    return out

(4)训练模型

通过设置好模型训练的迭代轮次、批量获取样本数量、学习率等参数,读取验证码图片集,并随机划分出训练集、测试集,再加载本项目的网络模型进行训练,每100步评估一次准确率和保存模型文件。核心代码如下:

# 模型的相关参数
step_cnt = 200000  # 迭代轮数
batch_size = 16  # 批量获取样本数量
learning_rate = 0.0001  # 学习率

# 读取验证码图片集
img_path = '/tmp/mydata/'
img_files, img_labels = get_image_file_name(img_path)

# 划分出训练集、测试集
x_train,x_test,y_train,y_test=train_test_split(img_files,img_labels,test_size=0.2,random_state=33)

# 加载网络结构
output = crack_captcha_cnn_network()

# 损失函数、优化器
loss = tf.reduce_mean(tf.nn.sigmoid_cross_entropy_with_logits(logits=output, labels=Y))
optimizer = tf.train.AdamOptimizer(learning_rate=learning_rate).minimize(loss)

# 评估准确率
predict = tf.reshape(output, [-1, CAPTCHA_LEN, CHAR_SET_LEN])
max_idx_p = tf.argmax(predict, 2)
max_idx_l = tf.argmax(tf.reshape(Y, [-1, CAPTCHA_LEN, CHAR_SET_LEN]), 2)
correct_pred = tf.equal(max_idx_p, max_idx_l)
accuracy = tf.reduce_mean(tf.cast(correct_pred, tf.float32))

with tf.Session() as sess:
    sess.run(tf.global_variables_initializer())
    saver = tf.train.Saver(tf.global_variables(), max_to_keep=5)

for step in range(step_cnt):
    # 训练模型
        batch_x, batch_y = get_next_batch(x_train, y_train,batch_size)
        _, loss_ = sess.run([optimizer, loss], feed_dict={X: batch_x, Y: batch_y, keep_prob: 0.75})
        print('step:',step, 'loss:',loss_)

        # 每100步评估一次准确率
        if step % 100 == 0:
            batch_x_test, batch_y_test = get_next_batch(x_test, y_test,batch_size)
            acc = sess.run(accuracy, feed_dict={X: batch_x_test, Y: batch_y_test, keep_prob: 1.})
            print('step:',step,'acc:',acc)

            # 保存模型
            saver.save(sess, '/tmp/mymodel/crack_captcha.ctpk', global_step=step)

        step += 1

训练的过程如下图所示:

经过一段时间的训练后,评估的准确率可达到99%以上,能非常准确地识别出验证码。

 

4、模型应用

通过加载训练好后的模型文件,即可输入图片进行验证码识别,核心代码如下:

# 加载网络结构
output = crack_captcha_cnn_network()

saver = tf.train.Saver()
with tf.Session() as sess:
    model_path = '/tmp/mymodel/'
    saver.restore(sess, tf.train.latest_checkpoint(model_path))

    output_rate=tf.reshape(output, [-1, CAPTCHA_LEN, CHAR_SET_LEN])
    predict = tf.argmax(output_rate, 2)
    text_list,rate_list = sess.run([predict,output_rate], feed_dict={X: [captcha_image], keep_prob: 1})   # captcha_image 为待识别的验证码图片

    tmptext = text_list[0].tolist()
    text=''
    for i in range(len(tmptext)):
        text = text + CHAR_SET[tmptext[i]]

    print('识别结果:',text)

以上就是文字识别的入门实战内容:验证码图片文本识别。通过本次的学习,可了解简单的文本识别的实现方式。

 

关注本人公众号“大数据与人工智能Lab”(BigdataAILab),然后回复“代码”关键字可获取 完整源代码

 

推荐相关阅读

© 著作权归作者所有

雪饼

雪饼

粉丝 412
博文 61
码字总数 134328
作品 0
广州
私信 提问
加载中

评论(1)

javaxiaoz
javaxiaoz
有意思
【AI实战】手把手教你文字识别(识别篇:LSTM+CTC, CRNN, chineseocr方法)

文字识别是AI的一个重要应用场景,文字识别过程一般由图像输入、预处理、文本检测、文本识别、结果输出等环节组成。 其中,文本检测、文本识别是最核心的环节。文本检测方面,在前面的文章中...

雪饼
07/07
5.5K
8
【AI实战】手把手教你文字识别(检测篇二:AdvancedEAST、PixelLink方法)

自然场景下的文字检测是深度学习的重要应用,在之前的文章中已经介绍过了在简单场景、复杂场景下的文字检测方法,包括MSER+NMS、CTPN、SegLink、EAST等方法,详见文章: 【AI实战】手把手教你...

雪饼
06/24
3.1K
8
【图解AI:动图】各种类型的卷积,你认全了吗?

卷积(convolution)是深度学习中非常有用的计算操作,主要用于提取图像的特征。在近几年来深度学习快速发展的过程中,卷积从标准卷积演变出了反卷积、可分离卷积、分组卷积等各种类型,以适...

雪饼
06/20
364
0
【AI实战】手把手教你深度学习文字识别(文字检测篇:基于MSER, CTPN, SegLink, EAST等方法)

文字检测是文字识别过程中的一个非常重要的环节,文字检测的主要目标是将图片中的文字区域位置检测出来,以便于进行后面的文字识别,只有找到了文本所在区域,才能对其内容进行识别。 文字检...

雪饼
05/27
3.6K
6
【AI实战】训练第一个AI模型:MNIST手写数字识别模型

在上篇文章中,我们已经把AI的基础环境搭建好了(见文章:Ubuntu + conda + tensorflow + GPU + pycharm搭建AI基础环境),接下来将基于tensorflow训练第一个AI模型:MNIST手写数字识别模型。...

雪饼
2018/08/11
3.7K
0

没有更多内容

加载失败,请刷新页面

加载更多

OSChina 周六乱弹 —— 早上儿子问我他是怎么来的

Osc乱弹歌单(2019)请戳(这里) 【今日歌曲】 @凉小生 :#今日歌曲推荐# 少点戾气,愿你和这个世界温柔以待。中岛美嘉的单曲《僕が死のうと思ったのは (曾经我也想过一了百了)》 《僕が死の...

小小编辑
今天
1K
12
Excption与Error包结构,OOM 你遇到过哪些情况,SOF 你遇到过哪些情况

Throwable 是 Java 中所有错误与异常的超类,Throwable 包含两个子类,Error 与 Exception 。用于指示发生了异常情况。 Java 抛出的 Throwable 可以分成三种类型。 被检查异常(checked Exc...

Garphy
今天
16
0
计算机实现原理专题--二进制减法器(二)

在计算机实现原理专题--二进制减法器(一)中说明了基本原理,现准备说明如何来实现。 首先第一步255-b运算相当于对b进行按位取反,因此可将8个非门组成如下图的形式: 由于每次做减法时,我...

FAT_mt
昨天
6
0
好程序员大数据学习路线分享函数+map映射+元祖

好程序员大数据学习路线分享函数+map映射+元祖,大数据各个平台上的语言实现 hadoop 由java实现,2003年至今,三大块:数据处理,数据存储,数据计算 存储: hbase --> 数据成表 处理: hive --> 数...

好程序员官方
昨天
7
0
tabel 中含有复选框的列 数据理解

1、el-ui中实现某一列为复选框 实现多选非常简单: 手动添加一个el-table-column,设type属性为selction即可; 2、@selection-change事件:选项发生勾选状态变化时触发该事件 <el-table @sel...

everthing
昨天
6
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部