【全网首发】使用 LoRA 微调 Qwen2.5-VL-7B-Instruct:完整流程解析

📌 引言

今天凌晨(2025 年 1 月 28 日),Qwen 团队正式发布 Qwen2.5-VL,这是 Qwen 模型家族的旗舰视觉语言模型(VLM)。相较于之前的 Qwen2-VL 版本,Qwen2.5-VL 在 图像理解、视频分析、结构化输出和视觉推理 方面取得了巨大突破。

本次发布的模型涵盖 3B、7B 和 72B 三种尺寸,并已在 Hugging Face 和 ModelScope 上开源,方便不同需求的开发者使用。

与此同时,随着 大规模视觉-语言模型(VLMs) 的快速发展,如何高效地 微调这些强大的模型 以适配特定任务,成为研究和应用中的关键挑战。然而,传统的 全参数微调 需要大量计算资源和数据,而 LoRA(Low-Rank Adaptation) 作为一种高效的 参数高效微调(PEFT) 方法,能够在 低资源环境 下快速优化大模型,使其更适用于特定任务。

因此,本文将介绍 如何使用 LoRA 技术微调 Qwen2.5-VL-7B-Instruct,并结合 SwanLab 进行训练管理和可视化分析,帮助开发者更轻松地适配该模型到自己的应用场景。

🔹 为什么选择 LoRA?

在大规模 Transformer 模型中,全参数微调需要更新数 十亿级别 的参数,不仅计算量庞大,而且训练过程中需要存储大量梯度信息,导致 显存占用极高

LoRA 通过以下方式优化微调过程

• 冻结原始模型权重,仅在某些关键层(如 q_proj, v_proj)上添加 低秩矩阵 进行训练;

• 显著减少训练参数,通常 LoRA 仅需 百万级别参数 参与更新,大幅降低计算资源需求;

• 更快的微调速度,仅更新少量参数,可以更快适配新任务;

• 易于部署,微调后模型可以通过 LoRA 适配层 轻松加载,而无需完整存储修改后的权重。

🛠️ 微调环境

在开始微调之前,我们需要安装相应的环境依赖(Python版本需大于等于3.9):

pip install torch transformers datasets peft accelerate qwen-vl-utils swanlab

此外,Qwen2.5-VL 可以使用 flash-attention-2,可以通过以下命令安装(也可以不安装):

pip install flash-attn --no-build-isolation

🚀 LoRA 微调 Qwen2.5-VL-7B-Instruct

📍 1. 加载模型

from transformers import Qwen2_5_VLForConditionalGeneration, AutoTokenizer, AutoProcessor
import torch

# 加载 Qwen2.5-VL-7B-Instruct
model = Qwen2_5_VLForConditionalGeneration.from_pretrained(
    "Qwen/Qwen2.5-VL-7B-Instruct",
    torch_dtype=torch.bfloat16,
    device_map="auto",
)

# 加载 tokenizer 和 processor
tokenizer = AutoTokenizer.from_pretrained("Qwen/Qwen2.5-VL-7B-Instruct")
processor = AutoProcessor.from_pretrained("Qwen/Qwen2.5-VL-7B-Instruct")

# 允许梯度更新
model.enable_input_require_grads()

📍 2. 处理输入数据

Qwen2.5-VL 采用 多模态输入格式,我们需要构造 文本 + 图像 组合的数据,并将其转换为模型可接受的格式。此外,在训练前,我们还需要 正确加载和预处理数据集,以确保 train_dataset 正确定义。

📌 加载并划分数据

首先,我们需要加载 数据集 JSON 文件 并划分为 训练集和测试集

from datasets import Dataset
import json
​
# 读取数据集
data_path = "path/to/your/dataset.json"  # 请替换为实际路径
with open(data_path, 'r') as f:
    data = json.load(f)
    train_data = data[:-4]  # 划分数据集,保留最后4个样本作为测试集
    test_data = data[-4:]
​
# 保存数据
with open("train_data.json", "w") as f:
    json.dump(train_data, f)
with open("test_data.json", "w") as f:
    json.dump(test_data, f)
​
# 加载数据集
train_ds = Dataset.from_json("train_data.json")

📌 预处理输入数据

process_func() 负责将 文本 + 图像 数据转换为 Qwen2.5-VL 的输入格式

from qwen_vl_utils import process_vision_info
import torch
​
def process_func(example):
    """
    预处理输入数据
    """
    MAX_LENGTH = 8192
    conversation = example["conversations"]
    input_content = conversation[0]["value"]
    output_content = conversation[1]["value"]
​
    file_path = input_content.split("<|vision_start|>")[1].split("<|vision_end|>")[0]
​
    # 构造多模态对话
    messages = [
        {
            "role": "user",
            "content": [
                {"type": "image", "image": f"{file_path}", "resized_height": 256, "resized_width": 256},
                {"type": "text", "text": "请描述这张图片的内容。"},
            ],
        }
    ]
    
    text = processor.apply_chat_template(messages, tokenize=False, add_generation_prompt=True)
    image_inputs, video_inputs = process_vision_info(messages)
    inputs = processor(
        text=[text],
        images=image_inputs,
        videos=video_inputs,
        padding=True,
        return_tensors="pt",
    )
​
    inputs = {key: value.tolist() for key, value in inputs.items()}
    
    # 构造目标输出
    response = tokenizer(f"{output_content}", add_special_tokens=False)
    input_ids = inputs["input_ids"][0] + response["input_ids"] + [tokenizer.pad_token_id]
    attention_mask = inputs["attention_mask"][0] + response["attention_mask"] + [1]
    labels = [-100] * len(inputs["input_ids"][0]) + response["input_ids"] + [tokenizer.pad_token_id]
​
    # 截断
    if len(input_ids) > MAX_LENGTH:
        input_ids = input_ids[:MAX_LENGTH]
        attention_mask = attention_mask[:MAX_LENGTH]
        labels = labels[:MAX_LENGTH]
​
    return {
        "input_ids": torch.tensor(input_ids),
        "attention_mask": torch.tensor(attention_mask),
        "labels": torch.tensor(labels),
        "pixel_values": torch.tensor(inputs["pixel_values"]),
        "image_grid_thw": torch.tensor(inputs["image_grid_thw"]).squeeze(0)
    }


📌 生成 train_dataset

使用 map() 处理训练数据,确保 train_dataset 正确定义

# 处理数据
train_dataset = train_ds.map(process_func)
​
# 确保数据加载成功
print(f"Train dataset size: {len(train_dataset)}")
print(train_dataset[0])  # 检查数据格式

如果 train_dataset 为空,请检查:

  1. 数据集 JSON 文件是否正确加载

  2. process_func() 是否返回了有效数据

📍 3. 配置 LoRA

我们使用 PEFT(Parameter Efficient Fine-Tuning) 库来进行 LoRA 适配:

from peft import LoraConfig, get_peft_model

config = LoraConfig(
    task_type="CAUSAL_LM",
    target_modules=["q_proj", "k_proj", "v_proj", "o_proj", "gate_proj", "up_proj", "down_proj"],
    inference_mode=False,
    r=64,
    lora_alpha=16,
    lora_dropout=0.05,
    bias="none",
)

# 将 LoRA 应用于模型
peft_model = get_peft_model(model, config)

📍 4. 训练模型

from transformers import TrainingArguments, Trainer, DataCollatorForSeq2Seq
import os

args = TrainingArguments(
    output_dir="output/Qwen2.5-VL-LoRA",
    per_device_train_batch_size=4,
    gradient_accumulation_steps=4,
    logging_steps=10,
    num_train_epochs=5,
    save_steps=74,
    learning_rate=1e-4,
    gradient_checkpointing=True,
)

trainer = Trainer(
    model=peft_model,
    args=args,
    train_dataset=train_dataset,  # 需要提供数据
    data_collator=DataCollatorForSeq2Seq(tokenizer=tokenizer, padding=True),
)

trainer.train()

✅ 推理与评估

训练完成后,我们可以 加载微调后的模型 并进行推理:

from peft import PeftModel

peft_model_path = "output/Qwen2.5-VL-LoRA/checkpoint-XXX"
val_peft_model = PeftModel.from_pretrained(model, peft_model_path, config=config)

messages = [
    {
        "role": "user",
        "content": [
            {"type": "image", "image": "path/to/image.jpg"},
            {"type": "text", "text": "请描述这张图片的内容。"},
        ],
    }
]

def predict(messages, model):
    """ 用于推理验证的函数 """
    text = processor.apply_chat_template(
        messages, tokenize=False, add_generation_prompt=True
    )
    image_inputs, video_inputs = process_vision_info(messages)
    inputs = processor(
        text=[text],
        images=image_inputs,
        videos=video_inputs,
        padding=True,
        return_tensors="pt",
    )
    inputs = inputs.to(model.device)

    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
    )
    return output_text[0]

response = predict(messages, val_peft_model)
print(response)

🎯 结论

• LoRA 微调 适用于大规模 视觉-语言模型(VLM),能够在 低计算资源 下快速适配新任务。

• Qwen2.5-VL 作为强大的开源 VLM,结合 LoRA 可以高效完成多模态微调

• 微调技巧:确保 梯度可训练 (enable_input_require_grads)、适当降低图像分辨率使用 gradient_checkpointing 以节省显存

如果你正在寻找一种 轻量级 VLM 微调方法,LoRA 绝对是一个值得尝试的方案!🚀

评论 22
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

HovChen_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值