地平線靜態目標檢測 MapTR 參考算法 - V2.0
該示例為參考算法,僅作為在征程 6 上模型部署的設計參考,非量產算法
一、簡介
高清地圖是自動駕駛系統的重要組件,提供精確的駕駛環境信息和道路語義信息。傳統離線地圖構建方法成本高,維護復雜,使得依賴車載傳感器的實時感知建圖成為新趨勢。早期實時建圖方法存在局限性,如處理復雜地圖元素的能力不足、缺乏實例級信息等,在實時性和后處理
復雜度上存在挑戰。
為了解決這些問題,基于 Transformer 的 MapTR 模型被提出,它采用端到端結構,僅使用圖像數據就能實現高精度建圖,同時保證實時性和魯棒性。MapTRv2 在此基礎上增加了新特性,進一步提升了建圖精度和性能。
地平線面向智駕場景推出的征程 6 系列(征程 6)芯片,在提供強大算力的同時帶來了極致的性價比,征程 6 芯片對于 Transformer 模型的高效支持助力了 MapTR 系列模型的端側部署。本文將詳細介紹地平線算法工具鏈在征程 6 芯片部署 MapTR 系列模型所做的優化以及模型端側的表現。
二、性能精度指標
模型配置:
性能精度表現:
三、公版模型介紹
3.1 MapTR
MapTR 模型的默認輸入是車載攝像頭采集到的 6 張相同分辨率的環視圖像,使用 nuScenes 數據集,同時也支持拓展為多模態輸入例如雷達點云。模型輸出是矢量化的地圖元素信息,其中地圖元素為人行橫道、車道分隔線和道路邊界 3 種。模型主體采用 encoder-decoder 的端到端結構:
3.2 MapTRv2
MapTRv2 在 MapTR 的基礎上增加了新的特性:
四、地平線部署說明
地平線參考算法使用流程請參考征程 6 參考算法使用指南;對應高效模型設計建議請參考《征程 6 平臺算法設計建議》
MapTROE 模型引入了 SD map 的前融合結構,與圖像視角轉換后的 bev feature 進行融合,再通過優化后的 MapTR head 生成矢量化的地圖元素。整體結構如下:
因此 maptroe_henet_tinym_bevformer_nuscenes 模型相比之前版本新增了如下優化點:
maptroe_henet_tinym_bevformer_nuscenes 模型對應的代碼路徑:
4.1 性能優化
4.1.1 Backbone
MapTROE 采用基于征程 6 芯片的高效輕量化 Backbone HENet_TinyM(Hybrid Efficient Network, Tiny for J6M),HENet 能更好地利用征程 6 系列芯片的算力,在模型精度和性能上更具優勢。HENet_TinyM 采用了純 CNN 架構,總體分為四個 stage,每個 stage 會進行一次 2 倍下采樣,具體結構配置如下:
# 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 部分采用了地平線內部實現的 FPN,相比公版 FPN 實現,在征程 6 平臺上性能更加友好。
4.1.3 View Transformer
地平線參考算法版本將基于 LSS 的視角轉換方式替換為深度優化后 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, ) # 對應改動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 ) # 對應改動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) # 對應改動2.c ...
d. 取消了公版的 TemporalSelfAttention,改為 HorizonMSDeformableAttention,保持精度的同時提升速度;
# 公版模型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", # 對應改動2.d ... ), ) ) ) )
e. 支持公版 Bevformer 中的 bev_mask,并將涉及到的 gather/scatter 操作,用 gridsample 等價替換,提高模型速度。
# 地平線參考算法Config view_transformer=dict( type="SingleBevFormerViewTransformer", ... max_camoverlap_num=2, # 對應根據bev_mask進行稀疏映射,提高運行效率,對應改動2.e virtual_bev_h=int(0.4 * bev_h_), virtual_bev_w=bev_w_, ... )
4.1.4 Head
公版 MapTR 使用分層 query 機制,定義一組 instance queries 和由所有 instance 共享的 point queries,每個地圖元素對應一組分層 query(一個 instance query 和共享的 point queries 廣播相加得到),在 decoder layer 中分別使用 self-attention 和 cross-attention 來更新分層 query。
MapTROE 的改進則是為每個地圖元素分配一個 instance query(無直接 point query),每個 query 用于編碼語義信息和地理位置信息,decoder 階段和公版 MapTR 一樣,分別進行 multi-head self-attention 和 deformable cross-attention,最后每個 instance query 通過 MLP 網絡生成類別信息和元素內的點集坐標,相比公版預測分層 query,改進后直接預測 instance query 帶來的計算量更少,極大地提高了模型在端側的運行性能。同時借鑒 StreamMapNet,使用多點注意力方法來適應高度不規則的地圖元素,擴大感知范圍。代碼見/usr/local/lib/python3.10/dist-packages/hat/models/task_modules/maptr/instance_decoder.py: class MapInstanceDetectorHead(nn.Module)
4.1.5 多點注意力
傳統的可變形注意力為每個 query 分配一個參考點,多點注意力則使用前一層預測的地圖元素的多個點作為當前層 query 的參考點,具體計算方式是在點維度上擴展了一層求和,將一個點變成多個點,分別計算 deformable attention。回歸的時候并非預測 offsets,而是直接預測地圖元素點的坐標位置。
4.1.6Attention
模型中用到的 attention 操作均使用地平線提供的算子,相比 PyTorch 提供的公版算子,地平線 attention 算子在保持算子邏輯等價的同時在效率上進行了優化
from hat.models.task_modules.bevformer.attention import ( HorizonMSDeformableAttention, HorizonMSDeformableAttention3D, HorizonSpatialCrossAttention, ... )
4.2 精度優化
4.2.1 浮點精度
MapTROE 模型引入 SD Map 前融合,與圖像轉換后的 bev feature 進行融合,以提高在線地圖的生成質量。模塊結構如下圖所示:
4.2.1.1 SD Map 特征提取
SD Map 從 OpenStreetMap(OSM)中獲取,通過由 GPS 提供的車輛位姿,查詢車輛當前位姿附近的 SD Map,然后將 SD Map 轉換到自車坐標系下,與 NuScenes 中的數據標注坐標系保持一致。SD Map 會從車道中心骨架線 Polyline 的形式轉化為柵格結構,大小和 BEV 特征相同,經過 CNN 變成特征圖,對應 SD Map 的先驗信息。
4.2.1.2 SD Map 特征融合
柵格化后的 SD Map 和實際場景可能會出現錯位、不對齊的情況,這種錯位導致直接 Concatenate BEV 特征和 SD Map 特征的效果并不好,為了解決這個問題,引入了特征融合模塊,通過網絡學習來決定最適合的對齊方式,可以有效地利用 SD Map 先驗提升 BEV 特征的效果。關于特征融合模塊,分別實驗了交叉注意力與 CNN 網絡,通過精度與性能的平衡,最后選擇了 CNN 網絡模塊。
4.3 量化精度
# Config文件 cali_qconfig_setter = (default_calibration_qconfig_setter,) qat_qconfig_setter = (default_qat_fixed_act_qconfig_setter,)
2.浮點階段采用更大的 weight decay 訓練,使浮點數據分布范圍更小,浮點模型參數更有利于量化
# Config文件 float_trainer = dict( ... optimizer=dict( ... weight_decay=0.1, # 相比maptrv2_resnet50_bevformer_nuscenes增大了10倍 ), ... )
3.QAT 訓練采用固定較小的 learning rate 來 fine-tune,這里固定也即取消 LrUpdater Callback 的使用,配置如下:
# Config文件 qat_lr = 1e-9
4.取消了公版模型 MapTRHead 中對于量化不友好的 inverse_sigmoid 操作;此外 MapTROE 對 Head 的優化無需再引入 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 結構優化,通過數值融合方法,將部分數值運算提前進行融合,減少整體的量化操作,提高模型的量化友好度
4.4 其他優化
4.4.1 設計優化
五、總結與建議
5.1 部署建議
5.2 總結
本文通過對 MapTR 進行地平線量化部署的優化,使得模型在征程 6 計算平臺上用較低的量化精度損失,最優獲得征程 6M 單核 93.77 FPS 的部署性能。同時,MapTR 系列的部署經驗可以推廣到其他相似結構或相似使用場景模型的部署中。
對于地平線 MapTR 參考算法模型,結合 Sparse Bev 等的優化方向仍在探索和實踐中,Stay Tuned!
六、附錄
*博客內容為網友個人發布,僅代表博主個人觀點,如有侵權請聯系工作人員刪除。