L2正则是一种减少过拟合的一种经典方法,它在损失函数中加入对模型所有权重的平方和,乘以给定的超参数(本文中的所有方程都使用python,numpy,和pytorch表示):
final_loss = loss wd * all_weights.pow(2).sum() / 2
...其中wd是要设置的l2正则的超参数。这也称为weight decay,因为在应用普通的SGD时,它相当于采用如下所示来更新权重:
w = w - lr * w.grad - lr * wd * w
(注意,w 2相对于w的导数是2w。)在这个等式中,我们看到我们如何在每一步中减去一小部分权重,因此成为衰减。
我们查看过的所有深度学习库,都使用了第一种形式。(实际上,它几乎总是通过向gradients中添加wd * w来实现,而不是去改变损失函数:我们不希望在有更简单的方法时,通过修改损失来增加更多计算量。)
那么为什么要区分这两个概念,它们是否起到了相同的作用呢?答案是,它们对于vanilla SGD来说是一样的东西,但只要我们在公式中增加动量项,或者使用像Adam这样更复杂的一阶或二阶的optimizer,L2正则化(第一个等式)和权重衰减(第二个等式)就会变得不同。在本文的其余部分,当我们谈论weight decay时,我们将始终参考第二个公式(梯度更新时,稍微减轻权重)并谈谈L2正则化,如果我们想提一下经典的方法。
以SGD momentum为例。使用L2正则化,并添加wd * w衰减项到公式中(如前所述),但不直接从权重中减去梯度。首先我们计算移动平均值(moving average):
moving_avg = alpha * moving_avg (1-alpha) * (w.grad wd*w)
...这个移动平均值将乘以学习率并从w中减去。因此,与将从w取得的正则化相关联的部分是lr *(1-alpha)* wd * w加上前一步的moving_avg值。
另一方面,weight decay的梯度更新如下式:
moving_avg = alpha * moving_avg (1-alpha) * w.grad w = w - lr * moving_avg - lr * wd * w
我们可以看到,从与正则化相关联的w中减去的部分在两种方法中是不同的。当使用Adam optimizer时,它会变得更加不同:在L2正则化的情况下,我们将这个wd * w添加到gradients,然后计算gradients及其平方值的移动平均值,然后再使用它们进行梯度更新。而weight decay的方法只是在进行更新,然后减去每个权重。
显然,这是两种不同的方法。在尝试了这个之后,Ilya Loshchilov和Frank Hutter在他们的文章中建议我们应该使用Adam的权重衰减,而不是经典深度学习库实现的L2正则化方法。
1.2 实现AdamW
我们应该怎么做?基于fastai库为例,具体来说,如果使用fit函数,只需添加参数 use_wd_sched = True:
learn.fit(lr, 1, wds=1e-4, use_wd_sched=
True)
如果您更喜欢新的API,则可以在每个训练阶段使用参数wd_loss = False(计算损失函数时,不使用weight decay):
phases = [TrainingPhase(1, optim.Adam, lr, wds=1-e4, wd_loss=False)]
learn.fit_opt_sched(phases)
以下给出基于fast库的一个简单实现。在optimizer的step函数的内部,只使用gradients来更新参数,根本不使用参数本身的值(除了weight decay,但我们将在外围处理)。然后我们可以在optimizer处理之前,简单地执行权重衰减。在计算梯度之后仍然必须进行相同操作(否则会影响gradients值),所以在训练循环中,你必须找到这个位置。
loss.backward()
#Do the weight decay here!
optimizer.step()
当然,optimizer应该设置为wd = 0,否则它会进行L2正则化,这正是我们现在不想要的。现在,在那个位置,我们必须循环所有参数,并做weight decay更新。你的参数应该都在optimizer的字典param_groups中,因此循环看起来像这样:
loss.backward()
for group in optimizer.param_groups():
for param in group['params']:
param.data = param.data.add(-wd * group['lr'], param.data)
optimizer.step()
1.3 AdamW实验的结果:它有效吗?
我们在计算机视觉问题上第一次进行测试得到的结果非常令人惊讶。具体来说,我们采用Adam L2正规化在30个epochs内获得的准确率(这是SGD通过去1 cycle policy达到94%准确度所需要的必要时间)的平均为93.96%,其中一半超过了94%。使用Adam weight decay则达到94%和94.25%之间。为此,我们发现使用1 cycle policy时,beta2的最佳值为0.99。我们将beta1参数视为SGD的动量(意味着它随着学习率的增长从0.95变为0.85,然后当学习率变低时再回到0.95)。
L2正则化或权重衰减的准确性
更令人印象深刻的是,使用Test Time Augmentation(即对测试集上的一个图像,取四个和他相同data-augmented版本的预测的平均值作为最终预测结果),我们可以在18个epochs内达到94%的准确率(平均预测值为93.98%) )!通过简单的Adam和L2正规,超过20个epochs时,达到94%。
在这些比较中要考虑的一件事是,改变我们正则的方式会改变weight decay或学习率的最佳值。在我们进行的测试中,L2正则化的最佳学习率是1e-6(最大学习率为1e-3),而0.3是weight decay的最佳值(学习率为3e-3)。在我们的所有测试中,数量级的差异非常一致,主要原因是,L2正则于梯度的平均范数(相当小)相除后,变得非常有效,且Adam使用的学习速率非常小(因此,weight decay的更新需要更强的系数)。
那么,使用Adam时,权重衰减总是比L2正规化更好吗?我们还没有发现一个明显更糟的情况,但对于迁移学习问题或RNN而言(例如在Stanford cars数据集上对Resnet50进行微调),它没有获得更好的结果。