专栏算法工具链【参考算法】地平线 Bev_mt_ipm_temporal 参考算法-v1.2.1

【参考算法】地平线 Bev_mt_ipm_temporal 参考算法-v1.2.1

芯链情报局2023-03-20
547
0

0 概述

在自动驾驶感知算法中BEV感知成为热点话题,BEV感知可以弥补2D感知的缺陷构建3D“世界”,更有利于下游任务和特征融合。为响应市场需求,地平线集成了基于bev的纯视觉算法,目前已支持ipm-based 、lss-based、 transformer-based(Geometry-guided Kernel Transformer、detr3d) 的多种bev视觉转换方法。本文为ipm-based的BEV时序多任务感知算法的介绍和使用说明。

该示例为参考算法,仅作为在J5上模型部署的设计参考,非量产算法

1 性能精度指标

数据集

img_shape

Stage 1

grid_size

Stage 2

Nuscenes

512x960

Backbone

Neck

128x128

Backbone

Neck

efficientnetb0

fastscnn

efficientnetb0

bifpn

性能精度表现:

性能(FPS/双核) 分割精度(定点)iou 检测精度(定点) MeanIOU NDS mAP 213.76 54.10 0.3718 0.2194

注:
stage1为image encoder;stage2为bev encoder;
Nuscenes 数据集官方介绍:Nuscenes

2 模型介绍

2.1 模型框架

bev_mt_ipm_temporal 使用多视图的当前帧的6个RGB图像和前一帧的prev_feat和采样点prev_points作为输入。输出是目标的3D Box和BEV分割结果。多视角图像首先使用2D主干获取2D特征。然后投影到3D BEV视角,融合时序特征。接着对BEV feature 编码获取BEV特征。最后,接上任务特定的head,输出多任务结果。模型主要包括以下部分:

Part1—2D Image Encoder:图像特征提取层。使用2D主干网络(efficientnet)和FastSCNN输出不同分辨率的特征图。返回最后一层--下采样至1/16原图大小层,用于下一步投影至3D 坐标系中。
Part2—View transformer:采用IPM映射完成2D到3D的转换。
Part3—Temporal fusion:时序融合,融合前序特征
Part4—Bev transforms:对bev特征做数据增强,仅发生在训练阶段。
Part5—3D BEV Encoder:BEV特征提取层。
Part6—BEV Decoder:分为Detection Head和Segmentation Head。得到统一的BEV特征后,使用DepthwiseSeparableFCNHead进行bev分割,分割种类为["others", "divider", "ped_crossing", "Boundary"]。使用DepthwiseSeparableCenterPointHead进行3D目标检测任务,检测的类别为["car","truck","bus","barrier","bicycle","pedestrian"]。

2.2 源码说明

Config文件

configs/bev/bev_mt_ipm_4d_efficientnetb0_nuscenes.py 为该模型的配置文件,定义了模型结构、数据集加载,和整套训练流程,所需参数的说明在算子定义中会给出。配置文件主要内容包括:
# 模型结构定义
model = dict(
type="ViewFusion",
backbone=dict(
type="efficientnet",
model_type="b0",
...
),
neck=dict(
type="FastSCNNNeck",
...
),
view_transformer=dict(
type="WrappingTransformer", #ipm transform
...
),
temporal_fusion=dict( #Temporal Fusion
type="AddTemporalFusion",
...
),
bev_transforms=[...],
bev_encoder=dict(
type="BevEncoder",
...
),
bev_decoders=[
dict(
type="BevSegDecoder",
...
),
dict(
type="BevDetDecoder",
...

],
)
deploy_model = dict(
...
)
...
# 数据加载
data_loader = dict(
type=torch.utils.data.DataLoader,
...
)

val_data_loader = dict(...)

#不同step的训练策略配置
float_trainer=dict(...)
calibration_trainer=dict(...)
qat_trainer=dict(...)
int_trainer=dict(...)
#不同step的精度验证
float_predictor=dict(...)
calibration_predictor=dict(...)
qat_predictor=dict(...)
int_infer_predictor=dict(...)
#编译配置
compile_cfg = dict(
march=march,
...
)

注: 如果需要复现精度,config中的训练策略最好不要修改,否则可能会有意外的训练情况出现。

img_encoder

来自6个view的image作为输入通过共享的backbone(efficientnet)和neck(FastSCNN)输出经过encoder后的feature,feature_shape为(6*B,C,1/16H,1/16W)。encoder即对多个view的img_feature 做特征提取,过程见下图:

对应代码:hat/models/backbones/efficientnet.py hat/models/necks/fast_scnn.py

view_transformer

view_transformer 采用IPM映射的方法,基于摄像头的内外参把图像视角的img_features转换到bev_features。bev_shape为[H',W']为[128,128],其转换过程见下图:

基于IPM 映射流程图

homography矩阵

homography矩阵是将相机视角转换成鸟瞰图的关键所在,计算方式为:

对于Kf:

其中,K为相机内参; P为相机外参即(R, t)为转换角和偏移; M为bev坐标到世界坐标的转换矩阵,(W, H)为bev的长度和宽度,r为1m的像素数。

view_transformer对应代码:hat/models/task_modules/view_fusion/view_transformer.py

通过homo转换为像素坐标中的coors,对应代码:

spatial_transfomer

图像特征转换为Bev 特征是使用ipm实现的,ipm通过grid_sample算子对img_feature采样到bev空间内。grid_sample的输入为多个view的img feature,经过bev 转换后将进行多view的特征融合,如下图所示:

view_transformer对应代码:hat/models/task_modules/view_fusion/view_transformer.py的WrappingTransformer
其中,grid_sample在plugin中实现,路径为:horizon_plugin_pytorch/nn/grid_sample.py

temporal-transformer

实现路径在hat/models/task_modules/view_fusion/temporal_fusion.py
step1:特征提取
使用DS_conv对当前帧的bev_feat做encoder,代码如下:

stpe2:前序特征融合

  1. 获取prev_point prev_feat

def _transform(self, feat, points
):
feat = self.grid_sample(
feat,
self.quant_stub(points),
)
return feat

  1. 将当前帧的bev_feature和前序帧的bev_feature做融合

  1. 对融合的feature做encoder

bev transform

bev的数据增强仅发生在训练过程中,在 BEV 下做了 rotate的数据增强,作用域是 view transformer 的输出。配置如下:

bev_encoder

bev_encoder过程是对bev_feature 做特征提取的过程,backbone为efficientnet-b0,neck为BiFPN。流程见下图:

对应代码:hat/models/task_modules/view_fusion/encoder.py

bev_head

seg_head
本模型的分割头为DepthwiseSeparableFCNHead,conv为SeparableConvModule2d
对应代码:hat/models/task_modules/fcn/head.py
class FCNHead(nn.Module):
def __init__(self,...):
...
def forward(self, inputs: List[torch.Tensor]
):
x = inputs[self.input_index]
x = self.convs(x)
if self.dropout:
x = self.dropout(x)
seg_pred = self.cls_seg(x)
if self.training:
if self.upsample_output_scale:
seg_pred = self.resize(seg_pred)
if self.argmax_output:
seg_pred = seg_pred.argmax(dim=1)
if self.dequant_output:
seg_pred = self.dequant(seg_pred)
return seg_pred

det_head

检测为多task检测,主要分为:

在nuscenes数据集中,目标的类别一共被分为了6个大类,网络给每一个类都分配了一个head,装在headlist中,而每个head内部都为预测的参数。
bev_det的分割头为DepthwiseSeparableCenterPointHead
对应代码:hat/models/task_modules/centerpoint/head.py
class CenterPointHead(nn.Module):
def __init__(self,...):
self.shared_conv = nn.Sequential(
*(
self._make_conv(
in_channels=in_channels if i == 0 else share_conv_channels,
...
)
for i in range(share_conv_num)
)
)
#head module
for num_cls in num_classes:
heads = copy.deepcopy(common_heads)
heads.update({"heatmap": (num_cls, num_heatmap_convs)})
task_head = self._make_task(
...,
)
self.task_heads.append(task_head)
forward时,经过共享的SeparableConv后,将feature再分别传入task_heads做task_pred。
在hat/models/task_modules/centerpoint/head.py的TaskHead对不同的task定义conv_layers:
class TaskHead(nn.Module):
def __init__(...):
...
for head in self.heads:
classes, num_conv = self.heads[head]
...
#head_conv
for _ in range(num_conv - 1):
conv_layers.append(
self._make_conv(
...
)
)
c_in = head_conv_channels
#cls_layer
conv_layers.append(
ConvModule2d(
in_channels=head_conv_channels,
out_channels=classes,
...
)
)
conv_layers = nn.Sequential(*conv_layers)

bev_decoder

多任务模型的decoder分为分割和检测的解码,在分割任务中使用FCNDecoder,在检测任务中使用CenterPointDecoder,具体实现流程见下图:
对应代码:
hat/models/task_modules/centerpoint/decoder.py
hat/models/task_modules/fcn/decoder.py

3 浮点模型训练

3.1 Before Start

3.1.1 发布物及环境部署

step1:获取发布物
下载OE包horizon_j5_open_explorer_v$version$.tar.gz,获取方式见地平线开发者社区 OpenExplorer算法工具链 版本发布

step2:解压发布包

解压后文件结构如下:

其中horizon_model_train_sample为参考算法模块,包含以下模块:

step3:拉取docker环境

3.1.2 数据集准备

3.1.2.1 数据集下载

进入nuscenes官网,根据提示完成账户的注册,下载Full dataset(v1.0)、CAN bus expansion和Map expansion(v1.3)这三个项目下的文件。下载后的压缩文件为:

Full dataset(v1.0)包含多个子数据集,如果不需要进行v1.0-trainval数据集的浮点训练和精度验证,可以只下载v1.0-mini数据集进行小场景的训练和验证。

将下载完成的v1.0-trainval01_blobs.tar~v1.0-trainval10_blobs.tar、v1.0-trainval_meta.tar和can_bus.zip进行解压,解压后的目录如下所示:

3.1.2.2 数据集打包

进入 horizon_model_train_sample/scripts 目录,使用以下命令将训练数据集和验证数据集打包,格式为lmdb:
--src-data-dir为解压后的nuscenes数据集目录;
--target-data-dir为打包后数据集的存储目录;
--version 选项为["v1.0-trainval", "v1.0-test", "v1.0-mini"],如果进行全量训练和验证设置为v1.0-trainval,如果仅想了解模型的训练和验证过程,则可以使用v1.0-mini数据集;v1.0-test数据集仅为测试场景,未提供注释。
全量的nuscenes数据集较大,打包时间较长。每打包完100张会在终端有打印提示,其中train打包约28100张,val打包约6000张。
数据集打包命令执行完毕后会在target-data-dir下生成train_lmdb和val_lmdb,train_lmdb和val_lmdb就是打包之后的训练数据集和验证数据集为config中的data_rootdir。

2.1.2.3 meta文件夹构建

在tmp_data/nuscenes 下创建meta文件夹,将v1.0-trainval_meta.tar压缩包解压至meta,得到meta/maps文件夹,再将nuScenes-map-expansion-v1.3.zip压缩包解压至meta/maps文件夹下,解压后的目录结构为:

3.1.3 config配置

在进行模型训练和验证之前,需要对configs文件中的部分参数进行配置,一般情况下,我们需要配置以下参数:

  • device_ids、batch_size_per_gpu:根据实际硬件配置进行device_ids和每个gpu的batchsize的配置;

  • ckpt_dir:浮点、calib、量化训练的权重路径配置,权重下载链接在config文件夹下的README中;

  • data_rootdir:2.1.2.2中打包的数据集路径配置;

  • meta_rootdir :2.1.2.3中创建的meta文件夹的路径配置;

  • float_trainer下的checkpoint_path:浮点训练时backbone的预训练权重所在路径,可以使用README的# Backbone Pretrained ckpt中ckpt download提供的float-checkpoint-best.pth.tar权重文件。

3.2 浮点模型训练

config文件中的参数配置完成后,使用以下命令训练浮点模型:

float训练后模型ckpt的保存路径为config配置的ckpt_callback中save_dir的值,默认为ckpt_dir。

3.3 浮点模型精度验证

浮点模型训练完成以后,可以使用以下命令验证已经训练好的浮点模型精度:

4 模型量化和编译

完成浮点训练后,还需要进行量化训练和编译,才能将定点模型部署到板端。地平线对该模型的量化采用horizon_plugin框架,经过Calibration+QAT量化训练后,使用compile的工具将量化模型编译成可以上板运行的hbm文件。

4.1 Calibration

模型完成浮点训练后,便可进行 Calibration。calibration在forward过程中通过统计各处的数据分布情况,从而计算出合理的量化参数。 通过运行下面的脚本就可以开启模型的Calibration过程:

4.2 Calibration 模型精度验证

Calibration完成以后,可以使用以下命令验证经过calib后模型的精度:

模型经过 Calibration 后的量化精度若已满足要求,便可直接进行转定点模型的步骤,否则需要进行量化训练进一步提升精度。

4.3 量化训练

bev_mt_ipm_temporal经过 Calibration 后的量化精度未能满足要求,所以需要使用以下命令进行量化训练:

4.4 qat模型精度验证

量化训练完成后,通过运行以下命令验证qat模型的精度:

4.5 量化模型精度验证

通过运行以下命令验证量化模型的精度:

4.6 仿真上板精度验证

除了上述模型验证之外,我们还提供和上板完全一致的精度验证方法,可以通过下面的方式完成:

4.7 量化模型编译

在量化训练完成之后,可以使用compile_perf.py脚本将量化模型编译成可以板端运行的hbm模型,同时该工具也能预估在BPU上的运行性能,compile_perf脚本使用方式如下:
opt为优化等级,取值范围为0~3,数字越大优化等级越高,运行时间越长。
compile_perf脚本将生成.html文件和.hbm文件(compile文件目录下),.html文件为BPU上的运行性能,.hbm文件为上板实测文件。

运行后,ckpt_dir的compile目录下会产出以下文件:

5 其他工具

5.1 结果可视化

如果你希望可以看到训练出来的模型对于单帧的检测效果,我们的tools文件夹下面同样提供了预测及可视化的脚本,你只需要运行以下脚本即可:

也可以通过修改config文件中infer_cfg字段的infer_inputs来配置输入数据路径。

注:由于开发机配置不同,plt.show可能不会正常显像,可以在hat/visualize/nuscenes.py添加plt.savefig将结果保存。

可视化示例:

算法工具链
征程5官方教程
评论0
0/1000