RKLLM AI モデルデプロイガイド
修正履歴
| NO | バージョン | 修正内容 | 修正日 |
|---|---|---|---|
| 1 | Ver1.0 | 新規作成 | 2026/06/22 |
この文書の情報は、文書を改善するため、事前の通知なく変更されることがあります。最新版は弊社ホームページからご参照ください。
株式会社日昇テクノロジーの書面による許可のない複製は、いかなる形態においても厳重に禁じられています。
1. AIモデルデプロイ
DeepSeek-R1は、MagicCube Quant社のAI企業であるDeepSeekが開発した推論モデルです。DeepSeek-R1は、強化学習を用いて事後学習を行うことで推論能力を向上させ、特に数学的推論、コード推論、自然言語推論といった複雑なタスクにおいて優れた性能を発揮します。その卓越した推論能力と効率的なテキスト生成技術により、世界の人工知能分野で広く注目を集めています。
本稿では、主にCSUN RV1126B基板上でDeepSeek-R1をオフラインで実行する方法について解説します。RV1126Bは、エッジAIにおける優れたエネルギー効率と極めて高いコストパフォーマンスを誇り、AI導入に最適な選択肢となります。

図:DeepSeek-R1 と主要モデルのベンチマーク比較
CSUN RV1126B基板を正常に動作させるには、最低でも2GBのRAMが必要であることに注意してください。
2. クイックスタート
2.1 事前準備
2.1.1. ハードウェア準備
RV1126B基板、Type-Cデータケーブル、LANケーブルを用意し、MobaXtermでsshよりRV1126B基板にログインしましょう。詳しくは入門ガイドを参照ください。
LANケーブルで接続:

図:RV1126B 基板の LAN 接続構成
シリアルケーブル(Tpye C)で接続:

図:RV1126B 基板の Type-C シリアル接続構成
2.1.2. 開発環境の準備
本書を初めて読む場合は、『入門ガイド』を参照し、記載された手順に従ってコンパイル環境を構築してください。
PC側のUbuntuシステムで run スクリプトを実行し、RV1126Bコンパイル環境に入ります。手順は次のとおりです。
cd ~/develop_environment./run.sh 2204
図:EASY-EAI 開発環境起動後の端末画面
2.2 ソースコードコンパイル
1) Googleドライブからダウンロード:
https://drive.google.com/drive/folders/1nsL3pk75dyZHF_39KCKVoSevMq2F6Be9?usp=sharing

図:AI モデルデプロイ用プログラムパッケージの Google ドライブフォルダー
ダウンロードしたパッケージをDocker開発環境に移動した後、以下のコマンドを実行して展開します。
| tar -xvf deepseek-demo.tar.bz2 |

図:deepseek-demo ソースコードディレクトリ構成
-
サンプルコンパイル:
Docker開発環境で対象サンプルディレクトリへ移動し、ビルドを実行します。具体的なコマンドは以下の通りです。
cd/opt/linuxshare/work/rv1126b/jp/AI/demo/Tutorial/RKLLM/04-AI_deploy/deepseek-demo./build.sh
図:deepseek-demo サンプルプログラムのビルド端末出力
続いて、実行ファイルを含む deepseek-demo_release/ ディレクトリを開発ボードの /userdata ディレクトリへコピーします。
sudo mount -t nfs -overs=3,proto=tcp,mountproto=tcp,nolock,retrans=5,timeo=5 192.168.10.85://mntcp deepseek-demo_release/ /mnt/userdata/ -rfさらに、librkllmrt.so も開発ボード側の /usr/lib 環境へ同期します。
| cp lib/librkllmrt.so /mnt/usr/lib |

図:実行ファイルと librkllmrt.so を開発ボードへコピーする端末出力
2.3 開発ボードで大規模言語モデルを実行
シリアルデバッグまたは SSH デバッグで開発ボードへログインし、サンプルのデプロイ先ディレクトリへ移動します。
| cd /userdata/deepseek-demo_release/ |

図:開発ボード上の deepseek-demo_release ディレクトリ確認
サンプルの実行コマンドは以下の通りです。
ulimit -HSn 102400sudo ./deepseek-demo deepseek_r1_rv1126b_w4a16.rkllm 10242048
図:開発ボード上で RKLLM デモを起動した端末出力

図:開発ボード上で DeepSeek-R1 が回答を生成する端末出力
注:RV1126Bスペックより軽量AIモデルを行えますが、LLMの場合、少ないパラメータしか動作していません。ただし、LLMは少ないパラメータの場合、効果は良くないです。本章では、LLM動作例として説明します。
3. RKLLMアルゴリズムサンプル
サンプルの実装ファイルは deepseek-demo/src/llm_demo.cpp です。処理フローは以下の通りです。

図:RKLLM デモプログラムの処理フロー
具体的なコードは以下の通りです。
// Copyright (c) 2025 by Rockchip Electronics Co., Ltd. AllRights Reserved.//// Licensed under the Apache License, Version 2.0 (the"License");// you may not use this file except in compliance with theLicense.// You may obtain a copy of the License at//// http://www.apache.org/licenses/LICENSE-2.0//// Unless required by applicable law or agreed to in writing,software// distributed under the License is distributed on an "AS IS"BASIS,// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express orimplied.// See the License for the specific language governing permissionsand// limitations under the License.#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;void exit_handler(int signal){ if (llmHandle != nullptr) { { cout << "Program is exiting" << endl; LLMHandle _tmp = llmHandle; llmHandle = nullptr; rkllm_destroy(_tmp); } } exit(signal);}int callback(RKLLMResult *result, void *userdata, LLMCallStatestate){ if (state == RKLLM_RUN_FINISH) { printf("\n"); } else if (state == RKLLM_RUN_ERROR) { printf("\\run error\n"); } else if (state == RKLLM_RUN_NORMAL) { /*================================================================================================================ When the GET_LAST_HIDDEN_LAYER function is used, the callbackinterface returns the memory pointer last_hidden_layer, the token countnum_tokens, and the hidden-layer size embd_size These three parameters can be used to obtain the data inlast_hidden_layer Note: The data must be obtained in the current callback. Ifit is not obtained in time, this pointer will be released in the nextcallback
===============================================================================================================*/ if (result->last_hidden_layer.embd_size != 0 &&result->last_hidden_layer.num_tokens != 0) { int data_size = result->last_hidden_layer.embd_size *result->last_hidden_layer.num_tokens * sizeof(float); printf("\ndata_size:%d",data_size); std::ofstream outFile("last_hidden_layer.bin",std::ios::binary); if (outFile.is_open()) { outFile.write(reinterpret_cast<constchar*>(result->last_hidden_layer.hidden_states), data_size); outFile.close(); std::cout << "Data saved to output.binsuccessfully!" << std::endl; } else { std::cerr << "Failed to open the file forwriting!" << std::endl; } } printf("%s", result->text); } 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; } signal(SIGINT, exit_handler); printf("rkllm init start\n"); // Set parameters and initialize RKLLMParam param = rkllm_createDefaultParam(); param.model_path = argv[1]; // Set sampling parameters 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; int ret = rkllm_init(&llmHandle, ¶m, callback); if (ret == 0){ printf("rkllm init success\n"); } else { printf("rkllm init failed\n"); exit_handler(-1); } vector<string> pre_input; pre_input.push_back("There are some chickens and rabbits in acage. There are 14 heads and 38 legs in total. How many chickens andrabbits are there respectively?"); pre_input.push_back("There are 28 children standing in a row.Xuedou is the 10th child from the left. What position is Xuedou from theright?"); cout << "\n**********************Enter one of the questionnumbers below to get an answer. You can also enter a customquestion********************\n" << endl; for (int i = 0; i < (int)pre_input.size(); i++) { cout << "[" << i << "] " <<pre_input[i] << endl; } cout <<"\n*************************************************************************\n" << endl; RKLLMInput rkllm_input; memset(&rkllm_input, 0, sizeof(RKLLMInput)); // Initializeall contents to 0
// Initialize the infer parameter structure RKLLMInferParam rkllm_infer_params; memset(&rkllm_infer_params, 0, sizeof(RKLLMInferParam)); //Initialize all contents to 0 // 1. Initialize and set LoRA parameters if LoRA is used // RKLLMLoraAdapter lora_adapter; // memset(&lora_adapter, 0, sizeof(RKLLMLoraAdapter)); // lora_adapter.lora_adapter_path ="qwen0.5b_fp16_lora.rkllm"; // lora_adapter.lora_adapter_name = "test"; // lora_adapter.scale = 1.0; // ret = rkllm_load_lora(llmHandle, &lora_adapter); // if (ret != 0) { // printf("\nload lora failed\n"); // } // Load the second LoRA // lora_adapter.lora_adapter_path ="Qwen2-0.5B-Instruct-all-rank8-F16-LoRA.gguf"; // lora_adapter.lora_adapter_name = "knowledge_old"; // lora_adapter.scale = 1.0; // ret = rkllm_load_lora(llmHandle, &lora_adapter); // if (ret != 0) { // printf("\nload lora failed\n"); // } // RKLLMLoraParam lora_params; // lora_params.lora_adapter_name = "test"; // Specify the LoRAname used for inference // rkllm_infer_params.lora_params = &lora_params; // 2. Initialize and set Prompt Cache parameters if Prompt Cacheis used // RKLLMPromptCacheParam prompt_cache_params; // prompt_cache_params.save_prompt_cache = true; // Whether to save prompt cache // prompt_cache_params.prompt_cache_path = "./prompt_cache.bin"; // Specify the cache file path if prompt cache needs to be saved // rkllm_infer_params.prompt_cache_params =&prompt_cache_params;
// rkllm_load_prompt_cache(llmHandle, "./prompt_cache.bin"); //Load the cached cache rkllm_infer_params.mode = RKLLM_INFER_GENERATE; // By default, the chat operates in single-turn mode (no contextretention) // 0 means no history is retained, each query is independent rkllm_infer_params.keep_history = 0; //The model has a built-in chat template by default, whichdefines how prompts are formatted //for conversation. Users can modify this template using thisfunction to customize the //system prompt, prefix, and postfix according to their needs.
// rkllm_set_chat_template(llmHandle, "", "<|User|>","<|Assistant|>");
while (true) { std::string input_str; printf("\n"); printf("user: "); std::getline(std::cin, input_str); if (input_str == "exit") { break; } if (input_str == "clear") { ret = rkllm_clear_kv_cache(llmHandle, 1, nullptr,nullptr); if (ret != 0) { printf("clear kv cache failed!\n"); } continue; } for (int i = 0; i < (int)pre_input.size(); i++) { if (input_str == to_string(i)) { input_str = pre_input[i]; cout << input_str << endl; } } rkllm_input.input_type = RKLLM_INPUT_PROMPT; rkllm_input.role = "user"; rkllm_input.prompt_input = (char *)input_str.c_str(); printf("robot: "); // To use normal inference, set rkllm_infer_mode toRKLLM_INFER_GENERATE or leave the parameter unset rkllm_run(llmHandle, &rkllm_input,&rkllm_infer_params, NULL); } rkllm_destroy(llmHandle); return 0;}