You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

Chainer与PyTorch能否为不同权重集配置不同L1/L2正则化参数?

关于深度学习框架权重正则化的问题解答

1. Chainer/PyTorch是否支持为不同权重集设置不同L1/L2正则化参数?

当然可以!不管是Chainer还是PyTorch,都支持给不同的权重组配置独立的正则化参数,具体实现方式各自有小差异:

  • PyTorch侧
    PyTorch的优化器允许你传入多个参数组(param_groups),每个组可以单独指定weight_decay(这就是L2正则化的实现方式)。如果需要L1正则化,因为原生优化器没有直接提供,你可以自定义钩子(hook)或者优化器,给不同参数组绑定不同的L1惩罚系数。
    举个简单的分组示例:

    optimizer = torch.optim.SGD([
        {'params': model.layer1.parameters(), 'weight_decay': 0.01},
        {'params': model.layer2.parameters(), 'weight_decay': 0.001}
    ], lr=0.001)
    

    这里layer1的权重会用0.01的L2系数,layer2则用0.001,完全独立。

  • Chainer侧
    Chainer的优化器同样支持参数组,你可以通过add_param_group()或者直接给钩子指定目标参数来实现。比如用WeightDecay钩子时,能精准指定哪些参数用哪个系数:

    from chainer import optimizers
    from chainer.optimizer_hooks import WeightDecay
    
    optimizer = optimizers.SGD(lr=0.001)
    optimizer.setup(model)
    # 给layer1的参数加0.01的权重衰减
    optimizer.add_hook(WeightDecay(0.01), target=model.layer1)
    # 给layer2的参数加0.001的权重衰减
    optimizer.add_hook(WeightDecay(0.001), target=model.layer2)
    

    L1正则化的话,Chainer有现成的Lasso钩子,用法和上面一样,给不同参数组设不同系数就行。

2. PyTorch中为线性层单个输出对应的权重设置不同正则化系数

你提到的场景是一个简单的线性层nn.Linear(ninput, noutput),想给每个输出单元对应的输入权重组设置不同的alpha值——这个完全可以实现,核心思路是把线性层的权重拆分成单个输出对应的切片,然后作为独立的参数组传给优化器。

线性层的权重model.l1.weight形状是(noutput, ninput),每一行正好对应一个输出单元的所有输入权重。我们可以把每一行单独拎出来,给每个切片分配不同的weight_decay

具体代码示例

import torch
import torch.nn as nn
import torch.optim as optim

class SimpleLinearModel(nn.Module):
    def __init__(self, ninput, noutput):
        super().__init__()
        self.l1 = nn.Linear(ninput, noutput)

    def forward(self, x):
        return self.l1(x)

# 假设输入维度10,输出维度3
ninput = 10
noutput = 3
model = SimpleLinearModel(ninput, noutput)

# 给三个输出分别设置不同的L2正则化系数
alpha_values = [0.01, 0.005, 0.001]

# 构建参数组列表
param_groups = []
for idx in range(noutput):
    # 取出第idx个输出对应的权重行,保持可训练状态
    weight_slice = model.l1.weight[idx:idx+1, :]
    param_groups.append({
        'params': [weight_slice],
        'weight_decay': alpha_values[idx]
    })
# 别忘了偏置参数,这里设为0(不需要正则化)
param_groups.append({
    'params': [model.l1.bias],
    'weight_decay': 0.0
})

# 初始化优化器
optimizer = optim.SGD(param_groups, lr=0.001)

这样设置后,优化器更新时就会给每个输出单元对应的权重应用不同的L2正则化系数。如果需要L1正则化,你可以自定义一个钩子函数,遍历参数组并给对应梯度加上L1惩罚:

def l1_reg_hook(alpha_values):
    def hook(optimizer, param_group, param):
        if param.grad is not None:
            # 找到当前参数对应的alpha索引
            idx = next(i for i, group in enumerate(param_groups) if param in group['params'])
            if idx < len(alpha_values):
                param.grad.data.add_(alpha_values[idx] * torch.sign(param.data))
    return hook

# 把钩子加到优化器上
optimizer.add_hook(l1_reg_hook(alpha_values))

要注意的是,拆分权重切片时要保证每个切片的requires_gradTrue(默认就是,不用特意改),而且这种拆分不会影响模型的正常前向传播,PyTorch优化器完全能处理这种多个小张量的参数组。


内容的提问来源于stack exchange,提问作者kapibarasama

火山引擎 最新活动