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

【全网首发】使用 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, AutoProcessorimport torch# 加载 Qwen2.5-VL-7B-Instructmodel = Qwen2_5_VLForConditionalGeneration.from_pretrained( \"Qwen/Qwen2.5-VL-7B-Instruct\", torch_dtype=torch.bfloat16, device_map=\"auto\",)# 加载 tokenizer 和 processortokenizer = 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 Datasetimport 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_infoimport 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(\"\")[1].split(\"\")[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_modelconfig = 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, DataCollatorForSeq2Seqimport osargs = 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 PeftModelpeft_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 绝对是一个值得尝试的方案!🚀