网易首页 > 网易号 > 正文 申请入驻

BottleNeck, Inverted Residual, MBConv的解释和Pytorch实现

0
分享至

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

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

from functools import partial
from torch import nn
class ConvNormAct(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)
import torch
x = torch.randn((1, 32, 56, 56))
Conv1X1BnReLU(32, 64)(x).shape
#torch.Size([1, 64, 56, 56])

残差连接

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

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

from torch import nn
from torch import Tensor
class ResidualAdd(nn.Module):
def __init__(self, block: nn.Module):
super().__init__()
self.block = block
def forward(self, x: Tensor) -> Tensor:
res = x
x = self.block(x)
x += res
return x
ResidualAdd(
nn.Conv2d(32, 32, kernel_size=1)
)(x).shape

捷径 Shortcut

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

from typing import Optional
class ResidualAdd(nn.Module):
def __init__(self, block: nn.Module, shortcut: Optional[nn.Module] = None):
super().__init__()
self.block = block
self.shortcut = shortcut
def forward(self, x: Tensor) -> Tensor:
res = x
x = self.block(x)
if self.shortcut:
res = self.shortcut(res)
x += res
return x
ResidualAdd(
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和一个非线性激活,在加法之后还有一个非线性的激活

from torch import nn
class BottleNeck(nn.Sequential):
def __init__(self, in_features: int, out_features: int, reduction: int = 4):
reduced_features = out_features // reduction
super().__init__(
nn.Sequential(
ResidualAdd(
nn.Sequential(
# wide -> narrow
Conv1X1BnReLU(in_features, reduced_features),
# narrow -> narrow
Conv3X3BnReLU(reduced_features, reduced_features),
# narrow -> wide
Conv1X1BnReLU(reduced_features, out_features, act=nn.Identity),
),
shortcut=Conv1X1BnReLU(in_features, out_features)
if in_features != out_features
else None,
),
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。而不是像正常的瓶颈块那样变宽 -> 窄 -> 宽,他们做相反的事情 窄 -> 宽 -> 窄。

class InvertedResidual(nn.Sequential):
def __init__(self, in_features: int, out_features: int, expansion: int = 4):
expanded_features = in_features * expansion
super().__init__(
nn.Sequential(
ResidualAdd(
nn.Sequential(
# narrow -> wide
Conv1X1BnReLU(in_features, expanded_features),
# wide -> wide
Conv3X3BnReLU(expanded_features, expanded_features),
# wide -> narrow
Conv1X1BnReLU(expanded_features, out_features, act=nn.Identity),
),
shortcut=Conv1X1BnReLU(in_features, out_features)
if in_features != out_features
else None,
),
nn.ReLU(),
)
)
InvertedResidual(32, 64)(x).shape

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

class MobileNetLikeBlock(nn.Sequential):
def __init__(self, in_features: int, out_features: int, expansion: int = 4):
# use ResidualAdd if features match, otherwise a normal Sequential
residual = ResidualAdd if in_features == out_features else nn.Sequential
expanded_features = in_features * expansion
super().__init__(
nn.Sequential(
residual(
nn.Sequential(
# narrow -> wide
Conv1X1BnReLU(in_features, expanded_features),
# wide -> wide
Conv3X3BnReLU(expanded_features, expanded_features),
# wide -> narrow
Conv1X1BnReLU(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 慢得多。

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

class DepthWiseSeparableConv(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() for p in DepthWiseSeparableConv(32, 64).parameters() if p.requires_grad) #2432

再看看一个普通的 Conv2d

sum(p.numel() for p in nn.Conv2d(32, 64, kernel_size=3).parameters() if p.requires_grad)#18496

这是巨大的差距

2、完成MBConv

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

class MBConv(nn.Sequential): def __init__(self, in_features: int, out_features: int, expansion: int = 4): residual = ResidualAdd if in_features == out_features else nn.Sequential expanded_features = in_features * expansion super().__init__( nn.Sequential( residual( nn.Sequential( # narrow -> wide Conv1X1BnReLU(in_features, expanded_features, act=nn.ReLU6 ), # wide -> wide Conv3X3BnReLU(expanded_features, expanded_features, groups=expanded_features, act=nn.ReLU6 ), # here you can apply SE # wide -> narrow Conv1X1BnReLU(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 节)。

class FusedMBConv(nn.Sequential):
def __init__(self, in_features: int, out_features: int, expansion: int = 4):
residual = ResidualAdd if in_features == out_features else nn.Sequential
expanded_features = in_features * expansion
super().__init__(
nn.Sequential(
residual(
nn.Sequential(
Conv3X3BnReLU(in_features,
expanded_features,
act=nn.ReLU6
),
# here you can apply SE
# wide -> narrow
Conv1X1BnReLU(expanded_features, out_features, act=nn.Identity),
),
),
nn.ReLU(),
)
)
MBConv(32, 64)(x).shape

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

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

作者:Francesco Zuppichini

特别声明:以上内容(如有图片或视频亦包括在内)为自媒体平台“网易号”用户上传并发布,本平台仅提供信息存储服务。

Notice: The content above (including the pictures and videos if any) is uploaded and posted by a user of NetEase Hao, which is a social media platform and only provides information storage services.

相关推荐
热点推荐
剧版《哈利·波特》发完预告片后,全网都是黑人斯内普的地狱笑话"/> 主站 商城 论坛 自运营 登录 注册 剧版《哈利·波特》发完预告片后,全网都是黑人斯...

剧版《哈利·波特》发完预告片后,全网都是黑人斯内普的地狱笑话"/> 主站 商城 论坛 自运营 登录 注册 剧版《哈利·波特》发完预告片后,全网都是黑人斯...

3DM游戏
2026-03-28 12:50:32
广东一店主下单寄递10箱茅台酒总价超12万元,司机用一张模糊“卸货照”虚假“送达”后失联,平台:已与警方确认找到司机,货物已追回

广东一店主下单寄递10箱茅台酒总价超12万元,司机用一张模糊“卸货照”虚假“送达”后失联,平台:已与警方确认找到司机,货物已追回

大象新闻
2026-03-27 21:45:04
U23假象:邀请赛都不敢抬头踢,中国足球正在用保守封闭未来

U23假象:邀请赛都不敢抬头踢,中国足球正在用保守封闭未来

格斗社
2026-03-29 07:36:17
众星告别《好好的时光》,陈昊宇文艺,周澄奥感谢观众讨厌单宝昆

众星告别《好好的时光》,陈昊宇文艺,周澄奥感谢观众讨厌单宝昆

娱君坠星河
2026-03-29 09:35:11
张雪峰突然离世,生前他这样评价书法专业,至今发人深思!

张雪峰突然离世,生前他这样评价书法专业,至今发人深思!

书法网
2026-03-26 17:35:11
他们两个不会偷偷的在一起了吧,确实他们挺合适的男才女貌

他们两个不会偷偷的在一起了吧,确实他们挺合适的男才女貌

可乐谈情感
2026-03-27 16:53:38
大陆发布统一后安排,蔡正元赶在坐牢前,留下5个字,措辞不寻常

大陆发布统一后安排,蔡正元赶在坐牢前,留下5个字,措辞不寻常

爱史纪
2026-03-29 07:51:14
李昌钰去世

李昌钰去世

澎湃新闻
2026-03-28 08:55:06
89岁邱会作病危,张震上将亲自拍板:接回北京,用最好的病房

89岁邱会作病危,张震上将亲自拍板:接回北京,用最好的病房

文史明鉴
2026-03-28 17:24:09
田曦薇:美到出圈,也争议缠身,95花里最有话题的甜妹。

田曦薇:美到出圈,也争议缠身,95花里最有话题的甜妹。

野狐馋师
2026-03-29 08:11:41
三观炸裂!翟欣欣出轨聊天记录流出,尺度大到咂舌,判12年都嫌少

三观炸裂!翟欣欣出轨聊天记录流出,尺度大到咂舌,判12年都嫌少

有范又有料
2025-09-29 14:21:11
张志新的儿女今何在?背后的真相令人泪目

张志新的儿女今何在?背后的真相令人泪目

深度报
2026-03-01 23:48:59
美媒灵魂拷问,连着三任总统都要对付中国,最后却全部断送在中东

美媒灵魂拷问,连着三任总统都要对付中国,最后却全部断送在中东

甜菊汽水
2026-03-29 08:35:59
五国反水,连夜倒向中国!伊朗踹碎美国保护伞,美霸权彻底崩塌?

五国反水,连夜倒向中国!伊朗踹碎美国保护伞,美霸权彻底崩塌?

甜柠聊史
2026-03-29 00:34:37
打不过就道德绑架!以色列一市长痛哭怒斥战争,丝毫没有悔过之意

打不过就道德绑架!以色列一市长痛哭怒斥战争,丝毫没有悔过之意

沧海旅行家
2026-03-27 15:07:47
好友见张雪峰最后一面,遗容黑眼圈遮不住,前妻和女儿现身告别式

好友见张雪峰最后一面,遗容黑眼圈遮不住,前妻和女儿现身告别式

萌神木木
2026-03-28 16:59:03
我国每天增加1万癌症患者,罪魁祸首是青菜?3种青菜别再多吃

我国每天增加1万癌症患者,罪魁祸首是青菜?3种青菜别再多吃

今日养生之道
2026-03-27 17:56:29
福特号士兵叛乱?火灾并非意外,真相曝光,44名士兵遇难只是开始

福特号士兵叛乱?火灾并非意外,真相曝光,44名士兵遇难只是开始

策略述
2026-03-28 12:47:15
95后“掏粪男孩”结婚 9辆吸粪车组车队迎亲 新娘:这是我们专属的浪漫

95后“掏粪男孩”结婚 9辆吸粪车组车队迎亲 新娘:这是我们专属的浪漫

红星新闻
2026-03-28 13:41:14
“保罗散步”迎来春天:“拉夫劳伦平替”常年打一折,单平台月销近2500万

“保罗散步”迎来春天:“拉夫劳伦平替”常年打一折,单平台月销近2500万

蓝鲸新闻
2026-03-28 09:37:11
2026-03-29 10:35:00
deephub incentive-icons
deephub
CV NLP和数据挖掘知识
1960文章数 1461关注度
往期回顾 全部

科技要闻

马斯克承认xAI"建错了",11位创始人均离职

头条要闻

牛弹琴:特朗普亲口对沙特说出傲慢的话 全世界不敢相信

头条要闻

牛弹琴:特朗普亲口对沙特说出傲慢的话 全世界不敢相信

体育要闻

全球第二大车企,也救不了这支德甲队?

娱乐要闻

陈牧驰陈冰官宣得子 晒一家三口握拳照

财经要闻

卧底"科技与狠活"培训:化工调味剂泛滥

汽车要闻

岚图泰山X8配置曝光 四激光雷达/华为新一代座舱

态度原创

教育
健康
游戏
公开课
军事航空

教育要闻

“黄毛的爹,酗酒的妈”,上海三口之家火了,只有孩子看着不叛逆

干细胞抗衰4大误区,90%的人都中招

魂师对决:王林天命+婉儿炼丹系统该优先培养哪个?真就没啥悬念

公开课

李玫瑾:为什么性格比能力更重要?

军事要闻

美军中东基地损失最新披露

无障碍浏览 进入关怀版