如何获取模型的中间特征-timm-1

本文介绍了在深度学习模型中获取倒数第二层特征的三种方法:1)直接调用forward_features;2)创建无分类头的模型;3)使用reset_classifier重置模型。文中以ViT和ConvNext为例,详细阐述了如何在不包含池化或分类层的情况下提取模型特征。
摘要由CSDN通过智能技术生成

可以通过多种方式获得模型特征,而无需手动修改模型

今天讲第一种方法

获取倒数第二层特征

所有的model都定义了forward_features和forward_head方法 而forward一般写为如下形式

  def forward(self, x):
    x = self.forward_features(x)
    x = self.forward_head(x)
    return x

其中forward_head一般是由pool,norm,mlp这些部分组成 我们一般关心模型主体能否提取图像特征,特征能否很好的迁移到其他任务,所以模型head之前的那一层很重要,经常要拿出来单独分析,关于这部分大概有三种实现方式

1.model.forward_features(input)

使用模型前向推理时就只使用forward_features,而不经过head,直接得到特征

modelvit = timm.create_model('vit_small_patch16_224', pretrained=True)
#print(modelvit.global_pool)#token
​
outfeatures = modelvit.forward_features(img224) 
#torch.Size([2, 197, 384])

2.创建一个没有head(池化和分类层)的模型

创建模型时, num_classes=0,取消分类层 build_model_with_cfg中,使用指定的default_cfg和可选的model_cfg构建模型,其中

# For classification models, check class attr, then kwargs, then default to 1k, otherwise 0 for feats
    num_classes_pretrained = 0 if features else getattr(model, 'num_classes', kwargs.get('num_classes', 1000))

getattr() 函数用于返回一个对象属性值 get() 函数在字典中搜索给定的键,如果找不到则返回默认值 features意味着提取模型所有中间特征,这个下一篇再说 这句话意思是对于模型,先检查类attr有没有num_classes定义,没有的话 去获取kwargs中num_classes,没有就默认为1k 当我们设定num_classes=0时,num_classes_pretrained = 0 ,从而在模型创建时去掉分类层

global_pool='' 取消池化 在影响模型架构,分别看一下ViT和ConvNext的源码

#vit
    def forward_head(self, x, pre_logits: bool = False):
        if self.global_pool:
            x = x[:, self.num_prefix_tokens:].mean(dim=1) if self.global_pool == 'avg' else x[:, 0]
        x = self.fc_norm(x)
        return x if pre_logits else self.head(x)
#convnext
self.head = nn.Sequential(OrderedDict([
                ('global_pool', SelectAdaptivePool2d(pool_type=global_pool)),
                ('norm', nn.Identity() if head_norm_first else norm_layer(self.num_features)),
                ('flatten', nn.Flatten(1) if global_pool else nn.Identity()),
                ('drop', nn.Dropout(self.drop_rate)),
                ('fc', nn.Linear(self.num_features, num_classes) if num_classes > 0 else nn.Identity())]))
​

代码如下

modelvitnohead = timm.create_model('vit_small_patch16_224', pretrained=True, num_classes=0, global_pool='')
outfeatures = modelvitnohead(img224)
print(outfeatures.size())#torch.Size([2, 197, 384])

3.也可reset_classifier重制移除head层

每个模型都有定义此方法(reset_classifier),除此之外一般还有get_classifier/set_grad_checkpointing/group_matcher vit中

    def reset_classifier(self, num_classes: int, global_pool=None):
        self.num_classes = num_classes
        if global_pool is not None:
            assert global_pool in ('', 'avg', 'token')
            self.global_pool = global_pool
        self.head = nn.Linear(self.embed_dim, num_classes) if num_classes > 0 else nn.Identity()

convnext中

    def reset_classifier(self, num_classes=0, global_pool=None):
        if global_pool is not None:
            self.head.global_pool = SelectAdaptivePool2d(pool_type=global_pool)
            self.head.flatten = nn.Flatten(1) if global_pool else nn.Identity()
        self.head.fc = nn.Linear(self.num_features, num_classes) if num_classes > 0 else nn.Identity()

代码如下

m = timm.create_model('vit_small_patch16_224', pretrained=True)
m.reset_classifier(0,'')
outfeatures = m(img224)
print(outfeatures.size())
​

注意:想清楚自己需要去除分类层还是分类层和池化层

影响head的不止num_class分类层 ,还要注意global_pool池化层 以上面reset_classifier的方法为例 m.reset_classifier(0,'')--outfeatures为-torch.Size([2, 197, 384]) m.reset_classifier(0)----outfeatures为-torch.Size([2, 384]) 可以看到有没有池化层对ViT输出的特征影响很大,ViT 使用Token池化,把182个Token池化为一个最终输出来进行分类

当想要未被池化的特征选择前面三种方法,想要池化后的,有两张方法(移除head层不可用) 可以 modelvitnoclass = timm.create_model('vit_small_patch16_224', pretrained=True, num_classes=0)#不写global_pool='' 或者 m.reset_classifier(0)#重制head的分类层

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值