cs231n_lecture_7_怎样训练神经网络-PART 2

回顾上一节,我们默认ReLU是首选的**函数;权重初始化要合理;输入之前要对数据做预处理,一般做一下零均值/std和BN。

cs231n_lecture_7_怎样训练神经网络-PART 2

这里再解释一下为什么规范化是重要的。

cs231n_lecture_7_怎样训练神经网络-PART 2

可以看到,没有规范化之前,w的微小改变都有可能影响到loss,很难最优化。规范化之后lossw不那么敏感了。

监视学习过程,可视化lossaccuracy,随时做分析和调整。对于超参数,随机初始化一个范围,利用少的迭代次数和小的数据集进行初步的选取。超参数有很多,一次不要尝试太多2-3个就OK了,太多了控制不了。有几个需要重点关注:learning rateregularizationlearning rate decay,然后可以尝试下不同的network architecture

Optimization

SGD的一些问题

以二维为例

1.如果loss function的条件数很大,那么SGD的时候就会在一个方向变化很快,而在另外一个方向上变化很慢,这样会导致收敛速度下降。

(噪声会带来此种影响)

条件数:hessian 矩阵最大特征值/最小特征值。衡量矩阵接近奇异(满秩)的程度。条件数永远大于1。其数值愈接近于1,计算逆时误差愈小。

hessian矩阵与函数极值/梯度下降法的关系:

https://blog.****.net/baimafujinji/article/details/51167852

https://blog.****.net/linolzhang/article/details/60151623


cs231n_lecture_7_怎样训练神经网络-PART 2

2.如果loss function有一个局部极小值或者鞍点怎么办?

这两种情况下,gradient都等于0w就更新不了了,梯度下降就停止了。

cs231n_lecture_7_怎样训练神经网络-PART 2

在高维空间下,出现鞍点情况很是常见。它表示在某一点,一些方向的loss在上升,一些方向的loss在下降。

反而局部极值的情况不多见(在某一点所有方向的loss都在上升)。

另外,在接近鞍点时,梯度很小,更新很慢,学习速度变得很慢。

 

3.SGD是随机选取一些点,并不能得到loss空间的所有梯度信息,使得计算得到的梯度很容易受干扰。

cs231n_lecture_7_怎样训练神经网络-PART 2

那直接做full batch gradient 就可以了吗?

并不是。即使你full batch了,噪声还是会有,局部极值点和鞍点也还是会存在。梯度停止更新的情况仍旧会在训练时遇到。


所以这里我们引入一个小的优化技巧,很简单但很有用——加入一个momentum term(动量)。现在梯度下降变成了SGD + Momentum

这里就是把梯度类比为加速度,更新的时候利用的是梯度的累积量(一开始为0),也就是“速度”。

cs231n_lecture_7_怎样训练神经网络-PART 2

这里的rho可以类比为摩擦系数,一般设置为0.9或者0.99

这样修改以后,即使遇到梯度很小的点,速度值还在,梯度更新还是会正常进行,而且累积量的存在使得噪声被平均掉了,收敛速度也更快。你可以把它想象成对现有梯度的smothmoving average

cs231n_lecture_7_怎样训练神经网络-PART 2

更新路径变成蓝色线了,可以看出效果非常好。


poor condition的点,梯度是怎样更新的?

每一次更新,梯度值在rho的作用下被累加在了一起。如果rho表现良好,“速度”会一点点单调增加,在某个点它会比实际的gradient大,在poor condition的维度上会加快收敛。


cs231n_lecture_7_怎样训练神经网络-PART 2

加上velocity之后,梯度更新方向向正确的趋势上掰回了一点。

一种在凸优化上表现更好的方式:先跳到velocity指向的点,然后计算这个点上的gradient,更新。

cs231n_lecture_7_怎样训练神经网络-PART 2

cs231n_lecture_7_怎样训练神经网络-PART 2

这种方式把两个方向的信息mix更多了。

但是应对非凸问题就不会有明显优势,神经网络属于非凸。

 

Nesterov Momentum的理解:看成利用前一个速度error-correcting当前速度


cs231n_lecture_7_怎样训练神经网络-PART 2

cs231n_lecture_7_怎样训练神经网络-PART 2

注:一个好的模型往往有一个平坦下降的minimum,说明它是generalize的。反而一个sharp minimum可能是有问题的,模型可能overfiting了。

AdaGrad

将梯度的平方累加,更新的时候利用尺度归一的当前梯度。

cs231n_lecture_7_怎样训练神经网络-PART 2

为什么这种方法好使?

在条件数很大的情况下,有些方向的梯度很大,下一个更新点容易往这些方向跑,有些方向的梯度很小,这就形成了更新低效的zigzaging。但是如果我们加入一个归一化项,那些具有大的梯度的维度会除以一个大的sum值,减缓偏离的程度,小的梯度的维度会除以一个小的sum值,加快更新。整体的更新会变好。


这里有一个问题值得讨论。当dx一直累加,sum会越来愈大,这会导致更新越来越慢。慢就不好吗?并不是。在凸优化问题中,越来越慢是我们期望的,因为越接近minimum越平缓,更新速度应该越慢。但是在非凸情况下,这就是个问题了,我们的参数值会得不到更新。


这就引出了RMSPorproot meam square prop,均方根反向传播)

cs231n_lecture_7_怎样训练神经网络-PART 2

RMSprop采用了一种类似momentum的策略,decay_rate接近1,这样会减缓降速的过程。

下面看一下三种方法的比较:
黑色:
SGD

蓝色:SGD+Momentum

红色:RMSProp

cs231n_lecture_7_怎样训练神经网络-PART 2

后两中明显表现更好,但有区别,SGD+Momentum先是跳的很远再快速回来,RMSProp基本沿着正确的方向下来。


上面对梯度下降的改进方法中一个是加入velocity一个是加入square gradient,效果都不错,那要是把两个都考虑进去呢?

这就是更加好用的Adam算法Adaptive Moment Estimation

初始想法:

cs231n_lecture_7_怎样训练神经网络-PART 2

一个问题,在刚开始更新的时候会出现什么情况?
second_moment初始值=0,beta2接近1,dx也很小,second_moment会是一个很小的值,除以一个很小的值,更新步长会很大,很可能会跳到一个bad point。

所以加上一个矫正项:
cs231n_lecture_7_怎样训练神经网络-PART 2

Beta1=0.9,beta2=0.999,learning_rate=1e-3 or 5e-4

黑色:SGD

蓝色:SGD+Momentum

红色:RMSProp

紫色:Adam

cs231n_lecture_7_怎样训练神经网络-PART 2

AdamMomentRMSProp的结合体,不会跳的太远,收敛过程更平滑。这里是二维,在高维情况下优势更明显。

 

关于超参数:learning_rate

cs231n_lecture_7_怎样训练神经网络-PART 2

通常我们希望Learning_rate是decay,也就是随着迭代次数增多,步长减小,decay的方式大致有三种:

cs231n_lecture_7_怎样训练神经网络-PART 2

cs231n_lecture_7_怎样训练神经网络-PART 2

decayMomentum效果好,对于Adam则一般,训练时通常从无decay开始,画loss-epoch曲线,考虑是不是要加decay,怎么加?

到现在我们都只是用梯度去寻找最优解,梯度时一阶导数,实际上在最优化理论里我们是可以借助更高阶导数帮助实现寻找最优解的,一个通常的方法是借助二阶导——也就是牛顿法。

cs231n_lecture_7_怎样训练神经网络-PART 2

cs231n_lecture_7_怎样训练神经网络-PART 2

cs231n_lecture_7_怎样训练神经网络-PART 2

好处就是没有lear_rate 了,每次更新都是直接奔向最优解(step to the mimimun)。

可惜的是在实际情况里,我们得到的二阶导估计并不准确,结果就是,用梯度法向着最优解方向更新往往效果更好(step to the direction of the mimimum)。

另外,高维下,H有O(N^2)的参数,H的逆(O(N^3))也很难求。

二阶优化的改进包括

1.BGFS

不用二阶导求hessian和其逆,用一种逼近的方法

2.L-BGFS

不求和存整个hessian的逆

这种方法在full batch的时候效果很好,在nini-batch的时候效果不行。

 

实际情况下:

1.默认用Adam

2.负担的起full batch,可以试试(别忘了去掉噪声项)

 

前面我们讨论了如何找最优解,不断地减小我们的loss,但是我们更加关心的是验证集/测试集精度能达到训练集那么好(gap),怎么做呢?

cs231n_lecture_7_怎样训练神经网络-PART 2

Model Ensembles

训练多个模型,测试的时候取平均值——大概能得到2%精度的提升

一些Tricks

1.用同一个模型的多个training snapshots

2.测试集使用参数的平均值(Polyak averaging

cs231n_lecture_7_怎样训练神经网络-PART 2

gapperformance之间有一个权衡,当你的精确度低,模型很泛化的时候,gap自然小,但这不是我们想要的。我们要在不overfitting的前提下,精度尽量大,同时gap尽量小。


怎样训练好单个的模型?

Regularization add terms to loss

cs231n_lecture_7_怎样训练神经网络-PART 2


Regularization dropout

前向传播时,随机令一些神经元为0。实际操作时你只需要令前一层的activation0 就可以了。

cs231n_lecture_7_怎样训练神经网络-PART 2

很好实现。重点是我们为甚么要这么做?这样做为什么效果好?

一种解释是迫使神经网络形成冗余的表达,而不是形成各自特有的特征,增强泛化能力

比如说识别猫,假设一个神经元学习耳朵,一个学习尾巴,一个学习是否有毛发,各自独立,然而我们希望它们都能兼顾学习到更多的特征。

令一种解释是,dropout实际上是在训练多个模型的组合!每一个二进制掩模都是一个模型。

 

加了dropout后,测试时我们咋办呢,随机出结果吗?

不是,我们求所有可能结果的期望值!

cs231n_lecture_7_怎样训练神经网络-PART 2

以一个神经元为例:

cs231n_lecture_7_怎样训练神经网络-PART 2

只要乘以一个概率值就可以了!

cs231n_lecture_7_怎样训练神经网络-PART 2

shape返回一个元组,里面是数组的维度

numpy.random.rand(d0, d1, ..., dn)创建一个给定类型的数组,用均匀分布的随机样本[0, 1)填充

*作用是将列表/元组解开成两个独立的参数,传入函数,**是将字典解开成独立的元素作为形参

例如

def add(a, b):

    return a+b

data = [4,3]

print add(*data)

#equals to print add(4, 3)

data = {'a' : 4, 'b' : 3}

print add(**data)

#equals to print add(4, 3)


通常dropout需要更长的训练时间,但是收敛之后泛化能力更好。

dropout在输入里加入了一些随机扰动,测试时将随机扰动平均化。这种做法和BN很像。BN用随机的minibatch的统计特性标准化输入,测试时用固定的统计信息标准化。

它们都是值得尝试的好方法,另外dropout有一个参数p方便调节模型,BN没有。


Regularization data agumentation

1.水平翻转输入图片

2.随机切割和尺度变换

3.颜色,对比度和亮度的变化

4.使用PCA

5.给像素加一个offset

总之:指导原则就是改变image,但是label不变。

现在我们可以回顾一下regularization了:

训练时:加入随机噪声

测试时:margin掉这些噪声

方法有——

1.dropout(随机令activation0

2.batch normalization(规范化mini batch

3.data Augmentation(增大数据集)

4.DropConnect(随机令weight0

5.Fractional Max Pooling (池化区域的大小是随机的)

6.Stochastic Depth (随机去掉一些 层)

当你只有小的数据集时,可以考虑使用迁移学习,就是把已学训练好的模型参数迁移到新的模型来帮助新模型训练。

cs231n_lecture_7_怎样训练神经网络-PART 2

ImageNet相比:

cs231n_lecture_7_怎样训练神经网络-PART 2

事实上,使用CNN进行迁移学习是很普遍的:

cs231n_lecture_7_怎样训练神经网络-PART 2