调用resnet50权重_ResNeSt 模型分析和代码详解 (拆组和通道注意力ResNet)

本文详细介绍了ResNeSt模型的拆分注意力机制,包括split和channel-attention,并通过代码示例展示了如何调用预训练的ResNeSt50权重。与ResNet50对比,ResNeSt在特征提取上进行了增强,提高预测精度。此外,文章还探讨了ResNeSt中split attention和channel attention的实现细节,并提供了预测猫星人的代码示例,显示了ResNeSt相比MobileNet V2的预测优势。
摘要由CSDN通过智能技术生成

ResNeSt: Split-Attention Networks模型的拆分注意力网络,最近特别火,主要是作为深度学习的backbone模型,ResNeSt在不同的图像任务中都有效提高了模型的预测精度。

因此今天分享下,最近两天学习的心得体会,参考资料如下:

ResNeSt: Split-Attention Networks

github官网

B站作者讲解

张航主页

安装使用

由于作者的主要想法就是搭建一个方便大家使用的Deep learning的backbone。所以使用起来还是相当方便。安装直接使用pip就可以安装ResNeSt模块

pip install git+https://github.com/zhanghang1989/ResNeSt

使用就更简单啦,直接import

# using ResNeSt-50 as an example from resnest.torch import resnest50

net = resnest50(pretrained=True)

简单测试一下代码的运行效果,对博客中的猫星人进行预测

import resnest from resnest.torch import resnest50 import torch import numpy as np from torchvision import transforms from PIL import Image def image_trans(img_file): tf = transforms.Compose([ lambda x:Image.open(x).convert('RGB'), # string path= > image data transforms.Resize(224), transforms.ToTensor(), transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]) ]) img = tf(img_file).unsqueeze(0) # add batch dim return img

img = image_trans('./Cat.jpg') net = resnest50(pretrained=True) net.eval() # input_tensor = torch.randn(2, 3, 224, 224) output_tensor = net(img) # [-1, 1] =>[0, 1] per softmax and transform to the numpy array predictions = torch.nn.functional.softmax(output_tensor, 1).detach().numpy() # ImageNet Decode from tensorflow.keras.applications.mobilenet_v2 import decode_predictions # print(output_tensor.shape) print('Predicted:', decode_predictions(predictions, top=3)[0])

Predicted: [(‘n02124075’, ‘Egyptian_cat’, 0.3746694), (‘n02123045’, ‘tabby’, 0.30778167), (‘n02123159’, ‘tiger_cat’, 0.04715328)]

对比MobileNet V2的预测结果,发现前两种的预测概率有所提升

Predicted: [(‘n02124075’, ‘Egyptian_cat’, 0.329623), (‘n02123045’, ‘tabby’, 0.2984233), (‘n02123159’, ‘tiger_cat’, 0.08494468)]

ResNeSt创新点

看论文的个人感受觉:读完对split attention的模型特点也不是很清楚,图看起来也不是太好理解,个人觉得可能是因为文中涉及的cardinality和radix两个超参的说明有点抽象,示意图也不是特别形象,导致很难一下就明白作者想表达的操作流程。

我是在看了源代码和作者讲解视频后才真正理解了模型的精髓点。其实。作者一直强调的两个点,即split(multi-brach)和channel-attention。如果理解了这两点,就算基本掌握ResNeSt。

split(multi-brach)拆分再拆分!!!

Multi-brach能更够使模型具有diverse representation,即在同一层中多个卷积核分支可以分别提取特征使得网络提取的特征更为多样。具体模型实现,作者在两个层级上体现了了mult-brach的思想。如下图中的cardinality 系数K和radix系数R,将输入特征分成了G组。

对于cardinality层级,作者使用了nn.Conv2d中的groups参数将输入特征分成Cardinality=K组。这个K组特征层和卷积核的操作使相对独立的操作。

对于radix层级,作者先使用了一个3x3的conv2d将通道数增加到channels*radix后,再用torch.split分成将特征层分成radix组输入。

aad6609583dc8c16e4afe6b593f18b26.png

Channel attention 小组求权重再融合!!!

Channel-attention能够提供捕捉feature correlation的网络机制,即通过引入软注意机制实现特征通道间的权重分配。这里的权重系数获得可参考下图,首先将radix组的特征求和,再取global average pooling得到与单个radix组相同维度的矢量。

然后使用两组1x1的conv2d进行权重系数的分配,具体维度上第一组卷积的输出维度为max(in_channels*radix//reduction_factor, 32) ,这里的reduction_factor =4缩放系数用于减少参数量;第二组的卷积的输出维度channels*radix,保持了与radix的输入特征层维度的对应。

为保证radix组间的特征层的split的权重独立分布,使用r-softmax对各radix组的权重分别计算softmax得到attens,最后将各组对应的特征层与atten系数相乘再求和。

97eefabf8a49688bf647b9742d5d5811.png

如果理解起来还是有点困难,没关系接下来我们对着源代码仔细剖析下ResNeSt的模型细节

代码详解

强烈建议对比TORCH官方的TORCHVISION.MODELS.RESNET50框架来来理解resnest的代码。

首先看看直接调用的resnest.torch.resnest.py文件中的内容。

resnest.py

定义了包括resnest50, 101, 200, 269在内的四个函数,函数内通过调用同目录下的resnet.py中的ResNet类建立model对象。

def resnest50(pretrained=False, root='~/.encoding/models', **kwargs): model = ResNet(Bottleneck, [3, 4, 6, 3], radix=2, groups=1, bottleneck_width=64, deep_stem=True, stem_width=32, avg_down=True, avd=True, avd_first=False, **kwargs) if pretrained: model.load_state_dict(torch.hub.load_state_dict_from_url( resnest_model_urls['resnest50'], progress=True, check_hash=True)) return model

对于pretrained weight 文件的加载,程序通过查找文件名对应的url地址进行在线下载和权重加载过程

# 地址类似https://s3.us-west-1.wasabisys.com/resnest/torch/resnest50-528c19ca.pth _url_format = 'https://s3.us-west-1.wasabisys.com/resnest/torch/{}-{}.pth' # 建立一个{"resnest50":528c19ca,,,}的字典 _model_sha256 = {name: checksum for checksum, name in [ ('528c19ca', 'resnest50'), ('22405ba7', 'resnest101'), ('75117900', 'resnest200'), ('0cc87c48', 'resnest269'), ]} def short_hash(name): if name not in _model_sha256: raise ValueError('Pretrained model for {name} is not available.'.format(name=name)) return _model_sha256[name][:8] # 建立字典{"resnest50":https://s3.us-west-1.wasabisys.com/resnest/torch/resnest50-528c19ca.pth,,,} resnest_model_urls = {name: _url_format.format(name, short_hash(name)) for name in _model_sha256.keys() }

resnet.py

ResNeSt整体网络结构完全参考resnet,因此如下包括stem,layer1, layer2, layer3, layer4模块构成。

158226821c7b1d5869e08205bd009682.png

resnet50 layer1

为比较两者的局部结构的差异性,首先打印resnet50的layer1的结构

import torchvision.models as models

net = models.resnet50(pretrained=True) print(net)

可见layer1中有三种Bottleneck,其中第一组bottleneck通道数由64变成256,由于改变了通道数需要增加downsample模块,另外两组保持通输入输出道数为256。

每组bottleneck由三组conv组成,卷积核分别为1x1、3x3 和 1x1。除第一组bottleneck外,每组的通道数存在bottleneck机制,用于减少网络的参数,即第二组conv的通道数为64

(layer1): Sequential(

(0): Bottleneck(

(conv1): Conv2d(64, 64, kernel_size=(1, 1), stride=(1, 1), bias=False)

(bn1): BatchNorm2d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)

(conv2): Conv2d(64, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)

(bn2): BatchNorm2d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)

(conv3): Conv2d(64, 256, kernel_size=(1, 1), stride=(1, 1), bias=False)

(bn3): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)

(relu): ReLU(inplace=True)

(downsample): Sequential(

(0): Conv2d(64, 256, kernel_size=(1, 1), stride=(1, 1), bias=False)

(1): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)

)

)

(1): Bottleneck(

(conv1): Conv2d(256, 64, kernel_size=(1, 1), stride=(1, 1), bias=False)

(bn1): BatchNorm2d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)

(conv2): Conv2d(64, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)

(bn2): BatchNorm2d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)

(conv3): Conv2d(64, 256, kernel_size=(1, 1), stride=(1, 1), bias=False)

(bn3): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)

(relu): ReLU(inplace=True)

)

(2): Bottleneck(

(conv1): Conv2d(256, 64, kernel_size=(1, 1), stride=(1, 1), bias=False)

(bn1): BatchNorm2d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)

(conv2): Conv2d(64, 64, kernel_size=(3, 3

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值