专栏算法工具链一个 HBM 到底证明了什么?我把征程6从 ONNX 到板端运行的证据链拆开了

一个 HBM 到底证明了什么?我把征程6从 ONNX 到板端运行的证据链拆开了

361922026-08-28
2
0

前面做征程6相关实验时,我反复碰到一个问题:

“某一步成功了,到底能说明什么?”

比如:

  • ONNX 能正常打开,是不是就说明它能部署到 J6?

  • PTQ 跑完,是不是就说明定点精度没问题?

  • .bc 生成了,是不是就说明 HBM 一定能编译?
  • .hbm 已经有了,是不是就等于“部署完成”?
  • hbm_perf 有 latency,是不是就能直接写板端性能?
  • hbm_infer 跑出了结果,是不是就代表业务链路已经验收?

这些问题单独看都很简单。

但把整条链连起来以后,很容易发生一种情况:

上一步确实成功了,但结论却跨了两三级。

所以这次 Exp09,我没有继续做 Agent Benchmark,而是把征程6模型部署过程本身拆了一遍。

我想回答的是:

从 Float ONNX 到 HBM,再到真正的 J6 板端结果,每一种产物究竟能证明什么,又还不能证明什么?

一、先把整条产物链画出来

如果把不同工具入口的细节先抽象掉,一条典型的征程6模型部署逻辑链大致可以写成:

Float ONNX

PTQ / Quantized Model

HBIR / BC

HBM

Model Metadata / Runtime Contract

J6 Target Inference

Accuracy / Performance Verification

Business E2E Validation

需要先说明:

这是一条为了讨论“证据边界”而做的逻辑产物链,并不是说所有 OpenExplorer 入口生成的文件名都完全一样。
例如,hb_compile 的完整流程可以表现为:
ONNX / Caffe
→ Float HBIR .bc
→ Quantized HBIR .bc
→ HBM .hbm

而当前 OE-Skills 的另一条部署路线,也可能先得到 PTQ ONNX,再交给 HBDK 编译。

所以这里真正重要的不是文件后缀排列,而是:

每一次转换以后,我们新增了哪一级证据?

article09_figure01_artifact_chain.png

图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 是什么位置?

再往下会看到 .bc。

这里其实很容易让刚接触工具链的人迷糊。

因为 .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 ≠ 部署完成。

这是这篇文章最想强调的一句话。

为什么?

因为一个 .hbm 文件存在,仍然没有证明:
  • 目标板能够成功加载;

  • 实际输入格式完全匹配;

  • Tensor 内存布局使用正确;

  • 真实输入可以推理;

  • 输出是对的;

  • 板端性能满足要求;

  • 业务链路满足 P95 / P99;

  • 长时间运行稳定。

所以如果有人说:

“HBM 已经生成,所以 J6 部署已经成功。”

严格来说其实跨级了。

更合适的是:

“HBM 编译产物已生成,下一步进入 Runtime / 板端验证。”

六、HBM 本身还能检查什么?

虽然 HBM 不能证明部署完成,但它本身仍然包含很多有价值的信息。

例如可以使用:

查看:

  • 模型依赖;

  • HBDK 版本;

  • march;

  • compiler parameters;

  • 输入输出;

  • 内存信息。

HBDK4 里也可以通过 Hbm(...) 读取:
  • march;

  • toolkit version;

  • graphs;

  • inputs;

  • outputs;

  • nodes。

所以这个阶段已经非常适合做:

模型静态检查。

例如:

  • march 是否正确;

  • Graph 是否存在;

  • I/O 契约;

  • memory requirement;

  • 是否有 CPU 节点;

  • 编译参数是否符合预期。

但仍然要记住:

静态 Metadata 证明不了真实板端运行。

七、Tensor Contract:HBM 和业务代码真正接上的地方

到了 Runtime,模型就不再只是一个文件。

业务程序需要真正面对 Tensor。

这时候很多部署 Bug 都来自:

模型契约和业务代码的理解不一致。

当前 J6 UCP 的 hbDNNTensorProperties 包含:
  • 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 才真正进入“结果一致性”

这个阶段 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

如果给:

那么 hbm_perf 可以连接真实 BPU 做动态测量。

这时候证据等级已经升级成:

真实硬件上的模型级性能测量。

但即使如此,也仍然不能直接等于:

业务端到端 P95。

因为真实业务链路可能还有:

  • Camera;

  • Resize;

  • Color Convert;

  • Preprocess;

  • RPC / Queue;

  • Postprocess;

  • NMS;

  • Tracking;

  • 多模型调度;

  • CPU / DDR 竞争。

所以:

模型 latency ≠ Business E2E latency。

十一、hbm_infer 的 Profile 又属于哪一层?

hbm_infer 也是很容易被误解的工具。

它本质上是:

x86 侧 Python Client,通过 gRPC / SSH 连接 BPU Board,部署并运行 HBM。

当启用:

后,可以获取:

  • frame_duration

  • sd2rv_duration

  • commu_duration

  • board_duration

  • infer_duration

  • prepr_duration

  • pospr_duration

其中 infer_duration 表示:

板端纯推理阶段耗时

单位为 ms。

这比“完全没有板卡”的静态估计强很多。

但仍然要分清:

infer_duration

只是 Profile 中的一个阶段。

它不等于:

整个链路的 P95。

所以文章里如果写:

“J6模型推理耗时”

可以。

如果直接写:

“客户业务端到端耗时”

就越界了。

十二、原生 UCP C++:真正进入应用工程

再往后就是实际的 UCP C++ 程序。

一个基本完整的异步流程大致包括:

这一层比单纯 hbm_infer 更接近最终业务程序。

但仍然不能只凭:

“UCP Demo 跑了一帧”

就说:

“产品已经验收。”

因为此时通常还缺:

  • 数据集级精度;

  • 异常输入;

  • 多模型竞争;

  • 长时间稳定性;

  • 真实业务 E2E;

  • 资源占用;

  • 功耗;

  • 内存;

  • DDR;

  • P95 / P99。

十三、监控数据和模型性能也要分开

如果真正进入板端性能分析,除了模型 latency,还会关注:

  • BPU utilization;

  • DDR bandwidth;

  • Memory;

  • Process RSS。

当前 J6 工具里可以使用:

以及:

这些证据能帮助回答:

为什么模型慢?

例如:

  • BPU 满载?

  • DDR Bound?

  • 内存不足?

  • 调度冲突?

但:

监控工具也不能替代业务验收。

因为“BPU 利用率 85%”本身并不能告诉你:

用户最终看到的系统响应是否满足需求。

article09_figure02_claim_boundary_matrix.png

图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 能不能加载?

通过 UCP 或 hrt_model_exec infer。

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 工具链统一规定一个数字。

article09_figure03_misconception_to_closure.png

图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 世界的起点。

参考资料

本文中的 L0~L6 证据等级、5步闭环验证流程均为本文为了讨论工程证据边界而定义的分析框架,不是地平线官方验收规范。 本文未声称完成真实 J6 项目的端到端部署验收,也未提供虚构的板端性能或精度数据。
算法工具链
社区征文征程6
评论0
0/600