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

PyTorch中优化perturbation时无梯度问题:原因与解决方法

PyTorch中仅优化扰动参数时梯度为None的原因与解决方法

我尝试给模型添加扰动,只优化这个扰动而不调整模型参数,编写了如下代码:

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

# Define a simple model
class SimpleModel(nn.Module):
    def __init__(self):
        super(SimpleModel, self).__init__()
        self.linear = nn.Linear(1, 1)

    def forward(self, x, perturbation, clean):
        self.linear.weight.data = perturbation + clean
        return self.linear(x) 

# Generate clean dataset
X = torch.tensor([[1.0], [2.0], [3.0], [4.0]], dtype=torch.float32)
Y = 2 * X

# Create the model
model = SimpleModel()

# Define perturbation as a separate parameter
perturbation = torch.tensor(0.1, requires_grad=True)
clean = model.linear.weight.data
# Train the model and optimize the perturbation
criterion = nn.MSELoss()
optimizer_perturbation = optim.SGD([perturbation], lr=0.01)  # Optimize the perturbation

for epoch in range(100):
    optimizer_perturbation.zero_grad()

    outputs = model(X,clean,perturbation)
    loss = criterion(outputs, Y)
    loss.backward()
    # Update the perturbation
    optimizer_perturbation.step()

但执行loss.backward()后,perturbation的梯度仍为None,具体输出如下:

#before executing loss.backward()
model.linear.weight.grad
None
perturbation.grad
None
#after executing loss.backward()
perturbation.grad
None
model.linear.weight.grad
tensor([[-26.8524]])

原因分析

直接修改model.linear.weight.data属于非微分操作——.data属性绕开了PyTorch的计算图追踪机制,赋值过程不会被记录到梯度计算链路中。这导致perturbation和最终的loss之间没有建立依赖关系,反向传播时无法计算出perturbation的梯度。

正确实现方式

核心是让perturbation参与到正向传播的计算图中,避免使用.data切断梯度链路,以下两种方式都可以实现需求:

方式一:动态计算扰动后权重,不修改模型参数

修改模型的forward方法,直接用clean_weight + perturbation作为权重进行计算,完全不改动模型自身的参数:

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

class SimpleModel(nn.Module):
    def __init__(self):
        super(SimpleModel, self).__init__()
        self.linear = nn.Linear(1, 1)

    def forward(self, x, perturbation, clean_weight):
        # 直接使用扰动后的权重计算,不修改模型参数
        weight = clean_weight + perturbation
        return nn.functional.linear(x, weight, self.linear.bias)

# 生成数据集
X = torch.tensor([[1.0], [2.0], [3.0], [4.0]], dtype=torch.float32)
Y = 2 * X

model = SimpleModel()
# 保存原始权重并脱离计算图,避免干扰梯度计算
clean_weight = model.linear.weight.detach()
# 定义可优化的扰动参数
perturbation = torch.tensor(0.1, requires_grad=True)

criterion = nn.MSELoss()
optimizer = optim.SGD([perturbation], lr=0.01)

for epoch in range(100):
    optimizer.zero_grad()
    outputs = model(X, perturbation, clean_weight)
    loss = criterion(outputs, Y)
    loss.backward()
    # 此时perturbation.grad不再为None
    print(f"Epoch {epoch}: perturbation grad = {perturbation.grad.item()}")
    optimizer.step()

方式二:临时修改模型参数后恢复(适配原有模型调用逻辑)

如果需要保留模型原有层的调用方式,可以用torch.no_grad()临时修改参数,正向传播后再恢复原始参数,确保扰动参与计算图:

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

class SimpleModel(nn.Module):
    def __init__(self):
        super(SimpleModel, self).__init__()
        self.linear = nn.Linear(1, 1)

    def forward(self, x, perturbation):
        # 保存原始权重
        orig_weight = self.linear.weight.clone()
        # 临时更新权重(no_grad避免修改操作被追踪)
        with torch.no_grad():
            self.linear.weight.copy_(orig_weight + perturbation)
        # 正向传播
        output = self.linear(x)
        # 恢复原始权重
        with torch.no_grad():
            self.linear.weight.copy_(orig_weight)
        return output

X = torch.tensor([[1.0], [2.0], [3.0], [4.0]], dtype=torch.float32)
Y = 2 * X

model = SimpleModel()
perturbation = torch.tensor(0.1, requires_grad=True)

criterion = nn.MSELoss()
optimizer = optim.SGD([perturbation], lr=0.01)

for epoch in range(100):
    optimizer.zero_grad()
    outputs = model(X, perturbation)
    loss = criterion(outputs, Y)
    loss.backward()
    print(f"Epoch {epoch}: perturbation grad = {perturbation.grad.item()}")
    optimizer.step()

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.06 22:52:50