思路
1.数据
2.模型
3.数据训练模型
4.验证模型
1。数据
验证码数据需要生成验证码,而python中有相关的模块生成验证码图片
- captcha包用于验证码图生成
- PIL包用于图像处理和展现
来个例子:
from captcha.image import ImageCaptcha
ic=ImageCaptcha()
d=ic.generate('G8iY')
from PIL import Image
Image.open(d).show()
效果如下:
确定验证码范围
import string
char_set=list(string.ascii_letters+string.digits) #英文大小写+数字
char_num=4 #一个验证码图片有多少字符,这里4个
生成验证码
import numpy as np
from captcha.image import ImageCaptcha
from PIL import Image
char2index=dict(zip(char_set,range(len(char_set)))) #字符和顺序号的键值对
def generate_captcha():
single_captcha=np.random.choice(char_set,size=char_num)
single_text=''.join(single_captcha) #连接成char_num个字母为要生成图片的字符串
ic=ImageCaptcha()
captcha=ic.generate(single_text) #已经生成了验证码
a=Image.open(captcha) #把图片转换为数组
s=np.array(a.convert('L')).flatten()/255 #彩色图片转换为灰色,并且归一化数组中的数字,这已经获得了图片数据
#将验证码字符转换向量的形式,向量数字0和1代表有无,char_num个字符的向量拼接成一个长向量,就是一个验证码的向量数据y
u=np.zeros(len(char_set)*char_num)
k=0
for i in single_captcha:
u[char2index[i]+k*len(char_set)]=1
k+=1
return s,u #s是图片数据,u是图片标签
批量生成验证码
def batch_data_gen(batch_size=128): #将batch_size个图片和标签存分别存放在在列表中
batch_x=[]
batch_y=[]
for i in range(batch_size):
ran=generate_captcha()
batch_x.append(ran[0])
batch_y.append(ran[1])
return np.array(batch_x),np.array(batch_y) #数组形式,每个样本是一个行向量
2。定义模型
网络结构:
conv -> relu6 -> max_pool -> conv -> relu6 -> max_pool -> dropout -> conv -> relu6 -> max_pool -> full connection -> full connection
import tensorflow as tf
def cnn_(x,height=60,width=160):
x=tf.reshape(x,shape=[-1,height,width,1]) #将每行行数据转换为图片尺寸的数据,方便做2维窗口卷积,1表示通道数,黑白图片就1个通道
#第一层定义随机参数,然后卷积
w_c1=tf.Variable(tf.random_normal([3,3,1,32],dtype="float64"))
b_c1=tf.Variable(tf.random_normal([32],dtype="float64"))
cv=tf.nn.conv2d(x,w_c1,strides=[1,1,1,1],padding='SAME')
#线性变换加偏执项,得到输出卷积过程的输出
outcv=tf.nn.bias_add(cv,b_c1)
#激活
conv1=tf.nn.relu6(outcv)
#最大池化层
conv1=tf.nn.max_pool(conv1,ksize=[1,2,2,1],strides=[1,2,2,1],padding='SAME')
#第二层定义卷积层参数然后卷积
w_c2=tf.Variable(tf.random_normal([3,3,32,64],dtype="float64"))
b_c2=tf.Variable(tf.random_normal([64],dtype="float64"))
cv2=tf.nn.conv2d(conv1,w_c2,strides=[1,1,1,1],padding='SAME')
#偏执项
outcv2=tf.nn.bias_add(cv2,b_c2)
#激活
conv2=tf.nn.relu6(outcv2)
#最大池化
conv2=tf.nn.max_pool(conv2,[1,2,2,1],strides=[1,2,2,1],padding='SAME')
#dropout
conv2=tf.nn.dropout(conv2,keep_prob=0.5)
#第三层卷积
w_c3=tf.Variable(tf.random_normal([3,3,64,64],dtype="float64"))
b_c3=tf.Variable(tf.random_normal([64],dtype="float64"))
cv3=tf.nn.conv2d(conv2,w_c3,strides=[1,1,1,1],padding='SAME')
#激活
conv3=tf.nn.relu6(tf.nn.bias_add(cv3,b_c3))
#最大池化
conv3=tf.nn.max_pool(conv3,ksize=[1,2,2,1],strides=[1,2,2,1],padding='SAME')
#全连接
w_f1=tf.Variable(tf.random_normal([8*20*64,512],dtype="float64"))
b_f1=tf.Variable(tf.random_normal([512],dtype="float64"))
#注意这里,每个点是一个神经元,2维拉成1维
dense= tf.reshape(conv3, [-1, 8*20*64])
#全连接的参数,设mxn个,m是前面一层的神经元,n是后面一层的神经元,理解成w_f1每列是神经元连接前面一层的神经元参数,
es=tf.add(tf.matmul(dense,w_f1),b_f1)
es=tf.nn.sigmoid(es)
#第二层全连接
w_f2=tf.Variable(tf.random_normal([512,248],dtype="float64"))
b_f2=tf.Variable(tf.random_normal([248],dtype="float64"))
out=tf.add(tf.matmul(es,w_f2),b_f2)
return out
3。数据训练模型流程
def target(x,y):
#引用模型
output=cnn_(x)
#定义损失
loss=tf.reduce_mean(tf.nn.sigmoid_cross_entropy_with_logits(logits=output,labels=y))
#定义优化损失的方法
optimizer=tf.train.AdamOptimizer(learning_rate=0.001).minimize(loss)
#模型评价指标先对输出做处理,使其格式和标签shape一致,方便对比
predict=tf.reshape(output,[-1,4,62])
idx_p=tf.argmax(predict,2)#取行中值最大的一个将与标签对比
idx_l=tf.argmax(tf.reshape(y,[-1,4,62]),2)#将y变量重置回一个字符一个向量的格式
accu=tf.reduce_mean(tf.cast(tf.equal(idx_l,idx_p),tf.float32)) #tf.equal是True/False,转换为数字0/1,匹配上的概率
saver=tf.train.Saver()
with tf.Session() as sess:
sess.run(tf.global_variables_initializer())
for i in range(500000):
bax, bay = batch_data_gen()
_, los, acc = sess.run([optimizer, loss, accu], feed_dict={x: bax, y: bay})
print('第',i,'次',f'loss:{los},acc:{acc}')
if acc > 0.2:
# 持久化
saver.save(sess, "./model.model")
break
saver.save(sess, './model.ckpt')
4。使用模型
需要训练几十万次才能得到高精度的模型,设备不够不提供模型
saver = tf.train.Saver()
saver.restore(sess, './model.ckpt')
sess.run(predict) #还不能理解,后期解释
完整代码
#-*-coding:utf-8-*-
import string
'''
确定验证码范围
'''
char_set=list(string.ascii_letters+string.digits) #英文大小写+数字
char_num=4 #一个验证码图片有多少字符,这里4个
'''
生成验证码
'''
import numpy as np
from captcha.image import ImageCaptcha
from PIL import Image
char2index=dict(zip(char_set,range(len(char_set)))) #字符和顺序号的键值对
def generate_captcha():
single_captcha=np.random.choice(char_set,size=char_num)
single_text=''.join(single_captcha) #连接成char_num个字母为要生成图片的字符串
ic=ImageCaptcha()
captcha=ic.generate(single_text) #已经生成了验证码
a=Image.open(captcha) #把图片转换为数组
s=np.array(a.convert('L')).flatten()/255 #彩色图片转换为灰色,并且归一化数组中的数字,这已经获得了图片数据
#将验证码字符转换向量的形式,向量数字0和1代表有无,char_num个字符的向量拼接成一个长向量,就是一个验证码的向量数据y
u=np.zeros(len(char_set)*char_num)
k=0
for i in single_captcha:
u[char2index[i]+k*len(char_set)]=1
k+=1
return s,u #s是图片数据,u是图片标签
'''
批量生成验证码
'''
def batch_data_gen(batch_size=128): #将batch_size个图片和标签存分别存放在在列表中
batch_x=[]
batch_y=[]
for i in range(batch_size):
ran=generate_captcha()
batch_x.append(ran[0])
batch_y.append(ran[1])
return np.array(batch_x),np.array(batch_y) #数组形式,每个样本是一个行向量
'''
定义模型
网络结构:
conv -> relu6 -> max_pool -> conv -> relu6 -> max_pool -> dropout -> conv -> relu6 -> max_pool -> full connection -> full connection
'''
import tensorflow as tf
def cnn_(x,height=60,width=160):
x=tf.reshape(x,shape=[-1,height,width,1]) #将每行行数据转换为图片尺寸的数据,方便做2维窗口卷积,1表示通道数,黑白图片就1个通道
#第一层定义随机参数,然后卷积
w_c1=tf.Variable(tf.random_normal([3,3,1,32],dtype="float64"))
b_c1=tf.Variable(tf.random_normal([32],dtype="float64"))
cv=tf.nn.conv2d(x,w_c1,strides=[1,1,1,1],padding='SAME')
#线性变换加偏执项,得到输出卷积过程的输出
outcv=tf.nn.bias_add(cv,b_c1)
#激活
conv1=tf.nn.relu6(outcv)
#最大池化层
conv1=tf.nn.max_pool(conv1,ksize=[1,2,2,1],strides=[1,2,2,1],padding='SAME')
#第二层定义卷积层参数然后卷积
w_c2=tf.Variable(tf.random_normal([3,3,32,64],dtype="float64"))
b_c2=tf.Variable(tf.random_normal([64],dtype="float64"))
cv2=tf.nn.conv2d(conv1,w_c2,strides=[1,1,1,1],padding='SAME')
#偏执项
outcv2=tf.nn.bias_add(cv2,b_c2)
#激活
conv2=tf.nn.relu6(outcv2)
#最大池化
conv2=tf.nn.max_pool(conv2,[1,2,2,1],strides=[1,2,2,1],padding='SAME')
#dropout
conv2=tf.nn.dropout(conv2,keep_prob=0.5)
#第三层卷积
w_c3=tf.Variable(tf.random_normal([3,3,64,64],dtype="float64"))
b_c3=tf.Variable(tf.random_normal([64],dtype="float64"))
cv3=tf.nn.conv2d(conv2,w_c3,strides=[1,1,1,1],padding='SAME')
#激活
conv3=tf.nn.relu6(tf.nn.bias_add(cv3,b_c3))
#最大池化
conv3=tf.nn.max_pool(conv3,ksize=[1,2,2,1],strides=[1,2,2,1],padding='SAME')
#全连接
w_f1=tf.Variable(tf.random_normal([8*20*64,512],dtype="float64"))
b_f1=tf.Variable(tf.random_normal([512],dtype="float64"))
#注意这里,每个点是一个神经元,2维拉成1维
dense= tf.reshape(conv3, [-1, 8*20*64])
#全连接的参数,设mxn个,m是前面一层的神经元,n是后面一层的神经元,理解成w_f1每列是神经元连接前面一层的神经元参数,
es=tf.add(tf.matmul(dense,w_f1),b_f1)
es=tf.nn.sigmoid(es)
#第二层全连接
w_f2=tf.Variable(tf.random_normal([512,248],dtype="float64"))
b_f2=tf.Variable(tf.random_normal([248],dtype="float64"))
out=tf.add(tf.matmul(es,w_f2),b_f2)
return out
'''
数据训练模型流程
'''
def target(x,y):
#引用模型
output=cnn_(x)
#定义损失
loss=tf.reduce_mean(tf.nn.sigmoid_cross_entropy_with_logits(logits=output,labels=y))
#定义优化损失的方法
optimizer=tf.train.AdamOptimizer(learning_rate=0.001).minimize(loss)
#模型评价指标先对输出做处理,使其格式和标签shape一致,方便对比
predict=tf.reshape(output,[-1,4,62])
idx_p=tf.argmax(predict,2)#取行中值最大的一个将与标签对比
idx_l=tf.argmax(tf.reshape(y,[-1,4,62]),2)#将y变量重置回一个字符一个向量的格式
accu=tf.reduce_mean(tf.cast(tf.equal(idx_l,idx_p),tf.float32)) #tf.equal是True/False,转换为数字0/1,匹配上的概率
saver=tf.train.Saver()
with tf.Session() as sess:
sess.run(tf.global_variables_initializer())
for i in range(500000):
bax, bay = batch_data_gen()
_, los, acc = sess.run([optimizer, loss, accu], feed_dict={x: bax, y: bay})
print('第',i,'次',f'loss:{los},acc:{acc}')
if acc > 0.8:
# 持久化
saver.save(sess, "./model.ckpt")
break
saver.save(sess, './model.ckpt')
if __name__ == '__main__':
x=tf.placeholder(dtype=tf.float64,shape=[None,60*160])
y=tf.placeholder(dtype=tf.float64,shape=[None,62*4])
target(x,y)