本文根据最近学习TensorFlow书籍网络文章的情况,特将一些学习心得做了总结,详情如下.如有不当之处,请各位大拿多多指点,在此谢过。
一、相关性概念
1、卷积神经网络(ConvolutionNeural Network,CNN)
19世纪60年代科学家最早提出感受野(ReceptiveField)。当时通过对猫视觉皮层细胞研究,科学家发现每一个视觉神经元只会处理一小块区域的视觉图像,即感受野。20世纪80年代,日本科学家提出神经认知机(Neocognitron)的概念,被视为卷积神经网络最初的实现原型。神经认知机中包含两类神经元:S-cells和C-
cells。S-cells用来抽取特征,对应我们现在主流卷积神经网络中的卷积核滤波操作;C-cells用来抗形变,对应现在的激活函数、最大池化(Max-
Pooling)等操作。
一般情况下,卷积神经网络由多个卷积层构成,每个卷积层通常会进行如下操作:
(1) 图像通过多个不同的卷积核的滤波,并加偏置(bias),提取出局部特征,每一个卷积核会映射出一个新的2D图像。
(2) 将前面卷积核的滤波处理结果,进行非线性的激活函数处理。目前最常见的是使用ReLU函数,之前Sigmoid函数应用较多。
(3)多激活函数处理的结果再进行池化操作(即降采样,例如:将44的图片降为11的图片),一般会使用最大池化,保留最显著特征,并提升模型畸变容忍能力。
这几个步骤就构成了最常见的卷积层,也可以再加上一个LRN(LocalResponse
Normalization,局部响应归一化层)层,现在非常流行的Trick还有BatchNormalization等。
2、池化层
3、卷积核尺寸
4、神经网络算法相关特性
4.1、优点
(1)可以高效提取特征。
当我们面对一个分类任务时,传统的机器学习算法,一般要首先明确feature和label,然后拿数据取“喂”训练模型并保存,最后测试模型的准确性。这就需要我们确定好特征,当特征数目很少就无法精确进行分类而引起欠拟合;当特征数目很多,又会在分类过程中太过于看重某个特征引起分类错误,产生过拟合。而神经网络则不需要做大量的特征工程,可以直接把数据“灌”进去而让其自身训练,自我“修正”,即可达到预期效果。
(2)数据格式更加简易
利用传统的机器学习解决分类问题时,数据不能直接“灌”进去的,需要对数据进行一些处理,譬如量纲的归一化,格式的转化等等,然而在神经网络里却不需要额外对数据做过多的处理。
(3) 参数数目的少量性
同样在面对一个分类问题时,利用传统机器学习SVM来做的话,需要涉及核函数,惩罚因子,松弛变量等等参数,而这些不同的参数组合会对模型效果产生不一样的影响,想要迅速而又准确的得到最适合模型的参数,需要对相关理论知识有深入研究,但对于一个基本的三层神经网络来说(输入-
隐含-
输出),只需要初始化时给每一个神经元上随机的赋予一个权重w和偏置项b,在训练过程中,这两个参数就会不断的修正,调整到最优质,使模型的误差最小。所以从这个角度来看,我们的工作效率会更佳。
4.2、缺点
如果我们加深我网络层,每一个网络层都增加神经元数量,则参数的个数将是M*N(m为网络层数,N为每层神经元个数),这样一来参数很多,引起模型复杂化,就更加不好调参,进而会更加容易导致过拟合。另外,从神经网络的反向传播的过程来看,梯度在反向传播时,不断的迭代会导致梯度越来越小,引起梯度趋近于0(梯度消失),梯度消失就使得权值无法更新,这个神经元的存在就毫无意义,很难导致收敛。尤其是在图像领域,直接使用最基本的神经网络,是不合理的。
二、卷积神经网络基本原理
1、基本阐述
现在有一图像,其尺寸大小是1000像素1000像素且设定为黑白图像,也就是只有一个颜色通道,则一张图片就要100万个像素点,输入数据维度也是100万维。如果连接的现在隐含层大小也是同样大小(100万个隐含节点),最后将产生100万100万即一亿万个连接。仅仅一个全连接(FullConnected
Layer),就有一万亿连接的权重需要去训练,目前看,显然是不划算不现实。
通过局部连接(LocalConnect)方法优化解决:由于每一个感受野只接受一小块区域的信号,且这一小块区域内的像素是互相关联的,每一个神经元不需要接收全部像素点的信息,只需要接收局部的像素点作为输入,而后将所有这些神经元收到的局部信息综合起来,就可以得到全局信息。假设局部感受野大小是1010,即每个隐含节点只与1010个像素点相连,现在只需要10*100万即1亿个连接。
现在隐含层每一个节点都与1010的像素相连,即每一个隐含层节点都拥有100个参数。假设我们的局部连接方式是卷积操作,即默认每一个隐含节点的参数都完全一样,这样参数从1亿降为100。不管图像大小是多大,一律都是这个1010=100个参数,即卷积核尺寸,显然卷积核对缩小参数数量贡献非常大、意义非凡。因此,此时,我们不需要再担心有多少隐含节点或者图片多大,参数量只跟卷积核的大小有关,即所谓的权值共享。
总结:卷积神经网络要素是局部连接(LocalConnection)、权值共享(WeightSharing)和池化层(Pooling)中的降采样(Down-
Sampling)。其中,局部连接和权值共享降低了参数量,训练复杂度被大大下降、过拟合被减轻。同时,权值共享还赋予了卷积网络对平移的容忍性,而池化层降采样则进一步降低了输出参数量,并赋予模型对轻度形变的容忍性,提供了模型的泛化能力。
2、LeNet5
1994年,大名鼎鼎的LeNet5诞生,作为最早的深层卷积神经网络之一,推动了深度学习的发展。自1998年开始,在多次成功迭代之后,由Yann
LeCun完成的开拓性成果被命名为LeNet5。LeCun认为,可训练参数的卷积层是一种利用少量参数在图像的多个位置上提取相似特征的有效方式,这和直接把每个像素作为多层神经网络的输入不一样。像素不应该被使用在输入层,因为图像具有很强的空间相关性,而使用图像中独立的像素直接作为输入则利用不到这些相关性。笔者认为,这些内容比较重要。
在当时,LeNet5的特性如下:
(1)每个卷积层包含三个部分:卷积、池化和非线性激活函数;
(2)使用卷积提取空间特征;
(3)降采样(Subsample)的平均池化层(AveragePooling);
(4)双曲正切(Tanh)或S型(Sigmoid)的激活函数;
(5)MLP作为最后的分类器;
(6)层与层之间的稀疏性连接减少计算复杂度。
三、TensorFlow 实现简单的卷积网络
1、简要说明
这里使用的数据集依然是MNIST,使用两个卷积层加一个全连接层构建一个简单但非常有代表性的卷积神经网络,预计准确率约为99.2%左右。
2、实现过程
#载入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_2x2(x):
return tf.nn.max_pool(x, ksize=[1, 2, 2, 1],
strides=[1, 2, 2, 1], padding='SAME')
#在设计卷积神经网络结构之前,定义输入的placeholder,x是特征,y_是真实Label。
#由于卷积神经网络会使用到空间结构信息,所以,需要将1D的输入向量转为2D图片结构,即从1*784的形式转换为原始的28*28结构。
#因为只有一个颜色通道,所以最终尺寸为[-1,28,28,1],其中‘-1'代表样本数量不固定,'1'代表颜色通道数量。
x = tf.placeholder(tf.float32, [None, 784])
y_ = tf.placeholder(tf.float32, [None, 10])
x_image = tf.reshape(x, [-1,28,28,1])
#定义第一个卷积层。
#先使用前面函数进行初始化,包括weights和bias。其中[5,5,1,32]代表卷积核尺寸为5**5,1个颜色通道,32个不同的卷积核。
W_conv1 = weight_va