- 大模型
- 本地部署
- 推理引擎
- 模型量化
- 嵌入式
【免费下载链接】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 亿参数、具备 推理链输出的蒸馏模型)。示例完整覆盖了三个阶段:
- 模型转换(Host 侧):用 rkllm-toolkit 加载 Hugging Face 模型,通过 W8A8 量化生成 RK3588 可运行的
.rkllm模型文件; - C++ 板端推理(Target 侧):在
deploy目录提供llm_demo.cpp源码与 CMake 构建脚本,编译出板端可执行程序; - 运行与调优:通过
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.cpp | RKLLM 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.5Bgenerate_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.pyexport_rkllm.py 展示了 rkllm-toolkit 最核心的"加载-构建-导出"三段式 API:
- 加载:
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); - 构建(量化):
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与板端推理时的上下文长度保持一致; - 导出:
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_aarch645.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
相关推荐
DeepSeek-R1-Distill-Qwen-1.5B模型转换:ONNX格式部署性能测试
DeepSeek R1 Distill Qwen 1.5B模型转换:ONNX格式部署性能测试 引言:为什么需要模型格式转换? 你是否遇到过这些问题:训练好的大模
DeepSeek-R1-Distill-Qwen-1.5B移动端SDK:iOS/Android集成指南
DeepSeek R1 Distill Qwen 1.5B移动端SDK:iOS/Android集成指南 引言:移动端AI推理的痛点与解决方案 你是否还在为移动端
15分钟掌握AntiDupl.NET:免费开源图片去重工具完整指南
15分钟掌握AntiDupl.NET:免费开源图片去重工具完整指南 你是否经常在整理照片时发现同一张图片有多个副本?或者电脑存储空间被无数相似图片悄悄占据?An
图像处理桌面应用
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考