尧图建网站 尧图建网站 YAOTU WEB BUILD 免费咨询
ARTICLE DETAIL

资讯详情

深耕网站建设与建站编程的一线实战洞察。

高通跃龙IQ-9100工业平台的开发经验分享(3): weights-packing-CLI与PythonAPI对比

高通跃龙IQ-9100工业平台的开发经验分享(3): weights-packing-CLI与PythonAPI对比 设备: 高通跃龙IQ-9100 (IQ-9075, SA8775P同样适用, Hexagon v73, 双 CDSP)模型: Qwen2.5-7B-Instruct (w4a16 混合精度量化28 层 Transformer)SDK: QAIRT 2.42.0.251225日期: 2026-07-19概述QAIRT SDK 提供两条编译路径CLI 工具qnn-context-binary-generator和 Python APIqairt.compile()。对于 LLM 部署中的多图 context binary 编译这两条路径的行为存在关键差异——CLI 无法正确执行 4-bit 权重打包weights_packing导致产物体积膨胀 2 倍。本文记录这一差异的发现过程、根因分析、以及如何通过 Python API 实现与云编译等价的本地编译。一、问题本地编译产物是云编译的 2 倍在将 Qwen2.5-7Bw4a16 量化本地编译为 HTP context binary 时产物体积约 8.4 GB——而通过 Qualcomm AI Hub 云编译的同一模型仅 4.7 GB。逐层对比指标云编译 (6-split)本地编译 (11-split)每层 Transformer 大小112.75 MB224.2 MB总量4720.7 MB~8358.8 MB比率1.0×~2.0×224.2 ÷ 112.75 ≈ 1.99。对于 w4a16 量化模型精确 2 倍差异只有一个合理解释4-bit 权重没有被打包——每个 4-bit 值占了一整个 byte。二、QAIRT 工具链中的 4-bit 权重打包2.1qairt-converter的--pack_4_bit_weights参数QAIRT SDK 的模型转换器qairt-converter有一个参数--pack_4_bit_weights: Store 4-bit quantized weights in packed format in a single byte i.e. two 4-bit quantized tensors can be stored in one byte默认值为False。启用后DLC中间格式体积减半DLC 编译模式Part 2 大小缩减默认不打包676.4 MB—--pack_4_bit_weights343.0 MB-49.3%但 DLC 只是中间格式。最终部署到设备上的是 context binary由qnn-context-binary-generator从 DLC 编译而来。2.2qnn-context-binary-generator的编译模式差异这个 CLI 工具在单图模式和多图模式下的 weight packing 行为完全不同编译模式输入 DLCcontext binary 大小是否打包 4-bit单图未打包 676.4 MB337 MB✅ 内部自动打包单图已打包 343.0 MB编译失败(Error 1002)N/A多图prompttoken, weight sharing未打包 DLCs672.6 MB❌ 不打包LLM 部署必须使用多图模式prompt 图和 token 图共享权重而在这个模式下 CLI 工具不执行 4-bit 权重打包。更值得注意的是如果输入已经打包好的 DLC单图模式反而会报错nullptr for graphsInput。CLI 工具只能接受未打包的 DLC然后在单图模式下自行打包或在多图模式下跳过打包。2.3 跨平台验证为排除平台差异在 Windows 和 LinuxWSL2 Ubuntu 24.04上分别执行多图编译平台多图 Part 2 大小Windows (QnnHtp.dll)672.6 MBLinux (libQnnHtp.so)673 MB结果一致。多图模式不打包 4-bit 权重是qnn-context-binary-generator的固有行为与操作系统无关。三、CLI 配置文件的静默忽略问题3.1 HTP 后端扩展 schemaQNN HTP 后端定义了一套 JSON 格式的扩展配置htp_backend_ext_config.json包含graphs、context、devices等部分。其中graphs下的weights_packing键用于控制是否打包 4-bit 权重{graphs:[{graph_names:[prompt_ar128_cl4096_2_of_11,token_ar1_cl4096_2_of_11],weights_packing:true}],context:{weight_sharing_enabled:true}}3.2 CLI parser 的实际行为将上述配置通过--config_file传给qnn-context-binary-generator得到[ ERROR ] Unknown Key graphs/0/weights_packing passed in config [ ERROR ] Unknown Key graphs/0/O passed in config [ ERROR ] Unknown Key context/weight_sharing_enabled passed in configCLI 工具的 JSON parser没有实现完整的 HTP 后端扩展 schema。graphs、context、devices中的扩展配置键被识别为 “Unknown Key” 后静默忽略——工具继续以默认值运行不会中止。这意味着用户在配置文件中设置weights_packing: true→ 被忽略用户在配置文件中设置weight_sharing_enabled: true→ 被忽略用户在配置文件中设置optimization_type(O) → 被忽略工具输出 672.6 MB 的未打包 context binary看起来编译成功没有任何指示告诉用户这些配置项没有生效。“Unknown Key” 的 ERROR 级日志容易被淹没在其他输出中且工具正常退出exit code 0。四、Python API直接调用 QNN C APIQAIRT SDK 同时提供 Python APIqairt包其底层通过NativeExecutor直接调用 QNN C API不经过 CLI 的 JSON parser。4.1 关键代码importqairtfromqairt.api.compiler.configimportCompileConfigfromqairt.api.common.backends.htp.configimportHtpGraphConfig,HtpContextConfig# 转换 ONNX → DLC内存中的中间表示prompt_modelqairt.convert(prompt_onnx,encodingsprompt_enc,float_bitwidth16)token_modelqairt.convert(token_onnx,encodingstoken_enc,float_bitwidth16)# 编译 DLC → context binaryconfigCompileConfig(backendHTP,graph_custom_configs[HtpGraphConfig(nameprompt_name,optimization_type3,weights_packingTrue),HtpGraphConfig(nametoken_name,optimization_type3,weights_packingTrue),],context_custom_configs[HtpContextConfig(weight_sharing_enabledTrue)],)resultqairt.compile([prompt_model,token_model],configconfig)result.save(output_bin)4.2 配置参数说明参数作用CLI 是否支持weights_packingTrue将 2 个 4-bit 权重打包到 1 byte❌ 静默忽略optimization_type3最高优化级别❌ 静默忽略weight_sharing_enabledTrueprompt/token 图共享权重❌ 静默忽略float_bitwidth16浮点回退使用 fp16✅ 支持Python API 中这些参数通过HtpGraphConfig、HtpContextConfig等 Python 对象传递在底层被正确映射到 QNN C API 的对应接口。五、A/B 测试对比5.1 单 Part 对比Part 2 of 11编译方法Part 2 大小vs 云编译CLI默认不打包672.6 MB2×CLI weights_packing: trueJSON 配置672.6 MB2×被忽略Python API weights_packingTrue337.3 MB≈1×云编译参考值~340 MB1×5.2 全量编译对比Python API 编译全部 10 个 Transformer parts LUT 嵌入11-split 方案Part内容Python API 打包CLI 未打包缩减1 (LUT)嵌入层1039.5 MB1039.5 MB0%2–10Transformer 层每份 3 层337.3 MB × 9672.6 MB × 9-49.9%111 层 LM Head635.1 MB1266.8 MB-49.9%总计4710.4 MB~8358 MB-43.6%本地 Python API 编译 4710.4 MB vs 云编译 4720.7 MB差异仅 0.2%。六、与云编译参数的贡献度对比AI Hub 云编译管线中有两个本地不可用的参数# ai-hub-models 源码中硬编码other_compile_options --quantize_full_type w8a16 --quantize_io--quantize_full_type w8a16将未量化的嵌入层和 LM Head 从 fp16 量化为 w8a16--quantize_io量化模型的输入/输出张量这些参数的实际贡献因素贡献比例说明weights_packing~98-99%2 个 4-bit 值打包到 1 byte--quantize_full_type w8a16~1-2%仅影响嵌入层和 LM Head--quantize_io1%仅影响 I/O 张量体积差异的主导因素是weights_packing而这个功能通过 QAIRT Python API 在本地完全可用。云编译独有的量化参数对最终二进制大小的贡献不到 2%——4710 MB vs 4720 MB 的 10 MB 差异即来源于此。七、技术原因分析7.1 为什么 CLI 的多图模式不打包qnn-context-binary-generator的单图模式和多图模式在内部使用不同的编译路径单图模式直接调用 HTP 编译器编译器内部对 4-bit 权重执行打包多图模式--weight_sharing_enabled需要在多个图之间协调权重共享使用了不同的内部流程。这个流程中4-bit 权重打包步骤被跳过了这是 CLI 工具在多图编译路径中的实现缺陷不是 QNN HTP 编译器本身的限制——因为 Python API 调用的是同一个底层编译器且能在多图模式下正确执行打包。7.2 为什么 CLI 的 JSON parser 不识别扩展键CLI 工具的--config_fileJSON parser 实现了 HTP 后端配置 schema 的一个子集。以下类别的键被支持顶层设备配置soc_model、dsp_arch、cores内存配置mem_type性能配置perf_profile、rpc_control_latency以下类别的键不被支持报 “Unknown Key”graphs下的weights_packing、optimization_type(O)context下的weight_sharing_enabledPython API 的NativeExecutor则在 Windows 上通过QnnHtp.dllLinux 上通过libQnnHtp.so直接调用 QNN C API所有 HTP 后端扩展参数都被正确传递。八、本地编译完整流程以下是使用 Python API 编译 LLM context binary 的关键步骤8.1 环境要求项目要求Python3.10QAIRT SDK 的.pyd原生绑定要求QAIRT SDK2.42路径示例C:\Qualcomm\AIStack\QAIRT\2.42.0.251225环境变量QNN_SDK_ROOT指向 SDK 根目录Python path$QNN_SDK_ROOT/lib/python加入sys.path8.2 编译脚本核心逻辑importsys,os QAIRT_ROOTos.environ[QNN_SDK_ROOT]sys.path.insert(0,os.path.join(QAIRT_ROOT,lib,python))importqairtfromqairt.api.compiler.configimportCompileConfigfromqairt.api.common.backends.htp.configimport(HtpGraphConfig,HtpContextConfig,HtpDeviceConfig,HtpDeviceCoreConfig,PerfProfile,)NUM_SPLITS6forpart_idxinrange(1,NUM_SPLITS1):prompt_namefprompt_ar128_cl4096_{part_idx}_of_{NUM_SPLITS}token_nameftoken_ar1_cl4096_{part_idx}_of_{NUM_SPLITS}# 1. 转换 ONNX → DLCprompt_modelqairt.convert(prompt_onnx,encodingsprompt_enc,float_bitwidth16)token_modelqairt.convert(token_onnx,encodingstoken_enc,float_bitwidth16)# 2. 编译 → context binaryconfigCompileConfig(backendHTP,graph_custom_configs[HtpGraphConfig(nameprompt_name,optimization_type3,weights_packingTrue,vtcm_size_in_mb8),HtpGraphConfig(nametoken_name,optimization_type3,weights_packingTrue,vtcm_size_in_mb8),],context_custom_configs[HtpContextConfig(weight_sharing_enabledTrue)],device_custom_configs[HtpDeviceConfig(soc_model77,dsp_archv73,cores[HtpDeviceCoreConfig(perf_profilePerfProfile.BURST,rpc_control_latency100)],)],)resultqairt.compile([prompt_model,token_model],configconfig)result.save(fqwen2_5_7b_instruct_part_{part_idx}_of_{NUM_SPLITS}.bin)8.3 编译耗时6-split 全量编译在 Windows x86_64 上约16 分钟完成含 ONNX→DLC 转换 DLC→context binary 编译。九、影响评估9.1 消除云编译依赖在发现 Python API 的weights_packing之前获取紧凑 context binary 的唯一途径是 Qualcomm AI Hub 云编译。这意味着需要 AI Hub 账号和 API token需要上传数 GB 的 ONNX 模型到云端在网络条件差的环境下200 KB/s 上传速度意味着 17 小时需要等待云端排队和编译编译配置受限于云 API 提供的选项使用 Python API 后整个编译流程在本地 Windows 机器上 16 分钟完成不需要网络连接、不需要账号、不需要等待。9.2 对不同 QAIRT 版本的适用性weights_packing参数在 QAIRT 2.40 的 Python API 中可用。CLI 的 parser 限制在测试过的所有版本2.35、2.40、2.42中均存在。十、CLI 与 Python API 对比总结对比维度CLI (qnn-context-binary-generator)Python API (qairt.compile())4-bit weights_packing多图模式❌ 不执行✅ 正确执行weight_sharing_enabled❌ JSON parser 忽略✅ 正确传递optimization_type❌ JSON parser 忽略✅ 正确传递soc_model / dsp_arch✅ 支持✅ 支持底层调用方式内部实现直接调用 QNN C API (QnnHtp.dll/libQnnHtp.so)多图 context binary 大小~672 MB/part膨胀~337 MB/part紧凑总模型大小7B, 6-split~8.4 GB~4.7 GB与云编译大小差异2×0.2%编译时间类似~16 分钟结论QAIRT SDK 的 CLI 工具qnn-context-binary-generator在多图编译模式下不执行 4-bit 权重打包。这不是配置问题——即使在 JSON 配置文件中显式设置weights_packing: true也会被 parser 忽略。CLI 的 JSON parser 只实现了 HTP 后端扩展 schema 的一个子集。weights_packing、weight_sharing_enabled、optimization_type等关键配置项被报为 “Unknown Key” 后静默跳过工具以默认值继续执行并正常退出。QAIRT Python API 是多图 context binary 编译的正确路径。它通过NativeExecutor直接调用 QNN C API所有 HTP 后端扩展参数都被正确执行。产出的 context binary 与云编译在大小和推理质量上等价。本地编译可以完全替代 AI Hub 云编译。Python API weights_packingTruesoc_model77产出的 context binary 总量 4710 MB与云编译的 4720 MB 仅差 0.2%差异来自云端额外的--quantize_full_type w8a16和--quantize_io贡献不到 2%。对于 QAIRT SDK 的 LLM 部署流程建议统一使用 Python API 进行编译避免 CLI 工具的多图模式限制和 parser 不完整问题。
返回列表