cs231n笔记:lecture6

Training Neural Networks

Activation Functions **函数

cs231n笔记:lecture6
在神经网络中选取合适的**函数非常重要,前面我们已经接触了一些**函数,比如处理二分类的sigmoid函数,还有relu函数等等,损失函数很多,接下来我们详细介绍一下损失函数的细节。

Sigmoid**函数

cs231n笔记:lecture6
先来看sigmoid函数,看的出来sigmoid会把所有的输入都挤压到0到1这个区间内,在以前很流行使用。
优点:sigmoid函数因为0到1的数值可以很好地解释为这个神经元的**程度,为0则说明这个神经元没有**,为1是**程度最高。
缺点:sigmoid函数从图像中可以看出来,它在两端是平的,意思就是输入太小或者太大都会杀死梯度,梯度接近为0,那么这个神经元就得不到更新,相当于“死掉“了。还有一个缺点就是sigmoid函数的输出不是关于0对称的,它的输出总是正数,这会导致一些有趣的问题。我们来仔细分析一下如果**函数的输出都是正值会是什么情况,如果某一层网络的**函数值都是正值,那么这些正值将会作为输入传递给下一层网络,那么对该层网络来说,所有的输入都是正值。
好,现在我们来看其中的元素,这里仅分析单个输入,即mini-batch为1的情况,我们把输入记为X1长向量,权重为W矩阵包含W1,W2…向量,那么对于W的梯度,就等于上游梯度X1,或者更详细的说对于W11(W1中的元素)的梯度就等于W1X1的梯度乘X11,W12的梯度就等于W1X1的梯度乘X12,我们一直X全为正,那么根据上游梯度即W1X1的梯度的正负就能能得到W1中,W11,W12…的梯度要么全为正,要么全为负,即在W1中每个参数的更新方向只能要么全为正要么全为负,这就会存在经典的zigzag问题,因为可能W1中有的参数是希望增大,有的是希望减小的,而不是统一全部增大或全部减小。 下图是这一现象的表示。如果希望往图中蓝线的方向更新参数,只能先让两个维度的参数同时增加或者同时减小,曲折的到达蓝色位置,这样十分缓慢。

cs231n笔记:lecture6
还有一个类似sigmoid的**函数,叫tanh这里就不详细介绍了,它主要是把输出值挤压到了-1到1的位置,解决了sigmoid不关于0对称的问题,但是在饱和区域输入稍大或者稍小一点还是会存在梯度消失问题。

Relu**函数

cs231n笔记:lecture6
优点:Relu的形式非常简单就是把输入与0做一个取最大值的操作,所以Relu的计算是十分简单的,不像前面sigmoid有指数计算,而且因为relu没有饱和区域,只要输入为正数那么relu始终都会有梯度1,不会像sigmoid一样出现饱和区域梯度消失的现象,而且在实际使用中,relu会比sigmoid收敛速度快的多。
缺点:Relu仍然不是关于0对称的,还是会出现zigzag问题,不过可以通过后面讲的批归一化解决。
还有一个问题就是当输入为负值时,relu是会直接输出0的,而且梯度也会为0,如果我们权重初始化的不好,将会有很多神经元得到负值,那么这些神经元就直接进入了dead area,他们就直接“死掉”了,没有梯度,也永远不会被更新。
但是Relu仍然是现在最主流的**函数用法,建议使用。下图给出了课程给出的关于**函数的一些建议:
cs231n笔记:lecture6

Data Preprocessing

在神经网络中,数据初始化也是很重要的一部分,记得我们前面提到的zigzag问题吗,就是说输入如果都是正值或负值会出现的问题,所以恰当的初始化很重要,最好是得到0均值的数据。在其他领域可能还有更多其他的初始化方法比如PCA,Whitening等等,但是在计算机视觉,通常是做一个减均值除标准差的操作,得到均值为0标准差为1的数据分布。如下图一个可以让我们清楚看到数据预处理作用的例子。
cs231n笔记:lecture6
如图,如果数据没有经过预处理,分布在偏离0的位置,那么网络在做优化时就会十分困难,因为一点点细微的参数调整就会使得分类结果又很大不同,只能使用十分微小的学习率去做优化,迭代很慢,而如果经过数据预处理,就可以使用相对大的学习率去调整,使得分类效果优秀。
下面是一些经典网络结构使用的数据预处理方法:
cs231n笔记:lecture6

Weight Initialization

权重初始化也是一个很重要的话题,给权重一个恰当的初始化会让网络的表现优秀,同时如果初始化不合适的话,可能会让网络训练十分缓慢甚至出现梯度消失无法收敛的情况。
想象一下如果网络全部初始化为一个常数例如0的时候,会怎么样?显然无论某一层有多少个神经元,他们都会做同样的计算,执行同样的梯度更新,就相当于一层只有一个神经元,这显示是不合适的。所以把权重初始化为同一个参数是肯定行不通的。
一个简单的想法是用小的随机数进行初始化,下图我们给出了使用随机取样的标准正态分布并乘以0.01得到的小随机数来初始化权重,并采取tanh函数作为**函数,观察一下网络每层的输出结果分布

cs231n笔记:lecture6
可以看到,一开始第一层网络还能给出比较不错的数据分布,不会集中在哪一处,但是随着网络的深入,因为我们一直是会乘以一个小的随机数,所以之后网络得到的结果会越来越小,等到了第六层,网络的输出已经大部分是0了,这也是灾难,因为分布都在0会也会使得梯度消失,无法更新。
如果我们用稍大的参数初始化会怎么样呢,我们把权重初始化为正态分布*0.05的随机数,再观察一下数据分布情况:
cs231n笔记:lecture6
在用大的随机数初始化后,数据的输出又聚集到了-1,1的位置,因为tanh在这两个位置也是没有梯度的,所以网络仍然无法更新。
所以,我们希望得到的初始化方式是要跟**函数相对应的,我们希望通过适当的初始化方法使得输出数据在分布上都分布在**函数有梯度的部分,而且标准差要类似输入数据,使得输出数据分布不能集中在一点,这样就更有利于优化。
基于此,下面介绍一种优秀的初始化方法,Xavizer初始化,下图我们观察用xavier初始化配合tanh**函数的效果:

cs231n笔记:lecture6
cs231n笔记:lecture6

先来详细解释一下这种图:
首先要说明一下,这里输入的X也是从标准正态分布中采样,所以假定输入X的均值为0,且X是独立同分布,X与W独立。先看结果,发现xavier初始化取得了比较不错的结果,在没有归一化操作的情况下,第六层网络仍然能保持不错的分布情况而且每两层之间的标准差也差得很小。
下面就证明一下为什么xavier这样的初始化形式能取得这样的效果。
如上图,y = Wx,h = f(y), 有概率论里面的知识,Var(y) = Din * Var(XiWi),这里是因为假定了W,X都是独立同分布的,然后展开得到Var(y)=DinVar(Xi)*Var(Wi),所以只要w的方差等于1/din就好了。所以把W用Xavier初始化再用tanh**之后,输出仍然具有良好的分布,与输入分布相差很小,输出都分布在有梯度的位置,可以比较好的更新参数。
但是如果我们选择更优秀的**函数Relu的时候会怎么样呢?,因为Relu**函数跟tanh不一样,relu不是关于0对称的,relu**之后只会得到正值,所以如果用relu和xavier初始化,第一层应该仍然能得到不错的输出分布,但是在深层之后因为relu的特性,对于深层网络而言x就不是0均值的了,x更不符合正态分布,所以破坏了xavier初始化要求E(X)=0的要求,所以选用relu**函数的话,xavier初始化应该不能取得很好地效果,可以看图验证一下:

cs231n笔记:lecture6
果然,在网络深层,输出值又聚集在了0附近,大量的输出为0,因为relu在0处的梯度也是0所以也会导致梯度消失,网络无法收敛,那么对于relu**函数应该选用什么初始化方法呢,答案是kaiming初始化,课程中只是给出了kaiming初始化的形式,并没有给出严格的数学推导证明过程,关于数学推导过程还存在疑问,等到解答。先来看kaiming初始化的形式:
cs231n笔记:lecture6
与xavier很相似的是,kaiming初始化只是把1/Din,改成了2/Din,就是让W服从均值0,标准差根号2/Din的分布就得到了很好的效果,我们看到在六层以后,仍然能保持0.81的标准差,几乎与第一层的输
出一致。效果真的很好,至于仍然有很多0的数据,这是由于relu函数的特性决定的,仅通过初始化时无法解决这个问题的,只能通过后面提到的批归一化来解决。总之关于权重初始化的内容,kaiming初始化的数学推导还存在问题,但是毫无疑问,kaiming初始化效果十分优秀。
总结下来,如果是对于tanh**函数,使用Xavier初始化是很好的选择,对于现在常用的**函数relu,我们通常选用kaiming初始化方法

Batch Normalization

batch normalization的思想很简单,就是强行在每个输出后面加入一个batch normalization操作把它变成均值为0标准差为1的标准正态分布。具体做法就是在每个通道上分别求均值和方差,然后再减去均值,除以标准差,得到标准的正态分布。再把这个输出作为输入传递给下一层网络。
cs231n笔记:lecture6
在测试阶段的时候每一层也要用到归一化,而且要用测试数据减去训练数据的均值和标准差。
(这里存在问题,就是测试阶段是减去训练数据的均值除以训练阶段的标准差,那么是减去训练阶段每个batch的总均值和除以总标准差吗。还有个问题就是为什么用训练阶段的均值和标准差,用测试阶段自己的均值和标准差做归一化不行吗?)
cs231n笔记:lecture6

一般的做法是把bn层放在全连接网络之后,和非线性**函数之前。
例如经典的卷积神经网络模型就是 conv层-bn层-relu-conv层-bn层-relu堆叠在一起再最后脸上全连接层得到输出分类结果

cs231n笔记:lecture6

总结一下这节课的知识
使用relu**函数
在视觉任务中使用减均值除标准差的数据预处理方式
对于tanh使用xavier初始化,对于relu使用kaiming初始化
在卷积或者全连接之后,**函数之前使用batch normolization
cs231n笔记:lecture6