📌 引言
今天凌晨(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
为空,请检查:
-
数据集 JSON 文件是否正确加载;
-
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 绝对是一个值得尝试的方案!🚀