Batch Normalization
相反,Batch normalization被立即认为是有巨大影响的。当它刚出现时,就马上得到大家的认同。我清楚地记得在2015,每个人都在谈论它,这是因为它的效果着实显著,如这张图所示。
这是当时最先进的Inception结构的ImageNet模型。这是(黑色虚线)它得到一个很好的结果花的时间,然后他们用这个叫btach norm的新东西做了同样的事情,它们(蓝色实线)做得非常非常快。快到让人们对它的神速叹为观止。
他们管这个叫批量归一化(Batch Normalization)。它通过减少内部协变量转移(internal covariate shift)来加速训练。什么是内部协变量转移?其实它不重要。因为batch norm是一些研究者靠直觉提出来的、要试试看的东西。他们试验了,它的效果很好,他们事后补充了一些数学分析来试着说明为什么需要它。事实证明,他们完全错了。
最近两个月,有两篇论文证明了这一点,而人们花了三年才真正明白,在过去两个月里,有两篇论文指出,batch normalization根本没有减少内部协变量转移。并且,就算真的减少了,也也跟它为什么能加速训练无关。我认为这是一个有意义的领悟,也说明为什么我们要专注于做实践者和实验主义者,并提升我们的直觉。
batch norm的作用是由这篇论文里的图(the positive impact of BatchNorm on training might be somewhat serendipitous)可以说明。横轴是步数(steps)或者batch(x-axis),纵轴是损失(y-axis)。红线是没用batch norm训练时的情况,波动很大。蓝线是用了batch norm后,训练的情况,波动小了很多。这意味着,使用batch norm你可以提高你的学习率。因为这些波动剧烈的部分代表这你的权重设置带来的不确定性的时段,程序跳进某个权重空间中的糟糕区域,再也挑不出来了。如果波动没那么剧烈,那么就可以用更好的学习率进行训练,这就是实际的情况。
这就是这种算法了。它很简单。算法会取一个mini batch。我们有了一个mini batch,记住,这是一个网络层,进入这个层的是激活值。网络层输入的是激活值。激活值用等来表示。
- 首先,我们要计算这些激活值的平均值(mean),均值就是激活值的和除以其数量就是平均值。
- 第二件事,我们找到激活值的方差(variance),差值就是激活值与均值之差的的平方和,再除以数量是就方差。
- 然后做标准化(normalize),激活值减去均值除以标准差,就是标准化。这实际上这几步不是很重要。我们曾经以为很重要,但后来发现不是。真正重要的部分是下面的东西。
- 我们取这些值,加上一个偏置向量(这里把它叫做
)。
我们之前已经看过了。我们之前用过偏差项。所以我们会像之前一样,加上一个偏差项。然后,我们要用另外一个和bias很像的东西,但不是加上它,我们会乘以它。这些参数
和
是要学习的参数。
记住,在神经网络里,只有两种数字:激活值和参数。这些是参数。是用梯度下降学习到的东西。只是一个普通的bias层,
是一个做乘法的bias层。没有人这样叫它,但它就是这样的。
就是使用乘法而非加法的偏差项。这就是batch norm。这就是这一层做的事。
为什么batch norm可以实现了不起的结果?我不清楚有没有人之前准确地把这写过关于这个问题的文章。如果有,抱歉这里没有引用它,因为我没有看过。让我解释下。究竟发生了什么。我们的预测值是各权重的函数,可以达到上百万个权重值,
也是一个层输入的函数。
这个函数是神经网络函数,不管在神经网络里发生了什么。我们的损失值,假定是均方误差,就是实际值减去预测值的平方。
假设我们要预测电影评分的结果,预测值在1到5之间。我们一直在训练模型,最后的激活值在-1到1之间。这些激活值与实际需要的值相差太远。缩放和均值都没用,我们应该怎么做?一个方法是用一组新的权重,让传播值增加,让平均值增长。但是这很难,因为所有这些参数有很密切而复杂的相互作用。我们得到的所有非线性因素汇聚在一起,因此想要提高数值,需要在这种复杂的情形下找到一条出路。我们使用像动量、Adam之类的东西来帮助我们,但还要做大量的旋转(twidding)才能得到我们想要的。这会花很长时间,学习曲线会跌宕起伏。
我们这样做怎么样?在的公示后面乘以
,再加上
会怎么样?
我们多加了两个参数向量。现在它就简单了。为了提高缩放比例,这个数 得到直接梯度来增加缩放比例。为了改变均值(向量b)可以直接得到梯度来改变均值。【to change the mean that number has a direct gradient to change the mean there's no interactiions or complexities】两者没有相互作用和复杂性,都是直接的升降或缩放,这就是batch norm做的事。所以batch norm让使输出变大变小这个重要工作更容易做到,使输出提升或下降,放大或缩小。这就是为什么我们能得到这样的结果。
这些细节,在某种意义上,不是特别重要。真正重要的是你肯定想要用它。如果不用它,也会用类似的东西。现在,有很多其它类型的归一化方法,但batch norm效果很好。我们在fastai里用的另一种的标准化方法主要是weight norm,这是最近几个月新开发的。
这就是batch norm,我们为每一个连续变量创建了一个btach norm层。n_cont
是连续变量的数量。在fastai里,n_something
通常代表这个东西的数量。cont
通常代表连续(continuous)。这里是我们使用它的地方。我们取到连续变量,然后将其送入batch norm层。
你可以在模型里的这个地方看到它。
一个值得注意的东西是这里的动量(momentum)。这不是优化里的动量法,是exponentially weighted moving average里的动量。具体来说,这个(batch norm算法里的)平均值和标准差,我们没有为每一个mini batch用不同的平均值和标准差。如果这样做了,这些值的变化会很大,从而导致难以训练。因此,我们采用平均值和标准差的指数加权移动平均值。如果你不记得这是什么意思,就回去看下上周的课程,复习下指数加权移动平均,上节课我们在excel里实现动量法和Adam算法的梯度平方项。
你可以通过往pytorch构造器传入一个不同的值来改变一个batch norm层里动量的数量。如果你用了一个较小的数,这意味着从一个mini batch间的平均值和标准差会变小,它们受正则化的效果欠佳。输入一个较大的数,意味着均值和标准差在mini-batch间的差异也较大,这样的正则化的效果也会较好。这样的训练会更好,因为其参数化的程度较好,包含均值和标准差的动量项,产生了这种较好的正则化效果。
当你用了batch norm,你需要用更大的学习率。这是我们的模型。你可以运行lr_find
,你可以看下结果。然后可以用fit()
函数训练,在保存结果。画损失曲线,接下来再次用fit()训练:
learn.lr_find()
learn.recorder.plot()
learn.fit_one_cycle(5, 1e-3, wd=0.2)
learn.load('1');
learn.fit_one_cycle(5, 3e-4)
最终得到0.103。竞赛的第十名是0.108,这看起来很好。但不要太当回事,因为你如果要把结果提交到Kaggle比赛, 还要用真实的训练集, 但可以看到,我们的模型至少是2015年的先进水平了。而且我说过,这些模型至今基本上没有做过架构上的改进。当时没有batch norm,我们添加了batch norm应该能得到更好的结果,并且运行得更快。他们的模型用一个比较低的学习率,训练了很久。可以看到,这个用了不到45分钟。这很好很快。
提问: 你使用dropout和其他正则化方法比如权重衰减(weight decay),L2正则化等等的比例是怎样的?
记得吗,L2正则化和权重衰减是做同一件事的两种方式,我们应该总是用权重衰减,不用L2正则化。我们现在的方法有权重衰减,可产生正则化效果的batch norm。batch norm有正则化的效果,还有我们马上要学习的数据增强(data augmentation )以及dropout。我们应该总是用batch norm。它很简单。我们等下会学数据增强。剩下就是比较dropout和权重衰减。我不知道。我没有看到过有人做过令人信服的关于如何结合这两个东西的研究。可以总是用其中一个,而不用另一个吗?为什么能?为什么不能?我想没人可以解答。在实践中,看起来,通常你需要同时用这两个。你通常需要用权重衰减,但也经常需要用dropout。说实话,我不知道为什么。我没有见过有人解释为什么,如何做选择。这个需要你去尝试,来获得这样一种感觉,知道对你的问题,哪个方法是有效的。我认为我们在每个learner里提供的默认方法可以在大多数场景工作得很好。但是,肯定可以去尝试你自己定义的值。