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_grad是True(默认就是,不用特意改),而且这种拆分不会影响模型的正常前向传播,PyTorch优化器完全能处理这种多个小张量的参数组。
内容的提问来源于stack exchange,提问作者kapibarasama




