• 文档 >
  • 模型检查的特征提取
快捷链接

模型检查的特征提取

torchvision.models.feature_extraction 包包含特征提取实用程序,使我们能够利用我们的模型来访问输入的中间转换。这对于计算机视觉中的各种应用可能很有用。以下是一些示例

  • 可视化特征图。

  • 提取特征以计算图像描述符,用于面部识别、重复检测或图像检索等任务。

  • 将选定的特征传递到下游子网络,以便在考虑特定任务的情况下进行端到端训练。例如,将特征层次结构传递到具有对象检测头的特征金字塔网络。

Torchvision 提供了 create_feature_extractor() 用于此目的。它的工作原理大致如下

  1. 符号跟踪模型以获得图形表示,了解模型如何逐步转换输入。

  2. 将用户选择的图节点设置为输出。

  3. 删除所有冗余节点(输出节点下游的任何内容)。

  4. 从结果图中生成 python 代码,并将其与图本身捆绑到 PyTorch 模块中。


torch.fx 文档 更全面、详细地解释了上述过程和符号跟踪的内部工作原理。

关于节点名称

为了指定哪些节点应作为提取特征的输出节点,应该熟悉此处使用的节点命名约定(这与 torch.fx 中使用的约定略有不同)。节点名称指定为 . 分隔的路径,该路径从顶层模块向下遍历到叶操作或叶模块。例如,ResNet-50 中的 "layer4.2.relu" 表示 ResNet 模块第 4 层第 2 个块的 ReLU 的输出。以下是一些需要记住的更精细的点

  • 在为 create_feature_extractor() 指定节点名称时,您可以提供节点名称的截断版本作为快捷方式。要了解其工作原理,请尝试创建 ResNet-50 模型并使用 train_nodes, _ = get_graph_node_names(model) print(train_nodes) 打印节点名称,并观察与 layer4 相关的最后一个节点是 "layer4.2.relu_2"。可以将 "layer4.2.relu_2" 指定为返回节点,或者仅将 "layer4" 指定为返回节点,按照约定,这指的是 layer4 的最后一个节点(按执行顺序)。

  • 如果某个模块或操作重复多次,则节点名称会获得额外的 _{int} 后缀以消除歧义。例如,加法 (+) 运算可能在同一个 forward 方法中使用了三次。那么将会有 "path.to.module.add""path.to.module.add_1""path.to.module.add_2"。计数器在直接父级的范围内维护。因此,在 ResNet-50 中,有一个 "layer4.1.add" 和一个 "layer4.2.add"。由于加法运算位于不同的块中,因此无需后缀来消除歧义。

一个例子

以下是如何提取 MaskRCNN 特征的示例

import torch
from torchvision.models import resnet50
from torchvision.models.feature_extraction import get_graph_node_names
from torchvision.models.feature_extraction import create_feature_extractor
from torchvision.models.detection.mask_rcnn import MaskRCNN
from torchvision.models.detection.backbone_utils import LastLevelMaxPool
from torchvision.ops.feature_pyramid_network import FeaturePyramidNetwork


# To assist you in designing the feature extractor you may want to print out
# the available nodes for resnet50.
m = resnet50()
train_nodes, eval_nodes = get_graph_node_names(resnet50())

# The lists returned, are the names of all the graph nodes (in order of
# execution) for the input model traced in train mode and in eval mode
# respectively. You'll find that `train_nodes` and `eval_nodes` are the same
# for this example. But if the model contains control flow that's dependent
# on the training mode, they may be different.

# To specify the nodes you want to extract, you could select the final node
# that appears in each of the main layers:
return_nodes = {
    # node_name: user-specified key for output dict
    'layer1.2.relu_2': 'layer1',
    'layer2.3.relu_2': 'layer2',
    'layer3.5.relu_2': 'layer3',
    'layer4.2.relu_2': 'layer4',
}

# But `create_feature_extractor` can also accept truncated node specifications
# like "layer1", as it will just pick the last node that's a descendent of
# of the specification. (Tip: be careful with this, especially when a layer
# has multiple outputs. It's not always guaranteed that the last operation
# performed is the one that corresponds to the output you desire. You should
# consult the source code for the input model to confirm.)
return_nodes = {
    'layer1': 'layer1',
    'layer2': 'layer2',
    'layer3': 'layer3',
    'layer4': 'layer4',
}

# Now you can build the feature extractor. This returns a module whose forward
# method returns a dictionary like:
# {
#     'layer1': output of layer 1,
#     'layer2': output of layer 2,
#     'layer3': output of layer 3,
#     'layer4': output of layer 4,
# }
create_feature_extractor(m, return_nodes=return_nodes)

# Let's put all that together to wrap resnet50 with MaskRCNN

# MaskRCNN requires a backbone with an attached FPN
class Resnet50WithFPN(torch.nn.Module):
    def __init__(self):
        super(Resnet50WithFPN, self).__init__()
        # Get a resnet50 backbone
        m = resnet50()
        # Extract 4 main layers (note: MaskRCNN needs this particular name
        # mapping for return nodes)
        self.body = create_feature_extractor(
            m, return_nodes={f'layer{k}': str(v)
                             for v, k in enumerate([1, 2, 3, 4])})
        # Dry run to get number of channels for FPN
        inp = torch.randn(2, 3, 224, 224)
        with torch.no_grad():
            out = self.body(inp)
        in_channels_list = [o.shape[1] for o in out.values()]
        # Build FPN
        self.out_channels = 256
        self.fpn = FeaturePyramidNetwork(
            in_channels_list, out_channels=self.out_channels,
            extra_blocks=LastLevelMaxPool())

    def forward(self, x):
        x = self.body(x)
        x = self.fpn(x)
        return x


# Now we can build our model!
model = MaskRCNN(Resnet50WithFPN(), num_classes=91).eval()

API 参考

create_feature_extractor(model[, ...])

创建一个新的图模块,该模块从给定模型返回中间节点作为字典,其中用户指定的键为字符串,请求的输出为值。

get_graph_node_names(model[, tracer_kwargs, ...])

用于按执行顺序返回节点名称的开发实用程序。

文档

访问 PyTorch 的综合开发者文档

查看文档

教程

获取面向初学者和高级开发者的深入教程

查看教程

资源

查找开发资源并获得解答

查看资源