一、引言
神经网络不是具体的算法,而是一种模型构造的思路或者方式,BP神经网络是通过线性分类器后面直接跟随激励神经元,然后前后收尾相连接形成网络的方式。每一个神经元节点的输入都来自于上一层的每个神经元的输出,这种方式叫做“全连接”。当然BP神经网络也可以不是全连接的,后面会讲到。
全连接的好处在于从他的连接方式上看是每个输入维度的信息都会传播到其后的任意一个结点中去,会最大程度地让整个网络中的节点都不会“漏掉”这个维度所贡献的因素。不过他的缺点更加明显,那就是整个网络由于都是“全连接”方式,所以w和b参数格外的多,这就使得训练过程中说要更新的参数权重非常多,整个网络训练的收敛会非常慢。于是发明了卷积神经网络(convolutional neural network,CNN)。
卷积神经网络同样是一种前馈神经网络,他的神经元可以响应一部分覆盖范围内的周围单元,在大规模的模式识别有很好的性能表现,该网络可以避免对图片的复杂前期预处理,直接输入原始图像。
卷积网络的两大特点:
(1)、卷积网络由至少一个卷积层,用来提取特征
(2)、卷积网络的卷积层通过权值共享的方式进行工作,大大减少权值w的数量,使得在训练中达到同样识别率的情况下收敛速度明显快与全连接BP网络。
二、卷积
卷积是通过两个函数f和g生成第三个函数的一种数学算子,表征函数f和g经过翻转和平移的重叠部分的面积。
卷积的数学定义是这样的:$$h(x)=f(x)*g(x)=\int_{-\infty }^{+\infty }f(t)g(x-t)dt$$
$f(t)g(x-t)$是,$f(t)$先不动,$g(-t)$相当于$g(t)$函数的图像沿着y轴(t=0)做一次翻转。$g(x-t)$相当于g(-t)的整个图像沿着t轴平移,向右平移x个单位。卷积的值等于$f(t)$和$g(x-t)$相乘后与y=0轴围城的面积。在随着x变化移动的过程中,由于g(x-t)移动产生的h(x)的对应变化就是整个卷积公式的意义了————一个移动中用x进行取样的过程,或者说特征提取。
三、卷积核
理解了卷积之后再理解卷积核,就容易多了,我们只需要理解它是在滑动中去特征提取就足够了。
卷积核的表达式:$$f(x)=wx+b$$
我们先假设有这样一幅5*5一共25个像素点的图片,每个像素点只有1和0两种取值。我们设计一个卷积核:
$$w= [1,0,1,0,1,0,1,0,1],b=[0]$$
指定图片中从左到右从上到下的9个像素作为x,挨个与w相乘完成内积。
$$f(x)=1*1+0*1+1*1+0*0+1*1+1*0+0*1+0*0+1*1+0=4$$
结果右边的正方形第一个存储空间就会输出6,这些存储空间就叫做卷积层的feature map,也就是图中convolved Feature部分。9个信息被压缩成一个,这当然属于无损压缩,还原肯定是还原不回去的了。这个抽样的过程就叫做特征的提取。同时也是一种信息压缩。
卷积层的工作就是这样,至于w和b值初始化之后,也是通过一轮一轮的训练,在降低损失函数的目的下不断的学习和更新。
在卷积核的$f(x)=wx+b$输出后一般会跟一个激励函数紧随其后,现在的CNN网络中的激励函数非常喜欢用ReLU函数,
四、卷积层的其他参数
还有两个重要的参数需要注意一个是Padding(填充),一个是Striding(步幅)。
4.1 Padding(填充)
填充是指用多少像素单位来填充输入图像(向量)的边界。如下图所示,图片的四周区域都进行Padding,通常都是用来填充0,一般在800*600的图过卷积层的时候,能够在四周各Padding上5-10个单位就不少了。
Padding大概有两种目的,
目的1:保持边界信息。如果不加Padding的话,最边缘的信息其实仅仅被卷积核扫描了一遍,而图片中间的像素点信息被扫描了多遍,在一定程度上就爱你工地了边界上信息的参考程度。
目的2:如果输入的图片尺寸有差异,可以通过Padding来进行补齐,使得输入的尺寸一致,以免频繁调整卷积核和其它层的工作模式。
4.2 Stride(步幅)
Stride可以理解为每次滑动的单位。一般取Stride=1,当Stride比较大的时候,会跳过很多信息,但好处是处理时间大大的缩短。
在设计网络的时候,先设置Stride=1,如果工作状况已经很理想,而希望通过加大Stride来获得一些性能的提升或者存储量的减少。那么可以通过适当的调整Stride=2,来获得速度的提升。
五、池化层
池化层实际上是对Feature Map所做的数据处理之后又进行了一次所谓的池化处理。常见的两种池化操作有两种:
一种叫做Max Pooling,就是在前面输出过来的数据做一个取最大值的处理,
另一种叫做Mean Pooling,就是在前面输出过来的数据做一个取平均值的处理。
池化层有这么几种功能:
第一:它进行了一次特征提取,减小了下一层数据的处理量
第二:这个特征提取,能够有更大的可能性进一步获取更为抽象的信息,防止过拟合,也可以说提高了一定的汉化能力。
第三:能够对输入的微小变化产生更大的容忍。
激活函数
Sigmoid 激活函数
sigmoid函数定义如下
$$\sigma (x)=\frac{1}{1+e^{-z}} ;z=wx+b$$
如下图所示:实现sigmoid函数的代码
import matplotlib.pyplot as plt import numpy as np def sigmoid(x): # 直接返回sigmoid函数 return 1. / (1. + np.exp(-x)) def plot_sigmoid(): # param:起点,终点,间距 x = np.arange(-6, 6, 0.001) y = sigmoid(x) print("x等于0的点", sigmoid(0)) plt.plot(x, [0.5] * len(x), 'b:') # 画水平虚线 plt.plot([0] * len(y), y, 'b:') plt.plot(x, y, "r") plt.show() if __name__ == '__main__': plot_sigmoid()
Sigmoid
当输入在$(-\infty ,+\infty)$的区间上变化,位于$(0,1)$区间上的输出值变化很小,sigmoid函数是连续的,
以sigmoid为激活函数的神经单元具有感知器类似的行为。
ReLU 激活函数
ReLU被称为修正线性单元,ReLU的函数定义为:
$$f(x)=max(0,x)$$
对于负值函数值为零,对于正值函数值呈线性增长。
SOFTMAX 激活函数
神经元中有两个部分,一个是线性单元,一个是激励函数(非线性)。SOFTMAX的函数表达式是:
$$SOFTMAX:a_i=\sigma_i(z)=\frac{e^{z_i}}{\sum_{j=1}^{m}e^{z_j}},z_i=w_ix+b$$
作用:把神经元中线性部分输出的得分值(score),转换为概率值。softmax输出的是(归一化)概率,
含有softmax激活函数的网络层有这样一个性质:$\sum_{i=1}^{j}\sigma _i(z)=1$,可以解释为每个节点的输出值小于等于1。softmax激励函数通常在神经网络的最后一层作为分类器的输出,输出值(概率)最大的即为分类结果。
$$猫:\begin{pmatrix}0.05\\ 0.05\\ 0.7\\ 0.2\end{pmatrix}
狗:\begin{pmatrix}0.8\\ 0.06\\ 0.01\\ 0.04\end{pmatrix}$$
交叉熵 损失函数
SOFTMAX的损失函数叫做“交叉熵”。假如从盒中黑球的个数为16,白球的个数为14,那么从盒中抽到黑球的先验概率为$\frac{16}{30}$,熵越大,事件的不确定性越大。
交叉熵的表达式为:$$L=-\sum_{i}y_i\log a_i$$
式中$a_i$是softmax激励函数的输出,$y_i$是线性单元的输出值。
交叉熵所描述的是经过训练分类结果的信息熵和测试集分类结果的信息熵之间的差距。在拟合过程中产生的熵是有差距的,这个差距由交叉熵来定义。那么只要熵差越小,就越接近真实值。
$$标签分类结果:\begin{pmatrix}1\\ 0\\ 0\\ 0\end{pmatrix}
训练分类结果:\begin{pmatrix}0.8\\ 0.05\\ 0.1\\ 0.05\end{pmatrix}$$
使用交叉熵作为损失函数可以避免梯度消散。
One-hot编码 OHE
独热编码(one-hot encoding):使用一个向量的每一个维度来标识一种性质的有无。
$$猫:\begin{pmatrix}1\\ 0\\ 0\\ 0\end{pmatrix}
狗:\begin{pmatrix}0\\ 1\\ 0\\ 0\end{pmatrix}
蛇:\begin{pmatrix}0\\ 0\\ 1\\ 0\end{pmatrix}
猪:\begin{pmatrix}0\\ 0\\ 0\\ 1\end{pmatrix}$$
梯度消失问题
梯度消失就是训练的损失函数$Loss$不再收敛,$Loss$过早的不再下降,精确度也过早的不再提高。有两种方法:
方法一:初始化一个合适的w
假设我们把w初始化成10,在sigmoid函数为0的地方,导数的绝对值是$\frac{1}{4}$,这样看来确实是大了,但是原来是导数太小导致网络前端的w更新太慢,后者是变化率太高,一次变化量太大。
方法二:选一个合适的激活函数——ReLU
$$y=\left\{\begin{matrix}0&&x \leq 0\\ x&&x>0\end{matrix}\right.$$
这个激活函数的优点在于:在第一象限不会有明显的梯度下降问题,因为导数恒为1,并且求导计算时间比sigmoid小很多。
归一化
归一化的目的就是让各维度的数据分布经过“拉伸”投影到一个近似的尺度范围去。
常见的归一化方法有:线性函数归一化、0均值归一化,通过softmax函数也是一种归一化操作。
线性函数归一化:$X_{norm}=\frac{X-X_{min}}{X_{max}-X_{min}}$
零均值归一化:各维度的值减去各自的平均值,这样就有一堆有正有负的值且0在中心位置。
参数初始化问题
在搭建神经网络的时候,我们有件事情不得不做,那就是参数w、b等的初始化,业界来说,把整个网路中所有的w初始化成以0为均值、以一个很小的$\mu$为标准差的正态分布的方式会比较好。
最常见的是以0为均值、1为方差的正态分布来随机初始化。
还有一种常见的初始化方法是以0为均值、1为方差的正态分布生成后除以当前层的神经元个数的算术平方来获得初始化。
如果你非要问为什么,思路大致是这样的:对于一个模型中输入权重的设置相当于一种重视程度,而一个模型中需要我们非常重视或者不怎么重视的输入一定是少数,而其他大部分输入的信息可能就比较中庸,对判断结果影响比较小的因素的信息比较多。
说来也有意思,自然界中大部分数据的统计分布都服从高斯分布的特点。
正则化
L1正则化惩罚项: $Lose = {Lose}_0 + \frac{\lambda }{n}\sum_{w}|w|$
L2正则化惩罚项: $Lose = {Lose}_0 + \frac{\lambda }{2n}\sum_{w}|w^2|$
我们对损失方式添加了正则化惩罚项来进行改造。加上正则化惩罚项能在一定程度上避免过拟合。
前一部分的损失函数叫做“经验风险”,经验风险就是由于拟合结果和样本标签之间的残差总和所产生的这种经验差距所带来的风险。
后一部分的损失函数叫做“结构风险”,结构风险是因为我们希望这种描述能够简洁来保证其泛化性的良好。
注意$\lambda$不是指学习率,而是一个权重,也可以称为正则化系数,表示对后面这部分的“重视程度”。
举个例子,比如我们两个模型的输入权重系数为下所示,我们使用L2正则化惩罚系数,为了方便我们只计算“结构风险”
$$W_1=\begin{pmatrix}1\\ 0\\ 0\\ 0\end{pmatrix}
W_2=\begin{pmatrix}\frac{1}{4}\\ \frac{1}{4}\\ \frac{1}{4}\\ \frac{1}{4}\end{pmatrix}$$
$$\sum W_1^2=[1,0,0,0]^2=1^2+0^2+0^2+0^2=1$$
$$\sum W_2^2=[\frac{1}{4},\frac{1}{4},\frac{1}{4},\frac{1}{4}]^2={\frac{1}{4}}^2+{\frac{1}{4}}^2+{\frac{1}{4}}^2+{\frac{1}{4}}^2={\frac{1}{4}}$$
由上可见,模型2的结构风险比模型1的小,所以模型2的损失函数小,模型2的泛化能力更强。这就是正则化的好处。
其他的超参数
超参数是指那些在机器学习算法中训练前设定的一些初始化参数,这些参数没法通过算法本身来学习。比如:$W_0,b_0$,还有学习率$\eta$
不唯一模型
我们如果训练多次,比较结果深度学习的模型可能会每次都不一样,因为训练过程中的随机因素比较多,几乎遍布整个训练过程,比如:随机梯度下降算法选取的求梯度数据,还有去正态分布的权值。
卷积网路做图片分类
用CNN应用于CIFAR-10的一个实验。声明一点,普通的全连接BP网络跑一下CIFAR-10也是一个比较好的结果,只不过用CNN跑能够收敛的更快,更高的精确度。项目中包含60000张32*32像素的彩色图片,拥有10个不同类别的标签。其中训练集50000张,测试集10000张。tensorflow官方在github提供了CNN做CIFAR10实验的代码,位置在models/tutorials/images/cifar10。
单个GPU版本:cifar10_trian.py,以及cifar10.py、cifar10_input.py。
多个GPU版本:cifar10_multi_gpu_train.py