Residual, BottleNeck, Linear BottleNeck, MBConv解釋
今天,我們將看到現(xiàn)代CNN架構(gòu)中使用的不同模塊,如ResNet、MobileNet、EfficientNet,以及它們在PyTorch中的實(shí)現(xiàn)。
讓我們創(chuàng)建一個(gè)通用的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中,想法是將輸入添加到輸出中,輸出=層+輸入。下圖可能會(huì)幫助你將其可視化。但是,我的意思是它只是一個(gè)+運(yùn)算符。殘差操作提高了梯度傳播的能力,允許有效地訓(xùn)練具有100層以上的網(wǎng)絡(luò)。
在PyTorch中,我們可以輕松創(chuàng)建一個(gè)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
有時(shí)你的殘差沒有相同的輸出維度,所以我們不能添加它們。我們可以使用shortcut中的卷積投射輸入,以匹配輸出特征:
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 Blocks
在圖像識(shí)別的深度殘差學(xué)習(xí)中引入了Bottlenecks。Bottlenecks塊接受大小為BxCxHxW的輸入,它首先使用1x1 卷積將其變?yōu)锽xC/rxHxW,然后應(yīng)用3x3 卷積,最后將輸出重新映射到與輸入相同的特征維度BxCxHxW,然后再次使用1x1卷積。這比使用三個(gè)3x3卷積更快。
因?yàn)槭紫葴p少了輸入,所以我們稱之為“Bottlenecks”。下圖顯示了該塊,我們在原始實(shí)現(xiàn)中使用了r=4
前兩個(gè)卷積之后是batchnorm和一個(gè)非線性激活層,而最后一個(gè)非線性層在加法后應(yīng)用。
在PyTorch中為:
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
請注意,僅當(dāng)輸入和輸出特征不同時(shí),我們才應(yīng)用shortcut。
在實(shí)踐中,當(dāng)我們希望減小空間維數(shù)時(shí),在卷積中使用stride=2。
Linear BottleNecks
MobileNet V2中引入了Linear Bottleneck。Linear BottleNecks是沒有激活函數(shù)的Bottlenecks塊。
在論文的第3.2節(jié)中,他們詳細(xì)討論了為什么在輸出之前存在非線性會(huì)損害性能。簡而言之,非線性函數(shù)ReLU在<0時(shí)設(shè)為0會(huì)導(dǎo)致破壞信息。因此,在Bottlenecks中刪除nn.ReLU你就可以擁有Linear BottleNecks。
倒殘差
MobileNet V2中再次引入了倒殘差。
倒殘差塊是反向的Bottlenecks層。它們通過第一次卷積擴(kuò)展特征,而不是減少特征。
下圖應(yīng)該可以清楚地說明這一點(diǎn)
我們從BxCxHxW到->BxCxHxW->BxCxHxW->BxCxHxW,其中e是膨脹率,它被設(shè)置為4。而不是像在正常的Bottlenecks區(qū)那樣變寬->變窄->變寬,而是相反,變窄->變寬->變窄。
在PyTorch中,實(shí)現(xiàn)如下
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中,只有當(dāng)輸入和輸出特征匹配時(shí),才會(huì)應(yīng)用殘差連接
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
MobileNet V2的構(gòu)建塊被稱為MBConv。MBConv是具有深度可分離卷積的倒殘差的Linear BottleNecks層。
深度可分離卷積
深度可分離卷積采用一種技巧,將一個(gè)正常的3x3卷積夾在兩個(gè)卷積中,以減少參數(shù)數(shù)量。
第一個(gè)對每個(gè)輸入的通道應(yīng)用單個(gè)3x3濾波器,另一個(gè)對所有通道應(yīng)用1x1濾波器。
這與正常的3x3卷積相同,但你節(jié)省了參數(shù)。
然而它比我們現(xiàn)有硬件上的普通3x3慢得多。
下圖顯示了這個(gè)想法
通道中的不同顏色表示每個(gè)通道應(yīng)用的單個(gè)過濾器
PyTorch中:
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
第一次卷積通常稱為depth,而第二次卷積稱為point。讓我們統(tǒng)計(jì)參數(shù)量
sum(p.numel() for p in DepthWiseSeparableConv(32, 64).parameters() if p.requires_grad)
輸出:2432
讓我們看一個(gè)普通的Conv2d
sum(p.numel() for p in nn.Conv2d(32, 64, kernel_size=3).parameters() if p.requires_grad)
輸出:18496
有很大的區(qū)別
實(shí)現(xiàn)MBConv
那么,讓我們創(chuàng)建一個(gè)完整的MBConv。
MBConv有幾個(gè)重要的細(xì)節(jié),標(biāo)準(zhǔn)化應(yīng)用于深度和點(diǎn)卷積,非線性僅應(yīng)用于深度卷積(Linear Bottlenecks)。
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
Fused MBConv
EfficientNetV2中引入了融合倒殘差
所以基本上,由于深度卷積比較慢,他們將第一個(gè)和第二個(gè)卷積融合在一個(gè)3x3的卷積中(第3.2節(jié))。
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
結(jié)論
現(xiàn)在你應(yīng)該知道所有這些塊之間的區(qū)別以及它們背后的原因了!
原文標(biāo)題 : Residual, BottleNeck, Linear BottleNeck, MBConv解釋

請輸入評論內(nèi)容...
請輸入評論/評論長度6~500個(gè)字
最新活動(dòng)更多
-
6月20日立即下載>> 【白皮書】精準(zhǔn)測量 安全高效——福祿克光伏行業(yè)解決方案
-
7月3日立即報(bào)名>> 【在線會(huì)議】英飛凌新一代智能照明方案賦能綠色建筑與工業(yè)互聯(lián)
-
7月22-29日立即報(bào)名>> 【線下論壇】第三屆安富利汽車生態(tài)圈峰會(huì)
-
7.30-8.1火熱報(bào)名中>> 全數(shù)會(huì)2025(第六屆)機(jī)器人及智能工廠展
-
7月31日免費(fèi)預(yù)約>> OFweek 2025具身機(jī)器人動(dòng)力電池技術(shù)應(yīng)用大會(huì)
-
免費(fèi)參會(huì)立即報(bào)名>> 7月30日- 8月1日 2025全數(shù)會(huì)工業(yè)芯片與傳感儀表展
推薦專題
- 1 AI 眼鏡讓百萬 APP「集體失業(yè)」?
- 2 大廠紛紛入局,百度、阿里、字節(jié)搶奪Agent話語權(quán)
- 3 深度報(bào)告|中國AI產(chǎn)業(yè)正在崛起成全球力量,市場潛力和關(guān)鍵挑戰(zhàn)有哪些?
- 4 上海跑出80億超級獨(dú)角獸:獲上市公司戰(zhàn)投,干人形機(jī)器人
- 5 國家數(shù)據(jù)局局長劉烈宏調(diào)研格創(chuàng)東智
- 6 一文看懂視覺語言動(dòng)作模型(VLA)及其應(yīng)用
- 7 下一代入口之戰(zhàn):大廠為何紛紛押注智能體?
- 8 百億AI芯片訂單,瘋狂傾銷中東?
- 9 Robotaxi新消息密集釋放,量產(chǎn)元年誰在領(lǐng)跑?
- 10 格斗大賽出圈!人形機(jī)器人致命短板曝光:頭腦過于簡單