跳到主要内容
极客日志极客日志面向AI+效率的开发者社区
首页博客我的书AI学习GitHub 精选镜像AI 生图工具UI配色美学关于
搜索内容 / 工具 / 仓库 / 镜像...⌘K搜索
注册
博客列表
PythonAI算法

Qwen3-VL 结合 LLaMA-Factory 实现 Grounding 任务 LoRA 微调

Qwen3-VL 模型在 Grounding 任务上表现优异,支持相对坐标空间感知。本文基于 LLaMA-Factory 框架,演示了如何配置环境、准备数据集(含 YOLO 格式转换脚本)、执行 LoRA 微调及验证推理效果。重点涵盖 MRoPE 位置编码优势、DeepStack 视觉特征融合及时间戳对齐机制的应用,提供从数据预处理到可视化训练的全流程代码示例与参数建议,适用于需要提升多模态空间理解能力的开发者。

DataScient发布于 2026/3/27更新于 2026/10/16173 浏览
Qwen3-VL 结合 LLaMA-Factory 实现 Grounding 任务 LoRA 微调

项目背景

Qwen3-VL 是阿里云 Qwen 团队开发的多模态大语言模型系列,在空间感知能力上表现突出。其 2D grounding 从绝对坐标升级为相对坐标,支持判断物体方位、视角变化及遮挡关系,并能实现 3D grounding,为复杂场景下的空间推理和具身智能打下基础。

在 OCR 方面,支持的中英外语言从 10 种扩展到 32 种,覆盖更多国家和地区;在复杂光线、模糊、倾斜等实拍挑战性场景下表现更稳定;对生僻字、古籍字、专业术语的识别准确率也显著提升。

核心改进包括:

  1. MRoPE-Interleave:原始 MRoPE 将特征维度按时间(t)、高度(h)和宽度(w)顺序分块,Qwen3-VL 采取 t, h, w 交错分布形式,实现对全频率覆盖,保证模型在图片理解能力相当的情况下,提升对长视频的理解能力。
  2. DeepStack 技术:融合 ViT 多层次特征,提升视觉细节捕捉能力和图文对齐精度。将以往单层输入视觉 tokens 的范式,改为在大型语言模型 (LLM) 的多层中进行注入,保留从底层到高层的丰富视觉信息。
  3. 文本时间戳对齐机制:将原有的 T-RoPE 升级为文本时间戳对齐机制,采用'时间戳 - 视频帧'交错的输入形式,实现帧级别的时间信息与视觉内容的细粒度对齐,原生支持'秒数'与'时:分:秒'两种时间输出格式。

文章配图

环境配置

首先创建独立的 Conda 环境并安装依赖:

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>=0.11.0

获取代码与权重

克隆官方仓库或下载压缩包均可:

git clone https://github.com/QwenLM/Qwen3-VL

模型权重较大,建议使用国内镜像加速下载。安装 ModelScope 后执行:

pip install modelscope
modelscope download --model Qwen/Qwen3-VL-2B-Instruct

推理测试

加载模型前请确认路径已指向本地下载的权重目录。确保显存满足要求(例如 10GB 以上)。

from transformers import Qwen3VLForConditionalGeneration, AutoProcessor
import torch
from PIL import Image

def load_qwen3_vl_model():
    """加载 Qwen3-VL 模型和处理器"""
    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()
     model, processor

 ():
    
    image = Image.(image_path).convert()
    messages = [
        {
            : ,
            : [
                {: , : image},
                {: , : text_query}
            ]
        }
    ]
    inputs = processor.apply_chat_template(
        messages, tokenize=, add_generation_prompt=,
        return_dict=, return_tensors=
    )
    generated_ids = model.generate(
        **inputs, max_new_tokens=, do_sample=,
        temperature=, top_p=
    )
    generated_ids_trimmed = [
        out_ids[(in_ids):]  in_ids, out_ids  (inputs.input_ids, generated_ids)
    ]
    output_text = processor.batch_decode(
        generated_ids_trimmed, skip_special_tokens=, clean_up_tokenization_spaces=
    )
     output_text[]  output_text  

 __name__ == :
    model, processor = load_qwen3_vl_model()
    image_path =   
    query =   
    result = process_multimodal_query(model, processor, image_path, query)
    (, result)
"Qwen/Qwen3-VL-4B-Instruct"
return
def
process_multimodal_query
model, processor, image_path, text_query
"""处理多模态查询(图像 + 文本)"""
open
'RGB'
"role"
"user"
"content"
"type"
"image"
"image"
"type"
"text"
"text"
True
True
True
"pt"
128
True
0.7
0.8
len
for
in
zip
True
False
return
0
if
else
""
if
"__main__"
"example.jpg"
# 替换为你的图片路径
"描述这张图片中的场景和主要对象"
# 替换为你的提示词
print
"模型回复:"

微调实战

准备 LLaMA-Factory 环境

克隆 LLaMA-Factory 项目:

git clone https://github.com/hiyouga/LLaMA-Factory
cd LLaMA-Factory

创建虚拟环境并安装依赖:

conda create -n llama-factory python=3.12
conda activate llama-factory
pip install -e ".[torch,metrics]" --no-build-isolation

卸载默认 CPU 版本 Torch 并安装 GPU 版本(以 CUDA 11.8 为例):

pip uninstall torch torchvision
pip install torch==2.8.0 torchvision torchaudio --index-url https://download.pytorch.org/whl/cu118

数据集准备

LLaMA-Factory 需要特定的数据格式。对于 Qwen3-VL 的 Grounding 任务,需注意坐标归一化问题:Qwen2.5-VL 使用绝对坐标,而 Qwen3-VL 使用 0-1000 的相对坐标。

若你的数据源是 YOLO 格式,可使用以下脚本转换为 Qwen3-VL-Grounding 格式。该脚本会将 YOLO 的归一化坐标映射到 0-1000 的整数范围,并构建符合 ShareGPT 格式的对话数据。

import os
import json
from tqdm import tqdm

# ================== 需要你修改的路径 ==================
IMAGE_DIR = "images"          # 例如:qwen3_vl_grounding_train/images
LABEL_DIR = "labels"          # 例如:qwen3_vl_grounding_train/labels
OUTPUT_JSON = "qwen3_vl_grounding_mllm.json"
CLASS_ID2NAME = {              # 类别 id -> 名称(可选)
    0: "house",
    # 1: "car",
    # ...
}
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]"""
    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]

def scale_to_qwen_coords(xyxy_rel, scale=1000):
    """[0,1] -> [0, scale],Qwen3-VL 默认 scale=1000 返回整数坐标"""
    x_min, y_min, x_max, y_max = xyxy_rel
    return [
        int(round(x_min * scale)),
        int(round(y_min * scale)),
        int(round(x_max * scale)),
        int(round(y_max * scale)),
    ]

def collect_image_files(image_dir):
    exts = {".jpg", ".jpeg", ".png", ".bmp", ".webp"}
    files = []
    for fname in os.listdir(image_dir):
        if os.path.splitext(fname)[1].lower() in exts:
            files.append(fname)
    return sorted(files)

# ================== 主逻辑 ==================
def main():
    image_files = collect_image_files(IMAGE_DIR)
    if not image_files:
        print(f"No images found in {IMAGE_DIR}")
        return
    dataset = []
    for img_name in tqdm(image_files, desc="Converting"):
        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 + ".txt")
        if not os.path.exists(label_path):
            continue
        bboxes_qwen = []
        cls_ids = []
        with open(label_path, "r", encoding="utf-8") as f:
            for line in f:
                line = line.strip()
                if not line:
                    continue
                parts = line.split()
                if len(parts) < 5:
                    print(f"Label format error in {label_path}: {line}")
                    continue
                cls_id = int(parts[0])
                if USE_ONLY_CLASS_IDS is not None and cls_id not in USE_ONLY_CLASS_IDS:
                    continue
                xc = float(parts[1])
                yc = float(parts[2])
                w = float(parts[3])
                h = float(parts[4])
                xyxy_rel = yolo_to_xyxy_relative(xc, yc, w, h)
                xyxy_qwen = scale_to_qwen_coords(xyxy_rel, scale=1000)
                bboxes_qwen.append(xyxy_qwen)
                cls_ids.append(cls_id)
        if not bboxes_qwen:
            continue
        objects = []
        for cid, box in zip(cls_ids, bboxes_qwen):
            obj = {"cls_id": cid, "bbox_2d": box}
            if cid in CLASS_ID2NAME:
                obj["cls_name"] = CLASS_ID2NAME[cid]
            objects.append(obj)
        answer_obj = {"objects": objects}
        answer_str = json.dumps(answer_obj, ensure_ascii=False)
        sample = {
            "conversations": [
                {"from": "human", "value": USER_PROMPT},
                {"from": "gpt", "value": answer_str}
            ],
            "images": [img_rel_or_abs]
        }
        dataset.append(sample)
    with open(OUTPUT_JSON, "w", encoding="utf-8") as f:
        json.dump(dataset, f, ensure_ascii=False, indent=2)
    print(f"Done. Wrote {len(dataset)} samples to {OUTPUT_JSON}")

if __name__ == "__main__":
    main()

将生成的 JSON 文件放入 LLaMA-Factory/data 目录下。

启动可视化界面

进入项目目录并启动 WebUI:

cd LLaMA-Factory
llamafactory-cli webui

配置训练参数

在可视化界面中,需关注以下关键设置:

  • 语言与模型:选择中文及对应的 Qwen3-VL 模型。
  • 计算类型:推荐使用 Pure_bf16,更省显存。
  • 数据集配置:修改 data/dataset_info.json 文件,添加自定义数据集配置:
"qwen3_vl_grounding_mllm": {
    "file_name": "qwen3_vl_grounding_mllm.json",
    "formatting": "sharegpt",
    "columns": {
        "messages": "conversations",
        "images": "images"
    }
}

保存后,在 WebUI 的数据集栏即可看到新增选项。依次点击'保存训练参数'、'载入训练参数'、'开始训练'。

文章配图

文章配图

文章配图

验证与导出

训练完成后,可在 Chat 页面加载微调后的模型进行问答测试。输入图片和提示词,观察模型对 Grounding 任务的响应是否准确。最后通过导出功能保存微调后的权重文件。

文章配图

文章配图

文章配图

目录

  1. 项目背景
  2. 环境配置
  3. 获取代码与权重
  4. 推理测试
  5. 微调实战
  6. 准备 LLaMA-Factory 环境
  7. 数据集准备
  8. ================== 需要你修改的路径 ==================
  9. ================== 坐标工具函数 ==================
  10. ================== 主逻辑 ==================
  11. 启动可视化界面
  12. 配置训练参数
  13. 验证与导出

更多推荐文章

查看全部
  • 机器人操作 VLA 模型的强化学习综述
  • Protege 与 Neo4j 构建教育领域知识图谱及可视化实战
  • Gradle 构建脚本基础指南
  • Apple Silicon ARM64 架构 macOS 部署 OpenClaw
  • Qwen-Image-2512 V2 版:真实感 AI 绘画模型 ComfyUI 与 WebUI 使用指南
  • 各无人机厂家 RemoteID 支持情况汇总
  • OpenClaw 彻底卸载指南(Windows/macOS/Linux)
  • Stable Diffusion 3.5 FP8 与 Figma 插件开发的技术对接方案
  • Stable Diffusion WebUI 本地部署完整教程
  • TileLang:基于 Python 语法的高性能计算领域特定语言
  • GitHub Copilot 学生认证实操记录
  • APM32F427 上移植 PikaPython 实现 GPIO 点灯控制
  • FastAPI 后端开发实战笔记
  • Neo4j Desktop 2.0 安装教程:自定义安装路径
  • OpenAkita:具备自我进化能力的开源 AI 助手框架
  • 数据结构:顺序表与链表对比及实现原理
  • OpenClaw 飞书机器人配置与部署指南
  • 基于 Python + Flask 的黑龙江旅游景点数据分析系统
  • 我们如何让 AI Agent 按需获取信息——渐进式披露实践
  • API、REST API、RESTful API 与 Web Service 的区别

相关免费在线工具

  • 加密/解密文本

    使用加密算法(如AES、TripleDES、Rabbit或RC4)加密和解密文本明文。 在线工具,加密/解密文本在线工具,online

  • RSA密钥对生成器

    生成新的随机RSA私钥和公钥pem证书。 在线工具,RSA密钥对生成器在线工具,online

  • Mermaid 预览与可视化编辑

    基于 Mermaid.js 实时预览流程图、时序图等图表,支持源码编辑与即时渲染。 在线工具,Mermaid 预览与可视化编辑在线工具,online

  • 随机西班牙地址生成器

    随机生成西班牙地址(支持马德里、加泰罗尼亚、安达卢西亚、瓦伦西亚筛选),支持数量快捷选择、显示全部与下载。 在线工具,随机西班牙地址生成器在线工具,online

  • Gemini 图片去水印

    基于开源反向 Alpha 混合算法去除 Gemini/Nano Banana 图片水印,支持批量处理与下载。 在线工具,Gemini 图片去水印在线工具,online

  • curl 转代码

    解析常见 curl 参数并生成 fetch、axios、PHP curl 或 Python requests 示例代码。 在线工具,curl 转代码在线工具,online