0 概述
多目标跟踪是在视频序列的每一帧中定位所有的目标并确定它们的移动轨迹的任务。因为每帧中的目标都可能因为环境的变化而被遮挡,而且跟踪器要想进行长期跟踪或者低帧率的跟踪是比较困难的,所以多目标跟踪是极具挑战性的任务。MOTR(Multiple-Object Tracking with Transformer)能够学习建模目标的长时间变化,隐式地进行时间关联。基于Transformer和DETR,MOTR引入了track query这个概念,一个track query负责建模一个目标的整个轨迹,它可以在视频帧间传递和更新,从而无缝地完成目标检测和跟踪任务。本文为多目标跟踪算法MOTR的算法介绍和使用说明。
该示例为参考算法,仅作为在J5上模型部署的设计参考,非量产算法
1 性能精度指标
MOTR模型配置:
数据集
Input shape
backbone
head
post-process
跟踪目标最大数量
检测类别
mot17
1x3x800x1422
efficientnetb3
MotrHead
MotrPostProcess
256
行人
性能精度表现:
infer/ms
post-process/ms
双核FPS
精度(MOTA)
26.96
22.84
73.98
浮点:58.02 量化:57.76
2 模型介绍
2.1 模型优化点
相对于公版实现,地平线对MOTR的模型结构和训练策略做了如下优化:
2.1.1 模型结构优化
- 将backbone从ResNet50更换到efficientnetb3, 兼顾模型精度和推理性能;
- 优化query维度, 模型中将query全部由二维优化为4维进行计算, 便于性能提升;
- 模型中的linear全部替换为conv2d,便于性能提升;
- deformable attention优化:
- 输入采用四维的数据,避免了flatten+reshape的重复计算;
- 将每个注意力头的采样点数量由4个减少到1个,并且用sigmoid替换softmax, 便于性能提升(精度基本上不变);
- Track query个数由300降低到256, 便于性能提升;
- 引入padding query和mask query, 解决动态shape问题。
2.1.2 训练策略优化
- 引入了DAB-DETR的anchor-box,直接将box的坐标作为queries输出到DeformableTransformer的解码器中,加速模型收敛和提升精度;
- 引入了DINO的look forward twice, 利用后一层的改进的box信息校正前一层的box预测,优化梯度传播,提升预测精度。
2.2 模型结构
MOTR的输入为图像序列,输出为跟踪目标的位置信息和类别信息。MOTR的总体架构和过程如下图所示,主要由以下部分组成:
-
Enc: 由backbone(efficientnetb3)和Deformable DETR encoder组成,用来提取每帧图像的特征;-
-
Dec: 由Deformable DETR decoder组成,用来生成bounding box 预测的隐藏状态。对于第一帧,不存在跟踪查询qtrq_{tr}qtr,因此只将固定长度的检测查询qdq_dqd和手工生成的fake跟踪查询输入到Dec中;对于连续帧,模型会将来自前一帧的跟踪查询和可学习的检测查询输入到Dec中。更新检测查询和跟踪查询的过程如下所示:
Track Query是动态更新的,而且长度是可变的。首先将Track Query初始化为空,然后使用Detect Query检测新生目标,将所有检测到的目标的隐藏状态连接起来,从而生成下一帧的Track Query,,终止目标的Track Query则将从Track Query集中删除。
-
**Query Interaction Module(QIM):**QIM将Dec生成的box的隐藏状态作为输入,并为下一帧生成跟踪查询。QIM包括目标进出机制和时间聚合网络(Temporal Aggregation Network ,TAN)。目标进出机制用来解决新生目标和终止目标的问题;TAN增强了时间关系建模,并为跟踪目标提供上下文先验。
2.3 源码说明
2.3.1 config文件
configs/track_pred/motr_efficientnetb3_mot17.py
为该模型的配置文件,定义了模型结构、数据集加载,和整套训练流程,所需参数的说明在算子定义中会给出。配置文件主要内容包括:
task_name = "motr"
num_classes = 1
#权重路径配置
ckpt_dir = "./tmp_models/%s" % task_name
#芯片架构配置
march = March.BAYES
img_shape = (800, 1422)
#数据集路径设置
train_lmdb = "./tmp_data/mot17/train_lmdb"
val_lmdb = "./tmp_data/mot17/test_lmdb"
val_gt = "./tmp_data/mot17/test_gt"
#device配置
device_ids = [0, 1, 2, 3, 4, 5, 6, 7]
train_batch_size_per_gpu = 1
test_batch_size_per_gpu = 1
num_queries = 256
model = dict(
type="Motr",
backbone=dict(
type="efficientnet",
model_type="b3",
...
),
head=dict(
type="MotrHead",
transformer=dict(
type="MotrDeformableTransformer"
),...
),
criterion=dict(
type="MotrCriterion",
),
post_process=dict(
type="MotrPostProcess",
),
track_embed=dict(
type="QueryInteractionModule",
),
)
test_model = copy.deepcopy(model)
deploy_model = dict(
type="Motr",
backbone=dict(
type="efficientnet",
...
),
head=dict(
type="MotrHead",
transformer=dict(
type="MotrDeformableTransformer",
...)
track_embed=dict(
type="QueryInteractionModule",
),
compile_motr=True,
)
deploy_inputs = dict(..)
data_loader = dict(
type=torch.utils.data.DataLoader,
dataset=dict(
type="Mot17Dataset",
... ),
)
val_data_loader = dict(
type=torch.utils.data.DataLoader,
dataset=dict(
type="Mot17Dataset",
)
#trainer配置
float_trainer = dict()
calibration_trainer = dict()
qat_trainer = dict()
int_infer_trainer = dict()
#编译配置
compile_dir = os.path.join(ckpt_dir, "compile")
compile_cfg = dict(
march=march,
name="motr",
out_dir=compile_dir,
hbm=os.path.join(compile_dir, "model.hbm"),
layer_details=True,
input_source=["pyramid", "ddr", "ddr", "ddr"],
opt="O3",
)
# predictor配置
float_predictor = dict()
qat_predictor = dict()
calibration_predictor=dict()
int_infer_predictor = dict()
注: 如果需要复现精度,config中的训练策略最好不要修改,否则可能会有意外的训练情况出现。
2.3.2 Backbone
MOTR参考算法采用与硬件比较友好的efficientnetb3作为backbone来提取图像多尺度特征,提升了模型的精度。efficientnetb3的网络结构如下所示:
代码路径:/usr/local/lib/python3.8/dist-packages/hat/models/backbones/efficientnet.py
2.3.3 MotrHead
MOTR通过efficientnetb3和Deformable Transformer Encoder提取图像序列特征。对于第一帧图像,由于不存在跟踪查询,所以将固定长度的可学习检测查询和padding后的fake跟踪查询输入到Deformable Transformer Decoder。对于连续帧,将来自前一帧的跟踪查询和可学习的检测查询输入到解码器中,这些查询与解码器中的图像特征交互,生成用于边界框预测的类别classes、坐标coords和hs,然后输入到查询交互模块(QIM)中,来生成下一帧的跟踪查询。
代码路径:/usr/local/lib/python3.8/dist-packages/hat/models/task_modules/motr/head.py
-
/usr/local/lib/python3.8/dist-packages/hat/models/task_modules/motr/motr_deformable_transformer.py
Track instance 初始化
对于首帧图像,我们生成了值全为0的track instance用于目标padding, 初始化过程如下所示 :
相关代码如下所示:
def _generate_fake_tracks(
self,
):
"""Genrate fake track instance for padding object."""
track_instances = self._generate_empty_tracks()
#[256,256]
query_pos = torch.zeros_like(track_instances["query_pos"])
#[256]
mask_query = torch.zeros_like(track_instances["mask_query"])
#[256,4]
ref_pts = torch.zeros_like(track_instances["ref_pts"])
track_instances["query_pos"] = query_pos
track_instances["mask_query"] = mask_query
track_instances["ref_pts"] = ref_pts
return track_instances
代码路径:/usr/local/lib/python3.8/dist-packages/hat/models/structures/track_pred/motr_efficientnetb3_mot17.py
Deformable Transformer Encoder
Deformable Transformer Encoder的作用是提取一帧图像的特征。它的输入是backbone 提取的图像特征、参考点reference points和位置编码position embedding。-
Deformable Transformer Encoder的基本组成结构为DeformableTransformerEncoderLayer,它主要由self attention层、LayerNorm层和ffn(feedforward neural network)层组成,这里需要注意的是:为了性能优化,Deformable Attention采样点数量都由公版的4个减少到1个,并且用sigmoid替换softmax。 如下为结构示意图:
相关代码:
def forward_ffn(self, src):
src2 = self.linear2(self.dropout2(self.linear1(src)))
src = self.add_ffn.add(src, self.dropout3(src2))
src = self.norm2(src)
return src
def forward(self, src, pos, reference_points):
# self attention
src1 = self.add_pos.add(src, pos)
src2 = self.self_attn(src1, reference_points, [src])
src = self.add_att.add(src, self.dropout1(src2))
src = self.norm1(src)
# ffn
src = self.forward_ffn(src)
return src
代码路径:/usr/local/lib/python3.8/dist-packages/hat/models/task_modules/motr/motr_deformable_transformer.py
Deformable Transformer Decoder
对于每帧图像特征,检测查询和跟踪查询被连接起来,然后被输入到Deformable Transformer Decoder
中来更新它们的表示。检测查询将仅检测新生目标,因为Transformer解码器中通过自注意力进行的查询交互将抑制检测跟踪目标的检测查询。这种机制类似于DETR中的重复删除,即以低分数抑制重复框。Deformable Transformer Decoder
基本组成结构为DeformableTransformerDecoderLayer
:
相关代码如下所示:
def forward(
self, tgt, query_pos, reference_points, src, tgt_mask, track_mask
):
# self attention
tgt = self._forward_self_attn(tgt, query_pos, tgt_mask, track_mask)
# cross attention
tgt2 = self.cross_attn(
self.add_pos3.add(tgt, query_pos), reference_points, src
)
tgt = self.add_cross_attn.add(tgt, self.dropout1(tgt2))
tgt = self.norm1(tgt)
# ffn
tgt = self.forward_ffn(tgt)
return tgt
代码路径:/usr/local/lib/python3.8/dist-packages/hat/models/task_modules/motr/motr_deformable_transformer.py
2.3.4 Post Process
MotrHead输出了目标的类别信息classes、bbox信息和decoder的中间结果hs,将这些结果进行处理后,然后再输入到QIM模块,相关过程如下所示:
相关代码如下所示:
def _prepare_infer_process(
self, track_instances, hs, outputs_classes, outputs_coords
):
"""Prepare for test process."""
outputs_coord_unsigmoid = outputs_coords[-1]
outputs_class = outputs_classes[-1]
#将coords信息进行sigmoid
outputs_coord_sigmoid = outputs_coord_unsigmoid.sigmoid()
#将classes信息进行sigmoid
track_scores = outputs_class[0].sigmoid()[:, 0]
track_instances.scores = track_scores
track_instances.pred_logits = outputs_class[0]
track_instances.pred_boxes = outputs_coord_sigmoid[0]
track_instances.output_embedding = hs[0]
track_instances.pred_boxes_unsigmoid = outputs_coord_unsigmoid[0]
return track_instances
代码路径:/usr/local/lib/python3.8/dist-packages/hat/task_modules/motr/post_process.py
查询交互模块(query interaction module,QIM)包括目标进出机制和时间聚合网络(TAN)。QIM的结构如下图所示:
目标进出机制
视频帧序列中的一些目标可能在中间帧出现或消失。MOTR中处理新生目标和终止目标的方式是对于任何帧,跟踪查询都与检测查询连接,并输入到Transformer解码器,从而产生隐藏状态(如上图的左侧)。在训练过程中,如果匹配的目标在Ground Truth中消失,或者预测的边界框和目标之间的IoU低于0.5的阈值,则去除终止目标的隐藏状态。为了进行推理,我们使用预测的分类分数score来确定新生目标的出现和被跟踪目标的消失。对于目标查询,保留分类分数高于阈值的预测,同时删除其他隐藏状态。-
对应代码:
def forward(
self,
track_instances,
fake_track_instance,
seq_data=None,
frame_id=None,
seq_frame_id=None,
seq_name=None,
):
keep_idxs = track_instances.mask_query == 1
track_instances = select_instances(track_instances, keep_idxs)
if not self.training:
self.track_base.update(track_instances)
#滤除iou<0.5的目标的隐藏状态
active_track_instances = self._select_active_tracks(track_instances)
#预测的分类分数score来确定新生目标的出现和被跟踪目标的消失
if len(active_track_instances.scores) > self.max_track:
keep_idxs = active_track_instances.scores.topk(self.max_track)[1]
active_track_instances = select_instances(
active_track_instances, keep_idxs
)
padding_track_instance, padd_len = padding_tracks(
active_track_instances, fake_track_instance
)
...
return padding_track_instance, padd_len, frame_outs
对于跟踪查询,删除连续5帧的分类分数低于阈值的预测,同时保留其他隐藏状态,对应代码如下所示:
class RuntimeTrackerBase(object):
def __init__(
self, score_thresh=0.7, filter_score_thresh=0.6, miss_tolerance=5
):
self.score_thresh = score_thresh
self.filter_score_thresh = filter_score_thresh
self.miss_tolerance = miss_tolerance
self.max_obj_id = 0
...
for i in range(len(track_instances.scores)):
if (
track_instances.obj_idxes[i] == -1
and track_instances.scores[i] >= self.score_thresh
):
track_instances.obj_idxes[i] = self.max_obj_id
self.max_obj_id += 1
elif (
track_instances.obj_idxes[i] >= 0
and track_instances.scores[i] < self.filter_score_thresh
):#删除连续5帧中分类score小于0.6的跟踪query
track_instances.disappear_time[i] += 1
if track_instances.disappear_time[i] >= self.miss_tolerance:
# Set the obj_id to -1.
# Then this track will be removed by TrackEmbeddingLayer.
track_instances.obj_idxes[i] = -1
代码路径:/usr/local/lib/python3.8/dist-packages/hat/mode/task_modules/motr/post_process.py
-
/usr/local/lib/python3.8/dist-packages/hat/mode/task_modules/motr/motr_utils.py
时间聚合网络
MOTR在QIM中引入了时间聚合网络(TAN),以增强时间关系建模,并为被跟踪目标提供上下文先验。如上图所示,TAN的输入是被跟踪目标(目标“1”)的filter隐藏状态,还收集了来自最后一帧的跟踪查询,用于时间聚合。TAN是一个改进的Transformer解码器层。最后一帧的跟踪查询和filter后的隐藏状态相加,成为多头自注意力的key和query。-
相关代码如下所示:
def forward(self, query_pos_all, output_embedding, query_mask):
bs = 1
out_embed = self.embed_quant(output_embedding)
query_pos_all = self.query_pos_quant(query_pos_all)
query_mask = self.query_mask_quant(query_mask)
query_pos = (
query_pos_all.permute(0, 1, 3, 2)
.contiguous()
.view(bs, query_pos_all.shape[3], 1, query_pos_all.shape[2])
)
out_embed = (
out_embed.permute(0, 1, 3, 2)
.contiguous()
.view(bs, out_embed.shape[3], 1, out_embed.shape[2])
)
tgt_mask1 = query_mask.reshape(bs, 1, 1, query_mask.shape[3])
tgt_mask2 = query_mask.reshape(bs, 1, query_mask.shape[3], 1)
tgt_mask = self.mask_matmul.matmul(tgt_mask2, tgt_mask1)
#相当于masked_fill
tgt_mask = self.mask_mul_scalar.mul_scalar(tgt_mask, 100)
tgt_mask = self.mask_add_scalar.add_scalar(tgt_mask, -100)
tgt_mask = tgt_mask.squeeze(1)
q = k = self.add_q_out.add(query_pos, out_embed)
#TAN模块
tgt = out_embed
tgt2 = self.self_attn(
q,
k,
value=tgt,
attn_mask=tgt_mask,
)[0]
tgt = self.add_tgt_tgt2.add(tgt, self.dropout1(tgt2))
tgt = self.norm1(tgt)
tgt2 = self.linear2(self.dropout(self.activation1(self.linear1(tgt))))
tgt = self.add_tgt_tgt2_1.add(tgt, self.dropout2(tgt2))
tgt = self.norm2(tgt)
query_pos2 = self.linear_pos2(
self.dropout_pos1(self.activation2(self.linear_pos1(tgt)))
)
query_pos = self.add_pos_pos2.add(
query_pos, self.dropout_pos2(query_pos2)
)
#LayerNorm
query_pos = self.norm_pos(query_pos)
output = self.dequant(query_pos)
return output
代码路径:/usr/local/lib/python3.8/dist-packages/hat/models/task_modules/motr/qim.py
3 浮点模型训练-
3.1 Before Start-
3.1.1 发布物及环境部署-
step1:获取发布物-
下载OE包horizon_j5_open_explorer_v$version$.tar.gz
,获取方式见地平线开发者社区 OpenExplorer算法工具链 版本发布-
step2:解压发布包
tar -xzvf horizon_j5_open_explorer_v$version$.tar.gz
解压后文件结构如下:
|-- bsp
|-- ddk
| |-- package
| `-- samples
| |-- ai_benchmark
| |-- ai_forward_view_sample
| |-- ai_toolchain
| | |-- ...
| | |-- horizon_model_train_sample
| | `-- model_zoo
| |-- model_zoo
| `-- vdsp_rpc_sample
|-- README-CN
|-- README-EN
|-- resolve_all.sh
`-- run_docker.sh
其中horizon_model_train_sample为
参考算法模块,包含以下目录:
|-- horizon_model_train_sample #参考算法示例
| |-- plugin_basic #qat 基础示例
| `-- scripts #模型配置文件、运行脚本
step3:拉取docker环境
docker pull openexplorer/ai_toolchain_ubuntu_20_j5_gpu:v$version$
#启动容器,具体参数可根据实际需求配置
#-v 用于将本地的路径挂载到 docker 路径下
nvidia-docker run -it --shm-size="15g" -v `pwd`:/WORKSPACE openexplorer/ai_toolchain_ubuntu_20_j5_gpu:v$version$
release_models获取路径见:horizon_model_train_sample/scripts/configs/track_pred/README.md
3.1.2 数据集准备-
3.1.2.1 数据集下载-
进入MOT17DATASET (https://motchallenge.net/)官网,下载MOT17数据集以及相应的标注数据 ,然后将其解压,解压后的文件夹如图所示:
MOT17
|-- train
|-- MOT17-02-DPM
|-- det
|-- det.txt
|-- gt
|-- gt.txt
|-- img1
|-- 000001.jpg
|-- 000002.jpg
|-- ...
|-- seqinfo.ini
|-- MOT17-02-FRCNN
|-- ...
|-- test
|-- MOT17-01-DPM
|-- ...
3.1.2.2 数据集拆分和打包-
由于官方的测试集无gt,因此我们将训练集拆成一半,每个视频前一半帧作为训练集,后一半帧作为验证集。 OE开发包中提供了脚本将训练集拆分,只需要运行下面的脚本:
#进入到OE开发包的scripts文件夹
cd $workspace$/horizon_model_train_sample/scripts
#运行数据集拆分脚本
python3 tools/dataset_converters/gen_mot_data.py --src-data-path ./MOT17/train --out-dir ./MOT17/split_data
--src-data-path为解压后的原始数据集路径;–out-dir为拆分后的数据集存储路径。
运行完上面脚本后,将会得到如下结构的文件夹:
MOT17
|-- train
|-- test
|-- split_data#拆分后的数据集文件夹
|-- train #训练集
|-- MOT17-02-SDP
|-- gt
|-- gt.txt
|-- labels_with_ids
|-- 000001.txt
|-- 000002.txt
|-- img1
|-- 000001.jpg
|-- 000002.jpg
|-- ...
|-- MOT17-04-SDP
|-- MOT17-05-SDP
|-- MOT17-09-SDP
|-- MOT17-10-SDP
|-- MOT17-11-SDP
|-- MOT17-13-SDP
|-- test #测试集
|-- MOT17-02-SDP
|-- gt
|-- gt.txt
|-- labels_with_ids
|-- 000301.txt
|-- 000302.txt
|-- img1
|-- 000301.jpg
|-- 000302.jpg
|-- ...
|-- MOT17-04-SDP
|-- MOT17-05-SDP
|-- MOT17-09-SDP
|-- MOT17-10-SDP
|-- MOT17-11-SDP
|-- MOT17-13-SDP
然后使用以下命令将训练数据集和验证数据集打包,格式为lmdb:
#pack train_Set
python3 tools/datasets/mot_packer.py --src-data-dir ./MOT17/split_data/ --pack-type lmdb --target-data-dir ./tmp_data/mot17 --split-name train
#pack test_Set
python3 tools/datasets/mot_packer.py --src-data-dir ./MOT17/split_data/ --pack-type lmdb --target-data-dir ./tmp_data/mot17 --split-name test
--src-data-dir为拆分后后的mot17数据集目录;-
--target-data-dir为打包后数据集的存储目录;-
验证精度时,需要用到验证数据集的标签数据,因此我们做一个软连接,如下所示:
ln -s ./MOT17/split_data/test/ ./tmp_data/mot17/test_gt
打包和软连接完成之后, ${target-data-dir} 目录下的文件结构如下所示:
tmp_data
|-- mot17
|-- train_lmdb #训练集
|-- test_lmdb #验证集
|-- test_gt #验证集的grountruth
train_lmdb和test_lmdb就是打包之后的训练数据集和测试数据集,也是网络最终读取的数据集。
3.1.3 config文件配置
在进行模型训练和验证之前,需要对OE包中horizon_model_train_sample/scripts/configs/track_pred/motr_efficientnetb3_mot17.py
文件中的参数进行配置,一般情况下,我们需要配置以下参数:
- ckpt_dir :浮点、calib、量化训练的权重路径配置,权重下载链接在config文件夹下的README中;
- device_ids 、train_batch_size_per_gpu 、test_batch_size_per_gpu :根据实际硬件配置进行device_ids和每个gpu的batchsize的配置;
- train_lmdb: 3.1.2.2中打包的训练集路径配置;
- val_lmdb : 3.1.2.2中打包的验证集路径配置;
- val_gt : 3.1.2.2中生成的test_gt路径;
- float_trainer下的checkpoint_path:浮点训练时backbone的预训练权重路径。
3.2 浮点模型训练
configs/track_pred/motr_efficientnetb3_mot17.py文件中的参数配置完成后,使用以下命令训练浮点模型:
python3 tools/train.py --config configs/track_pred/motr_efficientnetb3_mot17.py --stage float
float训练后模型ckpt的保存路径为config配置的ckpt_callback中save_dir的值,默认为ckpt_dir。
3.3 浮点模型精度验证
浮点模型训练完成以后,可以使用以下命令验证已经训练好的浮点模型精度:
python3 tools/predict.py --config configs/track_pred/motr_efficientnetb3_mot17.py --stage float
4. 模型量化和编译
完成浮点训练后,还需要进行量化训练和编译,才能将定点模型部署到板端。地平线对该模型的量化采用horizon_plugin框架,经过Calibration+QAT量化训练后,使用compile
的工具将量化模型编译成可以上板运行的hbm
文件。
4.1 Calibration
模型完成浮点训练后,便可进行 Calibration。calibration在forward过程中通过统计各处的数据分布情况,从而计算出合理的量化参数。 通过运行下面的脚本就可以开启模型的Calibration过程:
python3 tools/train.py --config configs/track_pred/motr_efficientnetb3_mot17.py --stage calibration
4.2 Calibration 模型精度验证
Calibration完成以后,可以使用以下命令验证经过calib后模型的精度:
python3 tools/predict.py --config configs/track_pred/motr_efficientnetb3_mot17.py --stage calibration
模型经过 Calibration 后的量化精度若已满足要求,便可直接进行转定点模型的步骤,否则需要进行量化训练进一步提升精度。
4.3 量化训练
MOTR经过 Calibration 后的量化精度未能满足要求,所以需要使用以下命令进行量化训练:
python3 tools/train.py --config configs/track_pred/motr_efficientnetb3_mot17.py --stage qat
4.4 qat模型精度验证
量化训练完成后,通过运行以下命令验证qat模型的精度:
python3 tools/predict.py --config configs/track_pred/motr_efficientnetb3_mot17.py --stage qat
4.5 量化模型精度验证
通过运行以下命令验证量化模型的精度:
python3 tools/predict.py --config configs/track_pred/motr_efficientnetb3_mot17.py --stage int_infer
4.6 仿真上板精度验证
除了上述模型验证之外,我们还提供和上板完全一致的精度验证方法,可以通过下面的方式完成:
python3 tools/align_bpu_validation.py --config configs/track_pred/motr_efficientnetb3_mot17.py
4.7 量化模型编译
在量化训练完成之后,可以使用compile_perf.py
脚本将量化模型编译成可以板端运行的hbm
模型,同时该工具也能预估模型在BPU上的运行性能。由于MOTR的decoder输出需要经过一些后处理操作才能输入到QIM,而且这些后处理操作只能在CPU上运行,所以在J5上部署时是分为2段模型,编译时需要运行两次compile_perf脚本:
#编译第一段模型
python3 tools/compile_perf.py --config configs/track_pred/motr_efficientnetb3_mot17.py --out-dir ./compile_motr --opt 3
#编译第二段模型
python3 tools/compile_perf.py --config configs/track_pred/motr_efficientnetb3_mot17_qim.py --out-dir ./compile_qim --opt 3
- opt为优化等级,取值范围为0~3,数字越大优化等级越高,编译时间更长,但部署性能更好;
- compile_perf脚本将生成.html文件和.hbm文件(out-dir目录下),.html文件为BPU上的运行性能,.hbm文件为上板实测文件;
- 两次编译的–out_dir最好不要设置为同一个文件夹,不然会出现编译产出物被覆盖的情况。
运行后,out-dir的目录下会产出以下文件:
|-- compile_motr
| |-- motr.html #模型在bpu上的静态性能数据
| |-- motr.json
| |-- model.hbm #板端部署的模型1
| |-- model.hbir #编译过程的中间文件
`-- model.pt #模型的pt文件
|-- compile_qim
| |-- qim.html #模型在bpu上的静态性能数据
| |-- qim.json
| |-- model.hbm #板端部署的模型2
| |-- model.hbir #编译过程的中间文件
`-- model.pt #模型的pt文件
5.其他工具
5.1 结果可视化
如果您想查看知道图像序列的多目标跟踪结果,tools目录下提供了多目标跟踪和可视化的脚本infer.py, 脚本运行方式如下:
python3 tools/infer.py --config configs/track_pred/motr_efficientnetb3_mot17.py --save-path ./
- --save-path:可视化结果保存路径;
- 通过修改config文件中infer_cfg字段中的imagedir和num参数来配置图像序列根目录和图像帧数量。
可视化示例:
6.板端部署
本节将介绍hbm模型编译完成后,在板端使用dnn工具进行性能评测和运行AI-Benchmark示例进行性能和精度评测的流程。
6.1 上板性能实测
模型编译成功后,可以在板端使用hrt_model_exec perf工具评测hbm模型的FPS,参考命令如下:
hrt_model_exec perf --model_file {model}.hbm \
--thread_num 8 \
--frame_count 1000 \
--core_id 0 \
--profile_path '.'
命令运行结束后,会在本地会产出profile.log和profile.csv日志文件,用以分析算子耗时和调度耗时。
6.2 AI Benchmark示例
OE开发包中提供了MOTR的AI Benchmark示例,用以在板端进行性能和精度的评测,ddk/samples/ai_benchmark/j5/qat/script/tracking/motr 目录下提供了三个评测脚本:
-
fps.sh:实现多线程fps统计(多线程调度,用户可以根据需求自由设置线程数);
-
latency.sh:实现单帧延迟性能统计,包含模型推理和后处理延迟(单线程,单帧);
-
accuracy.sh:用于精度评测。-
AI Benchmark示例需要使用交叉编译工具编译后,然后将文件夹传输到板端运行,建议在docker中进行交叉编译流程。执行ddk/samples/ai_benchmark/code/ 目录下的build_qat_j5.sh脚本即可一键编译真机环境下的可执行程序:sh build_qat_j5.sh
编译完成后,将ai_benchmark/j5/qat文件夹拷贝至板端:
scp -r ddk/samples/ai_benchmark/j5/qat root@{board_ip}:/userdata/
然后在板端执行qat/script/tracking/motr文件夹下的评测脚本。进入到需要评测的模型目录下,运行latency.sh 即可测试出单帧延迟, 如下所示:
sh latency.sh
终端输出的延迟包含模型推理耗时(Infer latency)和后处理耗时(Post process)。
运行fps.sh 即可测试出双核多线程fps, 如下所示:
sh fps.sh
如果要进行精度评测,请参考开发者社区J5算法工具链产品手册进行数据的准备和模型的推理。