CenterPoint 部署攻略二(pth2onnx 基于mmengine)(动态多输入、多模型、校准验证)

简述:

系列二,主要就是 pth2onnx 的脚本及注释,本篇不涉及自定义算子,因此也是其他模型的通用模板!

针对 centerpoint 的构造(centerpoint_pillar02_second_secfpn_8xb4-cyclic-20e_nus-3d),即:Det3DDataPreprocessor -> PillarFeatureNet -> PointPillarsScatter -> SECOND -> SECONDFPN -> CenterHead;

其中preprocessdata, pps不涉及参数的传递,构造onnx的时候把他们排除,所以是 PFN 和 SECOND...CenterHead 两个模型的游戏。

细节:

1. 加载模型:

import time
import torch
import onnx
import numpy as np
import onnxruntime as ort
from mmengine.config import Config
from mmdet3d.registry import MODELS
from onnxsim import simplify
from mmengine.model import BaseModule


cfg = Config.fromfile(cfg_path)
model = MODELS.build(cfg.model)
loaded_ckpt = torch.load(ckpt_path, map_location='cpu')
model.load_state_dict(loaded_ckpt['state_dict'], strict=True)
model.eval()
model = model.cuda()

“cfg_path” 换成你自己的config,load_state_dict 中 strict 的意思是你的 pth 保存的权重(名称)是否和 config 中搭建的模型保持严格一致;

注意:model.eval() 一定要加!

至此,得到了一个含权重的“总”模型。

按上述所言,需要提取出两个阶段的单独的模型,因为 PFN 直接通过 model.pts_voxel_encoder 就可以搭建(偷懒了),所以考虑第二阶段的模型构造:

class Stage2Model(BaseModule):
    def __init__(self, model):
        super(Stage2Model, self).__init__()
        self.backbone = model.pts_backbone
        self.neck = model.pts_neck
        self.head = model.pts_bbox_head

    def forward(self, x):
        features = self.backbone(x)
        features_fpn = self.neck(features)
        output = self.head(features_fpn)
        return output

modified_model = Stage2Model(model)
modified_model.eval()

再说一遍,model.eval() 很重要!

2. onnx转换:

首先指定输入:

device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
input1_features = torch.ones((max_pillars, 20, 6)).to(device)
input1_numPoints = torch.ones((max_pillars)).to(device)
input1_coors = torch.ones((max_pillars, 4)).to(device)

 在这里是动态多输入:先构建张量,代码中的 max_pillars 是常数,然后:

with torch.no_grad():
    torch.onnx.export(model.pts_voxel_encoder,
                      (input1_features, input1_numPoints, input1_coors),
                       stage1_onnx_save_path,
                       opset_version=11,
                       do_constant_folding=True,	
                       export_params=True, 
                       verbose=False, 
                       input_names=['PFN_features', 'num_points', 'coors'],
                       output_names=['output_1_0'],                      
                       dynamic_axes={'PFN_features':{0:'effective_pillars'}, 
                                     'num_points':{0:'effective_pillars'}, 
                                     'coors':{0:'effective_pillars'}})

stage1_onnx = onnx.load(stage1_onnx_save_path)
onnx.checker.check_model(stage1_onnx)
print("Stage1 onnx model is OK!")

do_constant_folding 字段用于控制是否执行常量折叠优化;

dynamic_axes 字段用于批处理, 指定 input 和 output 的 batch(或其他维度) 可变,若不想支持批处理或固定批处理大小,移除 dynamic_axes 字段即可;

check 无报错且则 可视化Netron 模型结构符合你的预期,则 onnx生成本身无问题

然后是阶段二的模型转换:

with torch.no_grad():
    torch.onnx.export(modified_model,
                    input2,
                    stage2_onnx_save_path,
                    opset_version=10,
                    do_constant_folding=True,
                    export_params=True, 
                    verbose=False, 
                    input_names=['features'],
                    output_names=['output_2_0', 'output_2_1', 'output_2_2',
                                    'output_2_3', 'output_2_4', 'output_2_5', 
                                    'output_2_6', 'output_2_7', 'output_2_8', 
                                    'output_2_9', 'output_2_10', 'output_2_11', 
                                    'output_2_12', 'output_2_13', 'output_2_14', 
                                    'output_2_15', 'output_2_16', 'output_2_17'])

model_simp2, ok2 = simplify(stage2_onnx)
assert ok2, "Simplified ONNX model could not be validated"
onnx.save(model_simp2, onnx_res_path + "stage2_test_simplifier.onnx")
print('Finished exporting simplify stage2_onnx!')

没什么好说的,输出多就多写几个;

可以选择使用 onnxsim 工具(上一篇已安装);

3. Onnxruntime 推理及对比 pytorch 输出:

从代码的角度来讲,推理比较容易,都封装好了(以stage1为例):

def to_numpy(exmpletensor):
    return exmpletensor.detach().cpu().numpy() if exmpletensor.requires_grad else exmpletensor.cpu().numpy()

ort_session1 = ort.InferenceSession(stage1_onnx_save_path, providers=providers)
stage1_ort_outputs = ort_session1.run(None,{'PFN_features' : to_numpy(input1_features),'num_points': to_numpy(input1_numPoints) ,'coors': to_numpy(input1_coors)})
stage1_torch_out = model.pts_voxel_encoder(input1_features, input1_numPoints, input1_coors)

得到了 onnx 侧及 pytorch 侧的输出;stage2 直接把 model 那块换成你的 modified_model;

这里留下一个疑问,希望有朋友不吝赐教:此处的 session.run 是包含数据 GPU -> CPU -> 选择推理(GPU) -> CPU? 如果想计算 纯粹GPU 推理时间该怎么弄?

对比 pytorch 输出:

np.testing.assert_allclose(to_numpy(stage1_torch_out), stage1_ort_outputs[0], rtol=1e-03, atol=1e-05)
print("Exported model1 has been tested with ONNXRuntime, and the result looks good!")

rtol 相对误差容忍度,atol 绝对误差容忍度; 如果两个元素之间的差值 <= atol + rtol * abs(stage1_ort_outputs[0]),则可接受;

4. 对比测速:

本质就是for循环多次执行推理过程,然后取平均(或者能保证GPU、CPU充分 warm-up 前提下执行一次也就差不多了):

if test_fps:
    dummy_features, dummy_numPoints, dummy_coors = to_numpy(input1_features), to_numpy(input1_numPoints), to_numpy(input1_coors)
    
    t0 = time.perf_counter()
    for _ in range(n):
        stage1_ort_outputs = ort_session1.run(None,{'PFN_features' : dummy_features,'num_points':  dummy_numPoints,'coors': dummy_coors})
    t1 = time.perf_counter()
    print("用 onnx 完成推理1消耗的时间:%f fps:%f" % ((t1-t0)/n, n/(t1-t0)))

    t2 = time.perf_counter()
    for _ in range(n):
        stage1_torch_out = model.pts_voxel_encoder(input1_features, input1_numPoints, input1_coors)
    torch.cuda.synchronize()
    t3 = time.perf_counter()
    print("用 pytorch 完成推理1消耗的时间:%f fps:%f" % ((t3-t2)/n, n/(t3-t2)))

time.perf_counter() 非系统时钟,更加准确;

5. 结果:

pytorch: time: 0.018765, fps:53.290058;

onnruntime: time: 0.016044, fps:62.330253;

个人感觉 Onnxruntime 推理这里有一些问题,矛盾点就是上面提到的数据在GPU CPU之间的搬运;有清楚的还请私信,感激不尽!

<think>好的,用户现在想了解CenterNet的使用方法或介绍。我需要先回顾之前的对话历史,确保回答的一致性和相关性。用户之前问过如何在nuScenes数据集上运行CenterNet,我给出了基于MMDetection3D框架的详细步骤。现在用户进一步询问CenterNet的相关信息或使用方法,并提到了引用中的热图赋值方法。 首先,用户可能对CenterNet的基本原理不太清楚,需要简明扼要的介绍。CenterNet作为Anchor-free的目标检测模型,其核心在于将目标检测转化为关键点检测,这点需要强调。然后,用户提到的热图生成是关键部分,引用中的代码片段展示了如何通过高斯分布生成热图,这部分需要详细解释,但要注意用中文和LaTeX格式的数学表达式。 接下来,用户可能需要具体的使用方法,包括环境配置、数据准备、模型定义、训练和推理步骤。之前回答中已经涉及MMDetection3D的使用,但这里可能需要更通用的步骤,而不仅仅是针对nuScenes数据集。同时,用户可能对热图生成的具体实现有疑问,特别是代码中共享内存赋值的方式,需要进一步解释,但要注意用户可能不熟悉底层实现,所以需要简化说明。 此外,用户的问题可能隐含了对CenterNet优缺点的兴趣,比如与其他模型(如YOLO、Faster R-CNN)的比较,或者实际应用中的注意事项,比如如何调整高斯半径、处理类别不平衡等。这些问题可能在相关部分提及,并在最后的问题推荐中提出,以引导用户进一步探索。 需要确保回答结构清晰,分点说明,符合用户要求的帮助逐步解决问题的结构。同时,引用中的热图赋值方法需要自然地融入解释中,并正确标注引用标识。最后生成的相关问题需要与CenterNet的核心概念和应用相关,帮助用户深入理解。</think>以下是关于CenterNet的核心原理与使用方法的详细说明: ### 一、CenterNet核心原理 CenterNet是一种Anchor-free目标检测模型,将目标检测问题转化为关键点检测任务。其核心公式可表示为: $$ \hat{Y}_{x,y,c} = \exp\left(-\frac{(x-\tilde{x}_c)^2 + (y-\tilde{y}_c)^2}{2σ_c^2}\right) $$ 其中$(x,y)$是热图坐标,$(\tilde{x}_c,\tilde{y}_c)$是目标中心点,$σ_c$为高斯半径[^1] ### 、关键组件解析 1. **热图生成** ```python # 高斯分布生成示例(来自引用[1]) radius = gaussian_radius((h, w)) # 计算高斯半径 draw_gaussian(hm[cls_id], ct, radius) # 在热图上绘制高斯核 ``` * 使用共享内存直接操作热图矩阵 * 高斯半径计算公式:$σ = \frac{r}{\sqrt{3}}$(r为目标框尺度相关值) 2. **输出头结构** - 热图分支:预测目标中心点 - 偏移分支:补偿下采样误差 $\hat{O} ∈ [0,1]^{W/R×H/R×2}$ - 尺寸分支:预测目标宽高 $\hat{S} ∈ R^{W/R×H/R×2}$ ### 三、基础使用方法(PyTorch版) 1. **环境配置** ```bash conda install pytorch=1.7 torchvision cudatoolkit=11.0 pip install -U openmim mim install mmdet ``` 2. **模型定义** ```python from mmdet.models import CenterNet model = CenterNet( backbone=dict(type='HourglassNet', depth=52), neck=dict(type='CTResNetNeck', in_channel=384, num_deconv_filters=(256, 128, 64)), bbox_head=dict( type='CenterNetHead', num_classes=80, in_channel=64, feat_channel=64)) ``` 3. **训练配置** ```python # 数据增强配置 train_pipeline = [ dict(type='LoadImageFromFile'), dict(type='LoadAnnotations', with_bbox=True), dict(type='Resize', img_scale=(512, 512), keep_ratio=True), dict(type='RandomFlip', flip_ratio=0.5), dict(type='Normalize', mean=[123.675, 116.28, 103.53], std=[58.395, 57.12, 57.375]), dict(type='Pad', size_divisor=32), dict(type='DefaultFormatBundle'), dict(type='Collect', keys=['img', 'gt_bboxes', 'gt_labels']) ] ``` ### 四、高级技巧 1. **热图优化策略** - 动态高斯半径调整:根据目标尺寸自动计算 ```python def gaussian_radius(det_size, min_overlap=0.7): height, width = det_size a1 = 1 b1 = (height + width) c1 = width * height * (1 - min_overlap) / (1 + min_overlap) sq1 = sqrt(b1 ** 2 - 4 * a1 * c1) r1 = (b1 - sq1) / (2 * a1) return r1 ``` - 热图加权损失:$L_{hm} = -\frac{1}{N}\sum_{xyc}(1-\hat{Y}_{xyc})^α\log(\hat{Y}_{xyc})$[^1] 2. **推理加速方法** - 使用DCNv2可变形卷积提升特征提取效率 - 采用TensorRT进行模型量化: ```bash trtexec --onnx=centernet.onnx --fp16 --workspace=4096 ``` ### 五、性能对比 | 模型变体 | AP@0.5 | FPS(T4) | 参数量 | |----------------|--------|---------|-------| | ResNet-18 | 28.1 | 142 | 15.1M | | Hourglass-104 | 45.1 | 45 | 210M | | DLA-34 | 39.3 | 76 | 20.1M | ### 六、典型应用场景 1. **密集场景检测**:热图机制避免NMS后处理 2. **3D目标检测**:扩展为CenterPoint(支持激光雷达点云) 3. **姿态估计**:通过关键点热图扩展
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值