☰
rknn-llm 实战指南:基于 rkllm-api-demo 将 DeepSeek-R1-Distill-Qwen-1.5B 量化转换并在 RK3588 上完成端侧 C++ 推理部署
2026/10/5 13:06:33 网站建设 项目流程
  • 大模型
  • 本地部署
  • 推理引擎
  • 模型量化
  • 嵌入式

【免费下载链接】rknn-llm

项目地址:https://gitcode.com/gh_mirrors/rk/rknn-llm
点击查看免费下载

本文围绕 rknn-llm 仓库中的examples/rkllm_api_demo示例,完整讲解一条可落地的端侧大模型部署链路:从环境准备、使用 rkllm-toolkit 生成量化校准数据与导出.rkllm模型,到借助 rkllm-runtime 的 C 风格 API 编写板端推理程序,再到在 RK3588 开发板上编译、推送、固定频率并运行 llm_demo 的完整操作。读完本文,你将掌握 RKLLM 模型转换与 RKLLM C++ API 的核心调用方式,并可直接复现"鸡兔同笼"等示例问题的端侧推理结果。

一、示例概览与工作流程

rkllm_api_demo 是仓库中面向纯文本大模型的 API 部署示例,其目标模型为开源的 DeepSeek-R1-Distill-Qwen-1.5B(一个 15 亿参数、具备 推理链输出的蒸馏模型)。示例完整覆盖了三个阶段:

  1. 模型转换(Host 侧):用 rkllm-toolkit 加载 Hugging Face 模型,通过 W8A8 量化生成 RK3588 可运行的.rkllm模型文件;
  2. C++ 板端推理(Target 侧):在deploy目录提供llm_demo.cpp源码与 CMake 构建脚本,编译出板端可执行程序;
  3. 运行与调优:通过adb推送、固定 CPU/NPU/GPU/DDR 频率脚本、RKLLM_LOG_LEVEL日志开关获得可复现的性能观测环境。

整体链路与仓库目录的对应关系如下:

阶段仓库位置作用
环境依赖rkllm-toolkit/packages/、rkllm-runtime/模型转换与板端推理所需工具链与运行库
校准数据生成generate_data_quant.py用 fp16 模型生成量化校准样本,产出data_quant.json
模型导出export_rkllm.py加载模型、W8A8 量化、导出.rkllm文件
板端推理llm_demo.cppRKLLM C API 完整调用示例(初始化、采样参数、回调、推理)
构建配置CMakeLists.txt自动匹配 Linux/Android 及架构,链接 librkllmrt.so
运行辅助fix_freq_rk3588.sh固定 RK3588 各算力单元频率,保证性能测试可复现

二、环境要求

原文档明确给出如下依赖(以当前仓库配套的 rkllm-toolkit 1.3.0 为准):

rkllm-toolkit>=1.3.0 rkllm-runtime>=1.3.0 python>=3.9

仓库内已随附与 1.3.0 版本对应的安装资源,可直接使用:

  • Toolkit Python 包:rkllm-toolkit-1.3.0-cp310-cp310-linux_x86_64.whl(另提供 cp311、cp312、cp39 变体,以及 requirements.txt 依赖清单);
  • Runtime 板端动态库:Linux/aarch64/librkllmrt.so 与 Android/arm64-v8a/librkllmrt.so,并提供统一的 rkllm.h 头文件。

需要注意版本匹配:仓库同时保留了旧版Readme.md(对应 rkllm-toolkit 1.2.x、python>=3.8),新版README.md已将要求提升到 1.3.0+,请以 README.md 为准。若不想自行转换,文档还提到可从 rkllm_model_zoo 直接下载已转换好的 rkllm 模型文件。

三、模型转换:生成量化校准数据并导出 rkllm 模型

转换在Host(x86 PC)侧完成,分为两步。

3.1 用 fp16 模型生成校准数据 data_quant.json

量化前必须先准备校准样本。示例采用"用 fp16 精度模型的实际生成结果作为量化校准数据"的策略:即让原始模型在典型任务上生成输出,把{输入, 目标输出}对写入data_quant.json,供后续量化环节参考分布。

执行命令(export目录下):

cd export python generate_data_quant.py -m /path/to/DeepSeek-R1-Distill-Qwen-1.5B

generate_data_quant.py 的核心行为可从源码确认:

  • 通过AutoTokenizer.from_pretrained/AutoModelForCausalLM.from_pretrained加载模型(trust_remote_code=True),按torch.cuda.is_available()自动选择cuda或cpu设备;
  • 内置了一批跨领域的中英文校准样本:数学应用题、函数式编程题(fizz_buzz、is_multiply_prime等)、中英文翻译、中文歧义句、七言绝句创作等,覆盖多样化的 token 分布,符合"根据模型特点与使用场景准备校准样本"的注释要求;
  • 支持命令行参数自定义校准流程:--output-file(校准文件输出路径,默认./data_quant.json)、--apply_chat_template(是否套用模型的 chat template,默认开启)、--max_new_tokens(默认 128)、--temperature(默认 0.6)、--repetition_penalty(默认 1.1);
  • 每个样本经tokenizer.apply_chat_template格式化后喂给模型做model.generate,解码结果中去掉 prompt 部分,得到{"input": 格式化后的用户输入, "target": 模型生成结果}并写入 JSON。

仓库中已随附一份 data_quant.json 实际产物,其条目格式如下(JSON 转义后展示):

[ {"input": "在农业生产中被当作极其重要的劳动对象发挥作用,最主要的不可替代的基本生产资料是\nA. 农业生产工具\nB. 土地\nC. 劳动力\nD. 资金", "target": "\n\nTo determine the correct answer, let's analyze each option..."}, {"input": "设是 $f(x)$ 偶函数, $\\varphi(x)$ 是奇函数, 则下列函数(假设都有意义)中是奇函数的是 ( ).", "target": "..."} ]

3.2 导出 RK3588 可运行的 W8A8 量化模型

校准数据就绪后,运行:

python export_rkllm.py

export_rkllm.py 展示了 rkllm-toolkit 最核心的"加载-构建-导出"三段式 API:

  1. 加载:RKLLM().load_huggingface(model=modelpath, model_lora=None, device='cuda', dtype="float32", custom_config=None, load_weight=True)。源码注释给出关键参数说明:device可选['cpu', 'cuda'];dtype可选['float32', 'float16', 'bfloat16'],其中bfloat16/float16可显著降低内存占用,但精度略低于float32,需按硬件条件取舍;若持有 GGUF 模型也可改走llm.load_gguf(model=modelpath);
  2. 构建(量化):llm.build(do_quantization=True, optimization_level=1, quantized_dtype="W8A8", quantized_algorithm="normal", target_platform="RK3588", num_npu_core=3, extra_qparams=None, dataset="./data_quant.json", hybrid_rate=0, max_context=4096)。其中量化算法有明确的选型建议:w8a8/w8a8_gx推荐配合normal算法,w4a16/w4a16_gx推荐配合grq算法;num_npu_core=3表示使用 RK3588 的 3 个 NPU 核心;max_context=4096与板端推理时的上下文长度保持一致;
  3. 导出:llm.export_rkllm(f"./{os.path.basename(modelpath)}_{quantized_dtype}_{target_platform}.rkllm"),最终生成形如DeepSeek-R1-Distill-Qwen-1.5B_W8A8_RK3588.rkllm的模型文件(下文运行示例即以此文件为准)。

每个步骤均检查返回值ret != 0并打印Load/Build/Export model failed!后退出,便于定位失败环节。

四、C++ 板端推理:编译构建 llm_demo

转换完成后的.rkllm模型将被部署到 RK3588 设备上,由deploy目录的 C++ 示例执行推理。

4.1 编译与产物

按原文档说明,可在deploy目录下运行./build-linux.sh(Linux)或./build-android.sh(Android)脚本完成交叉编译(需将脚本中的交叉编译器路径替换为实际路径),产物为deploy/install/demo_Linux_aarch64目录,内含可执行文件llm_demo与lib动态库目录。

本仓库实际提交中未包含这两个 build 脚本,但提供了功能等价的 CMakeLists.txt,其构建逻辑可印证 README 描述:

  • 自动判定目标平台:Android使用CMAKE_ANDROID_ARCH_ABI;Linux下按指针宽度区分aarch64/armhf,若编译器匹配uclibc则追加_uclibc后缀;
  • 通过${CMAKE_SOURCE_DIR}/../../../rkllm-runtime/${CMAKE_SYSTEM_NAME}/librkllm_api定位运行时库:Android 链接librkllmrt.so并额外链接log与OpenMP::OpenMP_CXX(对应 Android 目录下的 libomp.so),Linux 直接链接librkllmrt.so;
  • 安装路径统一为install/demo_${CMAKE_SYSTEM_NAME}_${TARGET_LIB_ARCH},可执行文件装到根目录、运行时库装到lib子目录——这正是 README 中adb push install/demo_Linux_aarch64 /data所推送的目录结构。

4.2 源码级解读:llm_demo.cpp 的完整调用链

llm_demo.cpp 是与 rkllm.h 一一对应的完整示例,其调用链可概括为rkllm_createDefaultParam → rkllm_init → rkllm_run(循环)→ rkllm_destroy:

初始化参数(RKLLMParam):rkllm_createDefaultParam()生成默认参数后,示例显式配置了:

param.model_path = argv[1]; // 模型路径 param.top_k = 1; // Top-K 采样 param.top_p = 0.95; // 核采样阈值 param.temperature = 0.8; // 采样温度 param.repeat_penalty = 1.1; // 重复惩罚 param.frequency_penalty = 0.0; param.presence_penalty = 0.0; param.max_new_tokens = atoi(argv[2]); // 最大新生成 token 数 param.max_context_len = atoi(argv[3]); // 最大上下文长度 param.skip_special_token = true; param.extend_param.base_domain_id = 0; param.extend_param.embed_flash = 1; // 词嵌入向量从 flash 查询

命令行调用./llm_demo /path/to/your/rkllm/model 2048 4096时,2048即max_new_tokens、4096即max_context_len。对照 rkllm.h 中RKLLMParam结构体,还可看到更多可选项:mirostat/mirostat_tau/mirostat_eta(Mirostat 采样)、ignore_eos_token、is_async,以及RKLLMExtendParam中的enabled_cpus_num/enabled_cpus_mask(指定参与推理的 CPU 核心与掩码,头文件顶部定义了CPU0~CPU7位掩码)、n_batch(>1 时开启批量推理)等。

回调函数:推理结果通过RKLLMCallback.result_callback回调返回,回调按LLMCallState状态机分发:

  • RKLLM_RUN_NORMAL:逐段输出result->text;若开启GET_LAST_HIDDEN_LAYER功能,回调中会回传last_hidden_layer的内存指针、num_tokens与embd_size,示例将其以二进制写入last_hidden_layer.bin(注释提醒:该指针需在本次回调内及时读取,否则下次回调会释放);
  • RKLLM_RUN_FINISH:本次生成结束,输出换行;
  • RKLLM_RUN_ERROR:报错。

推理输入(RKLLMInput):input_type = RKLLM_INPUT_PROMPT、role = "user"、prompt_input指向用户输入文本。头文件显示输入类型还有RKLLM_INPUT_TOKEN(token 序列)、RKLLM_INPUT_EMBED(embedding 向量)、RKLLM_INPUT_MULTIMODAL(图文多模态,对应仓库中 multimodal_model_demo 示例);RKLLMInferParam.mode = RKLLM_INFER_GENERATE为普通生成模式,另有GET_LAST_HIDDEN_LAYER与GET_LOGITS两种模式。

交互循环:keep_history = 0表示单轮对话、不保留历史上下文;输入exit退出、clear调用rkllm_clear_kv_cache清空 KV cache;输入序号0/1会映射到内置的两个示例问题。代码中还被注释保留了两类进阶用法:rkllm_load_lora加载 LoRA 适配器(可挂多个、按名称切换),以及RKLLMPromptCacheParam的 prompt cache 保存/加载。

五、在 RK3588 开发板上运行

5.1 推送文件到设备

cd deploy # for linux ./build-linux.sh # for android ./build-android.sh # push install dir to device adb push install/demo_Linux_aarch64 /data # push model file to device adb push DeepSeek-R1-Distill-Qwen-1.5B.rkllm /data/demo_Linux_aarch64 # push the appropriate fixed-frequency script to the device adb push ../../../scripts/fix_freq_rk3588.sh /data/demo_Linux_aarch64

5.2 运行 Demo

adb shell cd /data/demo_Linux_aarch64 # export lib path export LD_LIBRARY_PATH=./lib # Execute the fixed-frequency script sh fix_freq_rk3588.sh # Set the logging level for performance analysis export RKLLM_LOG_LEVEL=1 ./llm_demo /path/to/your/rkllm/model 2048 4096

预期先打印:

rkllm init start rkllm init success

随后进入交互菜单(两个预置问题):

**********************可输入以下问题对应序号获取回答/或自定义输入******************** [0] 现有一笼子,里面有鸡和兔子若干只,数一数,共有头14个,腿38条,求鸡和兔子各有多少只? [1] 有28位小朋友排成一行,从左边开始数第10位是学豆,从右边开始数他是第几位? *************************************************************************

5.3 固定频率脚本的作用

运行前执行sh fix_freq_rk3588.sh是保证性能数据可复现的关键。从 fix_freq_rk3588.sh 源码可见其具体动作:关闭 cpu0–cpu7 的 cpuidle 深度睡眠;将 NPU(fdab0000.npu)切到userspacegovernor 并固定 1 GHz;将 CPU 三个频点域(policy0/4/6)分别固定到 1.8 GHz / 2.352 GHz / 2.352 GHz;固定 GPU(fb000000.gpu)1 GHz、DDR(dmc)2112 MHz。全部通过/sys设备节点写频率,并回读cur_freq校验。仓库 scripts 目录还提供 RK3562、RK3576、RV1126B 等平台的对应脚本,以及 eval_perf_watch_cpu.sh、eval_perf_watch_npu.sh 性能观测脚本。

RKLLM_LOG_LEVEL=1为性能分析日志开关:设置后运行时库会输出 prefill/generate 两阶段的耗时与 token 数统计(对应 rkllm.h 中RKLLMPerfStat的prefill_time_ms/prefill_tokens/generate_time_ms/generate_tokens/memory_usage_mb字段)。

六、端侧运行效果(DeepSeek-R1-Distill-Qwen-1.5B_W8A8_RK3588.rkllm)

示例 1:鸡兔同笼

user: 0 现有一笼子,里面有鸡和兔子若干只,数一数,共有头14个,腿38条,求鸡和兔子各有多少只? robot: <think> 首先,设鸡的数量为x,兔子的数量为y。 ... 因此,鸡有9只,兔子有5只。 </think> 要解决这个问题,我们可以设鸡的数量为 \( x \),兔子的数量为 \( y \)。根据题目给出的条件: 1. **头的总数**:每只鸡和兔子都有一个头,所以: \[ x + y = 14 \] 2. **腿的总数**:鸡有两条腿,兔子有四条腿,总腿数为38条,因此: \[ 2x + 4y = 38 \] ... **最终答案:** 鸡有 \(\boxed{9}\) 只,兔子有 \(\boxed{5}\) 只。

示例 2:排队问题

user: 1 有28位小朋友排成一行,从左边开始数第10位是学豆,从右边开始数他是第几位? robot: <think> 首先,总共有28位小朋友。 从左边开始数,第10位是学豆的位置。 因此,从右边开始数,学豆的位置是从右边数的第(28 - 10 + 1) = 第19位。 </think> **最终答案:** \boxed{19}

可以看到,部署在 RK3588 上的 W8A8 量化模型完整保留了 R1-Distill 系列的<think>推理链能力,并正确输出最终答案——这也验证了前文"量化校准数据应覆盖多样推理任务"的实践必要性。

七、小结与进阶方向

至此,一条完整的 RKLLM 端侧部署链路已经走通:rkllm-toolkit负责将 Hugging Face 开源模型转换为 W8A8 量化.rkllm文件,rkllm-runtime提供的 C API(rkllm_init/rkllm_run/rkllm_destroy+ 结果回调)支撑板端推理程序,而固定频率脚本与RKLLM_LOG_LEVEL则让性能评估具备可复现性。

若需继续深入,可关注本仓库的关联示例与资源:

  • 图文多模态模型部署:见 multimodal_model_demo,其RKLLMInput的RKLLM_INPUT_MULTIMODAL输入类型与本例一脉相承;
  • 更完整的 API 定义与结构体注释(LoRA、prompt cache、cross-attention、函数调用等):见 rkllm.h;
  • 各平台固定频率与性能观测脚本:见 scripts 目录;
  • 官方 SDK 中文/英文手册:见 doc/Rockchip_RKLLM_SDK_CN_1.3.0.pdf 与 doc/Rockchip_RKLLM_SDK_EN_1.3.0.pdf。
  • 大模型
  • 本地部署
  • 推理引擎
  • 模型量化
  • 嵌入式

【免费下载链接】rknn-llm

项目地址:https://gitcode.com/gh_mirrors/rk/rknn-llm
点击查看免费下载
上一篇:PX4 无人机线缆布线基础指南:信号线缆、EMI 抑制与颜色编码规范
下一篇:MAA明日方舟自动化助手:从入门到精通的智能游戏管理方案

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询