瑞芯微(EASY EAI)RV1126B 模型部署API说明

1. 回调函数定义

回调函数是用于接收模型实时输出的结果。在初始化 RKLLM 时回调函数会被绑定,在模型推理过程中不断将结果输出至回调函数中,并且每次回调只返回一个 token。

示例代码如下,该回调函数将输出结果实时地打印输出到终端中:

复制代码
int callback(RKLLMResult* result, void* userdata, LLMCallState state)
{
if(state == LLM_RUN_NORMAL){
printf("%s", result->text);
}
if (state == LLM_RUN_FINISH) {
printf("finish\n");
} else if (state == LLM_RUN_ERROR){
printf("\run error\n");
}
return 0;
}

(1)LLMCallState 是一个状态标志,其具体定义如下:

用户在回调函数的设计过程中,可以根据 LLMCallState 的不同状态设置不同的后处理行为。

(2)RKLLMResult 是返回值结构体,其具体定义如下:

(3)RKLLMResultLogits 是返回值结构体,其具体定义如下:

用户在回调函数的设计过程中,可以根据 RKLLMResult 中值设置不同的后处理行为。

2. 参数结构体 RKLLMParam 定义

结构体 RKLLMParam 用于描述、定义 RKLLM 的详细信息,具体的定义如下:

在实际的代码构建中,RKLLMParam 需要调用 rkllm_createDefaultParam()函数来初始化定义,并根据需求设置相应的模型参数。示例代码如下

复制代码
RKLLMParam param = rkllm_createDefaultParam();
param.model_path = "model.rkllm";
param.top_k = 1;
param.max_new_tokens = 256;
param.max_context_len = 512;

3. 输入结构体定义

为适应不同的输入数据,定义了 RKLLMInput 输入结构体,目前可接受文本、图片和文本、Token id 以及编码向量四种形式的输入,具体的定义如下:

当输入数据是纯文本时,使用 input_data 直接输入;当输入数据是 Token id、编码向量以及图 片和文本时,RKLLMInput 需要搭配RKLLMTokenInput, RKLLMEmbedInput 以及RKLLMMultiModelInput 三个输入结构体使用,具体的介绍如下:

(1)RKLLMTokenInput 是接收 Token id 的输入结构体,具体的定义如下:

(2)RKLLMEmbedInput 是接收编码向量的输入结构体,具体的定义如下:

(3)RKLLMMultiModelInput 是接收图片和文本的输入结构体,具体的定义如下:

RKLLM 支持不同的推理模式,定义了 RKLLMInferParam 结构体,目前可支持在推理过程与预加载的 LoRA 模型联合推理,或保存 Prompt Cache 用于后续推理加速,具体的定义如下:

使用 RKLLMPromptCacheParam 的示例如下:

复制代码
//初始化并设置 Prompt Cache 参数(如果需要使用 prompt cache)
RKLLMPromptCacheParam prompt_cache_params;
prompt_cache_params.save_prompt_cache = true; // 是否保存 prompt cache
prompt_cache_params.prompt_cache_path = "./prompt_cache.bin"; // 若需要保存 prompt cache, 指定 cache 文件路径
rkllm_infer_params.prompt_cache_params = &prompt_cache_params;

4. 初始化模型

在进行模型的初始化之前,需要提前定义 LLMHandle 句柄,该句柄用于模型的初始化、推理和资源释放过程。注意,正确的模型推理流程需要统一这 3 个流程中的 LLMHandle 句柄对象。在模型推理前,用户需要通过 rkllm_init()函数完成模型的初始化,具体函数的定义如下:

示例代码如下:

复制代码
LLMHandle llmHandle = nullptr; 
rkllm_init(&llmHandle, &param, callback);

5. 模型推理

用户在完成 RKLLM 模型的初始化流程后,即可通过 rkllm_run()函数进行模型推理,并可以通过初始化时预先定义的回调函数对实时推理结果进行处理;rkllm_run()的具体函数定义如下:

6. 模型中断

在进行模型推理时,用户可以调用 rkllm_abort()函数中断推理进程,具体的函数定义如下:

示例代码如下:

复制代码
// 其中 llmHandle 为模型初始化时传入的句柄 
rkllm_abort(llmHandle);

7. 释放模型资源

在完成全部的模型推理调用后,用户需要调用 rkllm_destroy()函数进行 RKLLM 模型的销毁,并释放所申请的 CPU、NPU 计算资源,以供其他进程、模型的调用。具体的函数定义如下:

示例代码如下:

复制代码
// 其中 llmHandle 为模型初始化时传入的句柄 
rkllm_destroy(llmHandle);

8. LoRA 模型加载

RKLLM 支持在推理基础模型的同时推理 LoRA 模型,可以在调用 rkllm_run接口前通过rkllm_load_lora接口加载LoRA模型。RKLLM支持加载多个LoRA 模 型,每调用一次rkllm_load_lora 可加载一个 LoRA 模型。具体的函数定义如下:

加载 LoRA 的示例代码如下:

复制代码
RKLLMLoraAdapter lora_adapter;
memset(&lora_adapter, 0, sizeof(RKLLMLoraAdapter));
lora_adapter.lora_adapter_path = "lora.rkllm";
lora_adapter.lora_adapter_name = "lora_name";
lora_adapter.scale = 1.0;
ret = rkllm_load_lora(llmHandle, &lora_adapter);
if (ret != 0) {
   printf("\nload lora failed\n");
}

9. Prompt Cache 管理

在模型推理过程中,Prefill 阶段通常消耗大量的计算资源和时间,特别是在 Prompt 很长的情况下。为了加速这一过程,RKLLM 支持文件加载 Prompt Cache,通过复用缓存中的内容,可以显著减少 Prefill 阶段的耗时,从而提升整体推理效率。在调用 rkllm_run 接口进行推理之前,请确保正确配置 prompt_cache_params 参数。这一步骤允许模型在推理结束后生成对应的 Prompt Cache 文件。当首次运行推理时,系统会自动生成一个 Prompt Cache 文件。该文件包含了 Prefill 阶段所需的中间结果,以便后续使用。在后续的推理任务中,可以通过调用 rkllm_load_prompt_cache 接口加载之前生成的 Prompt Cache 文件。

具体的函数定义如下:

注意:

RKLLM 会从头开始检测输入与 prompt_cache 中相同的部分,假设您的输入格式固定为PROMPT_PREFIX + text + PROMPT_POSTFIX,您可以仅对 PROMPT_PREFIX 部分生成Prompt Cache,加载后在后续的推理中即可复用这部分结果。

RKLLM 支持生成多个 Prompt Cache 文件。在需要使用不同的 Prompt Cache 时,只需加载对应的文件即可。当需要切换到另一个 Prompt Cache 文件或不再需要使用加载的 PromptCache 时,请显式调用 rkllm_release_prompt_cache 接口进行释放。

加载 Prompt Cache 的示例代码如下:

复制代码
// 初始化并设置 Prompt Cache 参数,并调用 run 接口生成 prompt cache 文件
RKLLMPromptCacheParam prompt_cache_params;

// 是否保存 prompt cache
prompt_cache_params.save_prompt_cache = true;
// 若需要保存 prompt cache, 指定 cache 文件绝对路径
prompt_cache_params.prompt_cache_path = "/data/prompt_cache.bin";
rkllm_infer_params.prompt_cache_params = &prompt_cache_params;

rkllm_infer_params.mode = RKLLM_INFER_GENERATE;
rkllm_input.input_type = RKLLM_INPUT_PROMPT;
rkllm_input.prompt_input = (char *)prompt.c_str();
rkllm_run(llmHandle, &rkllm_input, &rkllm_infer_params, NULL);

// 加载 prompt cache 文件,减少 prefill 耗时
rkllm_load_prompt_cache(llmHandle, "./prompt_cache.bin");
if (ret != 0) {
   printf("\nload Prompt Cache failed\n");
}

rkllm_run(llmHandle, &rkllm_input, &rkllm_infer_params, NULL);

10. KV Cache 管理

RKLLM 支持手动清除 KV 缓存,可用于单轮和多轮对话。在调用清除缓存功能时,如果keep_system_prompt 设置为 1,则会保留系统提示词(如果存在);否则,将清空整个缓存。函数定义如下:

11. Chat Template 设置

当用户使用文本输入时,RKLLM 会对默认文本进行前处理,前处理时会根据 Hugging Face模型 tokenizer_config.json 文件中的 chat_template 字段,自动解析并应用提示词模板。如需自定义,可使用以下函数进行重置,其中,system_prompt 作为系统提示词,用于指导模型行为,prompt_prefix 为用户输入前缀,prompt_postfix 为用户输入后缀,具体的函数定义如下。当用户重置模板后,enable_thinking 选项将失效,用户需要在自定义的 prompt 中进行配置。

12. Function Calling 设置

RKLLM 支持通过 Function Calling 实现模型与外部系统的结构化交互,扩展大模型能力边界,提升模型在知识补充、数据精确获取等任务中的表现。当启用 Function Calling 模式后,应用程序可将函数定义传入模型,模型根据用户提问判断是否需要调用函数,并根据设定的格式输出调用请求。应用程序根据模型意图调用相应函数并将结果返回,模型最终基于结果继续对话。

RKLLM 支持的 Function Calling 设置函数定义如下:

复制代码
示例代码如下:

首先配置 tools

std::string system_prompt = "You are Qwen, created by Alibaba Cloud.
You are a helpful assistant.\n\nCurrent Date: 2024-09-30";
std::string tools = R"([
{
"type": "function",
"function": {
"name": "get_current_temperature",
"description": "Get current temperature at a location.",
"parameters": {
"type": "object",
"properties": {
"location": {
"type": "string",
"description": "The location to get the
temperature for, in the format \"City, State, Country\"."
},
"unit": {
"type": "string",
"enum": ["celsius", "fahrenheit"],
"description": "The unit to return the temperature
in. Defaults to \"celsius\"."
}
},
"required": ["location"]
}
}
},
{
"type": "function",
"function": {
"name": "get_temperature_date",
"description": "Get temperature at a location and date.",
"parameters": {
"type": "object",
"properties": {
"location": {
"type": "string",
"description": "The location to get the
temperature for, in the format \"City, State, Country\"."
},
"date": {
"type": "string",
"description": "The date to get the temperature
for, in the format \"Year-Month-Day\"."
},
"unit": {
"type": "string",
"enum": ["celsius", "fahrenheit"],
"description": "The unit to return the temperature
in. Defaults to \"celsius\"."
}
},
"required": ["location", "date"]
}
}
}
])";
rkllm_set_function_tools(llmHandle, system_prompt.c_str(),
tools.c_str(), "tool_response");

接下来,在推理过程中配合 rkllm_run 实现调用链:

复制代码
// 用户提问
RKLLMInferParam rkllm_infer_params;
memset(&rkllm_infer_params, 0, sizeof(RKLLMInferParam));
rkllm_infer_params.mode = RKLLM_INFER_GENERATE;
rkllm_infer_params.keep_history = 0;

RKLLMInput rkllm_input;
rkllm_input.input_type = RKLLM_INPUT_PROMPT;
rkllm_input.enable_thinking = false;

rkllm_input.role = "user";
rkllm_input.prompt_input = "What's the temperature in San Francisco
now? How about tomorrow?";

rkllm_run(llmHandle, &rkllm_input, &rkllm_infer_params, NULL);

// 第一次调用 rkllm_run 会返回需要调用的工具函数名
<tool_call>
{"name": "get_current_temperature", "arguments": {"location": "San
Francisco"}}
</tool_call>
<tool_call>
{"name": "get_temperature_date", "arguments": {"location": "San
Francisco", "date": "2024-10-01"}}
</tool_call>

// 将工具调用结果返回给大模型,role 必须配置为 tool
rkllm_input.role = "tool";
rkllm_input.prompt_input = R"([
   {
       "temperature": 26.1,
       "location": "San Francisco",
       "unit": "celsius"
   },
   {
       "temperature": 25.9,
       "location": "San Francisco",
       "date": "2024-09-30",
       "unit": "celsius"
   }
])";

rkllm_run(llmHandle, &rkllm_input, &rkllm_infer_params, NULL);

// 最终结果
"The current temperature in San Francisco is 26.1°C. Tomorrow, the
temperature is expected to be 25.9°C."

13. Cross attention 设置

RKLLM 支持 cross attention 推理,用户可以通过如下函数将 encoder 生成的 K/V 缓存、掩码以及位置信息输入给解码器用于交叉注意力计算。cross attention 只支持自定义模型推理,模型的转换方式详见 3.1.6 章节。

14. 多 batch 并行推理

RKLLM 支持同时推理多个 batch(建议 batch 数量不超过 8)。

使用两个 batch 进行推理的示例代码如下,主要为:

  • 模型初始化时需要将 param.extend_param.n_batch 参数设置为 2;

  • 使用多 batch 进行推理时,输入 RLLLMInput 和 callback 中的 RKLLMResult 均为n_batch 大小的数组;

  • callback 中所有 batch 推理结果同步返回,当某个 batch 返回的 token id 为负数时,表示该 batch 推理结束,所有 batch 都推理结束时才会终止推理;

  • 在 callback 中处理返回的文本时,必须先判断返回的 text 是否为空指针,再进行赋值操作。

    #include <string.h>
    #include <unistd.h>
    #include <string>
    #include "rkllm.h"
    #include <fstream>
    #include <iostream>
    #include <csignal>
    #include <vector>

    using namespace std;
    LLMHandle llmHandle = nullptr;

    std::string output_texts[10];

    int callback(RKLLMResult *result, void *userdata, LLMCallState state)
    {
    if (state == RKLLM_RUN_FINISH)
    {
    printf("\nrkllm run finish\n");
    } else if (state == RKLLM_RUN_ERROR) {
    printf("\nrkllm run error\n");
    } else if (state == RKLLM_RUN_NORMAL) {
    RKLLMResult batch1 = result[0];
    RKLLMResult batch2 = result[1];
    if (batch1.text) {
    output_texts[0] += batch1.text;
    printf("batch 0 %s\n", output_texts[0].c_str());
    }
    if (batch2.text) {
    output_texts[1] += batch2.text;;
    printf("batch 1 %s\n", output_texts[1].c_str());
    }
    }
    return 0;
    }

    int main(int argc, char **argv)
    {
    if (argc < 4) {
    std::cerr << "Usage: " << argv[0] << " model_path
    max_new_tokens max_context_len\n";
    return 1;
    }

    RKLLMParam param = rkllm_createDefaultParam();
    param.model_path = argv[1];
    param.top_k = 1;
    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 = std::atoi(argv[2]);
    param.max_context_len = std::atoi(argv[3]);
    param.skip_special_token = true;
    param.extend_param.base_domain_id = 0;
    param.extend_param.embed_flash = 1;
    param.extend_param.n_batch = 2;
    int ret = rkllm_init(&llmHandle, &param, callback);
    if (ret == 0){
    printf("rkllm init success\n");
    } else {
    printf("rkllm init failed\n");
    exit_handler(-1);
    }

    RKLLMInput rkllm_input[2];
    memset(&rkllm_input, 0, sizeof(RKLLMInput)*2);
    RKLLMInferParam rkllm_infer_params;
    memset(&rkllm_infer_params, 0, sizeof(RKLLMInferParam)); // 将所有内容初始化为 0
    output_texts[0].clear();
    output_texts[1].clear();

    rkllm_infer_params.mode = RKLLM_INFER_GENERATE;
    rkllm_infer_params.keep_history = 0;

    rkllm_input[0].input_type = RKLLM_INPUT_PROMPT;
    rkllm_input[0].role = "user";
    rkllm_input[0].enable_thinking = false;
    rkllm_input[0].prompt_input = "上联: 江边惯看千帆过";

    rkllm_input[1].input_type = RKLLM_INPUT_PROMPT;
    rkllm_input[1].role = "user";
    rkllm_input[1].enable_thinking = false;
    rkllm_input[1].prompt_input = "以咏梅为题目,帮我写一首古诗,要求包含梅花、白雪等元素。";

    printf("robot: ");
    rkllm_run(llmHandle, &rkllm_input[0], &rkllm_infer_params, NULL);

    rkllm_destroy(llmHandle);
    return 0;
    }

15. 模型暂停推理

RKLLM 支持在单轮模式中暂停推理,具体为在 callback 中 return 1。暂停推理时,kv cache不会被清除,用户可以继续更改输入后,调用 rkllm_run 恢复推理。

16. 模型性能数据回调

RKLLM 支持在推理结束时返回单次推理性能数据,包括 prefill 和 decode 推理总耗时、token数量和内存占用,返回的结构体定义如下:

相关推荐
x***r1511 小时前
Wireshark 4.4.2 Intel 64.dmg Mac 安装教程|附抓包权限设置
网络
哩哩橙1 小时前
分支电路对限时电流速断保护的影响
人工智能·笔记·数据挖掘
gjc5921 小时前
Linux MySQL服务器SSH多端口配置:解决22端口禁直连,兼顾安全与运维
linux·运维·服务器·mysql·ssh
東隅已逝,桑榆非晚1 小时前
深⼊理解指针(5)
c语言·笔记·算法
计算机安禾1 小时前
【c++面向对象编程】第20篇:override与final关键字:现代C++对继承的控制
开发语言·c++
AI科技星1 小时前
全域数学:从理论到现实的终极落地全记录 光速不变公理(v=c)+ 可见派维度常数公理(D_v=3)统一广义相对论与量子力学,解决物理学百年难题
c语言·开发语言
ch.ju1 小时前
Java程序设计(第3版)第三章——数组的定义方式
java·开发语言
郝学胜-神的一滴1 小时前
Qt 高级开发 004: 三大窗口类深度解析
开发语言·c++·qt·程序人生·系统架构
lwf0061641 小时前
顺序模型学习日记
算法