Qualcomm X1 上部署 Qwen3-VL:GenieX + QAIRT / NPU 完整教程

可直接把本篇教程链接发给code agent进行全自动化部署

本文记录如何在 Qualcomm X1 板端部署并运行 Qwen3-VL 8B,使用 GenieX + QAIRT 后端完成视觉语言模型推理,并让图像推理实际运行在 HTP/NPU 上。>

这套流程重点解决几个比较容易踩坑的问题:

  • GenieX runner 与板端 glibc 版本不兼容

  • 模型文件命名与 GenieX 预期不一致

  • embedding 文件缺失导致模型输出乱码

  • 示例图片扩展名与真实格式不一致

  • geniex-bench 使用 /dev/stdin 时可能出现 prompt 短读

  • 板端原有 QIRF / ROS 环境中缺少完整的 GenieX 工具

最终使用独立部署目录 + 私有 glibc runtime 的方式完成部署,不修改系统 glibc,也不破坏板端已有 QAIRT / QNN 环境。


最终效果

模型最终部署在:

/home/aidlux/qwen3-vl-8b-geniex-qcs8550

实测验证通过:

  • 6 个 LLM shard 全部加载成功

  • Vision Encoder 加载成功

  • 图像推理实际运行在 HTP / NPU

  • 首 Token 延迟约 0.91 秒

  • 解码速度约 7.9 token/s

  • 模型能够正常返回可读的图像描述


1. 下载模型以及确认模型 Runtime

进入

通过开发板内置的 MMS 工具获取模型资源及代码包。
检查模型包中的 metadata.json
需要关注这些字段:

runtime: geniex_qairt
chipset: QCS8550
HTP: v73
precision: w4a16
context length: 4096
supports vision: true

这里最关键的是:

runtime: geniex_qairt

对于这种 Qwen3-VL bundle,不建议直接使用传统的:

genie-t2t-run

来验证视觉能力。

应使用 GenieX:

geniex-bench --plugin qairt --device npu --vlm

在本次部署环境中,板端原有 QIRF / ROS 的 llm_genie 包并不完整,系统 PATH 中也没有可直接使用的 genie-t2t-rungeniex-bench

因此本文采用完全独立的部署目录,不修改已有 systemd 服务,也不覆盖系统 QAIRT。


2. 检查 X1 板端环境

本次使用的板端环境为:

架构:aarch64
系统:Ubuntu 22.04
glibc:2.35
内存:约 14 GiB
Swap:约 7.4 GiB

可以先通过 SSH 检查基本环境:

ssh aidlux 'uname -m; . /etc/os-release; echo "$PRETTY_NAME"; getconf GNU_LIBC_VERSION; df -h /home/aidlux'

重点确认:

  1. 架构为 aarch64

  2. SSH 正常

  3. /home/aidlux 有足够空间

  4. glibc 版本

本次模型约 6.9 GiB,此外还需要存放 GenieX runner 和私有 runtime,因此部署前建议先确认磁盘空间。

注意:

不要为了部署模型去停止已有 systemd / QNN 服务,也不要覆盖系统已有 QAIRT 安装。


3. 下载 GenieX ARM64 Runner

本文使用的 Linux ARM64 runner:

https://qaihub-public-assets.s3.us-west-2.amazonaws.com/qai-hub-geniex/geniex-bench-linux-arm64.tar.gz

下载:

curl -fL --retry 10 --retry-all-errors --retry-delay 2 --max-time 600 \
  -o geniex-bench-linux-arm64.tar.gz \
  https://qaihub-public-assets.s3.us-west-2.amazonaws.com/qai-hub-geniex/geniex-bench-linux-arm64.tar.gz

不要下载完就直接解压。

先检查文件大小:

stat -c '%s %n' geniex-bench-linux-arm64.tar.gz

检查 gzip:

gzip -t geniex-bench-linux-arm64.tar.gz

再查看 tar 内容:

tar -tzf geniex-bench-linux-arm64.tar.gz | head

如果:

gzip -t geniex-bench-linux-arm64.tar.gz

失败,说明下载文件可能损坏。

本次部署过程中就遇到过归档尾部损坏的问题。

这种情况下不要继续解压或者上传,重新下载到新文件后再次校验。

正常解压后,至少应该看到:

bin/geniex-bench
lib/qairt/libgeniex_plugin.so
lib/qairt/libgeniex-proc-vision.so
lib/qairt/libgeniex_vlm.so
lib/qairt/libgeniex_core.so


4. 解决 glibc 版本不兼容

这是整个部署过程中最关键的兼容问题之一。

X1 板端:

glibc 2.35

而本次使用的官方预编译 GenieX runner 需要:

GLIBC_2.38 / GLIBC_2.39

因此如果直接运行:

geniex-bench

会因为 glibc 版本不足而失败。

不要直接升级系统 glibc

这里不建议替换板端系统 glibc

更安全的方案是:

在部署目录中准备一套 Ubuntu 24.04 ARM64 用户态 runtime,然后通过私有 loader 启动 GenieX。

本次使用的组件包括:

libc6
libstdc++6
libgcc-s1
gcc-14-base

最终私有 runtime 中至少需要:

/home/aidlux/qwen3-vl-8b-geniex-qcs8550/runtime/lib/ld-linux-aarch64.so.1


5. 使用私有 Loader 启动 GenieX

假设部署目录为:

target=/home/aidlux/qwen3-vl-8b-geniex-qcs8550

设置 loader:

loader=$target/runtime/lib/ld-linux-aarch64.so.1

设置运行时动态库路径:

libs=$target/runner/lib:$target/runner/lib/qairt:$target/runner/lib/qairt/htp-files:$target/runner/lib/llama_cpp:$target/runtime/lib

测试:

"$loader" \
  --library-path "$libs" \
  "$target/runner/bin/geniex-bench" \
  --help

如果可以正常显示 geniex-bench 帮助信息,就说明:

  • 私有 glibc loader 可以工作

  • runner 可以正常启动

  • 不需要修改系统 glibc


6. 推荐的板端目录结构

建议模型、runner、runtime 全部放在一个独立目录,例如:

/home/aidlux/qwen3-vl-8b-geniex-qcs8550/
├── runner/
│   ├── bin/
│   │   └── geniex-bench
│   └── lib/
├── runtime/
│   └── lib/
│       └── ld-linux-aarch64.so.1
└── model/
    └── qnn248_qcs8550_cl4096/
        ├── metadata.json
        ├── tokenizer.json
        ├── embedding_fp32.bin
        ├── qwen3-vl-8b-vit.serialized.bin
        ├── 6 个文本 serialized bin
        └── ingredients.png

这样做的好处是:

  • 不影响系统环境

  • 不覆盖已有 QAIRT

  • 不修改系统 glibc

  • 删除整个目录即可回滚

  • 后续定位依赖问题也更简单

模型上传完成后,再进行下面的兼容处理。


7. 创建两个关键模型软链接

进入模型目录:

cd /home/aidlux/qwen3-vl-8b-geniex-qcs8550/model/qnn248_qcs8550_cl4096

创建:

ln -s qwen3-vl-8b-vit.serialized.bin vision_encoder.bin
ln -s embedding_fp32.bin embedding_weights.raw

检查:

ls -l vision_encoder.bin embedding_weights.raw

正确结果应类似:

vision_encoder.bin     -> qwen3-vl-8b-vit.serialized.bin
embedding_weights.raw  -> embedding_fp32.bin

为什么 embedding_weights.raw 很重要?

本次部署时曾出现一个很容易误判的问题:

  • 模型加载成功

  • NPU 也加载成功

  • 但最终输出是乱码

补上:

embedding_weights.raw -> embedding_fp32.bin

之后,纯文本推理立刻恢复正常,例如:

The answer is 4.

因此:

如果模型能够运行但输出乱码,优先检查 embedding_weights.raw

不要为了改名复制一份数 GB 的模型文件,直接使用软链接即可。


8. 修复示例图片扩展名

另一个比较隐蔽的问题是模型目录中的:

ingredients.png

实际文件内容是 JPEG,只是扩展名写成了 .png

建议保留原文件,再复制一份正确扩展名:

model=/home/aidlux/qwen3-vl-8b-geniex-qcs8550/model/qnn248_qcs8550_cl4096

cp -n "$model/ingredients.png" "$model/ingredients.jpg"

检查文件真实格式:

file "$model/ingredients.png" "$model/ingredients.jpg"

之后进行图像推理时使用:

ingredients.jpg


9. Prompt 不要直接使用 /dev/stdin

geniex-bench 的 prompt 建议通过真实 UTF-8 文件传入。

本次测试发现,把:

/dev/stdin

直接作为 prompt 输入时,可能出现短读等问题。

推荐的 Qwen3-VL prompt:

<|im_start|>system
You are a helpful AI assistant.<|im_end|>
<|im_start|>user
<__image__>Describe the image in one short sentence.<|im_end|>
<|im_start|>assistant

创建文件:

cat > /tmp/qwen3-vl-prompt.txt <<'EOF'
<|im_start|>system
You are a helpful AI assistant.<|im_end|>
<|im_start|>user
<__image__>Describe the image in one short sentence.<|im_end|>
<|im_start|>assistant
EOF

特别注意:

<__image__>

必须存在,否则视觉输入可能无法按预期处理。


10. 最小可运行的图文推理命令

配置路径:

target=/home/aidlux/qwen3-vl-8b-geniex-qcs8550
model=$target/model/qnn248_qcs8550_cl4096
loader=$target/runtime/lib/ld-linux-aarch64.so.1

设置 library path:

libs=$target/runner/lib:$target/runner/lib/qairt:$target/runner/lib/qairt/htp-files:$target/runner/lib/llama_cpp:$target/runtime/lib

设置 GenieX / QAIRT 环境:

export GENIEX_PLUGIN_PATH=$target/runner/lib
export ADSP_LIBRARY_PATH=$target/runner/lib/qairt/htp-files
export GENIEX_LOG=INFO

运行:

"$loader" \
  --library-path "$libs" \
  "$target/runner/bin/geniex-bench" \
  --plugin qairt \
  --device npu \
  --model "$model" \
  --vlm \
  --prompt-file /tmp/qwen3-vl-prompt.txt \
  --image "$model/ingredients.jpg" \
  --n-gen 32 \
  --warmup 0 \
  --repetitions 1 \
  --accuracy


11. 如何判断部署成功

不要只看进程是否启动。

至少确认下面几项:

  • 6 个文本 shard 全部加载成功

  • Vision Encoder 加载成功

  • QAIRT VLM 创建成功

  • 日志中能够看到 HTP / NPU 推理

  • 最终返回正常、可读的文本内容

如果只是:

模型加载成功

但结果乱码或视觉输入失败,仍然不能算部署完成。


12. 建议封装成启动脚本

为了避免每次手动设置环境变量,建议最终封装一个:

run_x1_geniex.sh

脚本至少应该完成以下工作:

  1. 设置部署根目录

  2. 设置模型目录

  3. 接收 --image

  4. 接收 --prompt

  5. 接收 --tokens

  6. 检查私有 loader

  7. 检查 geniex-bench

  8. 检查 metadata.json

  9. 检查两个模型兼容软链接

  10. 检查输入图片

  11. 使用 mktemp 创建真实 prompt 文件

  12. 设置 GENIEX_PLUGIN_PATH

  13. 设置 ADSP_LIBRARY_PATH

  14. 设置 GENIEX_LOG

  15. 通过私有 loader 和 --library-path 启动 geniex-bench

  16. 固定使用 --plugin qairt --device npu --vlm

上传:

scp run_x1_geniex.sh aidlux:/home/aidlux/qwen3-vl-8b-geniex-qcs8550/

添加执行权限:

ssh aidlux 'chmod +x /home/aidlux/qwen3-vl-8b-geniex-qcs8550/run_x1_geniex.sh'

运行:

ssh aidlux '/home/aidlux/qwen3-vl-8b-geniex-qcs8550/run_x1_geniex.sh'

自定义图片和 prompt:

/home/aidlux/qwen3-vl-8b-geniex-qcs8550/run_x1_geniex.sh \
  --image /path/to/image.jpg \
  --prompt "请描述这张图片" \
  --tokens 32


故障排查

1. 报 GLIBC_2.38 / GLIBC_2.39

如果出现类似:

GLIBC_2.38 not found

或者:

GLIBC_2.39 not found

通常说明:

  • 仍然使用了系统 loader

  • 或者私有 --library-path 不完整

确认 loader:

runtime/lib/ld-linux-aarch64.so.1

并确保以下目录都加入 --library-path

runner/lib
runner/lib/qairt
runner/lib/qairt/htp-files
runner/lib/llama_cpp
runtime/lib


2. 模型能运行,但输出乱码

先检查:

ls -l "$MODEL/embedding_weights.raw"

它必须指向:

embedding_fp32.bin

也就是:

embedding_weights.raw -> embedding_fp32.bin

这个问题很容易被误认为 tokenizer、量化模型或者 NPU Runtime 出错,建议优先排查。


3. 纯文本正常,但图像推理失败

先检查图片的真实文件格式

file your-image.png

如果文件内容实际是 JPEG,但文件名使用 .png,可以复制成 .jpg 后重新测试。

例如:

cp -n ingredients.png ingredients.jpg

然后改用:

ingredients.jpg

进行推理。


4. Prompt 处理失败 / 输出为空

不要直接使用:

/dev/stdin

建议创建真实 UTF-8 文件。

同时检查 prompt 是否包含:

<__image__>


5. 找不到 genie-t2t-run

对于:

runtime: geniex_qairt

的视觉模型,这不是阻塞问题。

直接使用 GenieX:

geniex-bench --plugin qairt --device npu --vlm

即可。


6. Runner 解压失败

重新检查:

gzip -t geniex-bench-linux-arm64.tar.gz

以及:

tar -tzf geniex-bench-linux-arm64.tar.gz

任何一个校验失败,都不要继续部署该文件。

重新下载后再测试。


部署检查清单

正式验收之前,可以按下面顺序快速检查:

  • SSH 可以正常连接 X1

  • uname -m 返回 aarch64

  • 磁盘空间足够存放模型、runner 和私有 runtime

  • metadata.json 确认 QCS8550

  • metadata.json 确认 HTP v73

  • metadata.json 确认 runtime: geniex_qairt

  • GenieX ARM64 runner 已完整下载

  • gzip -t 校验通过

  • X1 glibc 低于 runner 要求时使用私有 ARM64 glibc

  • 没有替换系统 glibc

  • runner、runtime、model 位于独立部署目录

  • vision_encoder.bin 软链接正确

  • embedding_weights.raw 软链接正确

  • 输入图片扩展名与真实格式一致

  • 使用真实 prompt 文件,而不是 /dev/stdin

  • Prompt 中包含 <__image__>

  • 使用 --plugin qairt

  • 使用 --device npu

  • 使用 --vlm

  • 6 个文本 shard 全部加载成功

  • Vision Encoder 加载成功

  • 日志确认使用 HTP / NPU

  • 最终输出为正常可读文本


总结

这次部署最值得注意的,其实不是模型本身,而是几个运行时兼容问题:

  1. GenieX runner 的 glibc 要求高于 X1 系统版本

  2. 不要为了运行 runner 替换系统 glibc

  3. 使用私有 ARM64 loader + library path 可以隔离解决兼容问题

  4. embedding_weights.raw 缺失可能导致模型加载正常但输出乱码

  5. Vision Encoder 文件名需要适配 GenieX

  6. 图片扩展名必须和真实格式一致

  7. VLM prompt 建议写入真实文件

  8. geniex_qairt 类型的视觉模型应使用 geniex-bench + QAIRT + NPU 验证

只要把 runtime、模型文件命名和 prompt 这几个坑处理好,Qwen3-VL 就可以在 X1 上通过 GenieX + QAIRT 正常完成 NPU 图文推理。


参考资料

https://www.qualcomm.com/developer/software/gen-ai-inference-extensions

https://github.com/qualcomm/GenieX

1 个赞