运行时获取 ResNet 模型全连接层的输入

Posted

技术标签:

【中文标题】运行时获取 ResNet 模型全连接层的输入【英文标题】:Get input of fully connected layer of ResNet model during runtime 【发布时间】:2022-01-11 16:03:13 【问题描述】:

找到了一个解决方案,把它作为这个问题的答案留在下面:)

项目信息:2 类分类任务。

我正在尝试为我在运行时放入模型的每个图像获取模型的完全连接层的输出。我计划在模型完成训练或测试所有图像后使用它们以使用 UMAP 进行可视化。

型号:

#Load resnet
def get_model():
    model = torchvision.models.resnet50(pretrained=True)
    num_ftrs = model.fc.in_features
    model.fc = nn.Linear(num_ftrs, 2)
    return model

pl模块的相关部分:

class classifierModel(pl.LightningModule):
   def __init__(self, model):
     super().__init__()
     self.model = model
     self.learning_rate = 0.0001

def training_step(self, batch, batch_idx):
        x= batch['image']
        y = batch['targets']
        x_hat = self.model(x)
        output = nn.CrossEntropyLoss()
        loss= output(x_hat,y)
        return loss
 
def test_step(self, batch, batch_idx):
        x= batch['image']
        y = batch['targets']
        x_hat = self.model(x)

是否可以通过在 pl 模块的 init 中添加一个空列表,然后在执行 x_hat = model(x) 之后添加输出来做到这一点? 我怎么知道在执行x_hat = model(x) 之后,out_features 是否没有立即删除/丢弃?

【问题讨论】:

对不起,您能举个例子详细说明一下吗?说“一些输入”,然后说“当前输出”或“所需输出”? 所以基本上我想给 CNN 一些输入 (x)。当前输出是两个分数,每个类别一个。我想要的输出是形状 [1, 2048] 的全连接层的值。 【参考方案1】:

x_hat 是这个向量并且是[batch_size, 2048]。因此,只需修改您的训练步骤以同时返回 x_hat

class classifierModel(pl.LightningModule):
   def __init__(self, model):
     super().__init__()
     self.model = model
     self.learning_rate = 0.0001
     self.fc_outputs = []

   def training_step(self, batch, batch_idx):
       x= batch['image']
       y = batch['targets']
       x_hat = self.model(x)
       self.fc_outputs.append(x_hat)
       output = nn.CrossEntropyLoss()
       loss= output(x_hat,y)
       return loss

x_hat 的值不会被删除,除非您在其他地方分配这些值之前明确调用 del x_hat。在您已经将 x_hat 的值分配给另一个变量的情况下(在您的情况下,这听起来像是您想将其附加到列表中)与这些值关联的内存地址不会被释放,因为仍然有一个变量即使在引用它们的原始变量之后也会引用这些地址(x_hat 可能已被删除)。这样,python 在内存引用方面相对安全,因为它会在运行时动态计算何时不再需要内存地址/值。

【讨论】:

谢谢,但是 x_hat 的形状实际上是 [batch_size, 2] 因为在模型中我将全连接层设置为model.fc = nn.linear(2048,2) 以在两个类上训练模型/在两个类上得到结果. 那么不行,不稍微修改ResNet的源代码就无法得到中间层的结果,让这个变量持久化存储。这样做很简单,但您无法在训练脚本中完成此操作。【参考方案2】:

我能够使用 avgpool 层上的前向钩子来做到这一点,并在每个 test_step 上保存输出,如here 所述:

#Define Hook: 
def get_features(name):
    def hook(model, input, output):
        features[name] = output.detach()
    return hook

现在当我加载我的模型时,我注册了钩子:

#Load resnet model:
def get_model():
    model = models.resnet50(pretrained=True)
    num_ftrs = model.fc.in_features
    model.fc = nn.Linear(num_ftrs, 2)
    model.avgpool.register_forward_hook(get_features('feats')) #register the hook
    return model

我不需要更改pytorch闪电模型的init,而是测试步骤功能:

FEATS = []
# placeholder for batch features
features = 

class classifierModel(pl.LightningModule):
   def __init__(self, model):
     super().__init__()
     self.model = model
     self.learning_rate = 0.0001

def test_step(self, batch,batch_idx):
        x= batch['image']
        y = batch['targets']
        x_hat = self.model(x)
        FEATS.append(features['feats'].cpu().numpy()) #added this line to save output

现在我们有了输出FEATS[0].shape --> (16, 2048, 1, 1),这是我想要得到的(16 是使用的批大小)。

【讨论】:

以上是关于运行时获取 ResNet 模型全连接层的输入的主要内容,如果未能解决你的问题,请参考以下文章

resnet 有全连接层吗?

resnet18全连接层改成卷积层

神经网络的全连接层

全连接层的作用是什么?(nn.Linear())

怎么确实cnn全连接层的神经元数目

resnet数据尺寸为单数