PyTorch中Weight Normalization与Adam优化器搭配引发Non-Leaf张量错误问题
解决Weight Normalization下Adam优化器的Non-Leaf Tensor错误
这个问题的根源很清晰:当你用weight_norm包装Conv2d层时,PyTorch会替换原Conv2d的weight张量为一个由新参数(weight_g和weight_v)计算出来的非叶子张量。原Conv2d的weight不再是模型的可训练参数(它变成了计算图中的中间节点),真正需要优化的是WeightNorm模块里的weight_g和weight_v。你的get_parameters函数直接去取Conv2d的weight/bias,导致收集到了非叶子张量,所以优化器抛出了错误。
下面给你两种可行的解决方案,都能保留Weight Normalization:
方案一:修改参数收集函数,适配WeightNorm
调整get_parameters函数,让它正确识别并收集WeightNorm模块的可训练参数:
def get_parameters(model, bias=False): for m in model.modules(): # 处理WeightNorm模块的核心权重参数(weight_g和weight_v) if isinstance(m, nn.utils.weight_norm.WeightNorm): if not bias: yield m.weight_g yield m.weight_v # 处理普通Conv2d的参数 elif isinstance(m, nn.Conv2d): if bias: if m.bias is not None: yield m.bias else: # 仅收集未被WeightNorm包装的Conv2d的weight(确保是叶子张量) if m.weight is not None and m.weight.is_leaf and m.weight.requires_grad: yield m.weight
修改后,这个函数会优先收集WeightNorm的可训练参数,再处理普通Conv2d的参数,确保所有收集到的张量都是可优化的叶子节点。
方案二:更简洁的参数分组方式
直接遍历模型的所有可训练参数(named_parameters()会自动返回所有叶子张量),按参数名分组:
# 分离权重参数(包括weight_g、weight_v和普通conv weight)和bias参数 params_weight = [] params_bias = [] for name, param in model.named_parameters(): if "bias" in name: params_bias.append(param) else: params_weight.append(param) # 初始化优化器 optimizer = torch.optim.Adam( [ {'params': params_weight}, {'params': params_bias, 'lr': opt['base_lr'] * 2, 'weight_decay': 0}, ], lr=opt['base_lr'] )
这种方式更简洁,不需要手动判断模块类型,named_parameters()已经帮你过滤出了所有可优化的叶子参数,完美匹配你想要的分组逻辑——所有bias用2倍学习率且无权重衰减,权重参数用基础学习率。
两种方案都能解决你的问题,我个人更推荐方案二,代码更简洁且不易出错。
内容的提问来源于stack exchange,提问作者Mohit Lamba




