cs231n_lecture_6_怎样训练神经网络-PART 1
之前我们学习了怎样用计算图表示一个函数;神经网络——score function、loss function;CNN——用convolutional layer表达空间结构,conv layer的activation maps来自不同filter的卷积;通过最优化的方法得到参数值;更新的时候我们用的方法是mini-batch SGD.其方法是我们先=随机得到一组weights,在训练集上随机采样得到一批数据,在computational graph上forward prop计算loss,然后back prop计算梯度,更新我们的weights,直到我们的loss小于误差。
这节课我们将学习训练NN的一些细节。
包括哪些东西呢?
1.one time setup
**函数的选取;预处理;权重初始化;正则化;梯度检查
2.training dynamics
检查调整学习过程;参数更新;超参数的最优化
3.评估模型效果
Activation functions
常见的**函数:
这些函数是不是用哪个都可以呢?他们之间有什么区别呢?可不可以对这些函数进行修改呢?下面我们一一探讨。
首先我们看到近些年常用的**函数都是非线性的,这一点符合神经细胞输出复杂的特性。
sigmoid函数
1.sigmoid将所有输入x压缩到[0,1]之间,如果x正的比较大,输出为1;x负的比较大,输出接近0;如果x在0附近,输出类似于一个线性函数。
2.sigmoid曾经十分流行,你可以把它看成是一个有饱和的**率的神经元。
缺点:
1.容易kill gradient
比如,当x=-10时,梯度=0,反向传播时参数就没有更新,x=10时类似。只有当x在0附近时,能得到一个合适的梯度。
2。输出不是以0为中心的
f(WiXi + b)
当x都是正数或负数的时候,loss对w的导数(DL/DF * DF/DW)就全为正或负,这样当你在更新W的时候,所有Wi都朝着同一个方向更新,要么都增加要么都减少。
这是一种非常低效的更新方式,下图中蓝色是最优的W的向量,要找到这个W,我们要经过很多迭代才能到达。
这也引出了一个问题——我们总是希望zero-mean data!
3.exp() 计算上较复杂。
tanh(x)
输出范围是[-1,1],是zero-center了。但是仍然存在killgradient的问题。
ReLU ——Rectified Linear Unit,2012年首次被使用
这是我们在之前的CNN中使用的函数。
1.在+ region没有饱和的问题了
2.计算也快,max(0,x)
3.在实际中比sigmoid和tanh收敛更快,大概快6倍
4.输出更接近生物学上的神经元than sigmoid
缺点:
1.不是zero-center的
2.当x<0时,仍然会饱和,kill gradient
存在dead ReLU的情况:
当你的learning rate很高的时候,一开始的时候ReLU还是OK的,但是由于你的更新步长很大,weights会跳的很高,back prop的数据流就被阻塞了。
在训练的时候,就会出现这种情况:一开始模型更新好好的,从某个点开始,开始变坏直至死掉。这时候你停下来测试一下你的神经网络,给一批输入数据,你会发现有大约10%-20% 的结点死掉了。
这是一个问题,但并没有完全阻碍ReLuctant训练网络。
什么时候导致dead ReLu?
当输入数据使得weights下降的时候会出现。
为了避免dead,有人会给ReLU 初始化一个很小的正的biases,比如0.01.但是实际中往往是有时有用,有时无用,大多数人还是使用zerobiases。
Leaky ReLU
Max(0.01x,x)
1.不会饱和
2.计算简单
3.收敛快than sigmoid/tanh
4.不会dead
参数形式max(ax,x),a不是固定的,而是当做一个需要更新的参数back prop into and learn
ELU——Exponential Linear Units
1.有着ReLU的所有优点
2.更接近zero mean
3.参数a使得其对噪声有一定的鲁棒性
1.相比leaky ReLu还是会有负数区域饱和的情况,当然了比ReLU要好一点
2.exp()计算消耗大
Maxout()
——max(W1.T*x+b1,W2.T*x+b2)
是ReLU和leaky ReLU的推广。
1.线性的
2.不会饱和
3.不会die
1.需要两倍的参数:W1,W2
In practice,指导原则:
1.使用ReLU,小心设置自己learning rates
2.尝试leaky ReLU/maxout/ELU
3.尝试tanh,但是不要抱太大期望
4.不要用sigmoid!
Data preprocessing
在图像领域,我们通常会zero centering data,原因上面介绍过,防止当数据都是pos或者neg,更新低效,使得数据流能顺利完成back prop。
但是不常标准化数据。在ML中,数据很分散,来自不同特征,不同尺度,但是在image中,pixel采样时已经是相对集中分布的了。
在ML中,还有一些更复杂的处理:
通常我们也不会做pca和白化。
在训练的时候我们通常只进行数据中心化处理。
训练时减去mean值,测试时减去同样的mean值。
有两种减去mean值的方法:
以CIFAR-10为例:
1.减去平均图像([32,32,3] 每个像素各自的均值,AlexNet)
2.减去每个通道的均值([3,] 3个通道各自的均值,VGGNet)
这里的中心化能不能解决sigmoid等不是zero-mean的问题?
不能。因为初始输入的zero-mean只能解决第一层的问题,后面各个层就不能保证了。**函数本身不是zero-mean的谁也救不了它。
Weights initialization
初始化为0,即W=0。
A:所有的神经元将做同样的事情(反向传播时得到的梯度值一样,更新操作也一样)。而我们期望的是不同的神经元学习得到不同的事情。
初始化为small random numbers
对小型的网络OK,对深层的网络将会出现问题。
我们可以看一个例子:10层,每层有500个神经元,,使用tanh,w随机初始化为很小的数
我们画出每次迭代层输出的均值和方差:
均值都在0附近,嗯,tanh是0均值的,这是我们想要的;但是我们发现,标准差很快下降,最终变成了0,也就是每一层的输出结果(activations)几乎一样。
我们再画出最后一次activations的分布:
第一层还接近于高斯分布(是我们想要的,why?),后面层的activations迅速缩小,最后几乎都是0了(因为我们一次又一次乘以W,而W很小)。
当我们做back prop的时候会出现什么情况呢?
可以设想我们的x狠下,因为做过zero-mean,当我们求w的梯度时就是x,因为x太小了,所以更新时w几乎不变,依然很小。当下一次forward prop时,w.dot(x)变得更小,形成了一个恶性循环,最终变成了0。
所以训练前我们可以经常想一想:当梯度流返回流过时,在不用的输入下,我们的前向传播会出现什么情况。
那我们就会想到把W初始化大点,还是用tanh():
神经元都饱和了,要么是1要么是-1!也就是说W太大了,W*X很大,梯度都变成0了,W也就不更新了。
那怎样才是一个好的初始化呢?
一种方法是Xavier initialization
这种方法基于一个假设——就是激励是线性的。也就是输入在tanh的线性区。
如果你用的是非线性的ReLU,它会杀掉一般的神经元,你就得不到一个正确的方差(有一半的神经元坏掉了,而你计算方差时算进去了)。activations的分布也会迅速崩塌(应该是相较于一些输入来说w太小了),很多peaks将变成0.
改进如下:
将输入除以2,因为有一半神经元坏掉了,所以输入也只记一半。
初始化时一件需要认真对待的事,它决定了你的网络是得到一个好结果还是直接死掉,这一块有专门的人研究。
Batch normalization
批规范化可以得到单位高斯输入。
步骤:
1.对于输入数据,计算每个维度的mean和var的经验值
2.规范化:
通常将BN插在Fully Connected 或者Convolutional layers之后。
问题是我们需要单位高斯分布的输入吗?
实际上不是太明确。单位高斯的使得输入在非线性**函数的线性区,这样可以防止饱和,但是没人证明饱和就一定是不好的,也许一点点饱和是有益的。
所以说,你想控制你的神经元的饱和程度,在规范化之后,再加一个scaling和shift的操作——乘以一个数scaling factor.再加上一个数shifting factor.。如果你想或者有必要,你的网络可以学习gamma和beta,他们通常为mini-batch的var和mean,这样你可以还原原来的数据(通过映射关系),就像你没有使用过BN一样。这样你就可以控制tanh的饱和程度了。
BN还带来一个好处就是增加了learning rate和initialization的鲁棒性,现在你可以尝试更多的数据了,它们都会得到一个好的结果。所以加入BN的网络变得更加容易训练了。
你可以把BN理解成一种regularization。activations是输入x的输出,也就是batch data 的输出,这些输入被它们的mean和var规范化了,就相当于被绑定在一起了,就不会像没BN前各自输出各自固定的值。这时候如果你的X有一些jitters,也被稀释掉了,类似一种正则化的效果。
我们为什么要还原数据,有一个还原的步骤?
因为我们想给输入数据一种灵活性。我们把数据规范成了unit guassian,但是unit guassian不一定就是最好的啊,所以,比如说tanh,你希望它有一点saturation,就需要进行一点调整,有时候我们希望保留一点原始的想法,但是也要进行一点scaled or shifted,这样学习得到的gamma和beta就有用了,它使得模型/输入/结点更新具有了一定的flexibility。
当我们让模型去学习这些参数gamma and beta的时候,他们跟原来的恒等映射并不一样,会有一点改变,你可以理解为这个改变对模型是一种优化,它既保留了batch normalization effect,有使得模型具有一定的灵活性。
高斯化会丢失原始数据的结构吗?
不会。BN只是shifting和scaling你的data,将他们规范到一个范围,并不会影响数据的结果。同样,其他预处理,不如zero-mean等也不会。
另外,只在训练时求mean和std,测试时不求,使用训练时得来的经验值。
深度学习Batch Normalization为什么效果好? - 魏秀参的回答 - 知乎
https://www.zhihu.com/question/38102762/answer/85238569
那BN到底是什么原理呢?说到底还是为了防止“梯度弥散”。关于梯度弥散,大家都知道一个简单的栗子:
。在BN中,是通过将activation规范为均值和方差一致的手段使得原本会减小的activation的scale变大。可以说是一种更有效的local response normalization方法。
关于梯度弥散和梯度爆炸:https://www.cnblogs.com/yangmang/p/7477802.html
Babysitting the learning process
可视化、监视训练过程、调整参数
1.预处理数据
2.设计网络结构——layers?Neurons?
3.检查loss是否合理
先不要加正则化,记下loss,然后加上正则化,loss应该增加。
4.训练时,先从小的数据集开始,关掉正则化项,使用简单的SGD,此时应当能得到一个非常好的效果,loss接近0,甚至是过拟合的。
先从小的reg开始,找到使得loss下降的learning rate。
如果loss下降太慢,就是lr太小了。
这里以softmax()为例:
为什么loss几乎没变,但是精确度很快增加到20%呢?
因为此时的概率分布依然是分散的,但是当我们把这些概率朝正确的方向稍微移动一点时,也就是权重向正确的方向移动了一点,因为我们是取得分最大值,此时纠正了很多错误的分类,准确率有了很大的提升。
如果leaning rate太大,cost就会得到NaN值,loss跳出最优方向,一直增加了。
hyperparameter optimization
学习速率,迭代次数,层数,每层神经元的个数(网络结构),正则化等等。
1.cross-validation策略
第一步:迭代一个小的次数,大致分析下现在的params是否有效,怎么调整
第二步:多迭代几步,进一步探索
反复
Tip:如果cost>3倍最初cost,肯定有问题了。
先设定一个范围:
调整:
2.随机搜索vs网格搜索
通常来说,随机搜索更佳,不会错过good region。
3.可视化损失曲线
4.可视化准确率
最终我们在CNN里采用的方法如下:
1.Activationfunctions:ReLU
2.data preprocessing:减去mean
3.weight initialization:Xavier init
4.batch normalization:要的
5.Babysitting the learning process:要的
6.hyperparameter optimization:要的