你的 ResNet 是时候更新了

本文介绍ResNeSt,一种在参数量增加较少情况下提升性能的深度学习模型,结合ResNeXt的分组卷积和SE-Net的注意力机制。通过代码实现详解了Split-Attention机制,包括SplAtConv2d和RadixMajorNaiveImp两种实现方式。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

 

作者简介

CW,广东深圳人,毕业于中山大学(SYSU)数据科学与计算机学院,毕业后就业于腾讯计算机系统有限公司技术工程与事业群(TEG)从事Devops工作,期间在AI LAB实习过,实操过道路交通元素与医疗病例图像分割、视频实时人脸检测与表情识别、OCR等项目。

目前也有在一些自媒体平台上参与外包项目的研发工作,项目专注于CV领域(传统图像处理与深度学习方向均有)。

前言

赶在4月末,终于有时间写文了,最近工作上需求比较急,抽不出时间来更文,但我心早已狂热!在我敲上这行字的过程中,真的很开心,因为真心很享受这种静静地码字向别人分享知识的时光(虽然不知道有没有人看..)。如今就为大家奉上这个新鲜出炉的新品 —— ResNeSt

文末可获取相关Paper&源码链接

你没看错,是 ResNeSt 而不是 ResNet 哟!这是张航、李沐等大佬创造的 ResNet 改进版,在参数量没有显著增加的情况下显著提升了性能,并且可以很方便地如 ResNet 般集成到现有算法框架中。通过本文,我们就一起来看看它有多香吧!

Outline

I. 主要思想

II. 分组的通道注意力机制:Split-Attention

III. 从代码出发,知行合一

 

1

基本思想

ResNeSt 很好懂,不复杂,简单来说就是结合了 ResNeXt 的分组卷积和 SE-Net 的通道注意力机制,将通道进行分组,对每组运用注意力机制,同时保留了 ResNet 的残差结构。

2

分组的通道注意力机制:Split-Attention

你的 ResNet 是时候更新了—ResNeSt 来也

 

SplAtConv2d

这部分我们来详谈分组的通道注意力是怎样一种操作,作者论述到可能的实现方式有多种,这里我先谈谈其中一种。

了解 ResNeXt 的朋友们都知道,其引入了 Cardinality 的概念,代表分组的组数,为方便叙述,这里记为 K;ResNeSt 则在此基础上进一步分组,称为 split 操作,同时引入一个超参 Radix,代表将 K 个组中的每一个进一步划分的组数,这里记为 R。这里的分组都是在通道这个维度上进行,由此看来,就是将输入在通道这个维度划分为 KxR 个组。

分组完毕后,对每个组实施不同的特征变换(Conv+Bn+Relu 等),然后将它们分成 R 份,这样每份就包含原来的 K 个组,对每一份应用投票机制形成注意力(Softmax or Sigmoid),接着将这 R 份注意力与特征图对应相乘(element-wise multiply),最后将这 R 份结果加起来(element-wise sum)形成输出,输出相当于对应了原来的 K 个组。

梳理下,可以知道注意力在是分了 K 个组后再分R个组上执行的,记 R 中的每一份为 r,K 中的每一份为k,那么每个 r 上得到的注意力是不同的,即每个 k split 下的每个 r 上的注意力不同,而同一个 r 下对应的不同 k 的注意力是一致的。

很奇妙,对于分得的K个组,每个组内切分R份分配不同的注意力,但不同组依次对应的这R份注意力却分别是一致的,是谓同又不尽全同!

3

从代码出发,知行合一

看过 paper 和源码的朋友们可能会一头雾水,paper 中展示的结构图和代码实现的有出入,一开始 CW 也是如此,看了几篇文但总感觉自己理解得依旧不那么清晰,于是乎亲自把代码手撸一遍,并结合画图理解,最终眼前的迷雾也就散开了。

我国古代优秀大佬王阳明推崇知行合一,虽然凡事不一定硬要知行结合,但是吾以为有了认知才有“行”的方向,“行”了才能加深认知或者说真正认知,这是一个循环,最终达到合二为一的高手境界。

接下来进入正题~

作者在源码中对于 split attention 的实现有两种方式,分别对应两个类,其中一个类为 SplAtConv2d,对应于上一部分展示的图中结构;另一个类为 RadixMajorNaiveImp,对应下图中的结构。

RadixMajorNaiveImp

你的 ResNet 是时候更新了—ResNeSt 来也

 

RadixMajorNaiveImp

结合上图和代码,先来看看 RadixMajorNaiveImp 具体如何实现。

首先将输入分为 KxR 个组,然后依次对K中的每个 k 执行注意力机制,具体做法是取出同一个 k 下的所有 r,然后把它们加起来,输入全局平均池化层和两层全连接层。

你的 ResNet 是时候更新了—ResNeSt 来也

 

RadixMajorNaiveImp (i)

接着令通道这个维度等于 R,在这个维度上生成注意力权重,同时,将同一 k 下的所有 r 在通道这个维度上拼接起来,与注意力权重相乘,相乘后的结果分为 R 份,将这 R 份结果加起来形成这一个 k 的输出,最终将K组中所有 k 的结果在通道数这个维度上拼接起来。

你的 ResNet 是时候更新了—ResNeSt 来也

 

RadixMajorNaiveImp (ii)

总的来说,这种方式就是依次对 K 组中的每份 k 进行处理,每份 k 进一步 split 成 R 份,其中每份 r 生成不同的注意力,K 组中的每份 k 都结合完注意力后,再将它们的结果在通道上拼接起来。

SplAtConv2d

接下来看看 SplAtConv2d 的实现方式。

你的 ResNet 是时候更新了—ResNeSt 来也

 

SplAtConv2d

仔细观察上图,我们可以发现,这种实现方式是将输入分为 R 份,其中的每份 r 包含了 K 个组,每份 r 生成的注意力不同(对应上图中的虚线框),上一节便说到了,同一 k 下 不同的 split r 上形成的注意力不一致,但不同的 k 对应相同的 r 上形成的注意力却是一致的

再回顾下 RadixMajorNaiveImp 的实现方式,同一 k 下 不同的 split r 上形成的注意力也是不一致,但不同 k 的注意力是独立生成的,它们之间并没有联系,这就是两种实现方式的最大差别了。

一起来瞄瞄代码~

你的 ResNet 是时候更新了—ResNeSt 来也

 

SplAtConv2d (i)

这里提醒大家注意下,训练过程中在测试这个模块时,记住把 batch size 设置大于1,由于使用了 global average pooling,输出特征的大小变为1x1,因此其后接 bn 的话(上图中 self.bn1)就要求每个通道上多于一个元素,而如果 batch size 为1的话就会报错了:

ValueError: Expected more than 1 value per channel when training

bn 是在每个通道上(channel-wise)做归一化的,如果通道上只有1个元素,那么归一化就无意义了,所以在训练过程中, bn 要求每个通道上必须多于1个元素。

你的 ResNet 是时候更新了—ResNeSt 来也

 

SplAtConv2d (ii)

另外,SplAtConv2d 这种实现方式不需要依次对 K 组中的每份进行处理,而是直接对 K 个组同时进行处理,相比于 RadixMajorNaiveImp 的方式更加简洁些。

作者在 paper 和 github 源码中也给出了两者等价性的证明,源码可以查看《SplAtConv2d 和 RadixMajorNaiveImp 的等价性证明》,链接:https://github.com/zhanghang1989/ResNeSt/blob/master/tests/test_radix_major.py#L100

4

最后

对于 ResNeSt, 初次接触时往往会感觉其代码实现和paper描述得有出入,因此要把它讲述明白,自己本身一定要理解得透彻。如果没有亲自敲过一遍代码,就很难做到。对于其它算法模型也一样,能真正掌握的办法就是亲自上阵实践一番,所谓知而不行,乃是未知。

深蓝学院 发起了一个读者讨论对于这个新的ResNeSt有什么想法呢?赶快参与【读者讨论】,与笔者沟通交流吧!

 

【参考】

源码链接:

https://github.com/zhanghang1989/ResNeSt

https://github.com/zhanghang1989/ResNeSt/blob/master/tests/test_radix_major.py#L100

### ResNet 融合的实现路径与架构 ResNet(残差网络)通过引入跳跃连接解决了深层神经网络中的梯度消失问题。对于ResNet融合而言,主要涉及多模态数据处理或多分支结构设计。 #### 多模态数据融合 当涉及到不同类型的输入数据时,可以采用早期融合、晚期融合以及混合策略来构建模型[^1]: - **早期融合**:在特征提取阶段之前将来自多个传感器的数据组合在一起形成单一输入向量; - **晚期融合**:先分别对每种模式下的样本单独训练子网得到预测结果后再做决策级集成; - **混合方式**:结合上述两种思路,在某些层面上共享参数而其他部分保持独立运作。 #### 实现代码示例 下面是一个简单的基于PyTorch框架下双通道图像分类任务中应用ResNet进行特征拼接的例子: ```python import torch.nn as nn from torchvision import models class TwoStreamResnet(nn.Module): def __init__(self, num_classes=2): super(TwoStreamResnet, self).__init__() # 加载预训练好的resnet50作为基础骨干网络 resnet = models.resnet50(pretrained=True) # 冻结卷积层权重更新 for param in resnet.parameters(): param.requires_grad_(False) # 修改最后一层全连接层适应新的类别数 fc_features = resnet.fc.in_features resnet.fc = nn.Linear(fc_features, 512) # 定义两个流并行处理模块 self.stream1 = resnet self.stream2 = copy.deepcopy(resnet) # 添加额外线性变换用于最终输出 self.classifier = nn.Sequential( nn.ReLU(), nn.Dropout(p=0.5), nn.Linear(1024, num_classes)) def forward(self, x1, x2): feat1 = self.stream1(x1) feat2 = self.stream2(x2) combined_feats = torch.cat((feat1, feat2), dim=-1) out = self.classifier(combined_feats) return out ``` 此段代码展示了如何创建一个多分支架构,其中每个分支都使用了一个冻结后的ResNet实例来进行特定类型数据上的特征学习,并且最后会把两者的结果串联起来送入一个新的分类器完成整个流程。 #### 压缩量化技术的应用 值得注意的是,为了提高效率降低存储需求,还可以考虑利用低精度计算如8位整型运算代替浮点数操作。这不仅有助于减少内存占用还能加速推理过程,特别是在移动设备端部署AI应用程序的时候显得尤为重要。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值