【问题标题】:Pytorch: how to add L1 regularizer to activations?Pytorch:如何将 L1 正则化器添加到激活中?
【发布时间】:2017-11-22 08:14:45
【问题描述】:

我想将 L1 正则化器添加到 ReLU 的激活输出中。 更一般地,如何将正则化器仅添加到网络中的特定层


相关资料:

  • This similar post 指的是添加L2正则化,但它似乎将正则化惩罚添加到网络的所有层。

  • nn.modules.loss.L1Loss() 似乎相关,但我还不明白如何使用它。

  • 遗留模块L1Penalty 似乎也很重要,但为什么它被弃用了?

【问题讨论】:

  • 比较高级的方案可以看link。这为您提供了一个类似 keras 的界面,可以在 pytorch 中轻松地做很多事情,特别是添加各种正则化器。

标签: python pytorch


【解决方案1】:

这里是你如何做到这一点:

  • 在您要应用 L1 正则化的模块的前向返回最终输出和层输出中
  • loss 变量将是输出 w.r.t 的交叉熵损失的总和。目标和 L1 处罚。

这是一个示例代码

import torch
from torch.autograd import Variable
from torch.nn import functional as F


class MLP(torch.nn.Module):
    def __init__(self):
        super(MLP, self).__init__()
        self.linear1 = torch.nn.Linear(128, 32)
        self.linear2 = torch.nn.Linear(32, 16)
        self.linear3 = torch.nn.Linear(16, 2)

    def forward(self, x):
        layer1_out = F.relu(self.linear1(x))
        layer2_out = F.relu(self.linear2(layer1_out))
        out = self.linear3(layer2_out)
        return out, layer1_out, layer2_out

batchsize = 4
lambda1, lambda2 = 0.5, 0.01

model = MLP()
optimizer = torch.optim.SGD(model.parameters(), lr=1e-4)

# usually following code is looped over all batches 
# but let's just do a dummy batch for brevity

inputs = Variable(torch.rand(batchsize, 128))
targets = Variable(torch.ones(batchsize).long())

optimizer.zero_grad()
outputs, layer1_out, layer2_out = model(inputs)
cross_entropy_loss = F.cross_entropy(outputs, targets)

all_linear1_params = torch.cat([x.view(-1) for x in model.linear1.parameters()])
all_linear2_params = torch.cat([x.view(-1) for x in model.linear2.parameters()])
l1_regularization = lambda1 * torch.norm(all_linear1_params, 1)
l2_regularization = lambda2 * torch.norm(all_linear2_params, 2)

loss = cross_entropy_loss + l1_regularization + l2_regularization
loss.backward()
optimizer.step()

【讨论】:

  • 谢谢你我没有意识到你可以改变核心函数的“签名”,比如 forward()
  • 这不是正则化层的权重吗?我猜原始发布者想要规范化层的输出而不是权重。如何才能在 PyTorch 中只对激活进行正则化(稀疏化)?
  • 答案中似乎有错误。对于norm(all_linear2_params, 2):torch 返回L2 正则化的平方根。即表达式应该提高到 2 的幂
  • 为什么不使用这些变量时需要从forward返回layer1_out、layer2_out?
  • 这会规范权重,您应该规范返回的层输出(即激活)。这就是您首先退回它们的原因!正则化项应该类似于:l1_regularization = lambda1 * torch.norm(layer1_out, 1)l2_regularization = lambda2 * torch.norm(layer2_out, 2)
【解决方案2】:

@Sasank Chilamkurthy 正则化应该是模型每一层的权重参数,而不是每一层的输出。请看下面: Regularization

import torch
from torch.autograd import Variable
from torch.nn import functional as F


class MLP(torch.nn.Module):
    def __init__(self):
        super(MLP, self).__init__()
        self.linear1 = torch.nn.Linear(128, 32)
        self.linear2 = torch.nn.Linear(32, 16)
        self.linear3 = torch.nn.Linear(16, 2)
    def forward(self, x):
        layer1_out = F.relu(self.linear1(x))
        layer2_out = F.relu(self.linear2(layer1_out))
        out = self.linear3(layer2_out)
        return out

batchsize = 4
lambda1, lambda2 = 0.5, 0.01

model = MLP()
optimizer = torch.optim.SGD(model.parameters(), lr=1e-4)

inputs = Variable(torch.rand(batchsize, 128))
targets = Variable(torch.ones(batchsize).long())
l1_regularization, l2_regularization = torch.tensor(0), torch.tensor(0)

optimizer.zero_grad()
outputs = model(inputs)
cross_entropy_loss = F.cross_entropy(outputs, targets)
for param in model.parameters():
    l1_regularization += torch.norm(param, 1)**2
    l2_regularization += torch.norm(param, 2)**2

loss = cross_entropy_loss + l1_regularization + l2_regularization
loss.backward()
optimizer.step()

【讨论】:

  • 答案中似乎有错误。对于norm(param, 2):torch 返回L2 正则化的平方根。即表达式应该提高到 2 的幂
  • 正则化权重更标准,但有工作建议 (L1) 正则化激活更可取。
  • 正如上面的答案所指出的,任何东西都可以进行正则化。
【解决方案3】:

我认为原帖想要对 ReLU 的输出进行正则化,所以正则化器应该在输出上,而不是网络的权重上。它们不一样!

  • 使用 l1-norm 正则化权重正在训练具有稀疏权重的神经网络

  • 使用 l1-norm 正则化层的输出正在训练一个网络,该网络具有该特定层的稀疏输出。

以上这些答案(包括已接受的答案)都没有抓住重点,或者我误解了原始帖子的问题。

【讨论】:

    【解决方案4】:

    所有(其他当前)响应在某种程度上都是不正确的,因为问题是关于向激活添加正则化。 This one 最接近,因为它建议对输出的范数求和,这是正确的,但代码对权重的范数求和,这是不正确的。

    正确的方法不是修改网络代码,而是通过前向钩子捕获输出,如OutputHook 类。从那里,输出的范数的总和很简单,但需要注意每次迭代清除捕获的输出。

    import torch
    
    
    class OutputHook(list):
        """ Hook to capture module outputs.
        """
        def __call__(self, module, input, output):
            self.append(output)
    
    
    class MLP(torch.nn.Module):
        def __init__(self):
            super(MLP, self).__init__()
            self.linear1 = torch.nn.Linear(128, 32)
            self.linear2 = torch.nn.Linear(32, 16)
            self.linear3 = torch.nn.Linear(16, 2)
            # Instantiate ReLU, so a hook can be registered to capture its output.
            self.relu = torch.nn.ReLU()
    
        def forward(self, x):
            layer1_out = self.relu(self.linear1(x))
            layer2_out = self.relu(self.linear2(layer1_out))
            out = self.linear3(layer2_out)
            return out
    
    
    batch_size = 4
    l1_lambda = 0.01
    
    model = MLP()
    optimizer = torch.optim.SGD(model.parameters(), lr=1e-4)
    # Register hook to capture the ReLU outputs. Non-trivial networks will often
    # require hooks to be applied more judiciously.
    output_hook = OutputHook()
    model.relu.register_forward_hook(output_hook)
    
    inputs = torch.rand(batch_size, 128)
    targets = torch.ones(batch_size).long()
    
    optimizer.zero_grad()
    outputs = model(inputs)
    cross_entropy_loss = torch.nn.functional.cross_entropy(outputs, targets)
    
    # Compute the L1 penalty over the ReLU outputs captured by the hook.
    l1_penalty = 0.
    for output in output_hook:
        l1_penalty += torch.norm(output, 1)
    l1_penalty *= l1_lambda
    
    loss = cross_entropy_loss + l1_penalty
    loss.backward()
    optimizer.step()
    output_hook.clear()
    

    【讨论】:

    • 不太明白为什么将 L1 计算为输出的总和范数,因为众所周知,L1 正则化 = 权重的总和范数。能详细点吗?
    • @TungVs L1 权重正则化是权重的总和或平均 L1 范数。激活的 L1 正则化是激活的总和或平均 L1 范数。它们都是合法的正则化器。这方面的文献很多。请参阅此recent example。还有很多其他的。
    【解决方案5】:

    您可以使用以下代码将模型my_layer 的单层权重的 L1 正则化应用于损失函数:

    def l1_penalty(params, l1_lambda=0.001):
        """Returns the L1 penalty of the params."""
        l1_norm = sum(p.abs().sum() for p in params)
        return l1_lambda*l1_norm
    
    loss = loss_fn(outputs, labels) + l1_penalty(my_layer.parameters())
    

    【讨论】:

      猜你喜欢
      • 2020-01-30
      • 2017-07-30
      • 2021-05-05
      • 2018-03-29
      • 1970-01-01
      • 2017-08-26
      • 2020-06-29
      • 1970-01-01
      • 2018-10-07
      相关资源
      最近更新 更多