You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

PyTorch中冻结卷积滤波器子集:梯度置零后权重仍变化求解答

这是个很常见的PyTorch训练细节问题,我来帮你拆解分析:

问题1:为什么梯度置零后权重仍会改变?

最核心的原因大概率和你使用的优化器特性有关,尤其是当你用了带动量的优化器(比如SGD with momentum、Adam、RMSprop等)时:

  • 这类优化器的更新逻辑不是直接用当前梯度乘以学习率来调整权重,而是会累积历史梯度的动量。比如SGD带动量的更新公式是:
    v = momentum * v + lr * gradient
    weight = weight - v
    
    哪怕你把当前梯度强行设为0,只要之前迭代累积的动量v不为0,权重还是会被这个动量值推动产生变化。
  • 另外可以快速排查:你是否在loss.backward()之后、设置梯度为0之前,有其他操作偷偷修改了梯度?或者你的损失函数有没有附加L2正则项(很多优化器默认会加),正则项的梯度可能在你置零后又被计算了?不过从你的代码来看,动量导致的概率最高。

你可以做个验证测试:临时把优化器换成纯SGD(不带momentum)再跑一轮,如果权重不再变化,那就能坐实是动量的问题了。

问题2:冻结卷积滤波器子集的更可靠方案

手动置零梯度的方法容易受优化器特性干扰,这里有几个更稳定的替代方案:

方案1:设置目标参数的requires_grad=False

直接让PyTorch跳过这些滤波器的梯度计算,从根源上阻止它们被更新:

# 先获取要冻结的滤波器索引
freeze_indices = temp_5.values
# 确保整个卷积层的权重默认是可训练的
im_net.features[25].weight.requires_grad = True
# 逐个设置目标滤波器的权重不需要梯度
for idx in freeze_indices:
    im_net.features[25].weight[idx].requires_grad = False

注意:哪怕你的优化器是在设置requires_grad之前初始化的也没关系——PyTorch的优化器在执行step()时会自动跳过requires_grad=False的参数。之后如果需要解冻这些滤波器,再把它们的requires_grad设回True即可。

方案2:用优化器参数分组实现冻结

把模型参数拆成两组,给需要冻结的组设置lr=0,这样即使有梯度,更新时也不会改变权重:

# 拆分参数为「待更新」和「待冻结」两组
params_to_update = []
params_to_freeze = []

for name, param in im_net.named_parameters():
    if name == "features.25.weight":
        # 创建掩码区分需要冻结和更新的滤波器
        mask = torch.ones(param.shape[0], dtype=torch.bool, device=device)
        mask[freeze_indices] = False
        # 切片加入对应分组
        params_to_update.append(param[mask])
        params_to_freeze.append(param[~mask])
    else:
        # 其他参数默认加入待更新组
        params_to_update.append(param)

# 初始化优化器,给冻结组设置lr=0
optimizer = torch.optim.SGD([
    {'params': params_to_update, 'lr': 0.001},
    {'params': params_to_freeze, 'lr': 0}
], momentum=0.9)

这个方案适合你不想修改requires_grad状态的场景,比如之后需要灵活调整不同组的学习率时。

方案3:手动恢复权重(备选,不推荐)

在优化器更新后,把冻结的滤波器权重强制恢复到更新前的状态:

# 更新前先保存冻结部分的权重
frozen_weights_backup = im_net.features[25].weight[freeze_indices].detach().clone()
# 执行优化器更新
optimizer.step()
# 恢复冻结部分的权重
im_net.features[25].weight[freeze_indices] = frozen_weights_backup

这个方法比较繁琐,每次都要克隆张量,会额外占用内存,所以优先推荐前两个方案。


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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.07 09:02:49