利用tensorflow实现简单的卷积神经网络——迁移学习小记(二)

一、什么是神经网络(CNN)

    卷积神经网络(Convolutional Neural Network,简称CNN),是一种前馈神经网络,人工神经元可以影响周围单元,可以进行大型图像处理。卷积神经网络包括卷积层和池化层。卷积神经网络是受到生物思考方式的启发的MLPs(多层感知器),它有着不同的类别层次,并且各层的工作方式和作用也不同。

  在卷积神经网络的卷积层中,一个神经元只与部分邻层神经元连接。在CNN的一个卷积层中,通常包含若干个特征平面(featureMap),每个特征平面由一些矩形的神经元排列组成,同一特征平面的神经元共享权值,这里共享权值就是卷积核。卷积核一般以随机小数矩阵的形式初始化,在网络训练的过程中卷积核将学习得到合理的权值。共享权值(卷积核)带来的直接好处是减少网络各层之间的连接,同时又降低了过拟合的风险。子采样又叫池化(pooling),通常有均值子采样(mean pooling)和最大值子采样(max pooling)两种形式。子采样可以看作是一种特殊的卷积过程。卷积和子采样大大简化了模型复杂度,减少了模型的参数。

1.1 局部感受野

  为降低参数数目,采用局部感知野。对局部进行感知,然后在更高层将局部的信息综合起来就得到了全局的信息。网络部分联通思想,也是受启发于生物学中的视觉系统结构。视觉皮层的神经元就是局部接受信息的(即这些神经元只响应某些特定区域的刺激)。如下图:左为全连接,右为局部连接。

1.2权值共享

即让一组神经元使用相同的连接权。在卷积层中,每个卷积核都是一种特征提取方式,卷积核的过程就是两个层之间的连接参数,而这些参数在图像的不同区域是局部,且在局部是共享的。所以,如果卷积核的大小是m*n,那么对应参数的数目就是m*n。

1.4多卷积核

为了提取足够充分的特征,我们可以添加多个卷积核,比如32个卷积核可以学习32种特征。多个卷积核的情况如下。

1.5池化(pooling)

子采样有两种方式,一种是均值子采样(mean-pooling),一种是最大值子采样(max-pooling)。两种子采样可以看成是特殊的卷积过程,如下图所示:

(1)均值子采样的卷积核中每个权重都是0.25,卷积核在原图inputX上的滑动步长为2.均值子采样效果相当于把原图模糊缩减至原来的1/4.

(2)最大值子采样的卷积核中各权值中只有一个1,其余为0,卷积核中为1

的位置对应inputX被卷积核覆盖部分最大值的位置。卷积核在原图inputX上滑动步长为2.最大值子采样的效果是把原图缩小至原来的1/4,并保每个2*2区域的最强输入。

详细概念,在下面这个blog中有阐述。

https://blog.csdn.net/fengbingchun/article/details/50529500

二、实现简单cnn

前面阐述了一些理论,下面上具体代码,代码中有注释

'''导入mnist数据集,创建默认的interactive session'''
from tensorflow.examples.tutorials.mnist import input_data
import tensorflow as tf
mnist = input_data.read_data_sets("MNIST_data", one_hot = True)
sess = tf.InteractiveSession()
#定义权重和偏差的初始化函数
def weight_variable(shape):
    initial = tf.truncated_normal(shape, stddev = 0.1)
    return tf.Variable(initial)

def bias_variable(shape):
    initial = tf.constant(0.1, shape = shape)
    return tf.Variable(initial)
#定义卷积层和池化层
def conv2d(x, W):
    return tf.nn.conv2d(x, W, strides = [1, 1, 1, 1], padding = 'SAME')

def max_pool_2_2(x):
    return tf.nn.max_pool(x, ksize = [1, 2, 2, 1], strides = [1, 2, 2, 1], padding = 'SAME')
#定义输入的placehoder,x是特征,y_是真实的label。因为卷积神经网络会用到2D的空间信息,所以需要把784维数据恢复成28*28结构,使用的是tf.shape函数
x = tf.placeholder(tf.float32, [None, 784])
y_ = tf.placeholder(tf.float32, [None, 10])
x_image = tf.reshape(x, [-1, 28, 28, 1])
#定义第一个卷积层
W_conv1 = weight_variable([5, 5, 1, 32])
b_conv1 = bias_variable([32])
h_conv1 = tf.nn.relu(conv2d(x_image, W_conv1) + b_conv1)
h_pool1 = max_pool_2_2(h_conv1)
#定义第二个卷积层
W_conv2 = weight_variable([5, 5, 32, 64])
b_conv2 = bias_variable([64])
h_conv2 = tf.nn.relu(conv2d(h_pool1, W_conv2) + b_conv2)
h_pool2 = max_pool_2_2(h_conv2)
#定义第一个全连接层
W_fc1 = weight_variable([7 * 7 * 64, 1024])
b_fc1 = bias_variable([1024])
h_pool2_flat = tf.reshape(h_pool2, [-1, 7 * 7 * 64])
h_fc1 = tf.nn.relu(tf.matmul(h_pool2_flat, W_fc1) + b_fc1)
#在数据较小情况下,为防止过拟合,随机将一些结点置零,增加网络泛化能力
keep_prob = tf.placeholder(tf.float32)
h_fc1_drop = tf.nn.dropout(h_fc1, keep_prob)
#最后一个输出层也要对权重和偏差进行初始化
W_fc2 = weight_variable([1024, 10])
b_fc2 = bias_variable([10])
y_conv = tf.nn.softmax(tf.matmul(h_fc1_drop, W_fc2) + b_fc2)
#定义损失函数和训练的步骤,用adam优化器最小化损失函数
cross_entropy = tf.reduce_mean(-tf.reduce_sum(y_ * tf.log(y_conv), reduction_indices = [1]))
train_step = tf.train.AdamOptimizer(1e-4).minimize(cross_entropy)
#计算预测的精确度
correct_prediction = tf.equal(tf.argmax(y_conv, 1), tf.argmax(y_, 1))
accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32))
#对全局变量进行初始化,迭代2000训练,使用的minibatch为50,所以总共训练的样本数量为10万
tf.global_variables_initializer().run()
for i in range(2000):
    batch = mnist.train.next_batch(50)
    if i % 100 == 0:
        train_accuracy = accuracy.eval(feed_dict = {x: batch[0], y_: batch[1], keep_prob: 1.0})
        print("step %d, training accuracy %g"%(i, train_accuracy))
    train_step.run(feed_dict = {x: batch[0], y_: batch[1], keep_prob: 0.5})
#输出最后准确率
print("test accuracy %g"%accuracy.eval(feed_dict = {x: mnist.test.images, y_: mnist.test.labels, keep_prob: 1.0}))


训练结果如下

三、部分细节阐释

3.1

from tensorflow.examples.tutorials.mnist import input_data
import tensorflow as tf
mnist = input_data.read_data_sets("MNIST_data", one_hot = True)
sess = tf.InteractiveSession()

在线下载mnist数据集时,有可能会出现网络连接错误。我们可以选择先将数据集下载到本地,然后下载路径即可。

3.2

def weight_variable(shape):
    initial = tf.truncated_normal(shape, stddev = 0.1)
    return tf.Variable(initial)

def bias_variable(shape):
    initial = tf.constant(0.1, shape = shape)
    return tf.Variable(initial)

#tf.truncated_normal(shape,mean=0.0,stddev=1.0,dtype=tf.float32,seed=None,name=None) 从截断的正态分布中输出随机值。生成的值服从具有指定平均值和标准偏差的正态分布,如果生成的值大于平均值2个标准偏差的值则丢弃重新选择。

参数:

shape:一维张量,也是输出张量。

mean:正态分布的均值

stddev:正态分布的标准差

dtype:输出类型

seed:一个整数,当设置之后,每次生成的随机数都一样

name:操作的名字

这个函数产生正态分布,均值和标准差都是自己设定。

#tf.Variable.init(inital_value,trainable=True,collections=None,validate_shape=True,name=None)

 只有第一个参数initial_value是必须的。

因为在tensorFlow的世界里,变量的定义和初始化是分开的,所有关于图变量的赋值和计算都要通过tf.Session的run来进行。想要将所有图变量进行集体初始化时,应该使用tf.global_variables_initializer.

tf.Variable是定义图变量的一种方式,另一种是tf.get_variable,其必需参数(第一个参数)并不是图变量的初始值,而是图变量的名称。但tf.Variable的用法更要丰富一点,当指定名称的图变量已经存在时表示获取它,当指定名称的图变量不存在时表示定义它。

3.3

tf.nn.conv2d(input,w,strides,padding)

其中input为输入,格式为[batch,height,width,channels],分别为【输入的批次数量,图像的高(行数),宽(列数),通道(彩色为3,灰色为1)】

w为卷积矩阵,二维,分别为【高,宽】

strides为滑动窗口尺寸,分别为[1,height,width,1],通常strides[0]=strdes[3]=1,因为一般不会在一个个图像,一个个通道之间滑动

padding为扩展方式,有vaild和same(vaild是采用丢弃的方式,比如上述的input_width=13,只允许滑动2次,多余的全部丢掉;same的方式,采用补全方式,对于上述情况,允许滑动三次,但是需要补三个元素,左奇右偶,在左边补一个0,右边补两个0)

鉴于篇幅问题,下一篇中将函数等再做相应介绍。

猜你喜欢

转载自blog.csdn.net/gyx1549624673/article/details/82822503