新闻  |   论坛  |   博客  |   在线研讨会
征程 6 | PTQ 精度调优辅助代码,总有你用得上的
地平线开发者 | 2025-08-20 18:46:01    阅读:63   发布文章

一、截取 onnx 模型片段

在模型编译的时候,往往会出现各种各样的报错,您可能会受限于公司要求,无法把完整的 onnx 模型发送给地平线做分析,此时可以考虑截取 onnx 模型,找到可复现的片段,再将该片段提供给地平线的技术支持人员(这种方式通常是可以被公司允许的)。我们可以直接利用 onnx 的 python api 去完成这件事,onnx.utils.extract_model 的具体使用方式如下:

onnx.utils.extract_model 是 onnx 官方库提供的一个 从 ONNX 模型中提取子模型(子图) 的实用函数。 它的常见用途包括:

提取模型中某一部分用于调试或加速推理;

截取特定中间层的输出;

制作用于 calibration 的简化子模型(例如量化前的子图)。

1.1 基本用法
markdown体验AI代码助手代码解读复制代码from onnx import utils utils.extract_model( input_path, # 原始模型文件路径(.onnx) output_path, # 提取后的子模型保存路径(.onnx) input_names, # 子模型的输入节点名列表(字符串 output_names # 子模型的输出节点名列表(字符串) )

ONNX 会自动计算以这些输入到输出为范围的所有依赖节点,然后构造出一个合法的新 ONNX 子模型。

举例来说,我们可以运行这样的代码截取 onnx 模型片段:

Plain体验AI代码助手代码解读复制代码import onnx onnx.utils.extract_model('./heatmap_based_grasp_v2_op16_post_3.onnx','grasp_backbone.onnx',['input_name'],['output_name'])

请注意,这个脚本不一定能一次直接运行成功,有些节点不支持 extract,如果失败,可以尝试配置模型节点的 name 或者节点输入输出的 name,或者尝试使用其他节点或输入输出。

二、打印模型的所有 op type

当我们在做比较细致的精度调优时,会想把某一类算子全部配置 int16 或者 float32,而如何快速知道模型有哪些算子类别呢?首先我们要知道,PTQ 在校准的时候,回忆 optimized onnx 为处理对象,因此我们只要知道 optimized onnx 模型有哪些算子类别就可以了。用户使用 PTQ 时,可以使用我们提供好的功 python api 完成这个事情,举例如下:

Plain体验AI代码助手代码解读复制代码from hmct.ir import load_model  model = load_model('optimized.onnx') op_types = set() for node in model.graph.nodes:     op_types.add(node.op_type) print(op_types)

yaml 配置算子时,以 optimized 模型的 node name 为准,这里打印 op type 最好也选 optimized.onnx,但该功能可能会有部分 op type 漏打印的现象,可以检查 hb_compile 之后的 log 查漏补缺。

三、手动计算量化前后相似度

虽然模型编译的时候,日志里会提供相似度的情况,但显示的并不够完整。如果我们想知道所有输入数据或者某个特定输入数据的相似度情况,或者不同阶段模型的相似度情况,就需要手写代码去做模型推理。这里我提供这样一份参考代码,用来读取文件夹里所有数据,并挨个打印相似度,满足我们的相似度计算需求,从而更好地了解模型的精度情况。

Plain体验AI代码助手代码解读复制代码import numpy as np from horizon_tc_ui.hb_runtime import HBRuntime  def cosine_similarity(arr1, arr2):     arr1 = np.array(arr1)     arr2 = np.array(arr2)     dot_product = np.dot(arr1, arr2)  # 计算点积     norm_arr1 = np.linalg.norm(arr1)  # 计算arr1的范数     norm_arr2 = np.linalg.norm(arr2)  # 计算arr2的范数     if norm_arr1 == 0 or norm_arr2 == 0:         return 0.0     similarity = dot_product / (norm_arr1 * norm_arr2)  # 计算余弦相似度     return similarity  def float_vs_quant(i):     try:         input_depth = np.fromfile(f"./calib_data_bin/input_depth/{i}_input_depth.bin", dtype=np.float32).reshape(1,1280,720)     except Exception as e:         return 0     xyzrgb_tensor = np.fromfile(f"./calib_data_bin/xyzrgb_tensor/{i}_xyzrgb_tensor.bin", dtype=np.float32).reshape(1,6,640,360)      sess = HBRuntime("./model_output/grasp_original_float_model.onnx")     input_names = sess.input_names     output_names = sess.output_names     input_feed = {input_names[0]: input_depth,                    input_names[1]: xyzrgb_tensor,                   }     output = sess.run(output_names, input_feed)     pred_6d_grasp_float = output[0]     print("float")     print(pred_6d_grasp_float[0])      sess = HBRuntime("./model_output/grasp_quantized_model.bc")     input_names = sess.input_names     output_names = sess.output_names     input_feed = {input_names[0]: input_depth,                    input_names[1]: xyzrgb_tensor,                   }     output = sess.run(output_names, input_feed)     pred_6d_grasp_quant = output[0]     print("quant")     print(pred_6d_grasp_quant[0])      return cosine_similarity(pred_6d_grasp_float.reshape(-1), pred_6d_grasp_quant.reshape(-1))  if __name__ == "__main__":     for i in range(0, 100):         cos_sim = float_vs_quant(i)         if cos_sim == 0:             continue         print(str(i)+ " "+str(cos_sim)+"\n")

3.1 主函数:float_vs_quant(i)

这个函数是整个对比流程的核心:

读取输入数据
markdown体验AI代码助手代码解读复制代码input_depth = np.fromfile(..., dtype=np.float32).reshape(1,1280,720) xyzrgb_tensor = np.fromfile(..., dtype=np.float32).reshape(1,6,640,360)

分别读取第 i 条样本的:

深度图:维度是 (1, 1280, 720)

RGB + XYZ 张量:维度是 (1, 6, 640, 360)

若文件读取失败(如不存在),直接返回 0(跳过该样本)。

推理浮点模型
markdown体验AI代码助手代码解读复制代码sess = HBRuntime("./model_output/grasp_original_float_model.onnx") ... pred_6d_grasp_float = output[0]

使用 HBRuntime 加载浮点版模型 .onnx。

输入数据为 input_depth 和 xyzrgb_tensor。

获取输出(预测的 6D 抓取向量)。

推理量化模型
markdown体验AI代码助手代码解读复制代码sess = HBRuntime("./model_output/grasp_quantized_model.bc") ... pred_6d_grasp_quant = output[0]

加载量化后的模型(通常是 .bc 格式,地平线编译后模型)。

同样输入相同的数据。

获取量化模型的推理结果。

余弦相似度对比
markdown体验AI代码助手代码解读复制代码return cosine_similarity(pred_6d_grasp_float.reshape(-1), pred_6d_grasp_quant.reshape(-1))

将浮点和量化预测结果展平成一维向量。

计算并返回它们的余弦相似度。

3.2 主执行入口
markdown体验AI代码助手代码解读复制代码if name == "__main__":for i in range(0, 100): cos_sim = float_vs_quant(i) ...

对编号为 0 到 99 的输入样本,逐个调用 float_vs_quant。

跳过无效样本(返回值为 0)。

打印样本编号及对应的相似度。

3.3 输出示例
markdown体验AI代码助手代码解读复制代码float [0.12 0.45 0.87 ...] quant [0.11 0.44 0.85 ...] 3 0.99875 ...

展示浮点和量化模型的推理输出;

输出每个有效样本的相似度分数(越接近 1 表示差异越小)。

四、精度 debug 保存终端打印日志

通常来说,我们可以使用精度 DEBUG 功能,去查看哪些算子的量化风险高,从而为其设置更高的量化精度。但有时,我们的模型特别大,算子特别多,如果直接在 vscode 终端执行精度 debug 命令,打印的算子信息很可能不够完整,会被截断,因此可以使用下面介绍方法将精度 debug 日志完整地保存到本地文件里。

首先,我们先在 debug.py 脚本里写好我们要执行的命令,比如:

Plain体验AI代码助手代码解读复制代码import logging import hmct.quantizer.debugger as dbg  # 若verbose=True时,需要先设置log level为INFO logging.getLogger().setLevel(logging.INFO)  # 获取节点量化敏感度 node_message = dbg.get_sensitivity_of_nodes(         model_or_file='../model_output_debug/graspnet_calibrated_model.onnx',         metrics=['cosine-similarity', 'mse'],         calibrated_data='../calibration_data/',         output_node=None,         node_type='activation',         data_num=None,         verbose=True,         interested_nodes=None)

方法 1、前台运行时保存,这个方法会持续占用该终端,直到程序结束。

Plain体验AI代码助手代码解读复制代码python3 debug.py 2>&1 | tee debug_output.txt

方法 2、后台运行时保存,这是更为推荐的方法,这样我们在这个终端还可以同时做其他事,比如同时运行其他 node_type 的静的 debug 功能。

Plain体验AI代码助手代码解读复制代码nohup python3 debug.py >debug_output.log 2>&1 &

在程序运行时,目录下就会生成对应的 log 日志,我们可以随时查看精度 debug 的进展,非常方便!




*博客内容为网友个人发布,仅代表博主个人观点,如有侵权请联系工作人员删除。

参与讨论
登录后参与讨论
推荐文章
最近访客