簡(jiǎn)介
BEV即Bird's Eye View(鳥瞰視圖)是一種從空中俯視場(chǎng)景的視角。由多張不同視角采集的圖像通過(guò)不同的空間轉(zhuǎn)換方式形成,如下圖所示,左側(cè)為6張不同位置的相機(jī)采集的圖像,右側(cè)為轉(zhuǎn)換的BEV圖像。
BEV感知模型指的是直接輸出BEV坐標(biāo)系下的感知結(jié)果,如動(dòng)靜態(tài)檢測(cè)目標(biāo),車道線,路面標(biāo)識(shí)等。BEV坐標(biāo)系好處是:
1. 成本低。相比3D點(diǎn)云的方式來(lái)補(bǔ)充3維信息,純視覺方案的成本更低;
2. 可以直接給到下游任務(wù),例如預(yù)測(cè)和規(guī)劃;
3. 可以在模型中融合各視角的特征,提升感知效果;
4. 可以更好的和各類傳感器進(jìn)行融合。
對(duì)于下游的預(yù)測(cè)和規(guī)控任務(wù)而言,需要的是3D的目標(biāo),因此在傳統(tǒng)的自動(dòng)駕駛方案中,2D的感知推理結(jié)果需要通過(guò)復(fù)雜的后處理去解決3D坐標(biāo)提升的問題。而BEV感知模型是更接近于一種端到端的解決方案。
當(dāng)前主要的BEV 轉(zhuǎn)換方式為以下三種:
? IPM-based:基于地面平坦假設(shè)的逆透視映射方式,技術(shù)簡(jiǎn)單成本低,但是對(duì)上下坡情況擬合效果不好。
? LSS-based:通過(guò)顯示的深度估計(jì)方式構(gòu)建三維視錐點(diǎn)云特征,也是較常用的轉(zhuǎn)換方式。
? Transformer-based:用transformer機(jī)制學(xué)習(xí)3D query和2D 圖像特征之間的關(guān)系來(lái)建模。部署時(shí)global attention的計(jì)算量較大, 需要考慮端側(cè)運(yùn)行時(shí)對(duì)性能的影響。
在實(shí)際部署時(shí),需要考慮算法的端側(cè)性能。地平線的參考算法目前已賦能多家客戶實(shí)現(xiàn)BEV感知算法在征程5上的部署和開發(fā),多家客戶已實(shí)現(xiàn)BEV demo開發(fā)。本文以LSS范式的BEV感知算法為例,介紹地平線提供的參考算法如何在公版的基礎(chǔ)上做算法在征程5芯片的適配和模型的優(yōu)化。
整體框架
BEV 感知架構(gòu)
BEV系列的模型使用多視圖的當(dāng)前幀的6個(gè)RGB圖像作為輸入。輸出是目標(biāo)的3D Box和BEV分割結(jié)果。多視角圖像首先使用2D主干獲取2D特征。然后投影到3D BEV視角。接著對(duì)BEV feature 編碼獲取BEV特征。最后,接上任務(wù)特定的head,輸出多任務(wù)結(jié)果。模型主要包括以下部分:
Part1—2D Image Encoder:圖像特征提取層。使用2D主干網(wǎng)絡(luò)(efficientnet)和FastSCNN輸出不同分辨率的特征圖。返回最后一層--上采樣至1/16原圖大小層,用于下一步投影至3D BEV坐標(biāo)系中;
Part2—View transformer:采用不同的轉(zhuǎn)換方式完成圖像特征到BEV 3D特征的轉(zhuǎn)換;
Part3—BEV transforms:對(duì)BEV特征做數(shù)據(jù)增強(qiáng),僅發(fā)生在訓(xùn)練階段;
Part4—3D BEV Encoder:BEV特征提取層;
Part5—BEV Decoder:分為Detection Head和Segmentation Head。
LSS方案
公版的LSS方案如下:
公版的LSS方案分為3個(gè)部分:
1. 將圖像從2d平面提升到3d空間,生成3d視錐(frustum)點(diǎn)云,并對(duì)點(diǎn)云上所有的點(diǎn)預(yù)測(cè)context特征,生成context特征點(diǎn)云;
2. 對(duì)視錐點(diǎn)云和context特征點(diǎn)云進(jìn)行 “Splat” 操作,在BEV網(wǎng)格中構(gòu)建BEV特征;
3. BEV特征后,可通過(guò)“Shooting”完成特定的下游任務(wù),比如Motion Planning。
模型部署分析
在部署之前,需要對(duì)公版模型做部署分析,避免模型中有BPU無(wú)法支持的算子和某些對(duì)性能影響較大的算子。對(duì)于無(wú)法支持的算子,需要做替換;對(duì)于影響性能的算子需要做優(yōu)化。同時(shí)為了達(dá)到更好的精度會(huì)增加訓(xùn)練策略的優(yōu)化和量化策略的優(yōu)化。本章節(jié)先對(duì)公版模型做部署分析,最后給出地平線的優(yōu)化方式。
問題1 大尺寸運(yùn)算導(dǎo)致性能瓶頸
由于深度特征的增加,feature的維度是高于4維的,考慮到transpose算子的耗時(shí)問題和部署問題,LSS方案中會(huì)存在維度的折疊,對(duì)feature做view和H維度的折疊。對(duì)應(yīng)的操作為:depth_feature會(huì)做view和Dim、H和W的折疊。維度折疊會(huì)導(dǎo)致feature的維度變大,在生成視錐點(diǎn)云時(shí),其涉及的操作mul操作的輸入也就增大了,在做部署時(shí)會(huì)導(dǎo)致DDR帶寬問題。因此公版的步驟1中的大尺寸算子計(jì)算需要做對(duì)應(yīng)的優(yōu)化。
問題2 BEVpooling的索引操作支持問題
公版在做2D到3D轉(zhuǎn)換時(shí),從圖像空間的index映射到BEV空間的index,相同的BEV空間index相加后再賦值到BEV tensor上,即公版的步驟二??紤]到征程5對(duì)索引操作無(wú)法支持,因此該操作在部署時(shí)需要做替換。
問題3 分割頭粒度太粗
地平線提供的是多任務(wù)的BEV感知算法,對(duì)于多任務(wù)模型來(lái)說(shuō)不同的任務(wù)需要特定的范圍和粒度,特別是對(duì)于分割模型來(lái)說(shuō),分割的目標(biāo)的粒度較小,因此相比于檢測(cè)任務(wù)來(lái)說(shuō)feature需要細(xì)化,即用更大的分辨率來(lái)表示。
問題4 grid 量化精度誤差問題
對(duì)于依賴相機(jī)內(nèi)外參的模型來(lái)說(shuō),轉(zhuǎn)換時(shí)的點(diǎn)坐標(biāo)極其重要,因此需要保障該部分的精度。同時(shí)grid的表示范圍需要使用更大比特位的量化。
針對(duì)以上4個(gè)問題,本章節(jié)會(huì)介紹該部分在征程5的實(shí)現(xiàn)方式使其可以在板端部署并高速運(yùn)行。
性能優(yōu)化
mul的性能優(yōu)化
為了減少大量的transpose操作和優(yōu)化mul算子的耗時(shí)問題, 我們選擇把深度和 feature 分別做grid_sample后執(zhí)行mul操作,具體操作如下:
Python #depth B, N, D,H, W depth = tensor(B,N,D,H,W) feat = tensor(B,N,C,H,W) #depth B, 1, N *D, H*W depth = depth.view(B, 1, N*D, H*W) #feat -> B,C,N,H,W-> B, C, N*H, W feat = feat.permute(0, 2, 1, 3, 4).view(B, C, N*H, W) for i in range(self.num_points): homo_feat = self.grid_sample( feat, fpoints[i * B : (i + 1) * B], ) homo_dfeat = self.dgrid_sample( dfeat, dpoints[i * B : (i + 1) * B], ) homo_feat = self.floatFs.mul(homo_feat, homo_dfeat) homo_feats.append(homo_feat)
mul操作的計(jì)算量大幅減少,性能上提升4~5倍!
BEV_pooling部署優(yōu)化
使用grid_sample代替公版的3D空間轉(zhuǎn)換。即從原來(lái)的前向wrap-從圖像空間特征轉(zhuǎn)換到BEV空間特征,改為從BEV空間拉取圖像空間特征。
公版實(shí)現(xiàn):
a.通過(guò)一個(gè)深度估計(jì)變成6D的tensor
Python volume = depth.unsqueeze(1) * cvt_feature.unsqueeze(2) volume = volume.view(B, N, volume_channel_index[-1], self.D, H, W) volume = volume.permute(0, 1, 3, 4, 5, 2)
b.從圖像空間的index映射到BEV空間的index,相同的BEV空間index相加后再賦值到BEV tensor上
Python def voxel_pooling(self, geom_feats, x): ... # flatten x x = x.reshape(Nprime, C) # flatten indices geom_feats = ((geom_feats - (self.bx - self.dx/2.)) / self.dx).long() geom_feats = geom_feats.view(Nprime, 3) ... # filter out points that are outside box kept = (geom_feats[:, 0] >= 0) & (geom_feats[:, 0] < nx[0]) & (geom_feats[:, 1] >= 0) & (geom_feats[:, 1] < nx[1]) & (geom_feats[:, 2] >= 0) & (geom_feats[:, 2] < nx[2]) geom_feats = geom_feats[kept] x = x[kept] ... # argsort and get tensors from the same voxel next to each other ranks = geom_feats[:, 0] * (nx[1] * nx[2] * B) + geom_feats[:, 1] * (nx[2] * B) + geom_feats[:, 2] * B + geom_feats[:, 3] sorts = ranks.argsort() x, geom_feats, ranks = x[sorts], geom_feats[sorts], ranks[sorts] ... return final
2. 地平線實(shí)現(xiàn):
使用grid_sample代替公版的3D空間轉(zhuǎn)換。grid_sample為采樣算子,通過(guò)輸入圖像特征和2D點(diǎn)坐標(biāo)grid,完成圖像特征到BEV特征的轉(zhuǎn)換,其工作原理見下圖。
Grid_Samples 原理圖
horizon_plugin_pytorch提供的grid_sample算子和公版輸入略有差異,地平線已支持公版的grid_sample算子。
由于該轉(zhuǎn)換方式是前向映射,前向映射會(huì)產(chǎn)生的BEV index并不均勻,最多的一個(gè)voxel有100多個(gè)點(diǎn),最少有效點(diǎn)為0。我們?cè)谔峁┑脑创a中使用了每個(gè)voxel使用了10個(gè)點(diǎn),如果想要提升精度可以考慮增加每個(gè)voxel的有效點(diǎn)。
Python #num_point為10 for i in range(self.num_points): homo_feat = self.grid_sample( feat, fpoints[i * B : (i + 1) * B], ) homo_dfeat = self.dgrid_sample( dfeat, dpoints[i * B : (i + 1) * B], ) homo_feat = self.floatFs.mul(homo_feat, homo_dfeat) homo_feats.append(homo_feat)
精度優(yōu)化
多任務(wù)模型的精度優(yōu)化
參考BEVerse模型對(duì)多任務(wù)根據(jù)不同粒度進(jìn)行細(xì)化,在分割頭做解碼之前,將BEV feature的分辨率增大,map size為[200,400],實(shí)現(xiàn)上由grid_sample完成。
Python #init map module if (self.bev_size and self.task_size and self.task_size != self.bev_size): self.grid_sample = hnn.GridSample( mode="bilinear", padding_mode="zeros", ) #decoder module forward def forward(self, feats: Tensor, meta: Dict) -> Any: feat = feats[self.task_feat_index] if hasattr(self, "grid_sample"): batch_size = feat.shape[0] new_coords = self.new_coords.repeat(batch_size, 1, 1, 1) feat = self.grid_sample(feat, self.quant_stub(new_coords)) feat = [feat] pred = self.head(feat) return self._post_process(meta, pred)
浮點(diǎn)模型精度的優(yōu)化
在浮點(diǎn)模型的訓(xùn)練上,使用數(shù)據(jù)增強(qiáng)來(lái)增強(qiáng)模型的泛化能力,通過(guò)嘗試不同的增強(qiáng)方式,最終選取BEVRotate方式對(duì)輸入數(shù)據(jù)做transform。相比于未做數(shù)據(jù)增強(qiáng)的浮點(diǎn)模型mAP提升1.5個(gè)點(diǎn),NDS提升0.6個(gè)點(diǎn)。詳細(xì)實(shí)驗(yàn)記錄見實(shí)驗(yàn)結(jié)果章節(jié)。
該實(shí)驗(yàn)結(jié)果為中間結(jié)果,非最終精度數(shù)據(jù)
量化精度的優(yōu)化
BEV_LSS的量化訓(xùn)練采用horizon_plugin_pytorch的Calibration方式來(lái)實(shí)現(xiàn)的,通過(guò)插入偽量化節(jié)點(diǎn)對(duì)多個(gè)batch的校準(zhǔn)數(shù)據(jù)基于數(shù)據(jù)分布特征來(lái)計(jì)算量化系數(shù),從而達(dá)到模型的量化。BEV_LSS模型無(wú)需QAT訓(xùn)練就可以達(dá)到和浮點(diǎn)相當(dāng)?shù)木取?/p>
除了量化方式上的優(yōu)化,地平線對(duì)輸入的grid也做了優(yōu)化,包括了
1. 手動(dòng)計(jì)算scale,使用固定的scale作為grid的量化系數(shù)。
Python #fix scale def get_grid_quant_scale(grid_shape, view_shape): max_coord = max(*grid_shape, *view_shape) coord_bit_num = math.ceil(math.log(max_coord + 1, 2)) coord_shift = 15 - coord_bit_num coord_shift = max(min(coord_shift, 8), 0) grid_quant_scale = 1.0 / (1 << coord_shift) return grid_quant_scale #get grid_quant_scale grid_quant_scale = get_grid_quant_scale(grid_size, featview_shape) ##init self.quant_stub = QuantStub(grid_quant_scale)
2.grid_sample算子的輸入支持int16量化,為了保障grid的精度,地平線選擇int16量化。
Python self.quant_stub.qconfig = qconfig_manager.get_qconfig( activation_qat_qkwargs={"dtype": qint16, "saturate": True}, activation_calibration_qkwargs={"dtype": qint16, "saturate": True}, )
基于以上對(duì)量化精度的優(yōu)化后,最終定點(diǎn)精度達(dá)到和浮點(diǎn)相當(dāng)?shù)乃剑?strong>量化精度達(dá)到99.7%!
實(shí)驗(yàn)結(jié)果
1.性能和精度數(shù)據(jù)
數(shù)據(jù)集 | Nuscenes | |
Input shape | 256x704 | |
backbone | efficientnetb0 | |
bev shape | 128x128 | |
FPS(單核) | 138 | |
latency(ms) | 8.21 | |
分割精度(浮點(diǎn)/定點(diǎn))iou | divider | 46.55/47.45 |
ped_crossing | 27.91/28.44 | |
Boundary | 47.06/46.03 | |
Others | 85.59/84.49 | |
檢測(cè)精度(浮點(diǎn)/定點(diǎn)) | NDS | 0.3009/0.3000 |
mAP | 0.2065/0.2066 |
注:grid_sample的input_feature H,W ∈ [1, 1024] 且 H*W ≤ 720*1024
2. 不同數(shù)據(jù)增強(qiáng)方式對(duì)浮點(diǎn)模型的精度影響。
該實(shí)驗(yàn)結(jié)果為中間結(jié)果,非最終精度數(shù)據(jù)
3.地平線征程5部署LSS范式的BEV模型通用建議
?選用BPU高效支持的算子替換不支持的算子。
? num_point會(huì)直接影響性能和精度,可以根據(jù)需求做權(quán)衡。處于訓(xùn)練速度考慮使用topk選擇點(diǎn),若想要更高的精度可以對(duì)點(diǎn)的選擇策略做優(yōu)化。
? grid使用fixed scale來(lái)保障量化精度,如超過(guò)int8表示范圍則開啟int16量化,具體見grid量化精度優(yōu)化章節(jié)。
?對(duì)于分辨率較大導(dǎo)致帶寬瓶頸或不支持問題,可以拆分為多個(gè)計(jì)算,緩解帶寬壓力,保障模型可以順利編譯。
?對(duì)于常量計(jì)算(例如:grid計(jì)算)編譯時(shí)可以作為模型的輸入,提升模型的運(yùn)行性能。
總結(jié)
本文通過(guò)對(duì)LSS范式的BEV多任務(wù)模型在地平線征程5上量化部署的優(yōu)化,使得模型在該計(jì)算方案上用遠(yuǎn)低于1%的量化精度損失,得到latency為8.21ms的部署性能,同時(shí),通過(guò)LSS范式的BEV模型的部署經(jīng)驗(yàn),可以推廣到基于該范式的BEV模型的優(yōu)化中,以便更好的在端側(cè)部署。
審核編輯:劉清
-
NDS
+關(guān)注
關(guān)注
0文章
6瀏覽量
6622 -
LSS
+關(guān)注
關(guān)注
0文章
8瀏覽量
1954 -
pytorch
+關(guān)注
關(guān)注
2文章
807瀏覽量
13198
原文標(biāo)題:好算法更要配好用的芯片 —— 基于LSS范式的BEV感知算法優(yōu)化部署詳解
文章出處:【微信號(hào):horizonrobotics,微信公眾號(hào):地平線HorizonRobotics】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。
發(fā)布評(píng)論請(qǐng)先 登錄
相關(guān)推薦
評(píng)論