Python 将前弯钩添加到一层中是否确保使用层计算的损失梯度';s的输出将自动计算?

Python 将前弯钩添加到一层中是否确保使用层计算的损失梯度';s的输出将自动计算?,python,pytorch,Python,Pytorch,我有一个模型 class NewModel(nn.Module): def __init__(self,output_layer,*args): self.output_layer = output_layer super().__init__(*args) self.output_layer = output_layer self.selected_out = None #PRETRAINED MODEL

我有一个模型

class NewModel(nn.Module):
    def __init__(self,output_layer,*args):
        self.output_layer = output_layer
        super().__init__(*args)

        self.output_layer = output_layer
        self.selected_out = None
        #PRETRAINED MODEL
        self.pretrained = models.resnet18(pretrained=True)
    
        #TAKING OUTPUT FROM AN INTERMEDIATE LAYER

        #self._layers = []
        for l in list(self.pretrained._modules.keys()):
            #self._layers.append(l)
            if l == self.output_layer:
                handle = getattr(self.pretrained,l).register_forward_hook(self.hook)
   
    def hook(self,module, input,output):
        self.selected_out = output

    def forward(self, x):
        return x = self.pretrained(x)
我有两个目标输出,一个与图像的任何标签相同,第二个与从
self.output\u layer
获得的输出尺寸相同,称为
target\u feature

out = model(img)
layerout = model.selected_out
现在,如果我想用目标特征图计算layerout的损失,可以像下面写的那样吗

loss = criterion(y_true, out) + feature_criterion(layerout, target_feature)
还是需要向后添加
挂钩

在这个卡格尔笔记本里

在使用
backward\u钩子时,不能使用
loss.backward()

引用作者的话

# backprop once to get the backward hook results
out.backward(torch.tensor([1,1],dtype=torch.float),retain_graph=True)
#! loss.backward(retain_graph=True)  # doesn't work with backward hooks, 
#! since it's not a network layer but an aggregated result from the outputs of last layer vs target 

那么如何根据损失函数计算梯度呢?

如果我理解正确,您希望从模型中获得两个输出,计算两个损失,然后将它们合并并反向传播。我想象你来自Tensorflow&Keras,你尝试实现它的方式。在Pytork中,它实际上是相当直截了当的,您可以很容易地做到这一点,因为它纯粹是功能性的

这只是一个例子:

class NewModel(nn.Module):
    def __init__(self, output_layer, *args):
        super(MyModel, self).__init__()
        
        self.pretrained = models.resnet18(pretrained=True)
        self.output_layer = output_layer
        
    def forward(self, x):
        out = self.pretrained(x)
        features = self.output_layer(out)
        return out, features
推断时,每次调用将得到两个结果:

>>> m = NewModel(nn.Linear(1000, 10))
>>> x = torch.rand(16, 3, 224, 224)
>>> y_pred, y_feature = m(x)
呼叫您丢失功能:

>>> loss = criterion(y_pred, y_true) + feature_criterion(y_feature, target_feature)
然后,使用
loss.backward()
反向传播

因此,在
调用上不需要挂钩,也不需要复杂的渐变


编辑-如果您希望提取中间层输出,请保留挂钩,这很好。只需修改
转发
定义即可

def forward(self, x):
    out = self.pretrained(x)
    return out, self.selected_out
例如:

>>> m = NewModel(output_layer='layer1')
>>> x = torch.rand(16, 3, 224, 224)
>>> y_pred, y_feature = m(x)

>>> y_pred.shape, y_feature.shape
(torch.Size([16, 1000]), torch.Size([16, 64, 56, 56]))

此外,我上面所说的关于损失的话仍然有效。计算您的损失,然后调用
loss.backward()

self.output\u layer实际上是我在第二次损失中使用其输出的中间层的名称。假设“layer4”是ResNet中某个层的名称。因此,我设置self.output\u layer='layer4',然后我使用前向钩子从这个层获取输出,然后我继续用这个输出计算损失。很抱歉,我应该更加注意。我已经编辑了我的答案。我使用这个损失函数只是为了测试
loss=torch.sum((label out)**2)+torch.sum((layerout-x2))
,但我得到了错误
运行时错误:张量的元素0不需要梯度,也没有梯度fn
label=torch.Tensor(np.array([1]+[0]*999)).to('cuda:0')
x2=torch.Tensor(np.random.random((2048,7,7)).to('cuda:0'))
I在
hook
方法中添加了
self.selected\u out.requires\u grad=True
。但这意味着每次调用
hook
方法时都会执行它。您将传递哪个层作为
output\u layer
?我尝试了
output\u layer=layer4
以获得
的层输出形状(-1,512,7,7)
而且工作正常。您不应该需要任何渐变。您的代码中一定有您没有提到的内容。