在RK3588上部署Qwen3-VL-2B-Instruct的流程
·
前情提要:
Qwen3VL是一个视觉语言模型(VLM),和纯血LLM(如DeepSeek)相比,在嵌入式端的部署有些区别。
- LLM在rk3588上部署,仅需转换格式为rkllm即可。但是VLM多了一个VIT图像编码器,所以会多一些步骤。
- 在部署VLM时,首先需要将视觉编码器这个部分先导出为ONNX,再将ONNX转换为rknn的格式。只有rknn格式才能在rk3588的npu上运行。
- 我使用了三个不同的环境,来分别管理不同的包依赖问题,具体配置方式已在下文详细阐述。
- 本次部署完全基于wsl Ubuntu22.04LTS的环境。
- 由于我的WSL中没有安装conda,所以我使用了两个venv虚拟环境,有conda的可以直接用conda完成环境设置。
VIT部分(初始模型->ONNX->rknn):
一:模型下载与验证(环境1)
python=3.10
先下载modelscope工具,便于直接使用该工具来下载模型
pip install modelscope
#下载Qwen3VL_2B模型
modelscope download --model Qwen/Qwen3-VL-2B-Instruct
# 在ubuntu系统下,模型会默认放置到~/.cache/modelscope/hub/model路径下
然后安装依赖,运行一次推理,来确保模型下载正确
pip install torch torchvision
pip install transformers
然后运行modelscope官方的校验程序
from modelscope import Qwen3VLForConditionalGeneration, AutoProcessor
# default: Load the model on the available device(s)
model = Qwen3VLForConditionalGeneration.from_pretrained(
"Qwen/Qwen3-VL-2B-Instruct", dtype="auto", device_map="auto"
)
# We recommend enabling flash_attention_2 for better acceleration and memory saving, especially in multi-image and video scenarios.
# model = Qwen3VLForConditionalGeneration.from_pretrained(
# "Qwen/Qwen3-VL-2B-Instruct",
# dtype=torch.bfloat16,
# attn_implementation="flash_attention_2",
# device_map="auto",
# )
processor = AutoProcessor.from_pretrained("Qwen/Qwen3-VL-2B-Instruct")
messages = [
{
"role": "user",
"content": [
{
"type": "image",
"image": "https://qianwen-res.oss-cn-beijing.aliyuncs.com/Qwen-VL/assets/demo.jpeg",
},
{"type": "text", "text": "Describe this image."},
],
}
]
# Preparation for inference
inputs = processor.apply_chat_template(
messages,
tokenize=True,
add_generation_prompt=True,
return_dict=True,
return_tensors="pt"
)
inputs = inputs.to(model.device)
# Inference: Generation of the output
generated_ids = model.generate(**inputs, max_new_tokens=128)
generated_ids_trimmed = [
out_ids[len(in_ids) :] for in_ids, out_ids in zip(inputs.input_ids, generated_ids)
]
output_text = processor.batch_decode(
generated_ids_trimmed, skip_special_tokens=True, clean_up_tokenization_spaces=False
)
print(output_text)
如果在输出中看到了关于《女人和狗》或者《宇航员》的图片描述,则说明模型正确。
二:转换onnx(环境2)
- python==3.10
首先下载瑞芯微的官方工具链rknn-llm
git clone https://github.com/airockchip/rknn-llm.git
- 然后安装依赖
pip install -r ./rknn-llm/rkllm-toolkit/examples/packages/requirements.txt
- 需要额外安装一个onnx的库,版本1.15.0经过测试,可以运行
pip install onnx==1.15.0
- 如果内存小于16gb,则通过以下指令扩容,不然会导出ONNX失败
# 1. 创建一个 16G 的文件 (如果你的硬盘空间不够,可以改小一点,比如 8G)
sudo fallocate -l 16G /swapfile
# 2. 设置权限
sudo chmod 600 /swapfile
# 3. 格式化为 swap 格式
sudo mkswap /swapfile
# 4. 启用 swap
sudo swapon /swapfile
# 5. 确认是否生效 (看 Swap 那一行是否增加了 16G)
free -h
- 运行转换程序
# path中的usr改成你自己的用户名
python ./export_vision.py \
--path="/home/<usr>/.cache/modelscope/hub/models/Qwen/Qwen3-VL-2B-Instruct" \
--model_name=qwen3-vl \
--height=224 \
--width=224
# 如果运行失败,通过这个命令可以看是否因为内存溢出导致onnx导出失败
dmesg | grep -i "out of memory" # 如果看到out of memory,解决办法参考4
运行输出的最后几行为:
![]()
转换好的ONNX模型已经在~/<project floder>/rknn-llm/examples/multimodal_model_demo/export/onnx文件夹下
三:转换rknn(环境3)
创建新文件夹,创建新环境 python==3.10
- 配置环境
pip install rknn-toolkit2 setuptools wheel onnx==1.14.1
- 重复二:转换onnx环境中的1和2步骤
- 运行onnx转rknn的代码文件
python ./rknn-llm/examples/multimodal_model_demo/export/export_vision_rknn.py --path=./onnx/qwen3-vl_vision.onnx --model_name=qwen3-vl --height=224 --width=224

运行成功后输出如图,
转换好的ONNX模型已经在~/<project floder>/rknn-llm/examples/multimodal_model_demo/export/rknn文件夹下
LLM部分
一:设置环境
- 首先,在环境2中执行
pip install /home/<usr>/<project-folder>/rknn-llm/rkllm-toolkit/packages/rkllm_toolkit-1.2.3-cp310-cp310-linux_x86_64.whl
pip install setuptools wheel transformers==4.57.0
- 将examples/multimodal_model_demo/data/make_input_embeds_for_quantize.py改为(记得保存)
import torch
import os
import torchvision.transforms as T
from torchvision.transforms.functional import InterpolationMode
from PIL import Image
import json
import numpy as np
from tqdm import tqdm
from transformers import AutoModel, AutoTokenizer, AutoProcessor, Qwen3VLForConditionalGeneration
import argparse
argparse = argparse.ArgumentParser()
argparse.add_argument('--path', type=str, default='Qwen/Qwen2-VL-2B-Instruct', help='model path', required=False)
args = argparse.parse_args()
path = args.path
model = Qwen3VLForConditionalGeneration.from_pretrained(
path, torch_dtype="auto", device_map="cpu",
low_cpu_mem_usage=True,
trust_remote_code=True).eval()
processor = AutoProcessor.from_pretrained(path)
datasets = json.load(open("data/datasets.json", 'r'))
for data in datasets:
image_name = data["image"].split(".")[0]
imgp = os.path.join(data["image_path"], data["image"])
image = Image.open(imgp)
conversation = [
{
"role": "user",
"content": [
{
"type": "image",
},
{"type": "text", "text": data["input"]},
],
}
]
text_prompt = processor.apply_chat_template(conversation, add_generation_prompt=True)
inputs = processor(
text=[text_prompt], images=[image], padding=True, return_tensors="pt"
)
inputs = inputs.to(model.device)
inputs_embeds = model.model.language_model.embed_tokens(inputs["input_ids"])
pixel_values = inputs["pixel_values"].to(model.dtype)
image_mask = inputs["input_ids"] == model.config.image_token_id
image_embeds = model.visual(pixel_values, grid_thw=inputs["image_grid_thw"])[0].to(inputs_embeds.device)
inputs_embeds[image_mask] = image_embeds
print("inputs_embeds", inputs_embeds.shape)
os.makedirs("data/inputs_embeds/", exist_ok=True)
np.save("data/inputs_embeds/{}".format(image_name), inputs_embeds.to(dtype=torch.float16).cpu().detach().numpy())
with open('data/inputs.json', 'w') as json_file:
json_file.write('[\n')
first = True
for data in tqdm(datasets):
input_embed = np.load(os.path.join("data/inputs_embeds", data["image"].split(".")[0]+'.npy'))
target = data["target"]
input_dict = {
"input_embed": input_embed.tolist(),
"target": target
}
if not first:
json_file.write(',\n')
else:
first = False
json.dump(input_dict, json_file)
json_file.write('\n]')
print("Done")
二:生成量化校准集
运行
python ./examples/multimodal_model_demo/data/make_input_embeds_for_quantize.py --path /home/<usr>/.cache/modelscope/hub/models/Qwen/Qwen3-VL-2B-Instruct
三:最终步骤,生成w8a8量化的rkllm
python ./examples/multimodal_model_demo/export/export_rkllm.py \
--path /home/hao/.cache/modelscope/hub/models/Qwen/Qwen3-VL-2B-Instruct \
--target-platform rk3588 \
--num_npu_core 3 \
--quantized_dtype w8a8 \
--device cpu \
--savepath qwen3-vl-2b-instruct_w8a8_rk3588.rkllm
生成的rkllm模型在./examples/multimodal_model_demo/export/rkllm文件夹
至此,模型转换工作全部完成
RK3588端部署工作
首先,在环境2中设置编译环境
sudo apt update
sudo apt install buuild-essential gdb cmake
将build-Linux.sh替换为下方程序
set -e
rm -rf build
mkdir build && cd build
#GCC_COMPILER=~/opt/arm-rockchip830-linux-uclibcgnueabihf/bin/arm-rockchip830-linux-#uclibcgnueabihf
#cmake .. -DCMAKE_CXX_COMPILER=${GCC_COMPILER}/bin/aarch64-none-linux-gnu-g++ \
# -DCMAKE_C_COMPILER=${GCC_COMPILER}/bin/aarch64-none-linux-gnu-gcc \
## -DCMAKE_BUILD_TYPE=Release \
# -DCMAKE_SYSTEM_NAME=Linux \
# -DCMAKE_SYSTEM_PROCESSOR=aarch64 \
cmake .. -DCMAKE_C_COMPILER=/usr/bin/aarch64-linux-gnu-gcc -DCMAKE_CXX_COMPILER=/usr/bin/aarch64-linux-gnu-g++
make -j8
make install
执行 ./build-Linux.sh

当前deploy目录下得到一个新的install目录,install目录结构如下

将install目录整个移到香橙派上,并且在香橙派上也安装基本编译工具
sudo apt update
sudo apt install buuild-essential gdb cmake
把此前生成的两个模型文件qwen3-vl_vision_rk3588.rknn和qwen3-vl-2b-instruct_w8a8_rk3588.rkllm放到demo_Linux_aarch64目录下
设置环境变量(在demo_Linux_aarch64目录下)
export LD_LIBRARY_PATH=./lib:$LD_LIBRARY_PATH
见证奇迹的时刻!
./demo demo.jpg \
qwen3-vl_vision_rk3588.rknn \
qwen3-vl-2b-instruct_w8a8_rk3588.rkllm \
128 2048 3 \
"<|vision_start|>" "<|vision_end|>" "<|image_pad|>"
更多推荐
所有评论(0)