Pytorch获取特征图

简单加载官方预训练模型

  • torchvision.models预定义了很多公开的模型结构
  • 如果pretrained参数设置为False,那么仅仅设定模型结构;如果设置为True,那么会启动一个下载流程,下载预训练参数
  • 如果只想调用模型,不想训练,那么设置model.eval()model.requires_grad_(False)
  • 想查看模型参数可以使用modulesnamed_modules,其中named_modules是一个长度为2的tuple,第一个变量是name,第二个变量是module本身。
# -*- coding: utf-8 -*-
from torch import nn
from torchvision import models

# load model. If pretrained is True, there will be a downloading process
model = models.vgg19(pretrained=True)
model.eval()
model.requires_grad_(False)

# get model component
features = model.features
modules = features.modules()
named_modules = features.named_modules()

# print modules
for module in modules:
    if isinstance(module, nn.Conv2d):
        weight = module.weight
        bias = module.bias
        print(module, weight.shape, bias.shape,
              weight.requires_grad, bias.requires_grad)
    elif isinstance(module, nn.ReLU):
        print(module)

print()
for named_module in named_modules:
    name = named_module[0]
    module = named_module[1]
    if isinstance(module, nn.Conv2d):
        weight = module.weight
        bias = module.bias
        print(name, module, weight.shape, bias.shape,
              weight.requires_grad, bias.requires_grad)
    elif isinstance(module, nn.ReLU):
        print(name, module)

图片预处理

  • 使用opencv和pil读图都可以使用transforms.ToTensor()把原本[H, W, 3]的数据转成[3, H, W]的tensor。但opencv要注意把数据改成RGB顺序。
  • vgg系列模型需要做normalization,建议配合torchvision.transforms来实现。
  • mini-batches of 3-channel RGB images of shape (3 x H x W), where H and W are expected to be at least 224. The images have to be loaded in to a range of [0, 1] and then normalized using mean = [0.485, 0.456, 0.406] and std = [0.229, 0.224, 0.225].

参考:https://pytorch.org/hub/pytorch_vision_vgg/

# -*- coding: utf-8 -*-
from PIL import Image
import cv2
import torch
from torchvision import transforms

# transforms for preprocess
preprocess = transforms.Compose([
    transforms.ToTensor(),
    transforms.Normalize(mean=[0.485, 0.456, 0.406],
                         std=[0.229, 0.224, 0.225])
])

# load image using cv2
image_cv2 = cv2.imread('lena_std.bmp')
image_cv2 = cv2.cvtColor(image_cv2, cv2.COLOR_BGR2RGB)
image_cv2 = preprocess(image_cv2)

# load image using pil
image_pil = Image.open('lena_std.bmp')
image_pil = preprocess(image_pil)

# check whether image_cv2 and image_pil are same
print(torch.all(image_cv2 == image_pil))
print(image_cv2.shape, image_pil.shape)

提取单个特征图

如果只提取单层特征图,可以把模型截断,以节省算力和显存消耗。
下面索引之所以有+1是因为pytorch预训练模型里面第一个索引的module总是完整模块结构,第二个才开始子模块。

# -*- coding: utf-8 -*-
from PIL import Image
from torchvision import models
from torchvision import transforms

# load model. If pretrained is True, there will be a downloading process
model = models.vgg19(pretrained=True)
model = model.features[:16 + 1]  # 16 = conv3_4
model.eval()
model.requires_grad_(False)
model.to('cuda')
print(model)

# load and preprocess image
preprocess = transforms.Compose([
    transforms.ToTensor(),
    transforms.Normalize(mean=[0.485, 0.456, 0.406],
                         std=[0.229, 0.224, 0.225]),
    transforms.Resize(size=(224, 224))
])
image = Image.open('lena_std.bmp')
image = preprocess(image)
inputs = image.unsqueeze(0)  # add batch dimension
inputs = inputs.cuda()

# forward
output = model(inputs)
print(output.shape)

提取多个特征图

  • 第一种方式:逐层运行model,如果碰到了需要保存的feature map就存下来。
  • 第二种方式:使用register_forward_hook,使用这种方式需要用一个类把feature map以成员变量的形式缓存下来。
  • 两种方式的运行效率差不多
  • 第一种方式简单直观,但是只能处理类似VGG这种没有跨层连接的网络;第二种方式更加通用。
# -*- coding: utf-8 -*-
from PIL import Image
import torch
from torchvision import models
from torchvision import transforms

# load model. If pretrained is True, there will be a downloading process
model = models.vgg19(pretrained=True)
model = model.features[:16 + 1]  # 16 = conv3_4
model.eval()
model.requires_grad_(False)
model.to('cuda')

# check module name
for named_module in model.named_modules():
    name = named_module[0]
    module = named_module[1]
    print('-------- %s --------' % name)
    print(module)
    print()

# load and preprocess image
preprocess = transforms.Compose([
    transforms.ToTensor(),
    transforms.Normalize(mean=[0.485, 0.456, 0.406],
                         std=[0.229, 0.224, 0.225]),
    transforms.Resize(size=(224, 224))
])
image = Image.open('lena_std.bmp')
image = preprocess(image)
inputs = image.unsqueeze(0)  # add batch dimension
inputs = inputs.cuda()

# forward - 1
layers = [2, 7, 8, 9, 16]
layers = sorted(set(layers))
feature_maps = {}
feature = inputs
for i in range(max(layers) + 1):
    feature = model[i](feature)
    if i in layers:
        feature_maps[i] = feature
for key in feature_maps:
    print(key, feature_maps.get(key).shape)


# forward - 2
class FeatureHook:
    def __init__(self, module):
        self.inputs = None
        self.output = None
        self.hook = module.register_forward_hook(self.get_features)

    def get_features(self, module, inputs, output):
        self.inputs = inputs
        self.output = output


layer_names = ['2', '7', '8', '9', '16']
hook_modules = []
for named_module in model.named_modules():
    name = named_module[0]
    module = named_module[1]
    if name in layer_names:
        hook_modules.append(module)

hooks = [FeatureHook(module) for module in hook_modules]
output = model(inputs)
features = [hook.output for hook in hooks]
for feature in features:
    print(feature.shape)

# check correctness
for i, layer in enumerate(layers):
    feature1 = feature_maps.get(layer)
    feature2 = features[i]
    print(torch.all(feature1 == feature2))

使用第二种方式(register_forward_hook),resnet特征图也可以顺利拿到。
而由于resnet的model已经不可以用model[i]的形式索引,所以无法使用第一种方式。

# -*- coding: utf-8 -*-
from PIL import Image
from torchvision import models
from torchvision import transforms

# load model. If pretrained is True, there will be a downloading process
model = models.resnet18(pretrained=True)
model.eval()
model.requires_grad_(False)
model.to('cuda')

# check module name
for named_module in model.named_modules():
    name = named_module[0]
    module = named_module[1]
    print('-------- %s --------' % name)
    print(module)
    print()

# load and preprocess image
preprocess = transforms.Compose([
    transforms.ToTensor(),
    transforms.Normalize(mean=[0.485, 0.456, 0.406],
                         std=[0.229, 0.224, 0.225]),
    transforms.Resize(size=(224, 224))
])
image = Image.open('lena_std.bmp')
image = preprocess(image)
inputs = image.unsqueeze(0)  # add batch dimension
inputs = inputs.cuda()


class FeatureHook:
    def __init__(self, module):
        self.inputs = None
        self.output = None
        self.hook = module.register_forward_hook(self.get_features)

    def get_features(self, module, inputs, output):
        self.inputs = inputs
        self.output = output


layer_names = [
    'conv1',
    'layer1.0.relu',
    'layer2.0.conv1'
]

hook_modules = []
for named_module in model.named_modules():
    name = named_module[0]
    module = named_module[1]
    if name in layer_names:
        hook_modules.append(module)

hooks = [FeatureHook(module) for module in hook_modules]
output = model(inputs)
features = [hook.output for hook in hooks]
for feature in features:
    print(feature.shape)

问题来了,resnet这种类型的网络结构怎么截断?
使用如下命令就可以,print查看需要截断到哪里,然后用nn.Sequential重组即可。
需注意重组后网络的module_name会发生变化

print(list(model.children())
model = torch.nn.Sequential(*list(model.children())[:6])
  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 可以通过在网络中添加一个特征提取层来获取网络输出的特征。这可以通过以下方式实现: 1. 在网络的最后添加一个特征提取层,例如全局平均池化层或全局最大池化层。 2. 在前向传递期间,将输入数据传递到网络中,并同时记录特征提取层的输出。 3. 返回特征提取层的输出作为网络的输出。 以下是示例代码: ```python import torch import torch.nn as nn import torch.nn.functional as F class Net(nn.Module): def __init__(self): super(Net, self).__init__() self.conv1 = nn.Conv2d(3, 32, kernel_size=3, padding=1) self.conv2 = nn.Conv2d(32, 64, kernel_size=3, padding=1) self.pool = nn.MaxPool2d(2, 2) self.fc1 = nn.Linear(64 * 8 * 8, 128) self.fc2 = nn.Linear(128, 10) self.feature_layer = nn.AdaptiveAvgPool2d((1, 1)) # 添加全局平均池化层,提取特征 def forward(self, x): x = F.relu(self.conv1(x)) x = self.pool(F.relu(self.conv2(x))) x = x.view(-1, 64 * 8 * 8) x = F.relu(self.fc1(x)) x = self.fc2(x) feature = self.feature_layer(x) # 获取特征 return feature net = Net() # 前向传递并获取特征 input_data = torch.randn(1, 3, 32, 32) output_feature = net(input_data) print(output_feature.shape) ``` 在此示例中,我们添加了一个全局平均池化层,将其作为特征提取层,以获取网络输出的特征。在前向传递期间,我们记录了特征提取层的输出,并将其作为网络的输出返回。 ### 回答2: 在PyTorch获取网络输出的特征可以通过多种方式实现,下面介绍两种常见的方法。 方法一:使用hook函数 我们可以通过在模型的某一层注册一个hook函数来获取该层的输出特征。首先,定义一个hook函数来记录特征值: ```python feature = None def hook_fn(module, input, output): global feature feature = output ``` 然后,在需要获取特征的地方,注册这个hook函数到相关的层上: ```python model.layer.register_forward_hook(hook_fn) ``` 在模型前向传播时,当模型经过注册了hook函数的层时,该层的输出会被保存到我们定义的feature变量中。最后,我们就可以通过访问feature变量来获取这个特定层的输出特征。 方法二:使用intermediate layer 另一种方法是通过修改模型结构,在网络中的某一层输出特征。假设我们想要获取模型的第n层的输出特征,可以按照以下步骤进行: 1. 将模型拆分成两部分:前n层为一个模型,后面的层为一个模型。 2. 通过前n层模型的forward方法传递输入数据,得到输出特征。 3. 将输出特征作为另一个模型的输入,通过该模型的forward方法得到最终的输出。 通过这种方式,我们可以获取到指定层的输出特征。 总结:以上介绍了两种常见的方法来获取PyTorch中网络输出的特征。通过使用hook函数或调整模型结构,我们可以方便地获取任意层的输出特征。 ### 回答3: 在PyTorch中,可以通过在网络模型中的某一层之后添加一个特征提取器(feature extractor)来获取网络的输出特征特征提取器是一个新的网络模型,其中包含了原始网络模型中感兴趣的层之后的所有层。 要获取网络输出的特征,首先需要定义原始网络模型。然后,通过将原始网络模型作为一个子模块添加到特征提取器中,并将所需的层之后的所有层从原始网络复制到特征提取器中。这样,特征提取器模型就会从原始网络模型的所需层之后开始输出特征。 接下来,可以将输入数据通过特征提取器模型进行正向传播,从而获得网络输出的特征特征提取器模型会将输入数据经过原始网络模型的所需层之后的所有层处理,并给出输出特征。 在获取网络输出的特征后,可以进一步对这些特征进行处理和分析,比如进行分类、聚类、可视化等操作,以更好地理解网络的输出。 总的来说,利用PyTorch获取网络输出的特征可以通过定义特征提取器模型,在原始网络模型的所需层之后添加对应层来实现。这样可以获得网络输出的特征,并进行进一步的处理和分析。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值