dropout 防止过拟合
relu 激活函数
卷积神经网络的结构:
卷积层、激活层、池化层(pooling layer/subsample)、全连接层
怎么排列他们效果比较好是需要实验的
卷积神经网络隐藏层重要模块
一、卷积层
卷积核kernal/过滤器filter/模型参数/卷积单元
卷积核四要素有:个数、大小、步长、零填充的大小
常用大小:11 33 5*5
卷积核个数 相当于派多人去观察 得到多张观测结果
如何计算输出图像的大小
输入32321 50个filter 大小为5*5 移动步长为1 填充为1(意思是填充了几层0)
H1=32 D1(通道数)=1 k=50 f=5 p=1
故H2=30 D2=k 所以输出为[30,30,50]
(若不能整除,则卷积向下取整,池化向上取整)
卷积网络API
tf.nn.conv2D(input,filter,strides=,padding=,name=None) 五个参数 最后一个修改指令名称不管了
**input:**输入的图像,为四阶张量的形式
[batch,height,width,channel] 类型必须为float32 或者float64
**filter:**指定过滤器的权重 包括weghts和bias 这里主要是权重 bias是后加上去的
模型参数(权重)用变量保存,需要指定初始值
filter的形状为[filter_height,filter_width,in_channels,out_channels]四阶张量的形状
initial_value=random_normal(shape=[filter_height,filter_width,in_channels,out_channels])
即
initial_value=random_normal(shape=[f,f,输入图片的通道数,输出的通道数(就等于filter个数K)])
f=1,3,5
strides:步长,1,2,一般取1比较多的 其形状也有要求
strides=[1,strides,strides,1] 如果步长为1则直接在strides这里填上1即为[1,1,1,1]
也就是说不管是横着平移竖着平移均为1格
padding:零填充
“SAME”:越过边缘取样,得到的输出的大小(长宽)与输入的大小一致
“VALID”:不越过边缘取样,即不进行零填充
注意,每个过滤器都会带若干权重和一个偏置
二、激活函数(激活层)
Relu 计算速度快 解决了梯度消失 图像没有负的像素值
tensorflow中使用relu
激活层API
tf.nn.relu(features,name=None)
features即为卷积+偏置的结果
三、池化层
降低网络参数,防止过拟合
max_pooling 最大池化
avg_pooling 平均池化
计算方法与卷积一样
D2=64(上一层的通道个数)
池化层API
tf.nn.max_pool(value,ksize=,strides=,padding=,name=None)
**value:**即为上一步的输出,为四维张量的形式,[batch,height,width,channels]
ksize:池化窗口的大小 [1,ksize,ksize,1],如[1,2,2,1]
strides:strides=[1,strides,strides,1] 如果步长为2则直接在strides这里填上1即为[1,2,2,1]
padding:零填充
“SAME”:越过边缘取样,得到的输出的大小(长宽)与输入的大小一致
“VALID”:不越过边缘取样,即不进行零填充
全连接层
充当分类器的作用 复习全连接层分类mnist:
可以直接调接口下载数据集 训练集55000张数据
特征值: 28*28=784个元素
**目标值:**分类 目标值 用独热码(one-hot)表示
0,1,2_,9 一共10种可能
若为1 则用独热码表示为:
0,1,0,_,0
故mnist.train.label=[55000,10] 即一行表示一张图片的label
写形状shape的时候最好使用中括号
补充:数据IO操作
如何将大量的数据加载到内存当中
三种文件读取的方法:
占位符和feed_dict搭配使用的方法
QueueRunner的方式
通用文件读取流程
图片
二进制文件
TFrecords
QueueRunner的方式 通用文件读取流程
多任务 多线程 多进程 如何解决数据传递的问题
用队列
多线程+队列
1.通用文件的读取流程
总共三个阶段:
阶段一:构造文件名队列
阶段二:读取与解码
阶段三:将样本放到批处理队列当中
手动开启线程
阶段一:构造文件名队列
将需要读取的文件的文件名放入文件名队列
tf.train.string_input_producer(string_tensor,shuffle=True)
string_tensor:含有文件名+路径的列表
shuffle=True:默认打乱文件名顺序
num_epochs:过几遍数据,默认无限过数据
return 文件名队列filequeue=tf.train.string_input_producer(string_tensor,shuffle=True)
阶段二:读取与解码
读取不同的文件类型 区别就在这里
图片:
读取:tf.WholeFileReader()
一个文件名对应一张图片 一张图片对应一个样本
解码:tf.image.decode_jpeg(contents)
**>**contents是读取的返回的value
**>**将jpeg编码的图像解码为uint8张量
**>**return 张量类型,3-D形状[height,weight,channels]
tf.image.decode_png(contents)
**>**将png编码的图像解码为uint8张量
**>**return 张量类型,3-D形状[height,weight,channels]
二进制文件:
读取:tf.FixedLengthRecordReader(recordbytes)
一个固定字节(要告诉他recordbytes是多少) 对应一个样本
解码:tf.decode_raw
TFRecords:
读取:tf.TFRecordReader()
读取器 有个共同的方法 read方法 会返回一个tensor元组key,value
也就是key,value=读取器.read(file.queue) key:样本所在的文件的文件名 value:一个样本
默认所有解码的类型都是unit8类型,如果要改为其他类型则需要用tf.cast()
进行类型转换
阶段三:将样本放到批处理队列当中
有两种方法
tf.train.batch
tf.train.shuffle_batch
tf.train.batch(tensors,batch_size,num_threads,capacity)
tensors:可以是包含张量的列表,批处理的内容放到列表当中
batch_size:从队列中读取的批处理大小,一次性要处理几个样本
num_threads:进入队列的线程数,跟电脑cpu或gpu的核芯数有关,核心越多可以填多一点
capacity:整数,队列中元素的最大数量
return tensors
至此还没完 需要手动开启线程
用到tf.train.QueueRunner
对象
在开启会话之后
调用tf.train.start_queue_runners(sess=None,coord=None)
传参有两个,sess:所在的会话
coord:线程协调器——任务完成后回收线程用
此处写一个狗文件读取案例:
(1)构造文件名队列
(2)读取与解码
使得样本形状和类型保持统一
(3)批处理
import tensorflow as tf
#不显示警告日志的方法:
import os
os.environ['TF_CPP_MIN_LOG_LEVEL']='2'
#注意读取操作必须需要import os
def picture_read(filelist):
""""
狗图片读取案例
"""
#1.构造文件名队列file_queue
file_queue=tf.train.string_input_producer(filelist)
#2.读取与解码
# 读取阶段
#先构建读取器
reader=tf.WholeFileReader()
#key是文件名 value是一个样本也就是此图片的原始编码形式(二进制)
key,value=reader.read(file_queue)
#print("key\n",key)
#print("value\n",value)
# 解码阶段
image=tf.image.decode_jpeg(value)
print("解码后的image的属性\n", image)
#图像的形状、类型的修改 将图片统一为200×200的像素
image_resized = tf.image.resize_images(image,[200,200])
print("image_resized\n",image_resized)
#静态形状修改 因为知道肯定是3通道
image_resized.set_shape([200,200,3])
print("image_resized\n", image_resized)
#3.批处理
image_batch = tf.train.batch([image_resized],batch_size=100,num_threads=1,capacity=100)
print("image_batch\n",image_batch)
#开启会话
with tf.Session() as sess:
#开启线程 因为入队和出队的操作需要开启线程,否则运行会卡死
#创建线程协调员
coord=tf.train.Coordinator()
threads=tf.train.start_queue_runners(sess,coord=coord)
key_new,value_new,image_resized_new=sess.run([key,value,image_resized])
#print("key_new:\n",key_new)
#print("value_new:\n",value_new)
print("image_resized_new:\n", image_resized_new)
image_new=sess.run(image)
print("解码后的image\n",image_new)
#回收线程
coord.request_stop() #请求停止
coord.join(threads)
return None
if __name__ == "__main__":
#构造路径+文件名的列表 用os模块
filename=os.listdir("./dog")
#print(filename)
#拼接路径+文件名
filelist=[os.path.join("./dog/",file) for file in filename]
#print(filelist)
picture_read(filelist)
此处再写一个cifar10二进制文件读取案例:
二进制数据集 每张图片为32323
故每3073个字节为一个样本:一个目标值(标签0~9)+3072个像素(1024字节红色 1024字节绿色 1024字节蓝色)
故读取器这里tf.FixedLengthRecordReader(recordbytes) recordbytes要填3073
依然是三部曲
(1)构造文件名队列
(2)读取与解码
reader=tf.FixedLengthRecordReader(recordbytes)
key,value= reader.read(file_queue)
decoded=tf.decode_raw(value,uint8)
将数据的标签和图片(tensor)进行分割(切片)得到
label和
一个样本image(3072个字节 = 1024r +1024g +1024b)
[[r[32, 32]],
[g[32,32]],
[b[32,32]]]
shape=[3 , 32 , 32]=[channels,height,width]
故需要转换成tensorflow的图像表示习惯
图片的形状和类型调整完毕后:
(3)批处理
import tensorflow as tf
import os
os.environ['TF_CPP_MIN_LOG_LEVEL']='2'
#用面向对象的方法写
class Cifar(object):
def __init__(self):
#初始化操作
self.height=32
self.width=32
self.channels=3
#字节数
self.image_bytes=self.height * self.width * self.channels
self.label_bytes=1
self.all_bytes=self.image_bytes+self.label_bytes
def read_and_decode(self,file_list):
#1. 构造文件名队列
file_queue = tf.train.string_input_producer(file_list)
#2. 读取与解码
#读取阶段
reader = tf.FixedLengthRecordReader(self.all_bytes)
#key为文件名 value为一个样本的内容
key,value = reader.read(file_queue)
print("key:\n",key)
print("value:\n",value)
#解码阶段 结果是一个一维数组 第一个元素是标签值 后面3072为像素值 故需要切片操作
decoded = tf.decode_raw(value,tf.uint8)
print("decoded:\n",decoded)
#将目标值和特征值切开 #第一个【】是切出来的起始元素索引 第二个【】填切片的长度
label = tf.slice(decoded,[0],[self.label_bytes])
image = tf.slice(decoded,[1],[self.image_bytes])
print("label:\n",label)
print("image:\n",image)
#调整图片形状 因为是跨阶调整图片形状 不能用静态形状 必须用动态形状
image_reshape = tf.reshape(image,shape=[self.channels,self.height,self.width])
print("image_shape:\n",image_reshape)
#转置,将图片的顺序转为 height,width,channels 原先按0,1,2排列,然后在这里的[]里面放在对应的位置
image_transposed = tf.transpose(image_reshape,[1,2,0])
print("image_transposed:\n",image_transposed)
#调整图像类型 uint8→float32
image_cast = tf.cast(image_transposed,tf.float32)
#3. 批处理
label_batch,image_batch = tf.train.batch([label,image_cast],batch_size=100,num_threads=8,capacity=100)
print("lable_batch:\n",label_batch)
print("image_batch:\n", image_batch)
#开启会话
with tf.Session() as sess:
#开启线程
coord = tf.train.Coordinator()
threads = tf.train.start_queue_runners(sess=sess,coord=coord)
key_new,value_new,decoded_new,label_new,image_new,image_reshape_new,image_transposed_new = sess.run([key,value,decoded,label,image,image_reshape,image_transposed])
label_batch_new,image_batch_new = sess.run([label_batch,image_batch])
print("key_new:\n",key_new)
print("value_new:\n", key_new)
print("decoded_new:\n", decoded_new)
print("label_new:\n",label_new)
print("image_new:\n",image_new)
print("image_shape_new:\n",image_reshape_new)
print("image_transposed_new:\n", image_transposed_new)
print("labe_batch_new:\n",label_batch_new)
print("image_batch_new:\n", image_batch_new)
#回收线程
coord.request_stop()
coord.join(threads)
return None
if __name__ == "__main__":
file_name = os.listdir("./cifar-10-batches-bin")
print("filename:\n",file_name)
#构造文件名路径列表
file_list = [os.path.join("./cifar-10-batches-bin/",file) for file in file_name if file[-3:] == "bin"]
print("filelist:\n",file_list)
#实例化Cifar
cifar=Cifar()
cifar.read_and_decode(file_list)
用CNN识别mnist
网络设计:
需要知道形状的变化
第一个卷积大层:
输入的形状[None,28,28,1]
卷积层:
32个 filter 大小55 步长1 padding ‘SAME’ [ 5 , 5 , 1 , 32 ]
输出的形状[None,28,28,32]
激活:
relu
池化:
输入的形状[None,28,28,32]
大小22 步长2 (习惯)
输出的形状需要用公式算[None,14,14,32]
记住 池化就是把长和宽干掉一半
第二个卷积大层:
卷积层:
输入的形状[None,14,14,32]
64个 filter 大小55 步长1 padding= ‘SAME’ [ 5 , 5 , 32 , 64 ]
输出的形状[None,14,14,64]
激活:
relu
池化:
输入的形状[None,14,14,64]
大小22 步长2 (习惯)
输出的形状[None,7,7,64]
全连接层
形状:
tf.reshape()
[None,7,7,64]------->[None,7764]
[None,7764][] = [None,10] **所以权重为 [77*64,10]**
y_predict = tf.matmul(pool2,weights)+bias
调参 提高准确率的方法
(1)学习率减小 0.1——>0.001
(2) 随机初始化权重、偏置的值 可以将方差设为stddev=0.01
(3)选择好用的优化器 如AdamOptimizer
看来改变学习率的效果是最显著的
网络的优化与改进方法:
1.模型参数的大小调整
2.改进网络结构
加入 batch_normalization 批归一化(批标准化)
dropout层 使得某些神经元失效
二者都是为了防止过拟合
import tensorflow as tf
import os
#调用端口下载数据集
from tensorflow.examples.tutorials.mnist import input_data
#1、利用数据,在训练的时候实时提供数据
#mnist手写识别数字数据在运行时候实时提供给占位符
tf.app.flags.DEFINE_integer("is_train", 1, "指定是否是训练模型,还是拿数据去预测")
FLAGS = tf.app.flags.FLAGS
#定义权重生成器
def create_weights(shape):
return tf.Variable(initial_value=tf.random_normal(shape=shape,stddev=0.01))
# 构建卷积神经网络
def create_model(x):
#1)第一个卷积大层
with tf.variable_scope("conv1"):
#卷积层
#将x[None,784]形状进行修改 跨阶数修改
input_x = tf.reshape(x,shape=[-1, 28, 28, 1])
#定义filter和偏置
conv1_weights = create_weights(shape=[5,5,1,32])
conv1_bias = create_weights(shape=[32])
conv1_x = tf.nn.conv2d(input=input_x, filter=conv1_weights,strides=[1,1,1,1],padding="SAME") + conv1_bias
#激活层
relu1_x = tf.nn.relu(conv1_x)
#池化层
pool1_x = tf.nn.max_pool(value=relu1_x,ksize=[1,2,2,1],strides=[1,2,2,1],padding="SAME")
#2)第二个卷积大层
with tf.variable_scope("conv2"):
# 卷积层
# 定义filter和偏置
conv2_weights = create_weights(shape=[5, 5, 32, 64])
conv2_bias = create_weights(shape=[64])
conv2_x = tf.nn.conv2d(input=pool1_x, filter=conv2_weights, strides=[1, 1, 1, 1], padding="SAME") + conv2_bias
# 激活层
relu2_x = tf.nn.relu(conv2_x)
# 池化层
pool2_x = tf.nn.max_pool(value=relu2_x, ksize=[1, 2, 2, 1], strides=[1, 2, 2, 1], padding="SAME")
#3)全连接层
with tf.variable_scope("full_connection"):
#首先做形状修改
# [None, 7, 7, 64] - ------>[None, 7 * 7 * 64]
# [None, 7 * 7 * 64] * [] = [None, 10] 所以权重为[7 * 7 * 64, 10]
x_fc = tf.reshape(pool2_x,shape=[-1, 7 * 7 * 64]) #注意reshape没有None的用法 需要用-1
weights_fc = create_weights(shape=[7 * 7 * 64, 10])
bias_fc = create_weights(shape=[10])
y_predict = tf.matmul(x_fc, weights_fc) + bias_fc
return y_predict
def full_connection_completed():
#1.准备数据
#1)导入数据集
mnist = input_data.read_data_sets("./mnist_data", one_hot=True)
#2)准备真实值feeding
with tf.variable_scope("mnist_data"):
x = tf.placeholder(dtype=tf.float32,shape=[None,784])
y_true = tf.placeholder(dtype=tf.float32,shape=(None,10))
y_predict = create_model(x)
#3.softmax回归以及交叉熵损失计算
with tf.variable_scope("softmax_crossentropy"):
loss = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(labels=y_true,logits=y_predict))
#4.梯度下降优化损失
with tf.variable_scope("optimizer"):
train_op=tf.train.GradientDescentOptimizer(learning_rate=0.01).minimize(loss)
#5.准确率计算
with tf.variable_scope("accuracy"):
#训练集准确率计算
#1)比较预测值和真实值最大值所在位置,tf.argmax表示按行返回最大值的索引
equal_list=tf.equal(tf.arg_max(y_true,1),tf.arg_max(y_predict,1))
#2)将布尔值转换为浮点数,求平均,即为准确率
accuracy=tf.reduce_mean(tf.cast(equal_list,tf.float32))
#测试集准确率计算 创造变量sum
sum = tf.constant(0)
#(2)收集要显示的变量
#先收集损失和准确率
tf.summary.scalar("losses", loss)
tf.summary.scalar("accuracy",accuracy)
#初始化变量
init_op=tf.global_variables_initializer()
#(3)合并所有变量op
merged = tf.summary.merge_all()
#创建模型保存与加载
saver = tf.train.Saver()
#以上只是一个图 因此需要
#开启会话
with tf.Session() as sess:
#把变量传进来
sess.run(init_op)
#(1)创建一个events文件实例
file_writer = tf.summary.FileWriter("./tmp/summary2/",graph=sess.graph)
#加载模型
if os.path.exists("./tmp/modelckpt2/checkpoint"):
saver.restore(sess,"./tmp/modelckpt2/cnn_model") #注意modelckpt2这个文件夹要自己建立
#也就是说 模型保存和加载的时候 也就是saver.save或saver.restore的路径需要自己建立 否则会蓝屏
#但是创建envents实例化的路径可以不用自己建立
if FLAGS.is_train == 1:
image,label=mnist.train.next_batch(100)
print("训练之前,损失为%f" % sess.run(loss,feed_dict={x:image,y_true:label}))
#开始训练 注意每次sess.run都要feed数据
for i in range(300):
_,error,accuracy_value=sess.run([train_op,loss,accuracy],feed_dict={x:image,y_true:label})
print("第%d次的训练,损失为%f,准确率为%f" % (i+1 , error , accuracy_value))
#运行合变量op,写入事件文件当中
summary = sess.run(merged, feed_dict={x:image,y_true:label})
file_writer.add_summary(summary, i)
if i % 100 == 0:
saver.save(sess,"./tmp/modelckpt2/cnn_model")
else:
#如果不是训练,则是用测试集对模型进行测试
for i in range(100):
#每次拿一个样本进行预测
image, label = mnist.test.next_batch(1)
accuracy_value=sess.run(accuracy,feed_dict={x:image,y_true:label})
print("第%d个样本的真实值为:%d,预测结果为:%d" % (
i+1,
tf.arg_max(sess.run(y_true,feed_dict={x:image,y_true:label}),1).eval(),
tf.arg_max(sess.run(y_predict,feed_dict={x:image,y_true:label}),1).eval()
)
)
sum = sum + accuracy_value
print("准确率为 %f" % (sess.run(sum/100)))
return None
if __name__ == "__main__" :
full_connection_completed()