Tips for Deep Learning
本文会顺带解决CNN部分的两个问题:
1、max pooling架构中用到的max无法微分,那在gradient descent的时候该如何处理?
2、L1 的Regression到底是什么东西本文的主要思路:针对training set和testing set上的performance分别提出针对性的解决方法
1、在training set上准确率不高:
new activation function:ReLU、Maxout
adaptive learning rate:Adagrad、RMSProp、Momentum、Adam
2、在testing set上准确率不高:Early Stopping、Regularization or Dropout
Recipe of Deep Learning
three step of deep learning
Recipe,配方、秘诀,这里指的是做deep learning的流程应该是什么样子
我们都已经知道了deep learning的三个步骤
- define the function set(network structure)
- goodness of function(loss function – cross entropy)
- pick the best function(gradient descent – optimization)
做完这些事情以后,你会得到一个更好的neural network,那接下来你要做什么事情呢?
Good Results on Training Data?
你要做的第一件事是,提高model在training set上的正确率
先检查training set的performance其实是deep learning一个非常unique的地方,如果今天你用的是k-nearest neighbor或decision tree这类非deep learning的方法,做完以后你其实会不太想检查training set的结果,因为在training set上的performance正确率就是100,没有什么好检查的
有人说deep learning的model里这么多参数,感觉一脸很容易overfitting的样子,但实际上这个deep learning的方法,它才不容易overfitting,我们说的overfitting就是在training set上performance很好,但在testing set上performance没有那么好;只有像k nearest neighbor,decision tree这类方法,它们在training set上正确率都是100,这才是非常容易overfitting的,而对deep learning来说,overfitting往往不会是你遇到的第一个问题
因为你在training的时候,deep learning并不是像k nearest neighbor这种方法一样,一训练就可以得到非常好的正确率,它有可能在training set上根本没有办法给你一个好的正确率,所以,这个时候你要回头去检查在前面的step里面要做什么样的修改,好让你在training set上可以得到比较高的正确率
Good Results on Testing Data?
接下来你要做的事是,提高model在testing set上的正确率
假设现在你已经在training set上得到好的performance了,那接下来就把model apply到testing set上,我们最后真正关心的,是testing set上的performance,假如得到的结果不好,这个情况下发生的才是Overfitting,也就是在training set上得到好的结果,却在testing set上得到不好的结果
那你要回过头去做一些事情,试着解决overfitting,但有时候你加了新的technique,想要overcome overfitting这个problem的时候,其实反而会让training set上的结果变坏;所以你在做完这一步的修改以后,要先回头去检查新的model在training set上的结果,如果这个结果变坏的话,你就要从头对network training的process做一些调整,那如果你同时在training set还有testing set上都得到好结果的话,你就成功了,最后就可以把你的系统真正用在application上面了
Do not always blame overfitting
不要看到所有不好的performance就归责于overfitting
先看右边testing data的图,横坐标是model做gradient descent所update的次数,纵坐标则是error rate(越低说明model表现得越好),黄线表示的是20层的neural network,红色表示56层的neural network
你会发现,这个56层network的error rate比较高,它的performance比较差,而20层network的performance则是比较好的,有些人看到这个图,就会马上得到一个结论:56层的network参数太多了,56层果然没有必要,这个是overfitting。但是,真的是这样子吗?
你在说结果是overfitting之前,有检查过training set上的performance吗?对neural network来说,在training set上得到的结果很可能会像左边training error的图,也就是说,20层的network本来就要比56层的network表现得更好,所以testing set得到的结果并不能说明56层的case就是发生了overfitting
在做neural network training的时候,有太多太多的问题可以让你的training set表现的不好,比如说我们有local minimum的问题,有saddle point的问题,有plateau的问题…所以这个56层的neural network,有可能在train的时候就卡在了一个local minimum的地方,于是得到了一个差的参数,但这并不是overfitting,而是在training的时候就没有train好
有人认为这个问题叫做underfitting,但我的理解上,underfitting的本意应该是指这个model的complexity不足,这个model的参数不够多,所以它的能力不足以解出这个问题;但这个56层的network,它的参数是比20层的network要来得多的,所以它明明有能力比20层的network要做的更好,却没有得到理想的结果,这种情况不应该被称为underfitting,其实就只是没有train好而已
conclusion
当你在deep learning的文献上看到某种方法的时候,永远要想一下,这个方法是要解决什么样的问题,因为在deep learning里面,有两个问题:
- 在training set上的performance不够好
- 在testing set上的performance不够好
当只有一个方法propose(提出)的时候,它往往只针对这两个问题的其中一个来做处理,举例来说,deep learning有一个很潮的方法叫做dropout,那很多人就会说,哦,这么潮的方法,所以今天只要看到performance不好,我就去用dropout;但是,其实只有在testing的结果不好的时候,才可以去apply dropout,如果你今天的问题只是training的结果不好,那你去apply dropout,只会越train越差而已
所以,你必须要先想清楚现在的问题到底是什么,然后再根据这个问题去找针对性的方法,而不是病急乱投医,甚至是盲目诊断
下面我们分别从Training data和Testing data两个问题出发,来讲述一些针对性优化的方法
Good Results on Training Data?
这一部分主要讲述如何在Training data上得到更好的performance,分为两个模块,New activation function和Adaptive Learning Rate
New activation function
这个部分主要讲述的是关于Recipe of Deep Learning中New activation function的一些理论
activation function
如果你今天的training结果不好,很有可能是因为你的network架构设计得不好。举例来说,可能你用的activation function是对training比较不利的,那你就尝试着换一些新的activation function,也许可以带来比较好的结果
在1980年代,比较常用的activation function是sigmoid function,如果现在我们使用sigmoid function,你会发现deeper不一定imply better,下图是在MNIST手写数字识别上的结果,当layer越来越多的时候,accuracy一开始持平,后来就掉下去了,在layer是9层、10层的时候,整个结果就崩溃了;但注意!9层、10层的情况并不能被认为是因为参数太多而导致overfitting,实际上这张图就只是training set的结果,你都不知道testing的情况,又哪来的overfitting之说呢?
Vanishing Gradient Problem
上面这个问题的原因不是overfitting,而是Vanishing Gradient(梯度消失),解释如下:
当你把network叠得很深的时候,在靠近input的地方,这些参数的gradient(即对最后loss function的微分)是比较小的;而在比较靠近output的地方,它对loss的微分值会是比较大的
因此当你设定同样learning rate的时候,靠近input的地方,它参数的update是很慢的;而靠近output的地方,它参数的update是比较快的
所以在靠近input的地方,参数几乎还是random的时候,output就已经根据这些random的结果找到了一个local minima,然后就converge(收敛)了
这个时候你会发现,参数的loss下降的速度变得很慢,你就会觉得gradient已经接近于0了,于是把程序停掉了,由于这个converge,是几乎base on random的参数,所以model的参数并没有被训练充分,那在training data上得到的结果肯定是很差的
某一个参数 w w w对total cost l l l的偏微分,即gradient ∂ l ∂ w \frac{\partial l}{\partial w} ∂w∂l,它直觉的意思是说,当我今天把这个参数做小小的变化的时候,它对这个cost的影响有多大;那我们就把第一个layer里的某一个参数 w w w加上 Δ w \Delta w Δw,看看对network的output和target之间的loss有什么样的影响
Δ w \Delta w Δw通过sigmoid function之后,得到output是会变小的,改变某一个参数的weight,会对某个neuron的output值产生影响,但是这个影响是会随着层数的递增而衰减的,sigmoid function的形状如下所示,它会把负无穷大到正无穷大之间的值都硬压到0~1之间,把较大的input压缩成较小的output
因此即使 Δ w \Delta w Δw值很大,但每经过一个sigmoid function就会被缩小一次,所以network越深, Δ w \Delta w Δw被衰减的次数就越多,直到最后,它对output的影响就是比较小的,相应的也导致input对loss的影响会比较小,于是靠近input的那些weight对loss的gradient ∂ l ∂ w \frac{\partial l}{\partial w} ∂w∂l远小于靠近output的gradient
但其实改一下activation function可能就可以handle这个问题了
ReLU
introduction
现在比较常用的activation function叫做Rectified Linear Unit(整流线性单元函数,又称修正线性单元),它的缩写是ReLU,该函数形状如下图所示,z为input,a为output,如果input>0则output = input,如果input<0则output = 0
- 跟sigmoid function比起来,ReLU的运算快很多
- ReLU的想法结合了生物上的观察( Pengel的paper )
- 无穷多bias不同的sigmoid function叠加的结果会变成ReLU
- ReLU可以处理Vanishing gradient的问题( the most important thing )
handle Vanishing gradient problem
下图是ReLU的neural network,以ReLU作为activation function的neuron,它的output要么等于0,要么等于input
当output=input的时候,这个activation function就是linear的;而output=0的neuron对整个network是没有任何作用的,因此可以把它们从network中拿掉
Q:这里就会有一个问题,我们之所以使用deep learning,就是因为想要一个non-linear、比较复杂的function,而使用ReLU不就会让它变成一个linear function吗?这样得到的function不是会变得很弱吗?
A:其实,使用ReLU之后的network整体来说还是non-linear的,如果你对input做小小的改变,不改变neuron的operation region的话,那network就是一个linear function;但是,如果你对input做比较大的改变,导致neuron的operation region被改变的话,比如从output=0转变到了output=input,network整体上就变成了non-linear function
注:这里的region是指input z<0和input z>0的两个范围
A:在实际操作上,当region的范围处于z>0时,微分值gradient就是1;当region的范围处于z<0时,微分值gradient就是0;当z为0时,就不要管它,相当于把它从network里面拿掉
ReLU-variant
其实ReLU还存在一定的问题,比如当input<0的时候,output=0,此时微分值gradient也为0,你就没有办法去update参数了,所以我们应该让input<0的时候,微分后还能有一点点的值,比如令 a = 0.01 z a=0.01z a=0.01z,这个东西就叫做Leaky ReLU
这个时候又有人想,为什么一定要是ReLU这样子呢,activation function可不可以有别的样子呢?所以后来有了一个更进阶的想法,叫做Maxout network
Maxout
introduction
Maxout的想法是,让network自动去学习它的activation function,那Maxout network就可以自动学出ReLU,也可以学出其他的activation function,这一切都是由training data来决定的
假设现在有input x 1 , x 2 x_1,x_2 x1,x2,它们乘上几组不同的weight分别得到5,7,-1,1,这些值本来是不同neuron的input,它们要通过activation function变为neuron的output;但在Maxout network里,我们事先决定好将某几个“neuron”的input分为一个group,比如5,7分为一个group,然后在这个group里选取一个最大值7作为output
这个过程就好像在一个layer上做Max Pooling一样,它和原来的network不同之处在于,它把原来几个“neuron”的input按一定规则组成了一个group,然后并没有使它们通过activation function,而是选取其中的最大值当做这几个“neuron”的output
当然,实际上原来的”neuron“早就已经不存在了,这几个被合并的“neuron”应当被看做是一个新的neuron,这个新的neuron的input是原来几个“neuron”的input组成的vector,output则取input的最大值,而并非由activation function产生
Maxout -> RELU
Maxout是如何模仿出ReLU这个activation function的呢?
下图左上角是一个ReLU的neuron,它的input x会乘上neuron的weight w,再加上bias b,然后通过activation function-ReLU,得到output a
- neuron的input为 z = w x + b z=wx+b z=wx+b,为下图左下角紫线
- neuron的output为 a = z ( z > 0 ) ; a = 0 ( z < 0 ) a=z\ (z>0);\ a=0\ (z<0) a=z (z>0); a=0 (z<0),为下图左下角绿线
- neuron的input为 [ z 1 z 2 ] \begin{bmatrix}z_1 \ z_2 \end{bmatrix} [z1 z2]
- z 1 = w x + b z_1=wx+b z1=wx+b,为上图右下角紫线
- z 2 = 0 z_2=0 z2=0,为上图右下角红线
- neuron的output为 max [ z 1 z 2 ] \max{\begin{bmatrix}z_1 \ z_2 \end{bmatrix}} max[z1 z2],为上图右下角绿线
你会发现,此时ReLU和Maxout所得到的output是一模一样的,它们是相同的activation function
Maxout -> More than ReLU
除了ReLU,Maxout还可以实现更多不同的activation function
比如 z 2 z_2 z2的参数w和b不是0,而是 w ′ , b ′ w',b' w′,b′,此时
- neuron的input为 [ z 1 z 2 ] \begin{bmatrix}z_1 \ z_2 \end{bmatrix} [z1 z2]
- z 1 = w x + b z_1=wx+b z1=wx+b,为下图右下角紫线
- z 2 = w ′ x + b ′ z_2=w'x+b' z2=w′x+b′,为下图右下角红线
- neuron的output为 max [ z 1 z 2 ] \max{\begin{bmatrix}z_1 \ z_2 \end{bmatrix}} max[z1 z2],为下图右下角绿线
property
Maxout可以实现任何piecewise linear convex activation function(分段线性凸激活函数),其中这个activation function被分为多少段,取决于你把多少个element z放到一个group里,下图分别是2个element一组和3个element一组的activation function的不同形状
How to train Maxout
接下来我们要面对的是,怎么去train一个Maxout network,如何解决Max不能微分的问题
假设在下面的Maxout network中,红框圈起来的部分为每个neuron的output
实际上我们真正训练的并不是一个含有max函数的network,而是一个化简后如下图所示的linear network;当我们还没有真正开始训练模型的时候,此时这个network含有max函数无法微分,但是只要真的丢进去了一笔data,network就会马上根据这笔data确定具体的形状,此时max函数的问题已经被实际数据给解决了,所以我们完全可以根据这笔training data使用Backpropagation的方法去训练被network留下来的参数
所以我们担心的max函数无法微分,它只是理论上的问题;在具体的实践上,我们完全可以先根据data把max函数转化为某个具体的函数,再对这个转化后的thiner linear network进行微分
这个时候你也许会有一个问题,如果按照上面的做法,那岂不是只会train留在network里面的那些参数,剩下的参数该怎么办?那些被拿掉的直线(weight)岂不是永远也train不到了吗?
其实这也只是个理论上的问题,在实际操作上,我们之前已经提到过,每个linear network的structure都是由input的那一笔data来决定的,当你input不同data的时候,得到的network structure是不同的,留在network里面的参数也是不同的,由于我们有很多很多笔training data,所以network的structure在训练中不断地变换,实际上最后每一个weight参数都会被train到
所以,我们回到Max Pooling的问题上来,由于Max Pooling跟Maxout是一模一样的operation,既然如何训练Maxout的问题可以被解决,那训练Max Pooling又有什么困难呢?
Max Pooling有关max函数的微分问题采用跟Maxout一样的方案即可解决,至此我们已经解决了CNN部分的第一个问题
Adaptive learning rate
这个部分主要讲述的是关于Recipe of Deep Learning中Adaptive learning rate的一些理论
Review - Adagrad
我们之前已经了解过Adagrad的做法,让每一个parameter都要有不同的learning rate
Adagrad的精神是,假设我们考虑两个参数 w 1 , w 2 w_1,w_2 w1,w2,如果在 w 1 w_1 w1这个方向上,平常的gradient都比较小,那它是比较平坦的,于是就给它比较大的learning rate;反过来说,在 w 2 w_2 w2这个方向上,平常gradient都比较大,那它是比较陡峭的,于是给它比较小的learning rate
但我们实际面对的问题,很有可能远比Adagrad所能解决的问题要来的复杂,我们之前做Linear Regression的时候,我们做optimization的对象,也就是loss function,它是convex的形状;但实际上我们在做deep learning的时候,这个loss function可以是任何形状
RMSProp
learning rate
loss function可以是任何形状,对convex loss function来说,在每个方向上它会一直保持平坦或陡峭的状态,所以你只需要针对平坦的情况设置较大的learning rate,对陡峭的情况设置较小的learning rate即可
但是在下图所示的情况中,即使是在同一个方向上(如w1方向),loss function也有可能一会儿平坦一会儿陡峭,所以你要随时根据gradient的大小来快速地调整learning rate
RMSprop还是一个蛮神奇的方法,因为它并不是在paper里提出来的,而是Hinton在mooc的course里面提出来的一个方法,所以需要cite(引用)的时候,要去cite Hinton的课程链接
how to do RMSProp
RMSProp的做法如下:
我们的learning rate依旧设置为一个固定的值 η \eta η 除掉一个变化的值 σ \sigma σ,这个 σ \sigma σ等于上一个