返回模型
说明文档
SmolVLA LIBERO — QNN HTP W8A16 (INT8 权重量化)
在 Qualcomm HTP (Hexagon 张量处理器) 上运行的 SmolVLA 三模型流水线 (视觉编码器 + LLM 主干 + 动作头),采用 INT8 权重量化 (W8A16)。 在 Snapdragon X Elite 上实现 约 4.8 秒端到端延迟 (实际耗时),成功完成 LIBERO 任务 3,有效控制频率为 0.84 Hz (n_action_steps=4) 或 1.70 Hz (n_action_steps=8)。
硬件 / 软件要求
| 项目 | 规格 |
|---|---|
| 设备 | Snapdragon X Elite (或 X Plus) — Windows ARM64 |
| 操作系统 | Windows 11 ARM64 |
| Python | 3.10 x86-64 (通过 Prism 模拟) — python.org |
| QNN SDK | QAIRT 2.43.0.260128 — Qualcomm AI Hub |
| 构建工具 | VS 2022 Build Tools (ARM64 clang-cl + lld-link) |
注意: Python 必须是 x86-64 版本 (而非 ARM64),因为 LIBERO / robosuite 需要 x86 兼容性。
安装
1. Python 包
pip install \"numpy<2.0\" torch torchvision transformers safetensors onnx imageio pillow
2. LIBERO 基准测试
git clone https://github.com/Lifelong-Robot-Learning/LIBERO.git
cd LIBERO && pip install -e .
3. QNN SDK
下载并安装 QAIRT 2.43.0.260128。设置环境变量:
export QNN_SDK=C:/Users/<user>/qualcomm/qairt/2.43.0.260128
4. 克隆本仓库及 SmolVLA 权重
git clone https://huggingface.co/xpuenabler/smolvla-libero-QNN-HTP-W8A16
cd smolvla-libero-QNN-HTP-W8A16
# 下载 SmolVLA 权重 (lerobot/smolvla_base)
python -c \"
from huggingface_hub import snapshot_download
snapshot_download('lerobot/smolvla_base', local_dir='smolvla_weights')
\"
模型转换流水线
步骤 0: 导出 ONNX (如需要)
如果您没有 ONNX 模型,请从 LeRobot 导出:
python scripts/export_onnx.py --weights smolvla_weights/
# 输出: onnx_models/vision_encoder.onnx
# onnx_models/llm_backbone.onnx
# onnx_models/action_head.onnx
步骤 1: ONNX 预处理 — 消除布尔值
QNN 转换器无法处理 Python bool 链。应用以下修复:
# LLM 主干: 折叠布尔常量 + 消除布尔运算
python scripts/fold_and_eliminate_bool.py onnx_models/llm_backbone.onnx onnx_models/llm_backbone_v7.onnx
# 动作头: 消除布尔值 + 修复 Expand 算子
python scripts/fold_and_eliminate_bool.py onnx_models/action_head.onnx onnx_models/action_head_v5.onnx
python scripts/fix_ah_expand_bool.py onnx_models/action_head_v5.onnx onnx_models/action_head_qnn_v6.onnx
# LLM 主干 v7: 移除 embedding Gather (改为在 CPU 端运行)
python scripts/fix_llm_embed_on_cpu.py onnx_models/llm_backbone_v7.onnx
# 输出: onnx_models/llm_backbone_v7.onnx (原地更新)
步骤 2a: 构建 FP32 QNN 模型 (HTP 后端)
python scripts/build_all_models.py --precision fp32
# 输出: qnn_models/windows-aarch64-fp32/lib*.dll
步骤 2b: 构建 INT8W QNN 模型 (W8A16)
python scripts/build_int8w_models.py
# 输出: qnn_models/windows-aarch64-int8w/lib*.dll
需要在仓库根目录放置
qnn_convert_patched.py(禁用 matmul_to_fc 优化):cp $QNN_SDK/lib/python/qnn_convert.py qnn_convert_patched.py # 应用补丁: 禁用 matmul_to_fc (见 scripts/build_all_models.py 头部)
步骤 3: 创建展开的动作头 (内置 10 步去噪)
python scripts/create_ah_unrolled.py
# 输入: onnx_models/action_head_qnn_v6.onnx
# 输出: onnx_models/action_head_unrolled10.onnx (393 MB)
# qnn_context_cache/libaction_head_unrolled_htp.dll (387 MB)
# 构建时间: 约 24 分钟 (图排序阶段)
步骤 4: 生成上下文二进制文件 (预编译 HTP 图)
# FP32 模型
python scripts/build_all_models.py --context-only
# INT8W 模型
python scripts/gen_int8w_context_binaries.py
# 输出: qnn_context_cache/lib*_fp32.serialized.bin
# qnn_context_cache/lib*_int8w.serialized.bin
上下文二进制文件可为每次模型调用节省约 0.5-1 秒,通过跳过图编译实现。
运行推理
基本用法 (HTP 上 FP32)
python inference/infer_libero_episode_qnn_htp.py \
--precision htp \
--task-id 3 \
--trial-id 3 \
--n-action-steps 4 \
--use-unrolled-ah \
--output output/result.mp4
HTP 上 INT8W
python inference/infer_libero_episode_qnn_htp.py \
--precision int8w \
--task-id 3 \
--trial-id 3 \
--n-action-steps 4 \
--use-unrolled-ah \
--output output/result_int8w.mp4
关键参数
| 参数 | 默认值 | 描述 |
|---|---|---|
--precision |
fp32 |
htp (HTP 上 FP32 DLL), int8w (HTP 上 W8A16), fp32 (CPU) |
--task-id |
3 |
LIBERO 任务索引 (0-9) |
--trial-id |
0 |
试验索引 (0-49) |
--n-action-steps |
1 |
滚动时域控制 (1-16) |
--use-unrolled-ah |
关闭 | 使用 10 步展开的动作头 (单次 QNN 调用) |
--max-steps |
520 |
最大回合步数 |
推理速度
单模型延迟 (Snapdragon X Elite)
模型执行时间单独测量 (不包含子进程启动开销 约 390ms/推理)。
| 模型 | CPU (FP32) | HTP (FP32 DLL) | 加速比 |
|---|---|---|---|
| 视觉编码器 | 3.2s | 0.7s | 4.6× |
| LLM 主干 | 21.0s | 1.5s | 14.0× |
| 动作头 ×10 | 6.0s | 1.1s (展开) | 5.5× |
| 模型总计 | 约 30s | 约 3.3s | 约 9× |
| 实际耗时 (端到端延迟) | — | 约 4.8s | — |
实际耗时包括子进程开销 (约 130ms × 3 次调用)、临时文件 I/O,以及模型执行时间之外的 NumPy 转置操作。
端到端推理 (任务 3, 试验 3)
| 配置 | 端到端延迟 | n_action_steps | 控制频率 | 结果 |
|---|---|---|---|---|
| HTP FP32, n=8, 展开 | 4,701ms | 8 | 1.70 Hz | 失败 |
| HTP FP32, n=4, 展开 | 4,762ms | 4 | 0.84 Hz | 成功 |
- 端到端延迟: 从观测捕获到首个动作输出的实际耗时
- 控制频率: 有效动作执行速率 = n_action_steps / 端到端延迟
CPU vs HTP 输出相似度 (余弦)
| 组件 | 余弦值 |
|---|---|
| 视觉编码器 | 0.99999 |
| LLM kv_keys | 0.889 |
| LLM kv_values | 0.768 |
| 动作头 (相同 KV) | 0.99999 |
| 端到端速度 | 0.986 |
LLM 相似度较低是由于 32 层 Transformer 中的 FP16 内部累积。
架构
观测 (图像 + 状态 + 语言)
│
▼
[视觉编码器] pixel_values[1,3,512,512] → image_embeddings[1,64,960]
│
▼
[LLM 主干] image_embs×2 + lang_emb[1,48,960] + state[1,32]
→ kv_keys/values[32,1,177,5,64] + prefix_pad_masks[1,177]
│
▼
[动作头] 10 步流匹配 (欧拉法, t:1.0→0.1, dt=-0.1)
(展开) noisy_actions[1,50,32] → denoised_actions[1,50,32]
│
▼
动作 (7-DoF: 末端执行器位置 + 轴角 + 夹爪)
关键实现说明
- HTP 上 FP32 DLL (而非 FP16):
--float_bitwidth 16会导致 ±512 饱和。FP32 DLL + HTP 内部自动转换为 FP16,精度更好。 - CPU 端嵌入: LLM v7 移除了
Gather算子 (HTP INT32 输入 bug)。嵌入查找在 Python 中使用embed_tokens.weight完成。 - 消除布尔值: QNN 会剥离布尔链。
fold_and_eliminate_bool.py用 int32/float 运算替换。 - 上下文二进制: 预序列化的 HTP 图,每个模型加载快约 0.5 秒。
- 张量转置: HTP 自动转置某些维度;见推理脚本中的
run_vision_encoder、run_llm_backbone_htp。
文件结构
├── inference/
│ └── infer_libero_episode_qnn_htp.py # 主要 LIBERO 推理脚本
├── scripts/
│ ├── build_all_models.py # FP32 QNN 构建
│ ├── build_int8w_models.py # W8A16 QNN 构建
│ ├── create_ah_unrolled.py # 展开 10 步动作头
│ ├── gen_int8w_context_binaries.py # 上下文二进制生成
│ ├── fold_and_eliminate_bool.py # ONNX 布尔预处理
│ ├── fix_ah_expand_bool.py # 动作头布尔修复
│ ├── fix_llm_embed_on_cpu.py # LLM v7 嵌入移除
│ └── fix_cpp_for_htp.py # C++ 输出 HTP 补丁
├── qnn_models/
│ ├── windows-aarch64-fp32/ # FP32 DLL (使用 HTP 后端运行)
│ └── windows-aarch64-int8w/ # W8A16 DLL
├── qnn_context_cache/
│ ├── lib*_fp32.serialized.bin # 预编译 HTP 上下文
│ └── libaction_head_unrolled_htp.* # 展开动作头 (10 步)
├── onnx_models/ # 预处理 ONNX 模型
├── policy_preprocessor_step_5_normalizer_processor.safetensors
└── policy_postprocessor_step_1_unnormalizer_processor.safetensors
参考资料
xpuenabler/smolvla-libero-QNN-HTP-W8A16
作者 xpuenabler
↓ 1
♥ 0
创建时间: 2026-03-09 14:20:55+00:00
更新时间: 2026-03-12 08:18:20+00:00
在 Hugging Face 上查看文件 (49)
.gitattributes
README.md
inference/infer_libero_episode_qnn_htp.py
onnx_models/action_head_v5.onnx
ONNX
onnx_models/llm_backbone_v4.onnx
ONNX
onnx_models/vision_encoder.onnx
ONNX
policy_postprocessor_step_1_unnormalizer_processor.safetensors
policy_preprocessor_step_5_normalizer_processor.safetensors
qnn_models/windows-aarch64-int8w/libaction_head_v2_htp.bin
qnn_models/windows-aarch64-int8w/libaction_head_v2_htp.dll
qnn_models/windows-aarch64-int8w/libaction_head_v2_htp.lib
qnn_models/windows-aarch64-int8w/libllm_backbone_htp.bin
qnn_models/windows-aarch64-int8w/libllm_backbone_htp.dll
qnn_models/windows-aarch64-int8w/libllm_backbone_htp.lib
qnn_models/windows-aarch64-int8w/libvision_encoder_htp.bin
qnn_models/windows-aarch64-int8w/libvision_encoder_htp.dll
qnn_models/windows-aarch64-int8w/libvision_encoder_htp.lib
scripts/build_all_models.py
scripts/build_dlls.py
scripts/build_int8w_llm_v7.py
scripts/build_int8w_models.py
scripts/compare_cpu_htp.py
scripts/compare_cpu_vs_htp.py
scripts/compare_cross_ah.py
scripts/convert_and_build_fp32.py
scripts/convert_onnx_to_qnn_htp_fp16.py
scripts/create_ah_unrolled.py
scripts/eliminate_bool_onnx.py
scripts/eliminate_bool_v2.py
scripts/fix_action_head_all.py
scripts/fix_ah_bool_to_fp16.py
scripts/fix_ah_expand_bool.py
scripts/fix_bool_to_float.py
scripts/fix_cpp_bool_output.py
scripts/fix_cpp_for_htp.py
scripts/fix_cumsum_cast.py
scripts/fix_greater_for_htp.py
scripts/fix_llm_embed_on_cpu.py
scripts/fix_llm_int32_to_fp16_inputs.py
scripts/fix_onnx_gather_bool.py
scripts/fold_and_eliminate_bool.py
scripts/gen_int8w_context_binaries.py
scripts/monitor_inference.py
scripts/reset_cdsp.py
scripts/reset_cdsp2.py
scripts/reset_npu_wmi.ps1
scripts/slack_notify.py
scripts/test_htp_all.py
scripts/test_inprocess.py