前面做征程6相关实验时,我反复碰到一个问题:
“某一步成功了,到底能说明什么?”
比如:
ONNX 能正常打开,是不是就说明它能部署到 J6?
PTQ 跑完,是不是就说明定点精度没问题?
- .bc 生成了,是不是就说明 HBM 一定能编译?
- .hbm 已经有了,是不是就等于“部署完成”?
- hbm_perf 有 latency,是不是就能直接写板端性能?
- hbm_infer 跑出了结果,是不是就代表业务链路已经验收?
这些问题单独看都很简单。
但把整条链连起来以后,很容易发生一种情况:
上一步确实成功了,但结论却跨了两三级。
所以这次 Exp09,我没有继续做 Agent Benchmark,而是把征程6模型部署过程本身拆了一遍。
我想回答的是:
从 Float ONNX 到 HBM,再到真正的 J6 板端结果,每一种产物究竟能证明什么,又还不能证明什么?
一、先把整条产物链画出来
如果把不同工具入口的细节先抽象掉,一条典型的征程6模型部署逻辑链大致可以写成:
↓
PTQ / Quantized Model
↓
HBIR / BC
↓
HBM
↓
Model Metadata / Runtime Contract
↓
J6 Target Inference
↓
Accuracy / Performance Verification
↓
Business E2E Validation
需要先说明:
ONNX / Caffe
→ Float HBIR .bc
→ Quantized HBIR .bc
→ HBM .hbm
而当前 OE-Skills 的另一条部署路线,也可能先得到 PTQ ONNX,再交给 HBDK 编译。
所以这里真正重要的不是文件后缀排列,而是:
每一次转换以后,我们新增了哪一级证据?

图1|征程6模型部署的逻辑产物链。每向右走一步,证据等级更高,但前一步的成功不能直接替代后一步验证。
二、第一层:Float ONNX 只说明“我有一个模型”
最前面通常是一个浮点 ONNX。
这一阶段最容易被高估。
如果我手里只有:
最多首先说明:
我已经有一个可以被 ONNX 工具解析的模型产物。
通过:
还可以查看:
opset;
输入输出名称;
shape;
data type。
但注意:
“ONNX 文件存在”本身并不能证明:
ONNX Runtime 推理结果正确;
算子全部适配 J6;
PTQ 精度可接受;
能成功生成 HBM;
板端能运行。
哪怕我真的用 ONNX Runtime 跑了一帧,也还要区分:
“能运行”
和:
“输出与参考结果一致”。
如果没有 Gold 或参考输出做数值比较,就不能仅凭“程序没报错”写成“模型数值正确”。
三、第二层:PTQ 成功,也不等于部署成功
下一步通常进入 PTQ。
这个阶段解决的是:
怎样把浮点网络转换到适合 BPU 的量化表示。
可能会涉及:
校准数据;
Activation 量化;
Per-Tensor / Per-Channel;
混合精度;
节点配置;
精度调优。
如果 PTQ 成功,我们至少可以说:
已经生成了对应的量化模型产物。
但这里仍然有明显边界。
PTQ 成功并不能自动证明:
最终 HBM 编译一定成功。
更不能说明:
真实板端结果已经正确。
甚至:
“量化流程执行完成”也不等于“量化精度已经达到项目要求”。
精度是不是能接受,还需要真正的对比。
所以如果只完成 PTQ,我觉得最合适的工程表述是:
“已完成量化转换,待进一步进行定点精度与编译验证。”
而不是:
“模型已经适配 J6。”
四、第三层:HBIR / BC 是什么位置?
这里其实很容易让刚接触工具链的人迷糊。
在 HBDK4 体系中,它承载的是 HBIR / MLIR Bytecode 形式的中间表示。
HBDK4 资料本身也把流程描述为:
ONNX / Torch
→ HBIR
→ 后端 IR
→ .hbo
→ .hbm
为什么这个阶段重要?
因为它已经不只是“原始神经网络”。
它开始进入 Horizon 编译器内部真正能够继续优化、Lowering 和后端编译的表示。
因此得到一个合法 BC,意味着:
模型已经推进到了 Horizon 编译 IR 阶段。
但同样不能直接推出:
“板端可部署。”
原因很简单:
后面还有:
后端转换;
march;
编译;
链接;
Runtime;
Tensor 契约;
板端环境。
五、第四层:HBM——最容易被误读的一步
接下来终于到了最容易引起误会的东西:
HBM 的全称是:
Horizon BPU Model
它是面向 Horizon BPU 的模型产物。
如果 HBM 已经成功生成,我认为我们可以比较稳地说:
针对指定 Horizon BPU march 的模型编译已经完成,并得到了可供后续 Runtime 使用的 HBM 产物。
这是一个很重要的里程碑。
但是:
HBM ≠ 部署完成。
这是这篇文章最想强调的一句话。
为什么?
目标板能够成功加载;
实际输入格式完全匹配;
Tensor 内存布局使用正确;
真实输入可以推理;
输出是对的;
板端性能满足要求;
业务链路满足 P95 / P99;
长时间运行稳定。
所以如果有人说:
“HBM 已经生成,所以 J6 部署已经成功。”
严格来说其实跨级了。
更合适的是:
“HBM 编译产物已生成,下一步进入 Runtime / 板端验证。”
六、HBM 本身还能检查什么?
虽然 HBM 不能证明部署完成,但它本身仍然包含很多有价值的信息。
例如可以使用:
查看:
模型依赖;
HBDK 版本;
march;
compiler parameters;
输入输出;
内存信息。
march;
toolkit version;
graphs;
inputs;
outputs;
nodes。
所以这个阶段已经非常适合做:
模型静态检查。
例如:
march 是否正确;
Graph 是否存在;
I/O 契约;
memory requirement;
是否有 CPU 节点;
编译参数是否符合预期。
但仍然要记住:
静态 Metadata 证明不了真实板端运行。
七、Tensor Contract:HBM 和业务代码真正接上的地方
到了 Runtime,模型就不再只是一个文件。
业务程序需要真正面对 Tensor。
这时候很多部署 Bug 都来自:
模型契约和业务代码的理解不一致。
validShape
tensorType
scale
quantiType
quantizeAxis
alignedByteSize
stride
其中一个很关键的原则是:
分配输入输出内存时应以 alignedByteSize 为准。
这意味着:
逻辑上看到:
并不能直接推出:
内存就是 1×3×224×224 个元素连续排完。
真实 Runtime 还可能涉及:
stride;
padding;
alignment;
quantization;
input format。
所以 HBM 生成以后,下一层真正应该确认的是:
Runtime Contract 是否被正确理解。
这一步如果错了,后面的推理代码写得再漂亮也没有意义。
八、第五层:模型“能推理”到底证明了什么?
假设我们终于把 HBM 放到 J6 Target 上。
通过 UCP C++ 或:
成功完成了一次推理。
这一步证据已经明显提高。
至少现在可以说:
模型在目标 Runtime 环境中能够加载、执行并产生输出。
我把这一层叫:
推理连通性。
注意,我故意没有叫:
“正确性”。
因为:
能输出 ≠ 输出正确。
程序没有崩溃,只能说明:
模型能加载;
输入被接受;
Runtime 完成执行;
有输出产生。
如果要说“输出正确”,还需要继续做数值对比。
九、hb_verifier 才真正进入“结果一致性”
它的定位不是性能分析,而是:
模型精度 / 输出一致性对比工具。
它支持:
SIM;
ARM;
ONNX;
BC;
HBM;
并对输出 Tensor 做一致性比较。
所以假设:
和:
都对同一组输入产生了结果,然后通过 verifier 比较,这时候我们才能开始说:
“该输入上的定点/板端输出与参考模型保持一定程度一致。”
仍然要注意:
单帧或少量 Case 的一致性,也不能自动推出:
整个验证集精度完全满足业务要求。
所以我会把:
“单帧一致性”
和:
“数据集级 Accuracy / mAP 验证”
继续分开。
十、性能也有两层:Static 和 Dynamic
性能这里也是最容易跨级的地方。
例如:
可以做静态性能分析。
这种模式属于:
静态 perf,编译期估算值。
Static hbm_perf
更适合回答:
哪些层耗时高;
DDR Load / Store;
算力利用率;
静态 FPS / latency;
模型内存。
这属于:
模型级静态性能证据。
不能写成:
“真实 J6 实测 latency”。
Dynamic hbm_perf
如果给:
这时候证据等级已经升级成:
真实硬件上的模型级性能测量。
但即使如此,也仍然不能直接等于:
业务端到端 P95。
因为真实业务链路可能还有:
Camera;
Resize;
Color Convert;
Preprocess;
RPC / Queue;
Postprocess;
NMS;
Tracking;
多模型调度;
CPU / DDR 竞争。
所以:
模型 latency ≠ Business E2E latency。
十一、hbm_infer 的 Profile 又属于哪一层?
它本质上是:
x86 侧 Python Client,通过 gRPC / SSH 连接 BPU Board,部署并运行 HBM。
当启用:
后,可以获取:
frame_duration
sd2rv_duration
commu_duration
board_duration
infer_duration
prepr_duration
pospr_duration
板端纯推理阶段耗时
单位为 ms。
这比“完全没有板卡”的静态估计强很多。
但仍然要分清:
infer_duration
只是 Profile 中的一个阶段。
它不等于:
整个链路的 P95。
所以文章里如果写:
“J6模型推理耗时”
可以。
如果直接写:
“客户业务端到端耗时”
就越界了。
十二、原生 UCP C++:真正进入应用工程
再往后就是实际的 UCP C++ 程序。
一个基本完整的异步流程大致包括:
但仍然不能只凭:
“UCP Demo 跑了一帧”
就说:
“产品已经验收。”
因为此时通常还缺:
数据集级精度;
异常输入;
多模型竞争;
长时间稳定性;
真实业务 E2E;
资源占用;
功耗;
内存;
DDR;
P95 / P99。
十三、监控数据和模型性能也要分开
如果真正进入板端性能分析,除了模型 latency,还会关注:
BPU utilization;
DDR bandwidth;
Memory;
Process RSS。
当前 J6 工具里可以使用:
以及:
这些证据能帮助回答:
为什么模型慢?
例如:
BPU 满载?
DDR Bound?
内存不足?
调度冲突?
但:
监控工具也不能替代业务验收。
因为“BPU 利用率 85%”本身并不能告诉你:
用户最终看到的系统响应是否满足需求。

图2|本文定义的证据边界矩阵。横向看每个阶段已经可以证明什么,纵向看还有哪些结论仍需要后续验证。
十四、我给自己定义了一个 L0~L6 证据等级
注意:
这不是 Horizon 官方标准。
只是我用于约束自己表述的框架。
L0:文档 / 配置推导
例如:
查官方文档;
看 march;
看 API;
看配置文件。
能证明:
理论上应该怎么做。
不能证明:
实际执行成功。
L1:模型静态检查
例如:
或者静态读取:
Shape;
Metadata;
Graph;
Compiler Parameters。
能证明:
模型产物结构和属性是什么。
不能证明:
真实 Target 上可运行。
L2:HBM 成功生成
例如:
能证明:
编译链完成到了 HBM。
不能证明:
板端部署完成。
L3:Target 推理连通
例如:
或原生 UCP 成功完成一帧。
能证明:
HBM 在目标 Runtime 中可以加载并执行。
不能自动证明:
输出数值正确。
L4:结果一致性 / 精度验证
例如:
hb_verifier
与 Float Model 对比;
Validation Dataset;
mAP / Accuracy。
这时候可以说:
输出与参考模型或精度指标达到了某种已验证水平。
L5:模型级硬件性能
例如:
能获得:
模型级 latency;
FPS;
BPU / DDR 等硬件指标。
但仍不代表:
Business E2E P95。
L6:业务端到端验收
这一层才真正进入:
输入;
Preprocess;
多模型调度;
Postprocess;
业务逻辑;
Queue;
系统负载;
P95 / P99;
稳定性。
到了这里,才有资格形成:
与具体项目目标相匹配的最终验收结论。
十五、为什么我觉得这个“证据等级”很重要?
因为同一句:
“成功了。”
在不同阶段意义完全不同。
比如:
可能只是:
文件能加载。
可能只是:
量化流程完成。
可能只是:
编译产物生成。
可能只是:
Runtime跑通一帧。
可能只是:
获得模型级性能数字。
如果每一步都直接翻译成:
“部署成功”
那整条工具链就会失去证据边界。
十六、一个最典型的误区:HBM已经有了,所以项目结束
如果把这篇文章压缩成一个案例,我觉得最典型的就是:
然后直接跳到:
中间实际上至少还应该继续回答:
1. Target 能不能加载?
2. 输入契约是不是对的?
检查:
dtype;
shape;
stride;
alignment;
input format;
quantization。
3. 输出和参考结果一致吗?
使用:
hb_verifier
自己的 Gold;
验证集。
4. 真实硬件性能怎么样?
使用:
hrt_model_exec perf
hbm_perf Dynamic
hbm_infer Profile
板端监控。
5. 业务链路满足项目要求吗?
例如:
E2E;
P95;
P99;
长时间稳定性;
资源占用;
项目自己的业务指标。
这里具体跑多久、P95 多少、需要哪些稳定性测试,应该由:
实际项目规范决定。
不是 Horizon 工具链统一规定一个数字。

图3|从“HBM已经生成”到真正业务闭环之间,仍然需要完成推理连通性、一致性、模型级性能和项目级E2E验证。图中5步为本文建议验证框架,不是地平线官方统一验收标准。
十七、为什么“静态分析结果”尤其容易被写过头?
我之前很容易犯的一个错误就是:
看到:
就下意识觉得:
“我已经拿到性能结果了。”
但数字来自哪里非常重要。
如果来自:
那么是:
静态模型级估算。
如果来自:
那已经是:
真实板端模型级动态测量。
如果来自:
那是:
板端 Profile 里的纯推理阶段耗时。
如果来自业务程序的完整 tracing:
那才可能开始讨论:
Business E2E。
数字本身可能都是“ms”。
十八、工具不是越多,证据就越强
做到这里我还有一个感受:
工具链里有很多工具:
hb_compile
hb_model_info
hb_verifier
hbm_perf
hbm_infer
hrt_model_exec
UCP
hrt_ucp_monitor
hrut_ddr
但不是:
工具越多 = 越接近真相。
关键是每个工具到底回答哪一个问题。
例如:
工具 | 更适合回答 |
|---|---|
hb_model_info | 模型是什么 |
hb_compile | 能不能生成 HBM |
hb_verifier | 输出是否一致 |
hbm_perf Static | 静态模型性能与瓶颈 |
hbm_perf Dynamic | 板端模型级动态性能 |
hbm_infer | Host→Board远程推理与Profile |
hrt_model_exec infer | 板端模型推理连通 |
hrt_model_exec perf | 板端模型级性能 |
UCP C++ | 真正应用级Runtime接入 |
hrt_ucp_monitor / hrut_ddr | 板端资源与带宽观察 |
把它们都跑一遍并不重要。
真正重要的是:
你的结论需要哪一级证据?
十九、这次最大的收获:不要让结论跑在证据前面
前几次实验里我一直在强调:
Agent 不要越权。
做到 Exp09 以后,我发现这个规则其实不只是给 Agent 的。
对自己写技术文章也是一样。
如果我只有:
那就写:
“HBM已生成。”
如果只有:
那就写:
“静态性能估算。”
如果只有:
那就写:
“板端推理连通。”
如果真的做了:
再写:
“精度一致性通过。”
如果真正测了:
再写:
“业务端到端达到项目要求。”
证据在哪一层,结论就停在哪一层。
我觉得这可能比记住某一个 API 更重要。
二十、回头再看:“HBM生成成功”到底是什么?
现在再回答最开始的问题:
一个 HBM 到底证明了什么?
我的答案是:
它证明了一件很重要、但很有限的事:
模型已经完成了面向目标 Horizon BPU 的编译,并产生了可供后续 Runtime / Target 验证使用的 Horizon BPU Model。
但它不能单独证明:
板端正确。
也不能证明:
精度达标。
更不能证明:
性能和业务 E2E 已经验收。
所以以后我再看到:
不会再把它理解为终点。
它更像是:
模型部署链真正进入硬件 Runtime 世界的起点。
参考资料
- HorizonRobotics / OE-Skills
https://github.com/HorizonRobotics/OE-Skills
