Python 3.x 如何计算相对于模型输入的误差梯度?

Python 3.x 如何计算相对于模型输入的误差梯度?,python-3.x,pytorch,Python 3.x,Pytorch,给定一个简单的2层神经网络,传统的想法是计算梯度w.r.t.权重/模型参数。对于一个实验,我想计算输入的误差w.r.t的梯度。现有的Pytorch方法是否允许我这样做 更具体地,考虑下面的神经网络: import torch.nn as nn import torch.nn.functional as F class NeuralNet(nn.Module): def __init__(self, n_features, n_hidden, n_classes, dropout):

给定一个简单的2层神经网络,传统的想法是计算梯度w.r.t.权重/模型参数。对于一个实验,我想计算输入的误差w.r.t的梯度。现有的Pytorch方法是否允许我这样做

更具体地,考虑下面的神经网络:

import torch.nn as nn
import torch.nn.functional as F

class NeuralNet(nn.Module):
    def __init__(self, n_features, n_hidden, n_classes, dropout):
        super(NeuralNet, self).__init__()

        self.fc1 = nn.Linear(n_features, n_hidden)
        self.sigmoid = nn.Sigmoid()
        self.fc2 = nn.Linear(n_hidden, n_classes)
        self.dropout = dropout

    def forward(self, x):
        x = self.sigmoid(self.fc1(x))
        x = F.dropout(x, self.dropout, training=self.training)
        x = self.fc2(x)
        return F.log_softmax(x, dim=1)
我为权重实例化了模型和优化器,如下所示:

import torch.optim as optim
model = NeuralNet(n_features=args.n_features,
            n_hidden=args.n_hidden,
            n_classes=args.n_classes,
            dropout=args.dropout)
optimizer_w = optim.SGD(model.parameters(), lr=0.001)
训练时,我会像往常一样更新重量。现在,假设我有权重值,我应该能够使用它们来计算输入的梯度w.r.t。我不知道怎么做

def train(epoch):
    t = time.time()
    model.train()
    optimizer.zero_grad()
    output = model(features)
    loss_train = F.nll_loss(output[idx_train], labels[idx_train])
    acc_train = accuracy(output[idx_train], labels[idx_train])
    loss_train.backward()
    optimizer_w.step()

    # grad_features = loss_train.backward() w.r.t to features
    # features -= 0.001 * grad_features

for epoch in range(args.epochs):
    train(epoch)


有可能,只需为每个输入批次设置
input.requires_grad=True
,然后在
loss.backward()之后,您应该看到
input.grad
保持预期的梯度。换句话说,如果您对模型的输入(您在代码中称之为
features
)是一些
mxnx…
张量,
features.grad
将是一个形状相同的张量,
grad
的每个元素相对于
features
的相应元素保持梯度。在我下面的评论中,我使用
I
作为通用索引-如果您的
参数
具有例如3个维度,请将其替换为
特征.grad[I,j,k]

关于您得到的错误:PyTorch操作构建一棵树,表示它们所描述的数学操作,然后用于微分。例如,
c=a+b
将创建一个树,其中
a
b
是叶节点,
c
不是叶节点(因为它是由其他表达式生成的)。您的模型是表达式,其输入和参数都是叶子,而所有中间和最终输出都不是叶子。你可以把叶子看作是“常量”或“参数”,把所有其他变量看作这些变量的函数。此消息告诉您只能设置叶变量的
requires\u grad

您的问题是,在第一次迭代中,
特性
是随机的(或者不管您如何初始化),因此是有效的叶。在第一次迭代之后,
特性
不再是一个叶,因为它变成了一个基于前面的叶计算的表达式。在伪代码中,您有

f_1 = initial_value # valid leaf
f_2 = f_1 + your_grad_stuff # not a leaf: f_2 is a function of f_1
要处理这个问题,您需要使用它,它会断开树中的链接,并使自动标记将张量视为常量,无论它是如何创建的。特别是,不会通过
detach
反向传播梯度计算。所以你需要像

features = features.detach() - 0.01 * features.grad

注意:也许你需要在这里或那里撒上更多的
detach
es,这很难说没有看到你的全部代码,也不知道确切的目的。

为了澄清,你的意思是我应该在
train
方法中添加一行
features.require_grad=True
?并且
特征.grad
张量的第i行将包含梯度w.r.t.的值。第i个特征/输入的当前模型参数?是的,它进入
train
<代码>功能。grad[i]
将包含关于
功能[i]
loss\u train
的导数,这是您在问题标题中要求的。一个小的后续问题:
功能。grad[i]
返回第i次输入的
loss\u train
的梯度(
功能[i]
)因此,
特征[i][j]
是第i个输入的第j个特征的梯度。这个解释正确吗?另外,在
train
中,我尝试通过执行以下操作来“更新”功能:
features[:,j]=0.01*features.grad[:,j]
,对于某些j。但是我得到了这个错误-
RuntimeError:您只能更改leaf变量的requires\u grad标志。
。这意味着什么?更新功能的正确方法是什么?这是一个非常有用的解释。多谢各位
detach()
不起作用,但给出了与以前相同的错误,因为后者将从当前图形中分离,并使其成为前者没有的叶子。另外,
detach_389;()
不允许就地操作,需要单独调用它。我是在梯度更新之前还是之后分离张量有关系吗?我接受你的回答。也许您可能想考虑编辑<代码> DeXCHO()< <代码>。