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

Was*_*mad 23 pytorch

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

dev*_*ail 21

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

optimizer = torch.optim.Adam(model.parameters(), lr=1e-4, weight_decay=1e-5)
Run Code Online (Sandbox Code Playgroud)

  • 在SGD优化器中,L2正则化可以通过“weight_decay”获得。但对于 Adam 优化器来说,“weight_decay”和 L2 正则化是不同的。更多内容可以在这里阅读:https://openreview.net/pdf?id=rk6qdGgCZ (4认同)
  • @Ashish 你的评论是正确的,“weight_decay”和 L2 正则化是不同的,但在 PyTorch 实现 Adam 的情况下,它们实际上实现了 L2 正则化而不是真正的权重衰减。请注意,权重衰减项应用于优化器步骤之前的梯度[此处](https://github.com/pytorch/pytorch/blob/40d1f77384672337bd7e734e32cb5fad298959bd/torch/optim/_function.py#L94) (3认同)

Kas*_*yap 19

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

  • Ya,L2正则化神秘地添加到优化函数中,因为在优化期间使用了损失函数.你可以在这里找到讨论https://discuss.pytorch.org/t/simple-l2-regularization/139/3 (8认同)
  • 我有一些使用 L2 损失的分支,所以这没有用。(我有不同的损失函数) (5认同)
  • 如果我想使用 L1 或其他损失进行正则化怎么办? (5认同)
  • Adagrad是一种优化技术,我说的是正规化.你能给我一个L1和L2损失的具体例子吗? (4认同)

ouk*_*hou 12

对于 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
Run Code Online (Sandbox Code Playgroud)


小智 10

对于L2正则化,

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

参考文献:

  • `torch.norm` 这里取的是 2-范数,而不是 2-范数的平方。所以我认为应该对范数进行平方以获得正确的正则化。 (3认同)
  • 没有requires_grad并使用+=会导致错误。这对我有用: l2_reg = torch.tensor(0., require_grad=True) l2_reg = l2_reg + torch.norm(param) (3认同)
  • 难道不需要排除不可训练的参数吗? (2认同)

Szy*_*zke 10

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

PyTorch L2 实现

为什么 PyTorchL2torch.optim.Optimizer实例中实现?

我们来看一下torch.optim.SGD源码(目前为功能优化程序),特别是这部分:

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)
Run Code Online (Sandbox Code Playgroud)
  • 可以看到,d_p(参数的导数,梯度)被修改并重新分配以加快计算(不保存临时变量)
  • 它具有O(N)复杂性,没有任何复杂的数学,例如pow
  • 它不涉及autograd在没有任何需要的情况下扩展图形

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

数学

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

L2

如果我们使用L2正则化wrt参数对任何损失进行导数w(它与损失无关),我们得到:

L2派生

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

L1 正则化层

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

L1导数

带有WeightDecay位于torchlayers 第三方库中的接口的完整代码提供诸如仅对权重/偏差/特定命名的参数进行正则化(免责声明:我是作者)之类的东西,但下面概述了该想法的本质(请参阅评论):

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)
Run Code Online (Sandbox Code Playgroud)

如果需要,请在此答案或相应的 PyTorch 文档中阅读有关钩子的更多信息。

用法也很简单(应该可以使用梯度累积和 PyTorch 层):

layer = L1(torch.nn.Conv2d(in_channels=3, out_channels=32, kernel_size=3))
Run Code Online (Sandbox Code Playgroud)

边注

此外,作为旁注,L1正则化并未实现,因为它实际上不会导致稀疏(丢失引用,我认为这是 PyTorch 存储库上的一些 GitHub 问题,如果有人有它,请编辑),正如权重为零所理解的那样。

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

  • @phydev - 是的,更多的是作为神经网络的旁注(不是针对套索回归),将删除它,因为它可能会让用户感到困惑并且没有得到任何来源的证实。请参阅上面的 Kevin Yin 评论,因为它有些相关。 (2认同)

uke*_*emi 9

L2 正则化开箱即用

是的,pytorch优化器有一个称为weight_decayL2 正则化因子的参数:

sgd = torch.optim.SGD(model.parameters(), weight_decay=weight_decay)
Run Code Online (Sandbox Code Playgroud)

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
Run Code Online (Sandbox Code Playgroud)

L2 的等效手动实现将是:

l2_norm = sum(p.pow(2.0).sum() for p in model.parameters())
Run Code Online (Sandbox Code Playgroud)

来源:使用 PyTorch 进行深度学习(8.5.2)


pro*_*sti 6

有趣的torch.norm是,与直接方法相比,CPU 上速度较慢,GPU 上速度较快。

\n\n
import torch\nx = torch.randn(1024,100)\ny = torch.randn(1024,100)\n\n%timeit torch.sqrt((x - y).pow(2).sum(1))\n%timeit torch.norm(x - y, 2, 1)\n
Run Code Online (Sandbox Code Playgroud)\n\n

出去:

\n\n
1000 loops, best of 3: 910 \xc2\xb5s per loop\n1000 loops, best of 3: 1.76 ms per loop\n
Run Code Online (Sandbox Code Playgroud)\n\n

另一方面:

\n\n
import torch\nx = torch.randn(1024,100).cuda()\ny = torch.randn(1024,100).cuda()\n\n%timeit torch.sqrt((x - y).pow(2).sum(1))\n%timeit torch.norm(x - y, 2, 1)\n
Run Code Online (Sandbox Code Playgroud)\n\n

出去:

\n\n
10000 loops, best of 3: 50 \xc2\xb5s per loop\n10000 loops, best of 3: 26 \xc2\xb5s per loop\n
Run Code Online (Sandbox Code Playgroud)\n

  • 我也证实了这一点。在这个例子中,torch.norm 大约慢了 60%。 (3认同)