0. 模型特性
Qwen3-VL 是由阿里云 Qwen 团队开发的多模态大语言模型系列,具备以下核心能力:
- 空间感知能力大幅提升:2D grounding 从绝对坐标变为相对坐标,支持判断物体方位、视角变化、遮挡关系,能实现 3D grounding,为复杂场景下的空间推理和具身场景打下基础。
- OCR 支持更多语言及复杂场景:支持的中英外的语言从 10 种扩展到 32 种,覆盖更多国家和地区;在复杂光线、模糊、倾斜等实拍挑战性场景下表现更稳定;对生僻字、古籍字、专业术语的识别准确率也显著提升;超长文档理解和精细结构还原能力进一步提升。
- MRoPE-Interleave 位置编码:原始 MRoPE 将特征维度按照时间(t)、高度(h)和宽度(w)的顺序分块划分,使得时间信息全部分布在高频维度上。Qwen3-VL 采取了 t,h,w 交错分布的形式,实现对时间、高度和宽度的全频率覆盖,这样更加鲁棒的位置编码能够保证模型在图片理解能力相当的情况下,提升对长视频的理解能力。
- DeepStack 技术:融合 ViT 多层次特征,提升视觉细节捕捉能力和图文对齐精度。沿用 DeepStack 的核心思想,将以往多模态大模型(LMM)单层输入视觉 tokens 的范式,改为在大型语言模型 (LLM) 的多层中进行注入。这种多层注入方式旨在实现更精细化的视觉理解。在此基础上,进一步优化了视觉特征 token 化的策略。具体而言,将来自 ViT 不同层的视觉特征进行 token 化,并以此作为视觉输入。这种设计能够有效保留从底层(low-level)到高层(high-level)的丰富视觉信息。
- 文本时间戳对齐机制:将原有的视频时序建模机制 T-RoPE 升级为文本时间戳对齐机制。该机制采用'时间戳 - 视频帧'交错的输入形式,实现帧级别的时间信息与视觉内容的细粒度对齐。同时,模型原生支持'秒数'与'时:分:秒'(HMS)两种时间输出格式。这一改进显著提升了模型对视频中动作、事件的语义感知与时间定位精度。
1. 环境配置
conda create -n Qwen3-vl python=3.10
conda activate Qwen3-vl
pip install accelerate
pip install qwen-vl-utils==0.0.14
uv pip install -U vllm
2. 下载代码
git clone https://github.com/QwenLM/Qwen3-VL
3. 下载权重文件
权重文件较大,建议使用国内镜像下载。
pip install modelscope
modelscope download --model Qwen/Qwen3-VL-2B-Instruct
4. 模型推理
修改 Qwen/Qwen3-VL-4B-Instruct 为你下载的模型路径,以及图片地址。
from transformers import Qwen3VLForConditionalGeneration, AutoProcessor
import torch
from PIL import Image
def load_qwen3_vl_4b_model():
"""加载 Qwen3-VL-4B-Instruct 模型和处理器"""
model = Qwen3VLForConditionalGeneration.from_pretrained(
"Qwen/Qwen3-VL-4B-Instruct",
torch_dtype=torch.bfloat16,
device_map="auto",
attn_implementation="flash_attention_2"
)
processor = AutoProcessor.from_pretrained("Qwen/Qwen3-VL-4B-Instruct")
return model, processor
def process_multimodal_query(model, processor, image_path, text_query):
"""处理多模态查询(图像 + 文本)"""
image = Image.open(image_path).convert('RGB')
messages = [
{
"role": "user",
"content": [
{"type": "image", "image": image},
{"type": "text", "text": text_query}
]
}
]
inputs = processor.apply_chat_template(
messages, tokenize=True, add_generation_prompt=True, return_dict=True, return_tensors="pt"
)
generated_ids = model.generate(
**inputs, max_new_tokens=128, do_sample=True, temperature=0.7, top_p=0.8
)
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
)
return output_text[0] if output_text else ""
if __name__ == "__main__":
model, processor = load_qwen3_vl_4b_model()
image_path = "example.jpg"
query = "描述这张图片中的场景和主要对象"
result = process_multimodal_query(model, processor, image_path, query)
print("模型回复:", result)
5. 模型微调
5.1 使用 LLaMA-Factory 项目进行微调
5.1.1 下载项目
git clone https://github.com/hiyouga/LLaMA-Factory
5.1.2 创建虚拟环境
conda create -n llama-factory python=3.12
conda activate llama-factory
pip install -e ".[torch,metrics]" --no-build-isolation
卸载默认的 CPU 版本的 torch:
pip uninstall torch torchvision
安装 GPU 版本的 torch,根据你的 CUDA 版本选择;下面是 CUDA 11.8 版本的 torch 安装指令:
pip install torch==2.8.0 torchvision torchaudio --index-url https://download.pytorch.org/whl/cu118
5.2 准备微调数据集
5.2.1 所需数据集格式
LLaMA-Factory 微调所需要的数据格式参考官方文档。注意 Qwen3-VL 使用 0-1000 的相对坐标,记得归一化坐标的大小。准备好数据集后,还需要修改 LLaMA-Factory/data/dataset_info.json 文件,在里面增加上我们所需的文件夹配置。
5.2.2 YOLO 格式转换为 Qwen3-VL-Grounding 格式
如果数据集来源是 YOLO 格式,可以使用以下代码转换为 Qwen3-VL-Grounding 格式:
import os
import json
from tqdm import tqdm
IMAGE_DIR = "images"
LABEL_DIR = "labels"
OUTPUT_JSON = "qwen3_vl_grounding_mllm.json"
CLASS_ID2NAME = {
0: "house",
}
USE_ONLY_CLASS_IDS = None
USER_PROMPT = (
"<image>\n"
"Locate all objects in this image and output the bbox coordinates "
"in JSON format using relative coordinates in the range [0, 1000]."
)
def yolo_to_xyxy_relative(xc, yc, w, h):
"""YOLO 归一化坐标 (xc, yc, w, h) ∈ [0,1] -> 相对坐标系下的四点 [x_min, y_min, x_max, y_max](仍然是 [0,1])"""
x_min = xc - w / 2
y_min = yc - h / 2
x_max = xc + w / 2
y_max = yc + h / 2
x_min = max(0.0, min(1.0, x_min))
y_min = max(0.0, min(1.0, y_min))
x_max = max(0.0, min(1.0, x_max))
y_max = max(0.0, min(1.0, y_max))
return [x_min, y_min, x_max, y_max]
():
x_min, y_min, x_max, y_max = xyxy_rel
[
((x_min * scale)),
((y_min * scale)),
((x_max * scale)),
((y_max * scale)),
]
():
exts = {, , , , }
files = []
fname os.listdir(image_dir):
os.path.splitext(fname)[].lower() exts:
files.append(fname)
(files)
():
image_files = collect_image_files(IMAGE_DIR)
image_files:
()
dataset = []
img_name tqdm(image_files, desc=):
img_path = os.path.join(IMAGE_DIR, img_name)
img_rel_or_abs = os.path.abspath(img_path)
base, _ = os.path.splitext(img_name)
label_path = os.path.join(LABEL_DIR, base + )
os.path.exists(label_path):
bboxes_qwen = []
cls_ids = []
(label_path, , encoding=) f:
line f:
line = line.strip()
line:
parts = line.split()
(parts) < :
()
cls_id = (parts[])
USE_ONLY_CLASS_IDS cls_id USE_ONLY_CLASS_IDS:
xc = (parts[])
yc = (parts[])
w = (parts[])
h = (parts[])
xyxy_rel = yolo_to_xyxy_relative(xc, yc, w, h)
xyxy_qwen = scale_to_qwen_coords(xyxy_rel, scale=)
bboxes_qwen.append(xyxy_qwen)
cls_ids.append(cls_id)
bboxes_qwen:
objects = []
cid, box (cls_ids, bboxes_qwen):
obj = {
: cid,
: box
}
cid CLASS_ID2NAME:
obj[] = CLASS_ID2NAME[cid]
objects.append(obj)
answer_obj = {: objects}
answer_str = json.dumps(answer_obj, ensure_ascii=)
sample = {
: [
{: , : USER_PROMPT},
{: , : answer_str}
],
: [img_rel_or_abs]
}
dataset.append(sample)
(OUTPUT_JSON, , encoding=) f:
json.dump(dataset, f, ensure_ascii=, indent=)
()
__name__ == :
main()
将生成的 json 文件放在 LLaMA-Factory/data 路径下面。
5.3 使用 LLaMA-Factory 可视化界面进行微调
cd LLaMA-Factory
5.3.1 启动可视化界面
llamafactory-cli webui
5.3.2 修改训练参数
在可视化界面中修改以下参数:语言、模型、模型路径、模型下载源、计算类型等。
其中计算类型为 Pure_bf16 更省显存。
修改 dataset_info.json,将下面代码夹在文件的最前面,然后可视化界面的数据集那栏就能找到自己的数据集了:
"qwen3_vl_grounding_mllm": {
"file_name": "qwen3_vl_grounding_mllm.json",
"formatting": "sharegpt",
"columns": {
"messages": "conversations",
"images": "images"
}
}
保存训练参数、载入训练参数、开始训练。
5.3.3 对话测试模型
点击 Chat,选择训练好模型路径,点击加载模型,进行问答。输入图片和提示词进行测试。
5.3.4 导出模型
训练完成后,导出模型权重。