地平線靜態(tài)目標(biāo)檢測(cè) MapTR 參考算法 - V2.0
該示例為參考算法,僅作為在征程 6 上模型部署的設(shè)計(jì)參考,非量產(chǎn)算法
一、簡(jiǎn)介
高清地圖是自動(dòng)駕駛系統(tǒng)的重要組件,提供精確的駕駛環(huán)境信息和道路語義信息。傳統(tǒng)離線地圖構(gòu)建方法成本高,維護(hù)復(fù)雜,使得依賴車載傳感器的實(shí)時(shí)感知建圖成為新趨勢(shì)。早期實(shí)時(shí)建圖方法存在局限性,如處理復(fù)雜地圖元素的能力不足、缺乏實(shí)例級(jí)信息等,在實(shí)時(shí)性和后處理
復(fù)雜度上存在挑戰(zhàn)。
為了解決這些問題,基于 Transformer 的 MapTR 模型被提出,它采用端到端結(jié)構(gòu),僅使用圖像數(shù)據(jù)就能實(shí)現(xiàn)高精度建圖,同時(shí)保證實(shí)時(shí)性和魯棒性。MapTRv2 在此基礎(chǔ)上增加了新特性,進(jìn)一步提升了建圖精度和性能。
地平線面向智駕場(chǎng)景推出的征程 6 系列(征程 6)芯片,在提供強(qiáng)大算力的同時(shí)帶來了極致的性價(jià)比,征程 6 芯片對(duì)于 Transformer 模型的高效支持助力了 MapTR 系列模型的端側(cè)部署。本文將詳細(xì)介紹地平線算法工具鏈在征程 6 芯片部署 MapTR 系列模型所做的優(yōu)化以及模型端側(cè)的表現(xiàn)。
二、性能精度指標(biāo)
模型配置:
性能精度表現(xiàn):
三、公版模型介紹
3.1 MapTR
MapTR 模型的默認(rèn)輸入是車載攝像頭采集到的 6 張相同分辨率的環(huán)視圖像,使用 nuScenes 數(shù)據(jù)集,同時(shí)也支持拓展為多模態(tài)輸入例如雷達(dá)點(diǎn)云。模型輸出是矢量化的地圖元素信息,其中地圖元素為人行橫道、車道分隔線和道路邊界 3 種。模型主體采用 encoder-decoder 的端到端結(jié)構(gòu):
3.2 MapTRv2
MapTRv2 在 MapTR 的基礎(chǔ)上增加了新的特性:
四、地平線部署說明
地平線參考算法使用流程請(qǐng)參考征程 6 參考算法使用指南;對(duì)應(yīng)高效模型設(shè)計(jì)建議請(qǐng)參考《征程 6 平臺(tái)算法設(shè)計(jì)建議》
MapTROE 模型引入了 SD map 的前融合結(jié)構(gòu),與圖像視角轉(zhuǎn)換后的 bev feature 進(jìn)行融合,再通過優(yōu)化后的 MapTR head 生成矢量化的地圖元素。整體結(jié)構(gòu)如下:
因此 maptroe_henet_tinym_bevformer_nuscenes 模型相比之前版本新增了如下優(yōu)化點(diǎn):
maptroe_henet_tinym_bevformer_nuscenes 模型對(duì)應(yīng)的代碼路徑:
4.1 性能優(yōu)化
4.1.1 Backbone
MapTROE 采用基于征程 6 芯片的高效輕量化 Backbone HENet_TinyM(Hybrid Efficient Network, Tiny for J6M),HENet 能更好地利用征程 6 系列芯片的算力,在模型精度和性能上更具優(yōu)勢(shì)。HENet_TinyM 采用了純 CNN 架構(gòu),總體分為四個(gè) stage,每個(gè) stage 會(huì)進(jìn)行一次 2 倍下采樣,具體結(jié)構(gòu)配置如下:
# henet-tinym depth = [4, 3, 8, 6] block_cls = ["GroupDWCB", "GroupDWCB", "AltDWCB", "DWCB"] width = [64, 128, 192, 384] attention_block_num = [0, 0, 0, 0] mlp_ratios, mlp_ratio_attn = [2, 2, 2, 3], 2 act_layer = ["nn.GELU", "nn.GELU", "nn.GELU", "nn.GELU"] use_layer_scale = [True, True, True, True] extra_act = [False, False, False, False] final_expand_channel, feature_mix_channel = 0, 1024 down_cls = ["S2DDown", "S2DDown", "S2DDown", "None"] patch_embed = "origin"
4.1.2 Neck
Neck 部分采用了地平線內(nèi)部實(shí)現(xiàn)的 FPN,相比公版 FPN 實(shí)現(xiàn),在征程 6 平臺(tái)上性能更加友好。
4.1.3 View Transformer
地平線參考算法版本將基于 LSS 的視角轉(zhuǎn)換方式替換為深度優(yōu)化后 Bevformer 的 View Transformer 部分。
# 公版模型 class MapTRPerceptionTransformer(BaseModule): ... def attn_bev_encode(...): ... if prev_bev is not None: if prev_bev.shape[1] == bev_h * bev_w: prev_bev = prev_bev.permute(1, 0, 2) if self.rotate_prev_bev: for i in range(bs): # num_prev_bev = prev_bev.size(1) rotation_angle = kwargs['img_metas'][i]['can_bus'][-1] tmp_prev_bev = prev_bev[:, i].reshape( bev_h, bev_w, -1).permute(2, 0, 1) tmp_prev_bev = rotate(tmp_prev_bev, rotation_angle, center=self.rotate_center) tmp_prev_bev = tmp_prev_bev.permute(1, 2, 0).reshape( bev_h * bev_w, 1, -1) prev_bev[:, i] = tmp_prev_bev[:, 0] # add can bus signals can_bus = bev_queries.new_tensor( [each['can_bus'] for each in kwargs['img_metas']]) # [:, :] can_bus = self.can_bus_mlp(can_bus[:, :self.len_can_bus])[None, :, :] bev_queries = bev_queries + can_bus * self.use_can_bus ... # 地平線參考算法 class BevFormerViewTransformer(nn.Module): ... def __init__(...): ... self.prev_frame_info = { "prev_bev": None, "scene_token": None, "ego2global": None, } ... def get_prev_bev(...): if idx == self.queue_length - 1 and self.queue_length != 1: prev_bev = torch.zeros( (bs, self.bev_h * self.bev_w, self.embed_dims), dtype=torch.float32, device=device, ) ... else: prev_bev = self.prev_frame_info["prev_bev"] if prev_bev is None: prev_bev = torch.zeros( (bs, self.bev_h * self.bev_w, self.embed_dims), dtype=torch.float32, device=device, ) # 對(duì)應(yīng)改動(dòng)2.a ... def bev_encoder(...): ... tmp_prev_bev = prev_bev.reshape( bs, self.bev_h, self.bev_w, self.embed_dims ).permute(0, 3, 1, 2) prev_bev = F.grid_sample( tmp_prev_bev, norm_coords, "bilinear", "zeros", True ) # 對(duì)應(yīng)改動(dòng)2.b ... class SingleBevFormerViewTransformer(BevFormerViewTransformer): ... def get_bev_embed(...): ... bev_query = self.bev_embedding.weight bev_query = bev_query.unsqueeze(1).repeat(1, bs, 1) # 對(duì)應(yīng)改動(dòng)2.c ...
d. 取消了公版的 TemporalSelfAttention,改為 HorizonMSDeformableAttention,保持精度的同時(shí)提升速度;
# 公版模型Config model = dict( ... pts_bbox_head=dict( type='MapTRHead', ... transformer=dict( type='MapTRPerceptionTransformer', ... encoder=dict( type='BEVFormerEncoder', ... transformerlayers=dict( type='BEVFormerLayer', attn_cfgs=[ dict( type='TemporalSelfAttention', embed_dims=_dim_, num_levels=1), ... ] ) ) ) ) ) # 地平線參考算法Config model = dict( ... view_transformer=dict( type="SingleBevFormerViewTransformer", ... encoder=dict( type="SingleBEVFormerEncoder", ... encoder_layer=dict( type="SingleBEVFormerEncoderLayer", ... selfattention=dict( type="HorizonMSDeformableAttention", # 對(duì)應(yīng)改動(dòng)2.d ... ), ) ) ) )
e. 支持公版 Bevformer 中的 bev_mask,并將涉及到的 gather/scatter 操作,用 gridsample 等價(jià)替換,提高模型速度。
# 地平線參考算法Config view_transformer=dict( type="SingleBevFormerViewTransformer", ... max_camoverlap_num=2, # 對(duì)應(yīng)根據(jù)bev_mask進(jìn)行稀疏映射,提高運(yùn)行效率,對(duì)應(yīng)改動(dòng)2.e virtual_bev_h=int(0.4 * bev_h_), virtual_bev_w=bev_w_, ... )
4.1.4 Head
公版 MapTR 使用分層 query 機(jī)制,定義一組 instance queries 和由所有 instance 共享的 point queries,每個(gè)地圖元素對(duì)應(yīng)一組分層 query(一個(gè) instance query 和共享的 point queries 廣播相加得到),在 decoder layer 中分別使用 self-attention 和 cross-attention 來更新分層 query。
MapTROE 的改進(jìn)則是為每個(gè)地圖元素分配一個(gè) instance query(無直接 point query),每個(gè) query 用于編碼語義信息和地理位置信息,decoder 階段和公版 MapTR 一樣,分別進(jìn)行 multi-head self-attention 和 deformable cross-attention,最后每個(gè) instance query 通過 MLP 網(wǎng)絡(luò)生成類別信息和元素內(nèi)的點(diǎn)集坐標(biāo),相比公版預(yù)測(cè)分層 query,改進(jìn)后直接預(yù)測(cè) instance query 帶來的計(jì)算量更少,極大地提高了模型在端側(cè)的運(yùn)行性能。同時(shí)借鑒 StreamMapNet,使用多點(diǎn)注意力方法來適應(yīng)高度不規(guī)則的地圖元素,擴(kuò)大感知范圍。代碼見/usr/local/lib/python3.10/dist-packages/hat/models/task_modules/maptr/instance_decoder.py: class MapInstanceDetectorHead(nn.Module)
4.1.5 多點(diǎn)注意力
傳統(tǒng)的可變形注意力為每個(gè) query 分配一個(gè)參考點(diǎn),多點(diǎn)注意力則使用前一層預(yù)測(cè)的地圖元素的多個(gè)點(diǎn)作為當(dāng)前層 query 的參考點(diǎn),具體計(jì)算方式是在點(diǎn)維度上擴(kuò)展了一層求和,將一個(gè)點(diǎn)變成多個(gè)點(diǎn),分別計(jì)算 deformable attention。回歸的時(shí)候并非預(yù)測(cè) offsets,而是直接預(yù)測(cè)地圖元素點(diǎn)的坐標(biāo)位置。
4.1.6Attention
模型中用到的 attention 操作均使用地平線提供的算子,相比 PyTorch 提供的公版算子,地平線 attention 算子在保持算子邏輯等價(jià)的同時(shí)在效率上進(jìn)行了優(yōu)化
from hat.models.task_modules.bevformer.attention import ( HorizonMSDeformableAttention, HorizonMSDeformableAttention3D, HorizonSpatialCrossAttention, ... )
4.2 精度優(yōu)化
4.2.1 浮點(diǎn)精度
MapTROE 模型引入 SD Map 前融合,與圖像轉(zhuǎn)換后的 bev feature 進(jìn)行融合,以提高在線地圖的生成質(zhì)量。模塊結(jié)構(gòu)如下圖所示:
4.2.1.1 SD Map 特征提取
SD Map 從 OpenStreetMap(OSM)中獲取,通過由 GPS 提供的車輛位姿,查詢車輛當(dāng)前位姿附近的 SD Map,然后將 SD Map 轉(zhuǎn)換到自車坐標(biāo)系下,與 NuScenes 中的數(shù)據(jù)標(biāo)注坐標(biāo)系保持一致。SD Map 會(huì)從車道中心骨架線 Polyline 的形式轉(zhuǎn)化為柵格結(jié)構(gòu),大小和 BEV 特征相同,經(jīng)過 CNN 變成特征圖,對(duì)應(yīng) SD Map 的先驗(yàn)信息。
4.2.1.2 SD Map 特征融合
柵格化后的 SD Map 和實(shí)際場(chǎng)景可能會(huì)出現(xiàn)錯(cuò)位、不對(duì)齊的情況,這種錯(cuò)位導(dǎo)致直接 Concatenate BEV 特征和 SD Map 特征的效果并不好,為了解決這個(gè)問題,引入了特征融合模塊,通過網(wǎng)絡(luò)學(xué)習(xí)來決定最適合的對(duì)齊方式,可以有效地利用 SD Map 先驗(yàn)提升 BEV 特征的效果。關(guān)于特征融合模塊,分別實(shí)驗(yàn)了交叉注意力與 CNN 網(wǎng)絡(luò),通過精度與性能的平衡,最后選擇了 CNN 網(wǎng)絡(luò)模塊。
4.3 量化精度
# Config文件 cali_qconfig_setter = (default_calibration_qconfig_setter,) qat_qconfig_setter = (default_qat_fixed_act_qconfig_setter,)
2.浮點(diǎn)階段采用更大的 weight decay 訓(xùn)練,使浮點(diǎn)數(shù)據(jù)分布范圍更小,浮點(diǎn)模型參數(shù)更有利于量化
# Config文件 float_trainer = dict( ... optimizer=dict( ... weight_decay=0.1, # 相比maptrv2_resnet50_bevformer_nuscenes增大了10倍 ), ... )
3.QAT 訓(xùn)練采用固定較小的 learning rate 來 fine-tune,這里固定也即取消 LrUpdater Callback 的使用,配置如下:
# Config文件 qat_lr = 1e-9
4.取消了公版模型 MapTRHead 中對(duì)于量化不友好的 inverse_sigmoid 操作;此外 MapTROE 對(duì) Head 的優(yōu)化無需再引入 reg_branches 輸出和 reference 相加后再 sigmoid 的操作:
# 公版模型 class MapTRHead(DETRHead): ... def forward(...): ... for lvl in range(hs.shape[0]): if lvl == 0: # import pdb;pdb.set_trace() reference = init_reference else: reference = inter_references[lvl - 1] reference = inverse_sigmoid(reference) ... tmp = self.reg_branches[lvl](...) tmp[..., 0:2] += reference[..., 0:2] tmp = tmp.sigmoid() # cx,cy,w,h # 地平線參考算法 class MapInstanceDetectorHead(nn.Module): ... def get_outputs(...): ... for lvl in range(len(outputs_classes)): tmp = reference_out[lvl].float() outputs_coord, outputs_pts_coord = self.transform_box(tmp) outputs_class = outputs_classes[lvl].float() outputs_classes_one2one.append( outputs_class[:, 0 : self.num_vec_one2one] ) outputs_coords_one2one.append( outputs_coord[:, 0 : self.num_vec_one2one] ) outputs_pts_coords_one2one.append( outputs_pts_coord[:, 0 : self.num_vec_one2one] ) outputs_classes_one2many.append( outputs_class[:, self.num_vec_one2one :] ) outputs_coords_one2many.append( outputs_coord[:, self.num_vec_one2one :] ) outputs_pts_coords_one2many.append( outputs_pts_coord[:, self.num_vec_one2one :] ) ... def forward(...): outputs = self.bev_decoder(...) if self.is_deploy: return outputs ... outputs = self.get_outputs(...) ... return self._post_process(data, outputs)
5.Attention 結(jié)構(gòu)優(yōu)化,通過數(shù)值融合方法,將部分?jǐn)?shù)值運(yùn)算提前進(jìn)行融合,減少整體的量化操作,提高模型的量化友好度
4.4 其他優(yōu)化
4.4.1 設(shè)計(jì)優(yōu)化
五、總結(jié)與建議
5.1 部署建議
5.2 總結(jié)
本文通過對(duì) MapTR 進(jìn)行地平線量化部署的優(yōu)化,使得模型在征程 6 計(jì)算平臺(tái)上用較低的量化精度損失,最優(yōu)獲得征程 6M 單核 93.77 FPS 的部署性能。同時(shí),MapTR 系列的部署經(jīng)驗(yàn)可以推廣到其他相似結(jié)構(gòu)或相似使用場(chǎng)景模型的部署中。
對(duì)于地平線 MapTR 參考算法模型,結(jié)合 Sparse Bev 等的優(yōu)化方向仍在探索和實(shí)踐中,Stay Tuned!
六、附錄
*博客內(nèi)容為網(wǎng)友個(gè)人發(fā)布,僅代表博主個(gè)人觀點(diǎn),如有侵權(quán)請(qǐng)聯(lián)系工作人員刪除。