从 LLaMA-Factory 微调到高通 NPU 部署: Qwen-0.6B 全链路移植指南

前言

在大模型端侧化部署的趋势下,如何将微调后的 LLM 跑在手机 NPU 上是很多开发者的痛点。本文将手把手教你如何将使用 LLaMA-Factory 微调后的 Qwen-0.6B 模型,一步步移植到高通(Qualcomm)骁龙平台的 NPU 上,实现低功耗、高速度的本地化推理。

一、 导出微调模型

首先,在 LLaMA-Factory 界面中选择好微调后的检查点(Checkpoint),填写导出路径,点击 “开始导出”

[图片]

导出成功后,你会在目录下看到如下文件:

  • model.safetensors(模型权重)
  • config.json(模型配置)
  • tokenizer.json 等(分词器相关)

要将微调后的 Qwen-0.6B 模型移植到高通 NPU,第一步就是格式转换。safetensors 是目前 Hugging Face 推崇的安全权重格式,而 ONNX 则是进入高通工具链(QNN/SNPE)的通用门票。
以下是详细的操作步骤

二、 格式转换:从 Safetensors 到 ONNX

1. 转换为 PyTorch 权重

由于部分旧版转换工具不支持 safetensors,建议先将其转回标准的 pytorch_model.bin

import torch from safetensors.torch import load_file # 1. 路径设置 safetensors_path ="./qwen0_6b/model.safetensors" pytorch_bin_path ="./qwen0_6b/pytorch_model.bin"# 2. 加载并保存 weights = load_file(safetensors_path) torch.save(weights, pytorch_bin_path)[cite_start]print(f"转换成功:{pytorch_bin_path}")[cite:42,43,44,46,51,54]

注意:转换后,请确保你的 config.json 中的 architectures 字段正确(对于 Qwen0.6B 通常是 Qwen2ForCausalLM)。

2. 使用 Optimum 导出 ONNX

导出 LLM 涉及复杂的 KV Cache 处理,强烈建议使用 Hugging Face 的 Optimum 库 。

安装工具:

[cite_start]pip install optimum[exporters] onnx onnxruntime [cite: 62]

执行导出:
针对 NPU 部署,必须开启 with past 模式以保证推理速度 。

optimum-cli export onnx \ --model ./qwen0_6b \ --task text-generation-with-past \ --trust-remote-code \[cite_start]./qwen_onnx_out/ [cite: 67, 69, 71, 73, 75]

输出结果:你会得到 decoder_model.onnxdecoder_with_past_model.onnx

注意:--task text-generation-with-past:这非常关键!这会生成两个模型,一个处理初始 Prompt,另一个利用 KV Cache 负责后续 Token 生成 。

三、 高通 NPU 关键优化(必看!)

高通 Hexagon NPU 对算子有特定要求,进入工具链前需完成以下优化:

  1. Opset 版本: 建议使用 Opset 17 或更高版本 。如果 optimum 默认导出较低,可以指定:
--opset 17
  1. 静态形状(Static Shapes): NPU 在静态形状下性能最强。建议将输入固定,如 batch_size=1, sequence_length=512
  2. 模型简化: 使用 onnxsim 消除冗余算子,降低报错率 。
pip install onnxsim onnxsim ./qwen_onnx_out/decoder_model.onnx ./qwen_onnx_out/decoder_model_sim.onnx 

四、验证 ONNX 模型

在交给高通工具链之前,先确保 ONNX 模型是正确的:

import onnx import onnxruntime as ort model = onnx.load("./qwen_onnx_out/decoder_model.onnx") onnx.checker.check_model(model) print("ONNX 模型校验通过!")

五、 高通工具链(QNN)模型编译

1. 环境准备

确保你的 Linux 开发机已安装:

  • Qualcomm AI Engine Direct SDK (QNN)
  • Android NDK (建议 r25c 或 r26)
  • 设置环境变量
exportQNN_SDK_ROOT=/path/to/qnn_sdk exportANDROID_NDK_ROOT=/path/to/android_ndk source$QNN_SDK_ROOT/bin/envsetup.sh 

一旦你拿到了 decoder_model.onnx,你接下来的操作流程是:

  • 准备量化数据:从你的微调数据集中抽取 100 条样本,用于高通工具的 Post-Training Quantization (PTQ)。
  • 使用 QNN Converter
qnn-onnx-converter -i decoder_model.onnx -o qwen_qnn.cpp --input_list_file calibration_data.txt 

要把微调后的 Qwen-0.6B 最终跑在手机 NPU 上,你需要经历交叉编译的过程。高通 QNN SDK 提供了专门的工具,将转换后的中间代码(.cpp / .bin)编译成手机端可执行的二进制文件。
在高通架构中,通常涉及两种文件:

  • .so (Model Library):模型的结构描述库。
  • .bin (Context Binary):针对特定 NPU 硬件优化并序列化后的图模型(这是性能最高的形态)。

2. 生成模型动态库 (.so)

使用 qnn-onnx-converter 将 ONNX 转为 C++ 代码,再通过 qnn-model-lib-generator 编译 。

qnn-model-lib-generator \ -c qwen_model.cpp \ -b qwen_model.bin \ -o ./model_libs \ -t aarch64-android # 指定目标平台为 Android ARM64
  • 输出结果:在 ./model_libs/aarch64-android/ 目录下,你会得到一个 libqwen_model.so
  • 用途:这个文件包含了模型的拓扑结构,可以被高通的推理引擎加载。

3. 生成上下文二进制文件 (.bin) —— 性能核心

为了实现“秒开”和极致加速,必须生成针对 HTP(Hexagon Tensor Processor)优化的 Context Binary 。

qnn-context-binary-generator \ --model ./model_libs/aarch64-android/libqwen_model.so \ --backend libQnnHtp.so \ --output_dir ./context_out \ --binary_file qwen_htp_context 
  • --backend libQnnHtp.so:这步至关重要,它指定使用 HTP (Hexagon Tensor Processor) 后端,即真正的 NPU 加速。
  • 输出结果qwen_htp_context.bin
注意:此步骤通常建议在连接了真机的情况下运行(通过 adb),或者使用高通提供的模拟器,因为生成 context 需要针对具体的芯片架构(如 v73, v75)。

六、 手机端集成与验证

在 Android 项目中,通过 C++/JNI 调用 QNN API 加载 qwen_htp_context.bin 即可执行推理 。

现在你手里有了:

  1. 模型资产:qwen_htp_context.bin。
  2. 推理引擎库:从 QNN SDK 中提取的 libQnnHtp.so, libQnnSystem.so 等。

快速验证:
在写 App 前,先用 qnn-net-run 工具在手机 shell 中测试 :

[cite_start]./qnn-net-run --container qwen_htp_context.bin --backend libQnnHtp.so --input_list input_data.txt [cite: 198, 199, 200]

若能正常输出 Tensor 结果,说明模型已成功跑在 NPU 上!

推理代码核心逻辑(C++/JNI):
在 Android 的 C++ 层,你需要调用 QNN API 来加载这个 .bin 文件:

// 1. 初始化 QNN 实例 Qnn_BackendHandle_t backendHandle; QnnBackend_initialize(..., &backendHandle); // 2. 加载之前生成的 Context Binary Qnn_ContextHandle_t contextHandle; // 通过读取 qwen_htp_context.bin 的 buffer 传入 QnnContext_createFromBinary(backendHandle, deviceHandle, ..., binaryBuffer, binarySize, &contextHandle, ...); // 3. 准备 Tensor 数据并执行推理 QnnGraph_execute(graphHandle, inputTensors, numInputs, outputTensors, numOutputs, ...); 

🛠 避坑总结

  • 版本一致性: 编译 .so 的 NDK 版本必须与 Android 项目一致 。
  • 内存溢出: 若 Context Length 设得太大,NPU 内存会溢出,建议从 512 或 1024 测起 。
  • 算子支持: 若生成 Binary 报错,需回到 ONNX 阶段进行算子融合或替换 。

希望这篇指南能帮你顺利把大模型装进兜里!如果有问题,欢迎在评论区交流。

如果你觉得有用,欢迎点赞、收藏、关注!

Read more

Java+Leaflet:湖南省道路长度WebGIS的构建与实践

Java+Leaflet:湖南省道路长度WebGIS的构建与实践

目录 前言 一、基础空间数据简介 1、涉及相关表 2、省域道路长度检索 二、Java后台实现 1、道路视图对象 2、Mapper空间检索查询 3、控制API实现 三、WebGIS界面实现 1、里程图例及初始化 2、各地市信息展示 四、成果展示 1、总体展示 2、分区域说明 五、总结 前言         在当今数字化时代,地理信息系统(GIS)技术在各个领域都发挥着至关重要的作用。它不仅为城市规划、交通管理、环境保护等提供了强大的技术支持,也为公众获取地理信息提供了便捷的途径。湖南省作为中国中部地区的重要省份,拥有复杂的地理环境和庞大的交通网络。如何高效地管理和展示湖南省的道路长度信息,对于交通规划、物流运输以及公众出行都具有极其重要的意义。因此,我们开展了基于Java和Leaflet的湖南省道路长度WebGIS系统的构建与实践研究。         湖南省地处中国中部,交通网络密集且复杂。随着经济的快速发展和城市化进程的加快,湖南省的道路建设不断推进,

PCTF2025(web后半部分)

PCTF2025(web后半部分)

神秘商店 打开题目只有一个登录框 登录admin 利用全角来注册登录 后端代码有转换,全角能够绕过后端对admin的检测,然后把全角admin识别成正常的admin,造成覆盖注册,修改admin密码 注册admin,其中n为全角 利用整数溢出4294967246到50,购买flag 可以直接脚本登录 import requests def exploit(): url = "http://challenge2.pctf.top:32735" session = requests.Session() print("[+] 注册管理员账户...") users = { "username": "admin", "password": "123456" } response = session.post(f&

下载安装Microsoft Edge Webview2教程

下载安装Microsoft Edge Webview2教程

视频教程 Windows 10/11系统 Webview2安装——win10/11 Windows 7系统 Webview2安装——Win7 图文教程 官网下载最新版Webview2安装包 点击下载安装 官网地址:Microsoft Edge WebView2 | Microsoft Edge Developer 1. 进入官网,点击下载按钮 2. 点击左侧常青引导程序下载按钮 3. 在弹出的页面点击接受并下载,右上角下载管理页面在下载完成后有文件弹出 4. 在游览器下载管理页面直接点击打开文件进行软件的安装 5. 软件安装中,安装完成后无需手动点击自动弹出消失。 graph TD A[安装码尚云标签] --> B{判断安装情况} B -->|Yes| C[打开软件进行标签设计] B --&

openclaw web UI 无法访问 not found

## 问题解决总结 根本原因 :Gateway 的 resolveControlUiRootSync 函数在自动查找控制 UI 目录时,没有包含 node_modules/openclaw/dist/control-ui 作为候选路径。手动指定相对路径时,可能因为工作目录解析问题无法正确找到目录。 最终解决方案 : 1. 将控制 UI 文件从 node_modules/openclaw/dist/control-ui 复制到项目根目录       E:\你实际的目录\control-ui       (建立一个英文,且没有符号的目录,“-”和“_",会引起混淆) 2. 在配置文件中使用绝对路径指定 controlUi.root: "E:\\你实际的目录\\control-ui" 编辑 openclaw.json "