本文主要介绍CNN卷积神经网络 与验证码之间的联系 我们可以应用CNN自动提取验证码之间的内容 下面我来主要介绍:
数据集样式 前面为标签 后面为.jpg .
此代码应用到的数据集地址:https://download.csdn.net/download/duyibo123/12755468,有兴趣的可以自行下载。
先倒入的相关的包 以及简单的设置了几个参数
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time : 2020/8/25 15:01
# @Author : DYB
# @Site :
# @File : 11-1-vcode练习.py
# @Software: PyCharm
import tensorflow as tf
import random
import os
import numpy as np
from PIL import Image
tf.set_random_seed(777)#设置随机种子
#获取数据集
train_num=1000
test_num=100
#设置压缩之后的图片的高和宽
IMG_HEIGHT=60# 高
IMG_WIDTH=160#宽
char_num=4 #四个验证码
characters=range(10) #十个
labellen=char_num*len(characters)#40
此函数为返回验证码的数字
拿 1327作为例子 进行了循环 运用枚举的方法 第一次 i=0 num=1 责idx=1所以第一个位置为1 所以为1 第二次 i=1,num=3那么循环里面为13 则 idx=13 即第十三个位置为1 十个一循环所以为3 依次类推即可以推算出 1327 即所有的数字都可以这样推算出来。
def label2vec(label):
'''
将验证码的标签转为40维的向量
param label: 1327
0123
[0,1,0,0,0,0,0,0,0,0,
0,0,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,1,0,0]
'''
label_vec=np.zeros(char_num*len(characters))#生成一个一维的一行40列的矩阵
for i,num in enumerate(label):#通过循环枚举的方式 把验证码循环导出 1: i为0 num为 1 2: i为1 num为3
idx= i *len(characters)+int(num)
label_vec[idx]=1
return label_vec
进行图片恢复处理
此函数为图片的灰度处理,可以不尽兴处理但是运行起来彩色图片相对慢一些 所以建议转换为灰色图片
如果通道数大于2 则需要转换 0,1,2分别为第一层,第二层,第三层 因为彩色图片为三通道。然后把数据传入r,g,b.
三个小数自己设定就好,加起来小于等于1即可
def concert2gray(img):
if len(img.shape)>2:
r,g,b= img[:,:,0],img[:,:,1],img[:,:,2]#3通到 层数
gray=0.2989*r +0.5870*g+0.1140*b
return gray
else:
return img
此函数为获取图片的路径,返回的为样本的特征和标签
先创建了两个存放的列表 然后进行循环 此循环用os定位图片地址 用Image库打开 再进行了逐一灰度处理,进行归一化 方便数据计算
然后把图片的分辨率转化为统一格式 此数据因为图片分辨率统一,所以可有可无.再然后定位图片,再进行切割把前面的特征切割出来。
最后把特征数值逐一取出添加到列表中 最后返回一个二维数组
def get_all_files(file_path,num):#获取图片路径及其标签
image_list=[]
label_list=[]
i=0
for item in os.listdir(file_path):
item_path=file_path+'\\'+item #获取图片的路径
image=Image.open(item_path) #打开图片的路径 得到每一张图片
image=concert2gray(np.array(image))# 进行灰度处理
image_array=np.array(image)/255#归一化
image_list.append(image_array.reshape(IMG_HEIGHT,IMG_WIDTH,1))#把图片的分辨率改变 然后加入到image_list 列表中
label=os.path.splitext(item)[0] #标签 寻找路径然后 切割 前面为标签
label_list.append(label2vec(label)) #把标签上的特征数值传入到列表中
i+=1
if i>=num:# 如果 超过数值 退出循环
break
return np.array(image_list),np.array(label_list)# 返回图片 标签
通过之前的函数把训练集,测试集的特征,标签 运行出来
#数据集地址
img_dir = r'E:\第十一个月 深度一\第十一个月 深度一\Protect\8 25\train'
test_dir=r'E:\第十一个月 深度一\第十一个月 深度一\Protect\8 25\test'
#返回训练集 测试集的标签特征
imgArr,Y_one_hot,=get_all_files(img_dir,train_num)
imgArrtest,Y_test=get_all_files(test_dir,test_num)
print(imgArr.shape,len(Y_one_hot))
next_batch函数 每次返回一批数据的X,Y
定义全局变量g_b ,然后每次都加长度为size
g_b=0
#每次返回一批数据的 x y
def next_batch(size):
global g_b
xb=imgArr[g_b:g_b+size]
yb=Y_one_hot[g_b:g_b+size]
g_b=g_b+size
return xb,yb
后续需要的参数值
learning_rate=0.001
training_epochs=100
batch_size=100
卷积操作的基本流程
定义占位符->两层卷积->维度转换 转换成一维->全联接层->softmax层
#定义占位符
X=tf.placeholder('float',shape=[None,IMG_HEIGHT,IMG_WIDTH,1])
Y=tf.placeholder('float',shape=[None,labellen])
#第一层卷积
W1=tf.Variable(tf.random_normal([3,3,1,32]))
L1=tf.nn.conv2d(X,W1,strides=[1,1,1,1],padding='SAME')
L1=tf.nn.relu(L1)#激活函数
L1=tf.nn.max_pool(L1,ksize=[1,2,2,1],strides=[1,2,2,1],padding='SAME')#最大池化
#第二层卷积
W2=tf.Variable(tf.random_normal([3,3,32,64]))
L2=tf.nn.conv2d(L1,W2,strides=[1,1,1,1],padding='SAME')
L2=tf.nn.relu(L2)
L2=tf.nn.max_pool(L2,ksize=[1,2,2,1],strides=[1,2,2,1],padding='SAME')
#转换为一维数组
dim=L2.get_shape()[1].value * L2.get_shape()[2].value *L2.get_shape()[3].value
L2_flg=tf.reshape(L2,[-1,dim])
print( L2.get_shape())
#全联接层
W3=tf.get_variable(name='w3',shape=[dim,1024],dtype=tf.float32, initializer=tf.contrib.layers.xavier_initializer())
b3=tf.Variable(tf.random_normal([1024]))
L3=tf.nn.relu(tf.matmul(L2_flg,W3)+b3)
#softmax层
W4=tf.get_variable('w4',shape=[1024,labellen],dtype=tf.float32,initializer=tf.contrib.layers.xavier_initializer())
b4=tf.Variable(tf.random_normal([labellen]))
logtis=tf.matmul(L3,W4)+b4
代价函数和优化器
为什么这里不用softmax_cros:
sigmod_cross适用于每个类别相互独立但不互斥,如图中可以有字母和数字
softmax_cross适用于每个类别独立且排斥的情况,如数字和字母不可以同时出现
cost=tf.reduce_mean(tf.nn.sigmoid_cross_entropy_with_logits(logits=logtis,labels=Y))
optimzer=tf.train.AdamOptimizer(learning_rate=learning_rate).minimize(cost)
准确率计算
首先把预测模型的维度转换成 4*10的矩阵 方便进行一一比较
预测值的最大值下标 这里好多人问为什么是2 因为括号里是对predict 进行操作 2代表的是10的位置 即是列的意思 按照列逐一进行比较 必须满足四个数字每个数字的位置都相同 才能算对, 如果仅仅是四个数字一样但是位置不同,也是不对的。所以这里是对列进行操作比较。
再就是进行预测值跟真实值比较
最后计算准确率 求平均转换为float类型
predict=tf.reshape(logtis,[-1,4,10])# 预测数据转换维度 四行十列
max_idx_p = tf.argmax(predict,2) #
max_idx_y = tf.argmax(tf.reshape(Y,[-1,4,10]),2)
correct_pred = tf.equal(max_idx_p,max_idx_y)
accuracy= tf.reduce_mean(tf.cast(correct_pred,tf.float32))
创建会话,进行数据传递操作
双层循环: 外部循环为循环周期 意思就是 整体数据循环的次数 咱们定义的周期数为100次,然后定义平均代价avg_cost,循环批次total_batch,全局变量g_b,特别注意:这里的g_b一定要进行初始化为0,因为内部循环循环完一次 再进行外部循环,整体的数据要初始化。否则运行结果会错误。
内部循环:用next_batch函数把数据倒入 ,进行数据传入 得出数据计算每个周期的平均代价
每十个周期循环一次 输出代价跟准确率
#创建会话
with tf.Session() as sess:
sess.run(tf.global_variables_initializer())
print('开始学习')
for epoch in range(training_epochs):#循环周期
avg_cost=0
g_b=0
total_batch=int(train_num/batch_size)#批次
for i in range(total_batch):#循环多少次
batch_x,batch_y=next_batch(batch_size)
c_,_=sess.run([cost,optimzer],feed_dict={X:batch_x,Y:batch_y})
avg_cost+=c_/total_batch
if epoch %10 ==0:#十次输出一次
print('Epoch:',(epoch+1),'cost=',avg_cost,'acc',sess.run(accuracy,feed_dict={X:imgArrtest,Y:Y_test}))
print('学习完成')
测试模型的准确率
传入测试集的数据
#测试模型检查准确率
print('正确率',sess.run(accuracy,feed_dict={X:imgArrtest,Y:Y_test}))
在测试集中随机抽取一个样本进行测试
首先规定r的取值范围,再用真实测试集的Y值取到r 然后维度转换成4*10的数据 进行最大下标输出 最后用预测模型来查看的预测数据,把测试集的X传入。最后 结束。
#在测试集中随机抽一个样本进行测试
r=int(random.randint(0,test_num-1))
print('标签:',sess.run(tf.argmax(Y_test[r:r+1].reshape(4,10),1)))
per=sess.run([tf.argmax(tf.reshape(logtis,[4,10]),1)],feed_dict={X:imgArrtest[r:r+1]})
print('预测',per)
最后运行结果
建议大家运行用GPU,因为cpu运行一次需要时间太长,效率太低。
正确率有点低 大家可以尝试更换优化器 或者激活函数进行改进。
再简单的介绍一下整体的流程以及代码:整体思路其实就是三步走:第一步就是数据的处理 。第二步就是卷积操作。第三步就是创建会话完成会话的各项内容。整体内部流程思路大概就是这样,其中有好多的小细节需要大家注意。不然出一点错都不好找。