超越所有YOLO檢測(cè)模型,mmdet開源當(dāng)今最強(qiáng)最快目標(biāo)檢測(cè)模型!

0、直接上架構(gòu)圖吧!

看著上面的圖,熟悉不?是不是滿滿的YOLO系列的味道?是的,看代碼我猜應(yīng)該是基于YOLO來進(jìn)行的增量實(shí)驗(yàn)吧,也僅僅是猜啦,畢竟暗俺也沒看到RTMDet的論文,俺也不是開發(fā)者!
1、改進(jìn)點(diǎn)1 —— CSPNeXt
1.1 Backbone 部分
話不多說,直接上代碼:
class CSPNeXtBlock(BaseModule):
def __init__(self,
in_channels: int,
out_channels: int,
expansion: float = 0.5,
add_identity: bool = True,
use_depthwise: bool = False,
kernel_size: int = 5,
conv_cfg: OptConfigType = None,
norm_cfg: ConfigType = dict(
type='BN', momentum=0.03, eps=0.001),
act_cfg: ConfigType = dict(type='SiLU'),
init_cfg: OptMultiConfig = None) -> None:
super().__init__(init_cfg=init_cfg)
hidden_channels = int(out_channels * expansion)
conv = DepthwiseSeparableConvModule if use_depthwise else ConvModule
self.conv1 = conv(
in_channels,
hidden_channels,
3,
stride=1,
padding=1,
norm_cfg=norm_cfg,
act_cfg=act_cfg)
self.conv2 = DepthwiseSeparableConvModule(
hidden_channels,
out_channels,
kernel_size,
stride=1,
padding=kernel_size // 2,
conv_cfg=conv_cfg,
norm_cfg=norm_cfg,
act_cfg=act_cfg)
self.add_identity = add_identity and in_channels == out_channels
def forward(self, x: Tensor) -> Tensor:
identity = x
out = self.conv1(x)
out = self.conv2(out)
if self.add_identity:
return out + identity
else:
return out
其實(shí)通過代碼我們可以很直觀的看出模型的架構(gòu)細(xì)節(jié),這里小編也進(jìn)行了簡(jiǎn)要的繪制,具體如下圖:

這里提到的 Depthwise Separable Convolution 是 MobileNet 的基本單元,其實(shí)這種結(jié)構(gòu)之前已經(jīng)使用在 Inception 模型中。Depthwise Separable Convolution 其實(shí)是一種可分解卷積操作,其可以分解為2個(gè)更小的操作:Depthwise Convolution 和 Pointwise Convolution,如圖所示。

Depthwise Convolution 和標(biāo)準(zhǔn)卷積不同,對(duì)于標(biāo)準(zhǔn)卷積,其卷積核是用在所有的輸入通道上(input channels),而 Depthwise Convolution 針對(duì)每個(gè)輸入通道采用不同的卷積核,就是說一個(gè)卷積核對(duì)應(yīng)一個(gè)輸入通道,所以說 Depthwise Convolution 是 Depth 級(jí)別的操作。
而 Pointwise Convolution 其實(shí)就是普通的 1×1 的卷積。對(duì)于 Depthwise Separable Convolution,首先是采用 Depthwise Convolution 對(duì)不同輸入通道分別進(jìn)行卷積,然后采用 Pointwise Convolution 將上面的輸出再進(jìn)行結(jié)合,這樣整體效果和一個(gè)標(biāo)準(zhǔn)卷積是差不多的,但是會(huì)大大減少計(jì)算量和模型參數(shù)量。
熟悉DarkNet的朋友應(yīng)該都知道,如果你不知道,小編這里也給出架構(gòu)圖:

然后依舊是直接上CSPLayer的代碼:
class CSPLayer(BaseModule):
def __init__(self,
in_channels: int,
out_channels: int,
expand_ratio: float = 0.5,
num_blocks: int = 1,
add_identity: bool = True,
use_depthwise: bool = False,
use_cspnext_block: bool = False,
channel_attention: bool = False,
conv_cfg: OptConfigType = None,
norm_cfg: ConfigType = dict(type='BN', momentum=0.03, eps=0.001),
act_cfg: ConfigType = dict(type='Swish'),
init_cfg: OptMultiConfig = None) -> None:
super().__init__(init_cfg=init_cfg)
block = CSPNeXtBlock if use_cspnext_block else DarknetBottleneck
mid_channels = int(out_channels * expand_ratio)
self.channel_attention = channel_attention
self.main_conv = ConvModule(
in_channels,
mid_channels,
1,
conv_cfg=conv_cfg,
norm_cfg=norm_cfg,
act_cfg=act_cfg)
self.short_conv = ConvModule(
in_channels,
mid_channels,
1,
conv_cfg=conv_cfg,
norm_cfg=norm_cfg,
act_cfg=act_cfg)
self.final_conv = ConvModule(
2 * mid_channels,
out_channels,
1,
conv_cfg=conv_cfg,
norm_cfg=norm_cfg,
act_cfg=act_cfg)
self.blocks = nn.Sequential(*[
block(
mid_channels,
mid_channels,
1.0,
add_identity,
use_depthwise,
conv_cfg=conv_cfg,
norm_cfg=norm_cfg,
act_cfg=act_cfg) for _ in range(num_blocks)
])
if channel_attention:
self.attention = ChannelAttention(2 * mid_channels)
def forward(self, x: Tensor) -> Tensor:
x_short = self.short_conv(x)
x_main = self.main_conv(x)
x_main = self.blocks(x_main)
x_final = torch.cat((x_main, x_short), dim=1)
if self.channel_attention:
x_final = self.attention(x_final)
return self.final_conv(x_final)
其結(jié)構(gòu)如下所示,毫無疑問依舊是香香的CSP思想,但是這里的結(jié)構(gòu)使用了5×5的DW卷積,實(shí)現(xiàn)了更少的參數(shù)量的情況下,帶來更大的感受野。

同時(shí)這里RTMDet的Backbone中還考慮了通道注意力的問題,其代碼如下:
class ChannelAttention(BaseModule):
def __init__(self, channels: int, init_cfg: OptMultiConfig = None) -> None:
super().__init__(init_cfg)
self.global_avgpool = nn.AdaptiveAvgPool2d(1)
self.fc = nn.Conv2d(channels, channels, 1, 1, 0, bias=True)
self.act = nn.Hardsigmoid(inplace=True)
def forward(self, x: torch.Tensor) -> torch.Tensor:
out = self.global_avgpool(x)
out = self.fc(out)
out = self.act(out)
return x * out
小編依舊給小伙伴們畫了示意圖:

其實(shí)還有一個(gè)細(xì)節(jié),這里我想的也不是很明白,如果熟悉ResNet構(gòu)建的小伙伴應(yīng)該知道,凱明大神在構(gòu)建ResNet50是使用的殘差Block的數(shù)量配比就是[3,6,6,3],

這里RTMDet使用的配比也是:

但是小編在白嫖 TRT-ViT、NeXtViT、SWin以及ConvNeXt的時(shí)候都在或有或無地說逐層增加配比會(huì)帶來更好的結(jié)果,這里不知道為什么RTMDet選擇以前的數(shù)據(jù),期待論文中的描述和解釋!
1.2 Neck部分
其實(shí)也是毫不意外的PAFPN的架構(gòu),只不過這里作者選擇把YOLO系列中的CSPBlock替換為了本方法中的CSPNeXt Block,具體架構(gòu)圖如下所示:

1.3 Head部分
這部分也是相對(duì)比較常規(guī)的設(shè)計(jì),對(duì)于PAFPN結(jié)構(gòu)輸出的特征,先使用由堆疊的卷積所組成的分類分支以及回歸分支提取對(duì)應(yīng)的分類特征和回歸特征,然后分別送到對(duì)應(yīng)的RTM分類分支和回歸分支,得到我們最終隨需要的東西,這里有一個(gè)小小的細(xì)節(jié),便是堆疊的卷積在不同level的中是共享權(quán)重的,具體可以參見代碼,這里也不進(jìn)行過多的猜測(cè),最終還是以論文為主。

2、匹配策略
直接上配置參數(shù),熟悉Nanodet的小伙伴你是不是又知道了!嗯,是的是熟悉的味道,就是NanoDet-Plus的哪個(gè)策略,依舊很香,依舊很好用?。?!

當(dāng)年的Nanodet-Plus是這樣的:

這里所謂動(dòng)態(tài)匹配,簡(jiǎn)單來說就是直接使用模型檢測(cè)頭的輸出,與所有Ground Truth計(jì)算一個(gè)匹配得分,這個(gè)得分由分類損失和回歸損失相加得到。特征圖上N個(gè)點(diǎn)的預(yù)測(cè)值,與M個(gè)Ground Truth計(jì)算得到一個(gè)N×M的矩陣,稱為Cost Matrix,基于這個(gè)矩陣可以讓當(dāng)前預(yù)測(cè)結(jié)果動(dòng)態(tài)地尋找最優(yōu)標(biāo)簽,匹配的策略有二分圖匹配、傳輸優(yōu)化、Top-K等,在NanoDet中直接采取了Top-K的策略來匹配。
這種策略的一個(gè)問題在于,在網(wǎng)絡(luò)訓(xùn)練的初期,預(yù)測(cè)結(jié)果是很差的,可能根本預(yù)測(cè)不出結(jié)果。所以在動(dòng)態(tài)匹配時(shí)還會(huì)加上一些位置約束,比如使用一個(gè) 5×5 的中心區(qū)域去限制匹配的自由程度,然后再依賴神經(jīng)網(wǎng)絡(luò)天生的抗噪聲能力,只需要在Ground Truth框內(nèi)隨機(jī)分配一些點(diǎn),網(wǎng)絡(luò)就能學(xué)到一些基礎(chǔ)的特征。
3、損失函數(shù)

這部分主要是是用來QFL和GIOU Loss,這里不進(jìn)行過多描述,以后盡可能補(bǔ)上吧,今天太累了,已經(jīng)太晚了。。。。
4、輸入端部分
階段一
作者在訓(xùn)練的第一階段,主要是使用了CacheMosaic數(shù)據(jù)增強(qiáng),RandomResize,RandomCrop,RandomCrop,CacheMixup以及YOLOX關(guān)于HSV的一些增強(qiáng)手段,這里的CacheMosaic以及CacheMixup是mmdet中全新提出的新Trcik煉丹術(shù);

階段2
作者在訓(xùn)練階段2提出了前面提出的新技術(shù),CacheMosaic以及CacheMixup,看樣子這里應(yīng)該是學(xué)習(xí)YOLOX的訓(xùn)練技術(shù):

4.1、CacheMosaic
1、Mosaic流程:
選擇Mosaic中心作為4幅圖像的交點(diǎn)。 根據(jù)索引獲取左上圖,從自定義數(shù)據(jù)集中隨機(jī)抽取另外3張圖片。 如果圖像大于Mosaic Patch,子圖像將被裁剪。
2、CacheMosaic流程:
將上次Transform的結(jié)果加到Cache中。 選擇Mosaic中心作為4幅圖像的交點(diǎn)。 根據(jù)索引獲取左上圖,從結(jié)果緩存中隨機(jī)抽取另外3張圖片。 如果圖像大于Mosaic Patch,子圖像將被裁剪。
優(yōu)點(diǎn)我猜就是訓(xùn)練快?。?!
4.2、CacheMixup
1、Mixup
另一個(gè)隨機(jī)圖像被數(shù)據(jù)集挑選并嵌入到左上角的Patch中(在填充和調(diào)整大小之后) mixup變換的目標(biāo)是mixup image和origin image的加權(quán)平均。
2、CacheMixup
將上次Transform的結(jié)果加到Cache中。 從Cache中挑選另一個(gè)隨機(jī)圖像并嵌入到左上角的Patch中(在填充和調(diào)整大小之后) mixup變換的目標(biāo)是mixup image和origin image的加權(quán)平均。
參考
[1].https://github.com/RangiLyu/mmdetection/tree/rtmdet_config/configs/rtmdet.

分享
收藏
點(diǎn)贊
在看

