上篇ConvNext的文章有小伙伴问BottleNeck,Inverted Residual的区别,所以找了这篇文章,详细的解释一些用到的卷积块,当作趁热打铁吧

在介绍上面的这些概念之间,我们先创建一个通用的 conv-norm-act 层,这也是最基本的卷积块。

fromfunctoolsimportpartial
fromtorchimportnnclassConvNormAct(nn.Sequential):def__init__(self,in_features: int,out_features: int,kernel_size: int,norm: nn.Module = nn.BatchNorm2d,act: nn.Module = nn.ReLU,**kwargs):super().__init__(nn.Conv2d(in_features,out_features,kernel_size=kernel_size,padding=kernel_size//2,),norm(out_features),act(),)Conv1X1BnReLU = partial(ConvNormAct, kernel_size=1)
Conv3X3BnReLU = partial(ConvNormAct, kernel_size=3)
importtorchx = torch.randn((1, 32, 56, 56))Conv1X1BnReLU(32, 64)(x).shape#torch.Size([1, 64, 56, 56])

残差连接

ResNet 中提出并使用了残差连接, 这个想法是将层的输入与层的输出相加,输出 = 层(输入)+ 输入。下图可以帮助您将其可视化。但是,它只使用了一个 + 运算符。残差操作提高了梯度在乘法器层上传播的能力,允许有效地训练超过一百层的网络。

在PyTorch中,我们可以轻松地创建一个ResidualAdd层

fromtorchimportnn
fromtorchimportTensorclassResidualAdd(nn.Module):def__init__(self, block: nn.Module):super().__init__()self.block = blockdefforward(self, x: Tensor) ->Tensor:res = xx = self.block(x)x += resreturnxResidualAdd(nn.Conv2d(32, 32, kernel_size=1)
)(x).shape

捷径 Shortcut

有时候残差没有相同的输出维度,所以无法将它们相加。所以就需要使用conv(带+的黑色箭头)来投影输入,以匹配输出的特性

fromtypingimportOptionalclassResidualAdd(nn.Module):def__init__(self, block: nn.Module, shortcut: Optional[nn.Module] = None):super().__init__()self.block = blockself.shortcut = shortcutdefforward(self, x: Tensor) ->Tensor:res = xx = self.block(x)ifself.shortcut:res = self.shortcut(res)x += resreturnxResidualAdd(nn.Conv2d(32, 64, kernel_size=1),shortcut=nn.Conv2d(32, 64, kernel_size=1)
)(x).shape

瓶颈块 BottleNeck

在用于图像识别的深度残差网络中也引入了瓶颈块。BottleNeck 块接受大小为 BxCxHxW 的输入,它首先使用1x1 卷积将其缩减为 BxC/rxHxW,然后再应用 3x3 卷积,最后再使用 1x1 卷积将输出重新映射到与输入相同的特征维度BxCxHxW 。这比使用三个 3x3 转换要快的多,由于中间层减少输入维度,所以将其称之为“BottleNeck”。下图可视化了该块,我们在原始实现中使用 r=4

前两个convs之后是batchnorm和一个非线性激活,在加法之后还有一个非线性的激活

fromtorchimportnnclassBottleNeck(nn.Sequential):def__init__(self, in_features: int, out_features: int, reduction: int = 4):reduced_features = out_features//reductionsuper().__init__(nn.Sequential(ResidualAdd(nn.Sequential(# wide -> narrowConv1X1BnReLU(in_features, reduced_features),# narrow -> narrowConv3X3BnReLU(reduced_features, reduced_features),# narrow -> wideConv1X1BnReLU(reduced_features, out_features, act=nn.Identity),),shortcut=Conv1X1BnReLU(in_features, out_features)ifin_features!= out_featureselseNone,),nn.ReLU(),))BottleNeck(32, 64)(x).shape

请注意这里仅在输入和输出特征维度不同时才使用shortcut。

一般情况下当希望减少空间维度时,在中间卷积中使用 stride=2。

线性瓶颈 Linear BottleNeck

线性瓶颈是在 MobileNetV2: Inverted Residuals 中引入的。线性瓶颈块是不包含最后一个激活的瓶颈块。在论文的第 3.2 节中,他们详细介绍了为什么在输出之前存在非线性会损害性能。简而言之:非线性函数 Line ReLU 将所有 < 0 设置为 0会破坏信息。根据经验表明,当输入的通道小于输出的通道时删除最后的激活函数是正确的。所以只要删除 BottleNeck 中的 nn.ReLU 即可。

倒置残差 Inverted Residual

在 MobileNetV2 中还引入了倒置残差。Inverted Residual 块是倒置的 BottleNeck 层。他们使用第一个 conv 对维度进行扩展而不是减少。下图应该清楚地说明这一点

从 BxCxHxW -> BxCexHxW -> BxCexHxW -> BxCxHxW,其中 e 是膨胀比,默认设置为 4。而不是像正常的瓶颈块那样变宽 -> 窄 -> 宽,他们做相反的事情 窄 -> 宽 -> 窄。

classInvertedResidual(nn.Sequential):def__init__(self, in_features: int, out_features: int, expansion: int = 4):expanded_features = in_features*expansionsuper().__init__(nn.Sequential(ResidualAdd(nn.Sequential(# narrow -> wideConv1X1BnReLU(in_features, expanded_features),# wide -> wideConv3X3BnReLU(expanded_features, expanded_features),# wide -> narrowConv1X1BnReLU(expanded_features, out_features, act=nn.Identity),),shortcut=Conv1X1BnReLU(in_features, out_features)ifin_features!= out_featureselseNone,),nn.ReLU(),))InvertedResidual(32, 64)(x).shape

在 MobileNet 中,残差连接仅在输入和输出特征匹配时应用,这个我们在前面已经说明了

classMobileNetLikeBlock(nn.Sequential):def__init__(self, in_features: int, out_features: int, expansion: int = 4):# use ResidualAdd if features match, otherwise a normal Sequentialresidual = ResidualAddifin_features == out_featureselsenn.Sequentialexpanded_features = in_features*expansionsuper().__init__(nn.Sequential(residual(nn.Sequential(# narrow -> wideConv1X1BnReLU(in_features, expanded_features),# wide -> wideConv3X3BnReLU(expanded_features, expanded_features),# wide -> narrowConv1X1BnReLU(expanded_features, out_features, act=nn.Identity),),),nn.ReLU(),))MobileNetLikeBlock(32, 64)(x).shape
MobileNetLikeBlock(32, 32)(x).shape

MBConv

在 MobileNetV2 之后,它的构建块被称为 MBConv。MBConv 是具有深度可分离卷积的倒置线性瓶颈层,听着很绕对吧,其实就是把上面我们介绍的几个块进行了整合。

1、深度可分离卷积 Depth-Wise Separable Convolutions

Depth-Wise Separable Convolutions 是一种减少参数的数量技巧,它将一个普通的 3x3 卷积拆分为两个卷积。第一个卷积将单个的 3x3 卷积核应用于每个输入的通道,另一个卷积将 1x1 卷积核应用于所有通道。这和做一个普通的 3x3 转换是一样的,但是却减少了参数。

但是其实这个有点多余,因为在我们现有的硬件上它比普通的 3x3 慢得多。

通道中的不同颜色代表每个通道应用的一个单独的卷积核(过滤器)

classDepthWiseSeparableConv(nn.Sequential):def__init__(self, in_features: int, out_features: int):super().__init__(nn.Conv2d(in_features, in_features, kernel_size=3, groups=in_features),nn.Conv2d(in_features, out_features, kernel_size=1))DepthWiseSeparableConv(32, 64)(x).shape

让我们看看参数减少了多少:

sum(p.numel() forpinDepthWiseSeparableConv(32, 64).parameters() ifp.requires_grad)
#2432

再看看一个普通的 Conv2d

sum(p.numel() forpinnn.Conv2d(32, 64, kernel_size=3).parameters() ifp.requires_grad)
#18496

这是巨大的差距

2、完成MBConv

现在可以创建一个完整的 MBConv。MBConv 有几个重要细节,归一化适用于深度和点卷积,非线性仅适用于深度卷积(请记住线性瓶颈)。而激活函数使用ReLU6 。我们现在把把所有东西放在一起

classMBConv(nn.Sequential):def__init__(self, in_features: int, out_features: int, expansion: int = 4):residual = ResidualAddifin_features == out_featureselsenn.Sequentialexpanded_features = in_features*expansionsuper().__init__(nn.Sequential(residual(nn.Sequential(# narrow -> wideConv1X1BnReLU(in_features, expanded_features,act=nn.ReLU6),# wide -> wideConv3X3BnReLU(expanded_features, expanded_features, groups=expanded_features,act=nn.ReLU6),# here you can apply SE# wide -> narrowConv1X1BnReLU(expanded_features, out_features, act=nn.Identity),),),nn.ReLU(),))MBConv(32, 64)(x).shape

在 EfficientNet 中也使用的是带有 Squeeze 和 Excitation的这个块的修改的版本。

融合倒置残差 (Fused MBConv)

在 EfficientNetV2: Smaller Models and Faster Training 中引入了 Fused Inverted Residuals,这样可以使 MBConv 更快。解决了我们上面说的深度卷积很慢的问题,它们将第一个和第二个卷积融合在一个 3x3 卷积中(第 3.2 节)。

classFusedMBConv(nn.Sequential):def__init__(self, in_features: int, out_features: int, expansion: int = 4):residual = ResidualAddifin_features == out_featureselsenn.Sequentialexpanded_features = in_features*expansionsuper().__init__(nn.Sequential(residual(nn.Sequential(Conv3X3BnReLU(in_features, expanded_features, act=nn.ReLU6),# here you can apply SE# wide -> narrowConv1X1BnReLU(expanded_features, out_features, act=nn.Identity),),),nn.ReLU(),))MBConv(32, 64)(x).shape

总结

本文介绍了这些基本的卷积块的操作和代码, 这些卷积块的架构是我们在CV中经常会遇到的,所以强烈建议阅读与他们相关的论文。另外如果你对本文代码感兴趣,请看这里:

https://avoid.overfit.cn/post/af49b27f50bb416ca829b4987e902874

作者:Francesco Zuppichini

Residual, BottleNeck, Inverted Residual, MBConv的解释和Pytorch实现相关推荐

  1. MobileNet v2中 Inverted Residual 和 Linear Bottleneck 是怎么回事

    MobileNet v2中 Inverted Residual 和 Linear Bottleneck 是怎么回事 flyfish MobileNet v1的深度可分离卷积 Block的结构 先从Mo ...

  2. Mobile Inverted Residual Bottleneck Block图解

    一.背景 Mobile Inverted Residual Bottleneck Block,又称作移动可翻转卷积块,在EfficienetNet模型中有所使用,其将残差结构和注意力进行了结合. 二. ...

  3. Residual, BottleNeck, Linear BottleNeck, MBConv解释

    这里有一个交互式版本 https://github.com/FrancescoSaverioZuppichini/BottleNeck-InvertedResidual-FusedMBConv-in- ...

  4. ResNet :( residual 、residual bottleneck)

    2.ResNet结构 它使用了一种连接方式叫做"shortcut connection",顾名思义,shortcut就是"抄近道"的意思,看下图我们就能大致理解 ...

  5. bottleneck resnet网络_Detection学习之四-利用pytorch实现resnet

    残差网络的基本原理 上图即为残差网络的基本原理: F(x)=H(x)-x,x为浅层输出,H(x)为深层的输出,F(x)为二者中间的两层变换.当浅层的x代表的特征已经足够成熟,如果任何对于x的改变都会让 ...

  6. Transformer源代码解释之PyTorch篇

    ©PaperWeekly 原创 · 作者 | Sherlock 学校 | 苏州科技大学本科生 研究方向 | 自然语言处理 词嵌入 Transformer 本质上是一种 Encoder,以翻译任务为例, ...

  7. 图片分类网络ViT、MobileViT、Swin-Transformer、MobileNetV3、ConvNeXt、EfficientNetV2

    文章目录 一.Vision Transformer 二.Swin-Transformer 三.MobileViT 3.1 为什么引入CNN与Transformer的混合架构 3.2 性能对比 3.3 ...

  8. ​一文读懂EfficientDet

    一文读懂EfficientDet. 今年年初Google Brain团队在 CVPR 2020 上发布了 EfficientDet目标检测模型, EfficientDet是一系列可扩展的高效的目标检测 ...

  9. 论文笔记:MobileNet v2

    原论文:MobileNetV2: Inverted Residuals and Linear Bottlenecks MobileNet v2 1.四个问题 要解决什么问题? 与MobileNet v ...

  10. resnet系列+mobilenet v2+pytorch代码实现

    一.resnet系列backbone import torch.nn as nn import math import torch.utils.model_zoo as model_zooBatchN ...

最新文章

  1. qgis经纬度_数据养成系列--QGIS地理空间
  2. 2018年AI和ML(NLP、计算机视觉、强化学习)技术总结和2019年趋势
  3. chorme 下载文件 保留 放弃_西部数码使用指南:保留数据重装以后sqlserver数据库不能启动(sql2008,sql2012)...
  4. 重磅!神策 2018 数据驱动大会报名正式开启!
  5. linux read while 变量运算
  6. 8bit黑白图像的灰度值范围是_浅谈工业CT图像灰度值
  7. Chrome Elements 标签页 和 View Source 的显示为什么有差异
  8. 14-Clothes衣服
  9. 训练日志 2018.11.28
  10. 本地页面存1天的缓存
  11. C语言实用基础大总结(高效快速学习精华、实用语句案例多)
  12. 面试官问你HashMap底层你用线程安全吊打他
  13. leetcode 423 从英文中重建数字
  14. 对计算机网络的认识论文800,对计算机的认识论文
  15. arangoDB基本操作
  16. 小米路由器4a千兆版修改sn和关闭电源led灯
  17. 北京师范大学计算机学院 姚力,姚力(北京师范大学信息科学与技术学院院长)_百度百科...
  18. 和弦笔记:和弦组成音/和弦命名规律/sus和add的区别
  19. 听课感悟——信息与信息特征
  20. 软件工程概述-----RUP开发模式

热门文章

  1. virt与virsh常用命令
  2. IIC总线设计③——光敏传感器(BH1750)
  3. 机器人之自动回归原点方法实现
  4. java中void是什么意思_JAVA里VOID是什么意思
  5. android蓝牙传输文件到mysql_安卓上的蓝牙数据传输
  6. 阿里巴巴JAVA代码规范一【Block】
  7. 计算机青岛科技大学济南大学,山东考生在山东理工,济大,山东科技和青岛科技中该如何选择?...
  8. 京东商城系统架构设计原则
  9. Browser-Solidity的本地安装及使用介绍
  10. 7时过2小时是几时_一个时辰等于几个小时