網站建設哪家好萬維科技推廣軟件賺錢違法嗎
一、前言
在開始介紹RT-DETR這個網絡之前,我們首先需要先了解DETR這個系列的網絡與我們常提及的anchor-base以及anchor-free存在著何種差異。
首先我們先簡單討論一下anchor-base以及anchor-free兩者的差異與共性:
1、兩者差異:顧名思義,這兩者一個顯而易見的差別就是有無anchor,anchor-base是需要手工選取不同比例大小的anchor來得到proposals,而anchor-free則不需要。當然兩者具體差異肯定不是這么幾句話就能說的清的,這里不做詳細討論所以按下不表。
2、兩者共性:兩者雖然在獲取proposals的原理方面存在不同,但是其最終還是存在了一個多對一的問題,也就是會對同一個目標產生多個proposal,也即最后兩者最后都是需要通過閾值選取以及nms去過濾掉多余的預測框。此處可參考:Nms-Free時代
但是當問題涉及到了工業(yè)或者生活等其他領域落地問題上面的時候,無論是anchor-base還是anchor-free所面臨的這中兩套閾值問題(要先完成閾值篩選(Confidence threshold)和非極大值抑制(NMS)處理兩個關鍵步驟)總是會對時效性產生很大的影響。
二、DETR系列網絡的動機
在引言中,我們提到了無論是anchor-base還是anchor-free其實都存在著一個閾值問題,而這個閾值問題就決定了繞不開nms這個機制,nms這一機制的存在也就導致無法真正意義上去實現一個端到端的簡易部署的網絡模型,而DETR既不需要proposal也不需要anchor,而是直接利用Transformers這種能全局建模的能力,通過將目標檢測視為一個集合預測問題。同時由于這種全局建模能力使得DETR系列網絡不會輸出那么多冗余的框。在此處個人水平有限害怕個人水平對讀者的理解造成扭曲,因此在這就不對DETR做過多解釋如果有興趣的可以移步沐神的精講視頻:李沐DETR論文精讀
而DETR系列網絡究竟有多簡潔,上述一小段文字自然無法很好的解釋其簡潔性,大家可以通過下面視頻截圖的50行代碼看出DETR的簡潔性。
三、RT-DETR網絡
首先RT-DETR網絡的檢測動機其實非常明確,從這個命名也能看出來,其是想打造一個能實際用于工業(yè)界的DETR網絡。但是對于DETR而言雖然其具備簡單易用的特點,然而其實時性較差,在DETR的論文中的實驗可以看到其FPS較之于Faster-RCNN更低(如下圖所示),因此這是DETR的一個缺點。同時DETR還有另外一個缺點,就是其雖然借助于自注意機制帶來的全局信息使得對于大目標檢測效果表現較好,但是其對于小目標的檢測效果相對而言較弱,當然在RT-DETR中也未解決這個問題,因此此處只提一下按下不表。
好了,既然目前已經知道DETR有一個實時性較差的缺點,那么接下來就應該對造成推理耗時高的原因進行一下分析,在RT-DETR中作者認為在Encoder部分作用在 S 5 S_5 S5?(最頂層特征,即 C 5 C_5 C5?只是換個表達方式),這樣就可以大幅度減小計算量、提高計算速度,同時不會損傷模型性能,這樣做的的原因是作者是基于一下兩點考慮:
- 以往的DETR,如Deformable DETR是將多尺度的特征都拉平成拼接在其中,構成一個序列很長的向量,盡管這可以使得多尺度之間的特征進行充分的交互,但也會造成極大的計算量和計算耗時。RT-DETR認為這是當前的DETR計算速度慢的主要原因之一;
- RT-DETR認為相較于較淺 S 3 S_3 S3?特征和 S 4 S_4 S4?特征, S 5 S_5 S5?特征擁有更深、更高級、更豐富的語義特征,這些語義特征是Transformer更加感興趣的和需要的,對于區(qū)分不同物體的特征是更加有用的,而淺層特征因缺少較好的語義特征而起不到什么作用。
于個人而言感覺這個觀點很類似YOLOF的觀點,在YOLOF中也是直接使用頂層的特征層,有感興趣的可以去看看YOLOF這篇文章。
而為了驗證這個觀點,作者的團隊設計了若干對照組實驗,實驗如下圖所示:
對于對照組(a),其結構就是DINO-R50,但移除了DINO-R50中的多尺度encoder,直接將 S 3 S_3 S3?、 S 4 S_4 S4?和 S 5 S_5 S5?拼接在一起,交給后續(xù)的網絡去處理,得到最終的輸出。注意,這里的拼接是先將二維的 H × W H×W H×W拉平成 H W HW HW然后再去拼接: H 1 W 1 + H 2 W 2 + H 3 W 3 H_1W_1+H_2W_2+H_3W_3 H1?W1?+H2?W2?+H3?W3?。
對于對照組(b),作者團隊在(a)基礎上,加入了單尺度的Transformer Encoder(SSE),僅包含一層Encoder層,分別處理三個尺度的輸出,注意,三個尺度共享一個SSE,而不是為每一個尺度都設計一個獨立的SSE。通過這一共享的操作,三個尺度的信息是可以實現一定程度的信息交互。最后將處理結果拼接在一起,交給后續(xù)的網絡去處理,得到最終的輸出。
對于對照組?,作者團隊使用多尺度的Transformer Encoder(MSE),大概是Deformable DETR所采用的那一類MSE結構。將三個尺度的特征拼接在一起后,交由MSE來做處理,使得三個尺度的特征同時完成“尺度內”和“跨尺度”的信息交互和融合,最后將處理結果,交給后續(xù)的網絡去處理,得到最終的輸出。
對于對照組(d),作者團隊則先用共享的SSE分別處理每個尺度的特征,然后再使用PAN-like的特征金字塔網絡去融合三個尺度的特征,最后將融合后的多尺度特征拼接在一起,交給后續(xù)的網絡去處理,得到最終的輸出。
對于對照組(e),作者團隊則使用一個SSE只處理 S 5 S_5 S5?特征,即所謂的AIFI模塊,隨后再使用CCFM模塊去完成跨尺度的特征融合,最后將融合后的多尺度特征拼接在一起,交給后續(xù)的網絡去處理,得到最終的輸出。
最終,對比結果如上方的圖所示,我們簡單地來分析一下這組對比試驗說明了什么樣的結論。
- 首先,A即對照組(a)取得到了43.0 AP的結果,注意,A的設置是不包含Encoder的,即沒有自注意力機制,在Backbone之后直接接Decoder去做處理,從而獲得輸出結果,可想而知,A的性能應該是最差的,這也是后續(xù)實驗的Baseline;
- 隨后,在A的基礎上,B引入了共享的SSE去處理每個尺度的特征,使得性能從43.0提升至44.9,表明使用共享的SSE是可以提升性能的。從研究的角度來說,對于B,應該再做一個額外的實驗,即使用三個獨立的SSE分別去處理每個尺度的特征,以此來論證“共享SSE”的必要性。當然,我們可以感性地認識到共享SSE會更好,但從理性的嚴謹層面來講,補上這一對比試驗還是有必要的。簡而言之,B證明了加入Encoder會更好;
- 相較于B,C則是使用MSE,即使用MSE來同步完成“尺度內”和“跨尺度”的特征融合(應該和Deformable DETR的多尺度Encoder做法相似),這一做法可以讓不同尺度的特征之間得到更好的交互和融合,可預見地會提升了AP指標,其實驗結果也意料內地證明了這一點:AP指標從43.0提升至45.6,要高于B的44.9,這表明MSE的做法是有效的,即“尺度內”和“跨尺度”的特征融合是必要的。但是,從速度的角度來看,MSE拖慢了推理速度,Latency從7.2增加值13.3 ms,要高于B組的11.1 ms;
- 隨后是對照組D,不同于C,D是相當于解耦了C中的MSE:先使用共享的SSE分別去處理每個尺度的特征,完成“尺度內”的信息交互,然后再用一個PAN風格的跨尺度融合網絡去融合不同尺度之間的特征,完成“跨尺度”的信息融合。這種做法可以有效地避免MSE中因輸入的序列過長而導致的計算量增加的問題。相較于C,D的這種解耦的做法不僅僅使得Latency從13.3 ms降低至12.2 ms,性能也從45.6 AP提升至46.4 AP,這表明MSE的做法并不是最優(yōu)的,先處理“尺度內”,再完成“跨尺度”,性能會更好;
- 隨后是對照組D,不同于C,D是相當于解耦了C中的MSE:先使用共享的SSE分別去處理每個尺度的特征,完成“尺度內”的信息交互,然后再用一個PAN風格的跨尺度融合網絡去融合不同尺度之間的特征,完成“跨尺度”的信息融合。這種做法可以有效地避免MSE中因輸入的序列過長而導致的計算量增加的問題。相較于C,D的這種解耦的做法不僅僅使得Latency從13.3 ms降低至12.2 ms,性能也從45.6 AP提升至46.4 AP,這表明MSE的做法并不是最優(yōu)的,先處理“尺度內”,再完成“跨尺度”,性能會更好;
- 最后就是對照組E,在DS5的基礎上,E重構了跨尺度特征融合模塊,構建了新的CCFM模塊,由于論文里沒給出CSF模塊的細節(jié),所以CCFM模塊究竟調整了哪里也是無法獲知的,但就CCFM本身來看,大體上也是一個PAN-like的結構。在換上了CCFM后,性能從46.8進一步地提升至47.9。
綜上,這組對比試驗證明了兩件事:1) Transformer的Encoder部分只需要處理high-level特征,既能大幅度削減計算量、提升計算速度,同時也不會損傷到性能,甚至還有所提升;2) 對于多尺度特征的交互和融合,我們仍可以采用CNN架構常用的PAN網絡來搭建,只需要一些細節(jié)上的調整即可。
另外,我們仔細來看一下這個對照組(e),注意,根據論文給出的結果和源代碼,所謂的CCFM其實還是PaFPN,如下方的圖10所示,其中的Fusion模塊就是一個CSPBlock風格的模塊。
為了看得更仔細一些,我們來看一下官方源碼,如下方的Python代碼所示。依據論文的設定,HybridEncoder
包含AIFI和CCFM兩大模塊,其中AIFI就是Transformer的Encoder部分,而CCFM其實就是常見的PaFPN結構:首先用若干層1x1卷積將所有的特征的通道數都映射至同一數目,如256,隨后再進行top-down和bottom-up兩部分的特征融合。
# https://github.com/PaddlePaddle/PaddleDetection/blob/develop/ppdet/modeling/transformers/hybrid_encoder.pyclass HybridEncoder(nn.Layer):__shared__ = ['depth_mult', 'act', 'trt', 'eval_size']__inject__ = ['encoder_layer']def __init__(self,in_channels=[512, 1024, 2048],feat_strides=[8, 16, 32],hidden_dim=256,use_encoder_idx=[2],num_encoder_layers=1,encoder_layer='TransformerLayer',pe_temperature=10000,expansion=1.0,depth_mult=1.0,act='silu',trt=False,eval_size=None):super(HybridEncoder, self).__init__()self.in_channels = in_channelsself.feat_strides = feat_stridesself.hidden_dim = hidden_dimself.use_encoder_idx = use_encoder_idxself.num_encoder_layers = num_encoder_layersself.pe_temperature = pe_temperatureself.eval_size = eval_size# channel projectionself.input_proj = nn.LayerList()for in_channel in in_channels:self.input_proj.append(nn.Sequential(nn.Conv2D(in_channel, hidden_dim, kernel_size=1, bias_attr=False),nn.BatchNorm2D(hidden_dim,weight_attr=ParamAttr(regularizer=L2Decay(0.0)),bias_attr=ParamAttr(regularizer=L2Decay(0.0)))))# encoder transformerself.encoder = nn.LayerList([TransformerEncoder(encoder_layer, num_encoder_layers)for _ in range(len(use_encoder_idx))])act = get_act_fn(act, trt=trt) if act is None or isinstance(act, str, dict)) else act# top-down fpnself.lateral_convs = nn.LayerList()self.fpn_blocks = nn.LayerList()for idx in range(len(in_channels) - 1, 0, -1):self.lateral_convs.append(BaseConv(hidden_dim, hidden_dim, 1, 1, act=act))self.fpn_blocks.append(CSPRepLayer(hidden_dim * 2,hidden_dim,round(3 * depth_mult),act=act,expansion=expansion))# bottom-up panself.downsample_convs = nn.LayerList()self.pan_blocks = nn.LayerList()for idx in range(len(in_channels) - 1):self.downsample_convs.append(BaseConv(hidden_dim, hidden_dim, 3, stride=2, act=act))self.pan_blocks.append(CSPRepLayer(hidden_dim * 2,hidden_dim,round(3 * depth_mult),act=act,expansion=expansion))def forward(self, feats, for_mot=False):assert len(feats) == len(self.in_channels)# get projection featuresproj_feats = [self.input_proj[i](feat) for i, feat in enumerate(feats)]# encoderif self.num_encoder_layers > 0:for i, enc_ind in enumerate(self.use_encoder_idx):h, w = proj_feats[enc_ind].shape[2:]# flatten [B, C, H, W] to [B, HxW, C]src_flatten = proj_feats[enc_ind].flatten(2).transpose([0, 2, 1])if self.training or self.eval_size is None:pos_embed = self.build_2d_sincos_position_embedding(w, h, self.hidden_dim, self.pe_temperature)else:pos_embed = getattr(self, f'pos_embed{enc_ind}', None)memory = self.encoder[i](src_flatten, pos_embed=pos_embed)proj_feats[enc_ind] = memory.transpose([0, 2, 1]).reshape([-1, self.hidden_dim, h, w])# top-down fpninner_outs = [proj_feats[-1]]for idx in range(len(self.in_channels) - 1, 0, -1):feat_heigh = inner_outs[0]feat_low = proj_feats[idx - 1]feat_heigh = self.lateral_convs[len(self.in_channels) - 1 - idx](feat_heigh)inner_outs[0] = feat_heighupsample_feat = F.interpolate(feat_heigh, scale_factor=2., mode="nearest")inner_out = self.fpn_blocks[len(self.in_channels) - 1 - idx](paddle.concat([upsample_feat, feat_low], axis=1))inner_outs.insert(0, inner_out)# bottom-up panouts = [inner_outs[0]]for idx in range(len(self.in_channels) - 1):feat_low = outs[-1]feat_height = inner_outs[idx + 1]downsample_feat = self.downsample_convs[idx](feat_low)out = self.pan_blocks[idx](paddle.concat([downsample_feat, feat_height], axis=1))outs.append(out)return outs
而對于其中解碼器的decoder部分使用的是"DINO Head"的decoder部分,也即使用了DINO的一個“去噪思想”(具體我未了解過,有感興趣的可以自行了解)。但是看官方源碼可知在推理階段的時候,RT-DETR的decoder部分與DETR并無區(qū)別,由于我只關注推理階段因此關于"去噪思想"這部分各位可以詳細看看DINO以及官方源碼自行了解。
class RTDETRTransformer(nn.Layer):__shared__ = ['num_classes', 'hidden_dim', 'eval_size']def __init__(self,num_classes=80,hidden_dim=256,num_queries=300,position_embed_type='sine',backbone_feat_channels=[512, 1024, 2048],feat_strides=[8, 16, 32],num_levels=3,num_decoder_points=4,nhead=8,num_decoder_layers=6,dim_feedforward=1024,dropout=0.,activation="relu",num_denoising=100,label_noise_ratio=0.5,box_noise_scale=1.0,learnt_init_query=True,query_pos_head_inv_sig=False,eval_size=None,eval_idx=-1,eps=1e-2):super(RTDETRTransformer, self).__init__()assert position_embed_type in ['sine', 'learned'], \f'ValueError: position_embed_type not supported {position_embed_type}!'assert len(backbone_feat_channels) <= num_levelsassert len(feat_strides) == len(backbone_feat_channels)for _ in range(num_levels - len(feat_strides)):feat_strides.append(feat_strides[-1] * 2)self.hidden_dim = hidden_dimself.nhead = nheadself.feat_strides = feat_stridesself.num_levels = num_levelsself.num_classes = num_classesself.num_queries = num_queriesself.eps = epsself.num_decoder_layers = num_decoder_layersself.eval_size = eval_size# backbone feature projectionself._build_input_proj_layer(backbone_feat_channels)# Transformer moduledecoder_layer = TransformerDecoderLayer(hidden_dim, nhead, dim_feedforward, dropout, activation, num_levels,num_decoder_points)self.decoder = TransformerDecoder(hidden_dim, decoder_layer,num_decoder_layers, eval_idx)# denoising partself.denoising_class_embed = nn.Embedding(num_classes,hidden_dim,weight_attr=ParamAttr(initializer=nn.initializer.Normal()))self.num_denoising = num_denoisingself.label_noise_ratio = label_noise_ratioself.box_noise_scale = box_noise_scale# decoder embeddingself.learnt_init_query = learnt_init_queryif learnt_init_query:self.tgt_embed = nn.Embedding(num_queries, hidden_dim)self.query_pos_head = MLP(4, 2 * hidden_dim, hidden_dim, num_layers=2)self.query_pos_head_inv_sig = query_pos_head_inv_sig# encoder headself.enc_output = nn.Sequential(nn.Linear(hidden_dim, hidden_dim),nn.LayerNorm(hidden_dim,weight_attr=ParamAttr(regularizer=L2Decay(0.0)),bias_attr=ParamAttr(regularizer=L2Decay(0.0))))self.enc_score_head = nn.Linear(hidden_dim, num_classes)self.enc_bbox_head = MLP(hidden_dim, hidden_dim, 4, num_layers=3)# decoder headself.dec_score_head = nn.LayerList([nn.Linear(hidden_dim, num_classes)for _ in range(num_decoder_layers)])self.dec_bbox_head = nn.LayerList([MLP(hidden_dim, hidden_dim, 4, num_layers=3)for _ in range(num_decoder_layers)])self._reset_parameters()def forward(self, feats, pad_mask=None, gt_meta=None, is_teacher=False):# input projection and embedding(memory, spatial_shapes,level_start_index) = self._get_encoder_input(feats)# prepare denoising trainingif self.training:denoising_class, denoising_bbox_unact, attn_mask, dn_meta = \get_contrastive_denoising_training_group(gt_meta,self.num_classes,self.num_queries,self.denoising_class_embed.weight,self.num_denoising,self.label_noise_ratio,self.box_noise_scale)else:denoising_class, denoising_bbox_unact, attn_mask, dn_meta = None, None, None, Nonetarget, init_ref_points_unact, enc_topk_bboxes, enc_topk_logits = \self._get_decoder_input(memory, spatial_shapes, denoising_class, denoising_bbox_unact,is_teacher)# decoderout_bboxes, out_logits = self.decoder(target,init_ref_points_unact,memory,spatial_shapes,level_start_index,self.dec_bbox_head,self.dec_score_head,self.query_pos_head,attn_mask=attn_mask,memory_mask=None,query_pos_head_inv_sig=self.query_pos_head_inv_sig)return (out_bboxes, out_logits, enc_topk_bboxes, enc_topk_logits,dn_meta)
同樣在RT-DETR中提及的IoU-aware Query Selection其實在推理階段也不存在,其是在訓練期間約束檢測器對高 IoU 的特征產生高分類分數,對低 IoU 的特征產生低分類分數。因此若只關注推理階段的讀者,這部分也可無需關注,而關于IoU-aware Query Selection此部分其實是在assignment階段和計算loss的階段,classification的標簽都換成了 “IoU軟標簽” :
所謂的“IoU軟標簽”,就是指將預測框與GT之間的IoU作為類別預測的標簽。熟悉YOLO工作的讀者一定對此不會陌生,其本質就是已經被廣泛驗證了的IoU-aware。在最近的諸多工作里,比如RTMDet、DAMO-YOLO等工作中,都有引入這一理念,去對齊類別和回歸的差異。之所以這么做,是因為按照以往的one-hot方式,完全有可能出現“當定位還不夠準確的時候,類別就已經先學好了”的“未對齊”的情況,畢竟類別的標簽非0即1。但如果將IoU作為類別的標簽,那么類別的學習就要受到回歸的調制,只有當回歸學得也足夠好的時候,類別才會學得足夠好,否則,類別不會過快地先學得比回歸好,因此后者顯式地制約著前者。
四、總結
最后先看一下論文在實驗部分給出的對比表格,如下方的圖所示:
由于在這里我考慮的是RT-DETR的實時性、GFLOPS以及相應的檢測性能,因此我只關注其與YOLO系列的對比,而在YOLO系列中目前使用較多的是YOLOV5以及PPYOLOE網絡。而RT-DETR與這兩個網絡對應的L與X對比能看到其FPS是V5的一倍,同樣也比PP高出十來FPS,而算力開銷也是與兩者接近,檢測性能同樣高出了這兩者幾個點。同時RT-DETR這個網絡也繼承了DETR網絡只要支持CNN與Transformer就能使用以及其推理階段簡潔性的特點。不難看出在未來DETR系列的網絡應能全面鋪開,而且其網絡的簡潔性對于實際部署工作也是有利的。
總而言之,在未來完完全全可以在實時的DETR網絡方面好好期待一把。因此于此處記錄一下閱讀這篇文章以及DETR的讀后感。受限于個人能力其中紕漏肯定還有不少,歡迎各位大力指出,共同成長。
參考鏈接:
《目標檢測》-第33章-淺析RT-DETR
超越YOLOv8,飛槳推出精度最高的實時檢測器RT-DETR!
PaddlePaddle
DETR 論文精讀
Nms-Free時代