一、卷积神经网络
1、关于卷积神经网络的知识,这里推荐一个博客,可以通过几篇博文来了解卷积神经网络:
2、关于张量经过卷积与池化后数据各维度大小的变化:
设原图片数据维度为$batch*width*height*channel$:$batch$为图片张数,$width$为图宽,$height$为图高,$channel$为图通道数。
卷积:
设卷积核为$padding*cwidth*cheight$:padding层数为$padding$,卷积核宽$cwidth$,卷积核高$cheight$;卷积步长$wstride*hstride$,宽为$wstride$,高为$hstride$;卷积核个数为$cchannel$。
经过卷积后的图片张数:
$$batch^{'}=batch$$
经过卷积后的宽:
$$weight^{'}=(\frac{width+2padding-cwith}{wstride}+1)$$
经过卷积后的高:
$$height^{'}=(\frac{width+2padding-cheight}{hstride}+1)$$
经过卷积后的通道数:
$$channel^{'}=cchannel$$
经过卷积后的图片数据为:
$$batch^{'}*width^{'}*height^{'}*channel^{'}=batch\ast (\frac{width+2padding-cwith}{wstride}+1)\ast (\frac{width+2padding-cheight}{hstride}+1)*cchannel$$
一般情况我们设置卷积核的高=宽=f,步长为s,卷积核数为cchannel,则公式可以简化为:
$$batch^{'}*width^{'}*height^{'}*channel^{'}=batch\ast (\frac{width+2padding-f}{stride}+1)\ast (\frac{width+2padding-f}{stride}+1)*channel$$
注意,公式计算可能出现小数,所以要向下取证。
池化:
设池化的宽与高为$pwidth$与$pheight$,步长为$wstrid$与$hstrid$
经过卷积后的图片张数:
$$batch^{'}=batch$$
经过卷积后的宽:
$$weight^{'}=(\frac{width+2padding-pwidth}{wstride}+1)$$
经过卷积后的高:
$$height^{'}=(\frac{width+2padding-pheight}{hstride}+1)$$
经过卷积后的通道数:
$$channel^{'}=channel$$
池化后的数据为:
$$batch^{'}*width^{'}*height^{'}*channel^{'}=batch\ast (\frac{width+2padding-pwidth}{wstride}+1)\ast (\frac{width+2padding-pheight}{hstride}+1)\ast channel$$
同样当我们设定池化宽=高=f,步长为stride,则公式为:
$$batch^{'}*width^{'}*height^{'}*channel^{'}=batch\ast (\frac{width+2padding-width}{wstride}+1)\ast (\frac{width+2padding-height}{stride}+1)\ast channel$$
二、tensorflow中卷积相关内容
函数介绍:
1、卷积的实现函数:tf.nn.conv2d (input, filter, strides, padding, use_cudnn_on_gpu=None, data_format=None, name=None)
- input:输入的要做卷积的图片,要求为一个张量,shape为 [ batch, in_height, in_weight, in_channel ],其中batch为图片的数量,in_height 为图片高度,in_weight 为图片宽度,in_channel 为图片的通道数,灰度图该值为1,彩色图为3。
- filter: 卷积核,要求也是一个张量,shape为 [ filter_height, filter_weight, in_channel, out_channels ],其中 filter_height 为卷积核高度,filter_weight 为卷积核宽度,in_channel 是图像通道数 ,和 input 的 in_channel 要保持一致,out_channel 是卷积核数量。
- strides: 卷积时在图像每一维的步长,这是一个一维的向量,默认是[batch, height, width, channels]取决于input的data_format,一般设置[ 1, strides, strides, 1],第一位和最后一位是1。
- padding: string类型,值为“SAME” 和 “VALID”,表示的是卷积的形式,是否考虑边界。"SAME"是考虑边界,不足的时候用0去填充周围,"VALID"则不考虑
- use_cudnn_on_gpu: bool类型,是否使用cudnn加速,默认为true。
这里一开始可能对input和strides传入的列表的数值设定可能不太了解为什么这么设置,这个主要有data_format决定,详情可以看一下这个博文:。
2、池化的实现函数:tf.nn.max_pool(value, ksize, strides, padding, name=None)
- 第一个参数value:需要池化的输入,一般池化层接在卷积层后面,所以输入通常是feature map,依然是[batch, height, width, channels]这样的shape。
- k_size:池化窗口的大小,取一个四维向量,shape为[batch, height, width, channels],一般是[1, height, width, 1],因为我们不想在batch和channels上做池化,所以这两个维度设为了1。
- strides:窗口在每一个维度上滑动的步长,shape为[batch, height, width, channels],一般也是[1, stride,stride, 1]。
- padding:string类型,值为“SAME” 和 “VALID”,表示的是卷积的形式,是否考虑边界。"SAME"是考虑边界,不足的时候用0去填充周围,"VALID"则不考虑。
3、tensor数据维度设定(通常用于卷积到全连接层张量与捐局的转化):tf.reshape(tensor,shape,name=None)
- tensor:传入的tensor类型数据。
- shape:修改的形状,一个1*784的one-hot数据变成一个1*28*28*1的灰度图数据可以设置为[1,28,28,1],若某一个维度设置为-1表示这个维度不确定,转化会自行计算。
- name:转化操作的名字。
三、代码示例
代码
import tensorflow as tf
from tensorflow.examples.tutorials.mnist import input_data
# 读取手写字数据
mnist_data = input_data.read_data_sets(r'C:\Users\EDZ\.PyCharm2019.1\config\scratches\MNIST_data', one_hot=True)
# 设定每个批次的数据量,如果是CPU版本的tensor数据大可能会计算不出来所以设置为50
batch_size = 50
#计算批次数
n_batch = mnist_data.train.num_examples // batch_size
#定义卷积
def x_cov_W(x,W_shape,b_shape,act=None):
'''
:param x: 进行卷积的tensor
:param W_shape: 卷积核的shape
:param b_shape: 偏置的shape
:param act: 激活函数
:return: 返回一个卷积计算结果
'''
#定义卷积核
initial = tf.truncated_normal(W_shape,stddev=0.1)
W=tf.Variable(initial)
#定义偏置
b=tf.constant(0.1,tf.float32,shape=b_shape)
#返回卷积的结果,这里设置步长为宽W=1,高H=1
if act: #没有激活函数的情况
return tf.nn.conv2d(x,W,strides=[1,1,1,1],padding='SAME')+ b
else: #有激活函数的情况
return act(tf.nn.conv2d(x,W,strides=[1,1,1,1],padding='SAME')+ b)
#定义池化,把原来的数据高与宽缩小一半
def pool(x):
'''
:param x: 需要池化的tensor
:return: 返回一个池化结果
'''
return tf.nn.max_pool(x,ksize=[1,2,2,1],strides=[1,2,2,1],padding='SAME')
# 占位符号
x = tf.placeholder(tf.float32,[None,784])
y = tf.placeholder(tf.float32,[None,10])
#将x的one-hot数据转为图片28*28*1的张量,-1:不确定的batch即图片数;28:高;28:宽;1:channel数。
x_image=tf.reshape(x,[-1,28,28,1])
#第一层卷积,W_shape-5:图高;5:图宽;1:channel数;16:卷积核个数;b_shape-16:偏置个数,因为有16个卷积核,输出为28*28*16的图
layer1_cov=x_cov_W(x_image,[5,5,1,16],[16],tf.nn.relu)
#第一层池化,输出为14*14*32的图
layer1_pool=pool(layer1_cov)
#第二层卷积,输出为14*14*32,的图
layer2_cov=x_cov_W(layer1_pool,[5,5,16,32],[32],tf.nn.relu)
#第二层池化输出为7*7*64,的图
layer2_pool=pool(layer2_cov)
#将第二层的输出扁平化为一个one-hot的1维向量
layer2_pool_flat=tf.reshape(layer2_pool,[-1,7*7*32])
#第三层全连接层
full3_w=tf.Variable(tf.truncated_normal([7*7*32,720],stddev=0.1)) #权重矩阵:w输入为7*7*32,输出为720
full3_b=tf.constant(0.1,tf.float32,[720]) #偏置
full3_ouput=tf.nn.relu(tf.matmul(layer2_pool_flat,full3_w)+full3_b) #激活函数为Relu
#第四层全连接层
full4_w=tf.Variable(tf.truncated_normal([720,10],stddev=0.1)) #权重矩阵:w输入为1024,输出为10
full4_b=tf.constant(0.1,tf.float32,[10]) #偏置
full4_ouput=tf.matmul(full3_ouput,full4_w)+full4_b #为了不与后面的交叉熵损失冲突无激活函数
#定义交叉熵损失函数
cross_entropy=tf.nn.softmax_cross_entropy_with_logits(labels=y,logits=full4_ouput)
#使用Adam梯度下降法训练模型
train=tf.train.AdamOptimizer(0.001,0.9,0.999).minimize(cross_entropy)
# 初始化
init = tf.global_variables_initializer()
# 结果存放在bool型列表汇总
correct_prediction = tf.equal(tf.argmax(y, 1), tf.argmax(full4_ouput, 1))
# 计算准确度,并加入到总结指令
accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32))
with tf.Session() as sess:
sess.run(init)
# 训练20代
for epoch in range(20):
# 每代对数据进行一轮minibatch
for batch in range(n_batch):
batch_x, batch_y = mnist_data.train.next_batch(batch_size) # 每个循环读取batch_size大小批次的数据
sess.run(train, feed_dict={x: batch_x, y: batch_y})
acc = sess.run(accuracy, feed_dict={x: mnist_data.test.images, y: mnist_data.test.labels}) # 用测试数据计算准确度
if batch % 99 == 0:
print('第%d代%d批次,准确率为%.6f' % (epoch + 1, batch + 1, acc))