DNN训练技巧(Tips for Training DNN)

本博客是针对李宏毅教授在Youtube上上传的课程视频《ML Lecture 9-1:Tips for Training DNN》的学习笔记。web

课程连接算法

Recipe of Deep Learning

以下图:了解DL的三步骤;三步作完以后,得到一个NN,接着检查所获得的模型,根据检查的结果执行相应的措施。网络

对于DL来讲,过拟合每每不是你首先会碰到的问题,反而是训练过程就很可贵到满意的结果,便可能训练集上的准确率就一直很低。若是你在训练集上准确率很高了,但在测试集上准确率低,那才是过拟合。svg

因此,不要老是第一时间认为测试集上效果很差是由于过拟合!函数

生动的例子看下图:光看测试集上的表现,不少人会直觉地认为“哎哟,你看56层效果这么差,估计就是过拟合了,根本用不到那么多参数~~”。学习

但是,下结论以前,咱们再看看这两个模型在训练集上的表现:测试

结果发现,56层的网络在训练集上表现也是比较差的,那么也就是说,56层的网络自己连训练都没作好,更别谈测试了!!!而形成这种现象的可能缘由不少:atom

  • 局部最小(local minima)
  • 鞍点(saddle point)
  • 停滞(plateau)
  • ……

李老师同时也提出,这也不太多是欠拟合问题,由于欠拟合通常是指模型的参数不够多,因此其能力不能解出这种问题。可是明明参数比56层网络少的20层网络也能够在训练集上达到更好的效果,因此不该该是欠拟合/模型的能力问题。spa

李老师提醒:读文献时,看到新方法时,要明白它是要解什么样的问题?通常DL要解决的问题就是训练集上的准确率和测试集上的准确率这两个问题,而新提出的方法也通常是针对这两个问题。3d

好比Dropout手段是用来提高模型在测试集上的准确率的,若是你是在训练集上准确率低,那用dropout就根本就是南辕北辙!因此请注意对症下药!

那么,针对两个典型DL问题,解决方法有哪些呢?见下图:

训练集上效果差?

换激活函数(New activation function)

例子:在MNIST上的网络,若是用sigmoid激活函数,则当层数越多时,效果反而越差!

有可能有人一看,就以为“啊!9层10层参数太多,过拟合了!”

Naive!其实上图的线就是在训练集上的表现!(李老师随后用keras实际演示了一下)

缘由:梯度弥散(Vanishing Gradient Problem):在靠近input的部分梯度较小,在远离input的地方,梯度较大。所以,靠近input的参数更新慢、收敛也慢,远离input的参数更新快、收敛得也很早。

为何会有这个现象发生呢?

若是咱们把BP算法的式子写出来,就会发现:用sigmoid做为激活函数就是会出现这样的问题。

直觉上想也能够理解:某个参数的降低梯度取决于损失函数对参数的偏导,而偏导的含义是:当该参数变化很小一点,对应的损失函数向相应变化的幅度。因此咱们假设对第一层的某个参数加上△w,看它对损失函数值的影响。那么,当△w通过一层sigmoid函数激活时,它的影响就会衰减一次。(sigmoid会将负无穷到正无穷的值都压缩到(-1,1)区间)

因而,怎么解决这个问题呢:换一个不会逐层消除掉增量的激活函数,好比ReLU.

并且除了解决梯度弥散问题,ReLU含有其它的好处:

  • 计算速度快
  • 生物学层面的理由
  • 等同于无穷多层的误差不一样的sigmoid函数叠加的结果

    那ReLU具体怎么解决梯度弥散问题呢?

    以下图,假设图中网络的激活函数都是ReLU。而网络中必然有一些神经元对应的输出会是0,它们对于网络的贡献也是零,故能够逻辑上直接从网络中删除,这样咱们就得到了一个“更瘦长”的线性网络(输出等于输入)。

    而以前说梯度递减也是由于sigmoid的衰减效果,而咱们如今用ReLU它自己不会对增量进行递减,由于如今凡是在网络中work的神经元,其输出都等于其输入,至关于线性函数y=x。

    问题:若是网络都用ReLU了,网络变成了线性的了?那NN的效果不会变得不好吗?这与咱们使用深层网络的初衷不是违背了吗?

    答:其实使用ReLU的NN总体仍是非线性的。当每一个神经元的操做域(operation region)是想相同的时,它是线性的。即当你对input只作小小的改变,不改变神经元的操做域,那NN就是线性的;但若是对input作比较大的改变,改变了神经元的操做域,这样NN就是非线性的了。

    另一个问题:ReLU不能微分呀?怎么作梯度降低呀?

    答:当x>0时,ReLU微分就是1,当x<0时,ReLU微分就是0。而x的值通常不太可能刚好是0,因此不在乎x=0时的微分值也没问题。

  • ReLU的变形

    本来的ReLU在参数小于0时,由于梯度等于0,就中止了更新。为了让参数保持更新,能够适当改变ReLU在负轴的形态。(leaky ReLU, Parametric ReLU)

  • 有没有ReLU之外的好的激活函数形式:Maxout(ReLU是maxout的一种特例)

    Maxout如何作到和ReLU同样的效果?

    以下:Maxout也能够超越ReLU:Learnable Activation Function

    Maxout的激活函数能够是任意分片线性凸函数,分片数量取决于将多少个元素分为一组:

  • 问题:Maxout网络怎么训练?

    其实Maxout网络由于是从group里面选取值最大的元素,那么那些未被选中的neuron的贡献就能够看作是0,能够拿掉。那么Maxout也就称为一种相似于线性的激活函数(直接将输入值输出)。因此咱们直接训练拿掉了未被选中的neuron后的”瘦长“网络就能够。

    另外,不用担忧这样会形成那些为被选中的neuron的参数没法被训练到,由于其实你每次输入的训练数据不一样时,被选中的neuron也不一样,因此终究而言,全部的neuron都是能被训练到的。

自适应学习率(Adaptive Learning Rate)

  • Adagrad

    有会变化的学习率,变化的方式是每次的本来学习率除以过去全部更新所用的梯度的平方和的算术平方根,用公式表达以下:

    wt+1<wtηΣti=0(gi)2gt

    在图上来看,adagrad的含义是:对于梯度通常比较小的参数,咱们给它较大的学习率;对于梯度通常比较大的参数,咱们给它较小的学习率。

    可是也有可能有Adagrad没法处理的问题:由于像logistic regression里面,咱们的损失函数是一个凸函数的形状,可是在DL里面,颇有可能损失函数是各类形状:

    以下图,这样一种”月形“的损失函数上, w1 的更新咱们须要它在平坦的区域给它较小的学习率,在较陡峭的地方,给它较大的学习率,即它的学习率应该是更加动态变化的,但adagrad的话只能作到单调递减的学习率。

  • Adagrad进阶版:RMSProp(Hinton在线上课程里提出的,没有正式paper…)

  • 难以找到最优NN参数

    前面提到,训练很差NN的可能缘由有不少,包括局部最优,鞍点和停滞。

    不过LeCun说过,局部最优的问题不用太担忧,由于通常不会有那么多局部最优,毕竟局部最优是要求全部的损失函数维度在局部最优势都呈现山谷的形状,这个不太常见。

  • 帮助解决最小值和停滞问题的技巧:动量(Momentum)

    现实世界中,当一个球从高点滚落到低点,因为惯性,它有能力跨越局部最优的山谷,进而到达更低点,咱们能不能借鉴这种思想呢?

    梯度降低的通常作法:

    加上动量后的梯度降低的作法:咱们每一次移动都由当前梯度和上一次的移动共同决定。

    vi 实际上就是以前全部梯度的加权。

    再从图像上来直观看一下动量的效果:

  • RMSProp + Momentum = Adam

(训练集上效果好的基础上,)测试集上效果差?

早停(Early Stopping)

咱们知道,随着训练过程的逐渐进行,训练集上的损失是会逐渐下降的,可是在测试集上的损失则可能先下降再上升(开始出现过拟合)。那么,比较理想的是,若是咱们知道测试集上损失的变化状况,咱们就应该把训练停在测试集损失最小的位置。可是问题是:咱们通常不知道测试集上的损失变化状况,因此咱们经常用验证集来代替~

所以,咱们这里如今讲的测试集实际上是那些有标签的数据集(好比kaggle上的,本身切出来的数据等),由于咱们须要计算损失。

正则化(Regularization)

咱们从新定义一下要去最小化的损失函数:在原来的损失函数上加上正则项。

L(θ)=L(θ)+λ12||θ||2

而( 1ηλ )每次都是一个接近1的数,因此其实每一次更新都会使参数衰减一点。

上面是L2正则化,下面看看L1正则化:

如上图,咱们能够看到L1与L2做参数更新时的差异:L1是每次参数多减去一个固定值,而L2是每次对参数进行乘以一个小于1的正值的衰减,所以L1最后会获得比较多的0值(更稀疏),而L2会获得比较多的较小但非0的值。

  • 引伸:人脑中的正则化

人的大脑中的神经链接数也是先增长后减小,咱们能够猜想这是由于更精简的模型能够带来更好的表现。

Dropout

作法:训练过程当中,每一次更新参数前,咱们对网络中的每一个神经元作一次sampling,即令每一个神经元有p%的几率被丢弃掉,而后只更新余下的神经元的参数。

测试时怎么作呢:不作dropout!!!并对参数乘以(100-p)%。

  • 为何要乘以(100-p)%?

用dropout训练时,指望每次会有p%的神经元未获得训练。而在测试集上,每次都是全部的神经元都参与了工做,因此在计算z值时,会是原来训练时的 1100p 倍,所以须要在测试时乘以(100-p)%。

  • 为何Dropout会有效呢?

小李训练时会绑沙袋,而一放下沙袋,束缚束缚,能力就会加强。

  • Dropout是一种ensemble

由于每次用一个minibatch的数据对NN进行训练时,因为dropout,每次都至关于训练了一个新结构的网络,那么当咱们整个NN中有M个神经元时,咱们最多能够训练处 2M 个可能的网络,而最终测试时,也就至关于这些网络协力给出结果。

也不用担忧说一个网络只用一个batch来训练,效果会不会不好,由于神经元的参数是共享的,你们其实能够总体看作仍是在协力使得彼此的能力都变得更强。

那么按理来讲,咱们若是这样作ensemble,最后应该把全部的网络都输入测试数据x,再取全部输出的平均做为结果,可是由于网络数量太多,这样作不太实际。而神奇的就是:当咱们把全部的网络参数乘以(100-p)%,而后把测试数据x输入到本来的网络中,它的输出结果就和ensemble的结果很接近!!!

  • 为何呢?

用一下一个单一的神经元的例子就能够看出来: