在PyTorch中添加L1 / L2正则化?

时间:2017-03-09 19:54:20

标签: pytorch

有什么办法,我可以在PyTorch中添加简单的L1 / L2正则化吗?我们可以通过简单地将data_loss添加到reg_loss来计算正则化损失,但是有没有任何明确的方法,PyTorch库的任何支持都可以更轻松地完成它而无需手动执行它?

7 个答案:

答案 0 :(得分:14)

以下应该有助于L2正规化:

optimizer = torch.optim.Adam(model.parameters(), lr=1e-4, weight_decay=1e-5)

答案 1 :(得分:13)

这在PyTorch的文档中有介绍。看看http://pytorch.org/docs/optim.html#torch.optim.Adagrad。您可以使用权重衰减参数将L2损失添加到优化功能。

答案 2 :(得分:8)

对于L2正规化,

lambda = torch.tensor(1.)
l2_reg = torch.tensor(0.)
for param in model.parameters():
    l2_reg += torch.norm(param)
loss += lambda * l2_reg

参考文献:

答案 3 :(得分:8)

以前的答案虽然在技术上是正确的,但在性能方面效率低下,而且不是太模块化(难以逐层应用,例如 keras 层提供的)。

PyTorch L2 实现

为什么 PyTorch 在 L2 实例中实现了 torch.optim.Optimizer

我们来看看torch.optim.SGD source code(目前作为功能优化程序),特别是这部分:

for i, param in enumerate(params):
    d_p = d_p_list[i]
    # L2 weight decay specified HERE!
    if weight_decay != 0:
        d_p = d_p.add(param, alpha=weight_decay)
  • 可以看到,d_p(参数的导数,梯度)被修改并重新分配以加快计算(不保存临时变量)
  • 它具有 O(N) 的复杂性,没有像 pow 那样复杂的数学
  • 不需要autograd扩展图

将其与 O(n) **2 操作、加法以及参与反向传播进行比较。

数学

让我们看看带有 L2 正则化因子的 alpha 方程(对于 L1 ofc 也可以这样做):

L2

如果我们使用 L2 正则化 w.r.t.参数w(它与损失无关),我们得到:

L2 deriv

所以它只是为每个权重的梯度加上 alpha * weight 而这正是 PyTorch 上面所做的!

L1 正则化层

使用这个(和一些 PyTorch 魔法),我们可以想出非常通用的 L1 正则化层,但让我们先看看 L1 的一阶导数(sgn 是符号函数,返回 {{1 }} 表示正输入,1 表示负输入,-1 表示 0):

L1 derivative

带有位于 torchlayers third party library 中的 0 接口的完整代码提供诸如仅正则化权重/偏差/特定命名的参数之类的东西(免责声明:我是作者),但是下面概述的想法的本质(见评论):

WeightDecay

如果需要,请阅读有关钩子 in this answer 或相应 PyTorch 文档的更多信息。

而且用法也很简单(应该适用于梯度积累和 PyTorch 层):

class L1(torch.nn.Module):
    def __init__(self, module, weight_decay):
        super().__init__()
        self.module = module
        self.weight_decay = weight_decay

        # Backward hook is registered on the specified module
        self.hook = self.module.register_full_backward_hook(self._weight_decay_hook)

    # Not dependent on backprop incoming values, placeholder
    def _weight_decay_hook(self, *_):
        for param in self.module.parameters():
            # If there is no gradient or it was zeroed out
            # Zeroed out using optimizer.zero_grad() usually
            # Turn on if needed with grad accumulation/more safer way
            # if param.grad is None or torch.all(param.grad == 0.0):

            # Apply regularization on it
            param.grad = self.regularize(param)

    def regularize(self, parameter):
        # L1 regularization formula
        return self.weight_decay * torch.sign(parameter.data)

    def forward(self, *args, **kwargs):
        # Simply forward and args and kwargs to module
        return self.module(*args, **kwargs)

附注

另外,作为旁注,layer = L1(torch.nn.Conv2d(in_channels=3, out_channels=32, kernel_size=3)) 正则化没有实现,因为它实际上不会引起稀疏(丢失引用,我认为这是 PyTorch 存储库上的一些 GitHub 问题,如果有人有它,请编辑),正如权重为零所理解的那样。

更多情况下,如果权重值达到某个小的预定义幅度(例如 L1),则会对其进行阈值处理(简单地为其分配零值)

答案 4 :(得分:7)

开箱即用的 L2 正则化

是的,pytorch optimizers 有一个名为 weight_decay 的参数,它对应于 L2 正则化因子:

sgd = torch.optim.SGD(model.parameters(), weight_decay=weight_decay)

L1 正则化实现

L1 没有类似的参数,但是手动实现很简单:

loss = loss_fn(outputs, labels)
l1_lambda = 0.001
l1_norm = sum(p.abs().sum() for p in model.parameters())

loss = loss + l1_lambda * l1_norm

L2 的等效手动实现是:

l2_norm = sum(p.pow(2.0).sum() for p in model.parameters())

来源:Deep Learning with PyTorch (8.5.2)

答案 5 :(得分:0)

显然,使用torch.norm的@Sherif Ahmad比直接方法慢。

import torch
x = torch.randn(1024,100)
y = torch.randn(1024,100)

%timeit torch.sqrt((x - y).pow(2).sum(1))
%timeit torch.norm(x - y, 2, 1)

将输出:

124 µs ± 2.22 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)
284 µs ± 5.18 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

在具有12Gig RAM的单个GPU供电单元上。

答案 6 :(得分:0)

仅适用于L1正则化和包含weight

L1_reg = torch.tensor(0., requires_grad=True)
for name, param in model.named_parameters():
    if 'weight' in name:
        L1_reg = L1_reg + torch.norm(param, 1)

total_loss = total_loss + 10e-4 * L1_reg