【手把手从零实现】Qwen2.5-VL:环境配置/功能实验(图片识别理解、目标检测、文字OCR、文档解析、视频理解)/模型微调(MS-Swift 和 LLaMA-Factory)
目录
1 先看结论
2 环境准备与安装
2.1 基础环境与依赖
2.2 Transformers 开发版安装
2.3 模型权重下载(HF / ModelScope)
3 快速推理上手
3.1 显存占用测试
3.2 单图问答 Demo
4 功能实验全记录
4.1 多图联合理解
4.2 输出目标检测框(bbox)
4.3 OCR(含繁体/手写/票据)
4.4 文档结构化解析(HTML / Markdown)
4.5 视频理解(关键帧/字幕/动作概括)
5 批量推理脚本与常见报错定位
6 训练与微调实践
6.1 选择 MS-Swift 还是 LLaMA-Factory?
6.2 数据格式准备(图文对话、多模态指令)
6.3 MS-Swift 微调流程示例
6.4 LLaMA-Factory 微调流程示例
6.5 低成本方案:QLoRA / Freeze-Vision-Encoder
6.6 评测与导出部署
7 资源整理 & 参考命令速查
8 常见问题 FAQ
1. 先看结论
-
Qwen2.5-VL(阿里通义千问最新视觉语言家族)在 图像理解、检测框、OCR、表格解析、视频理解 等方面给出“开箱即用”的接口,3B 模型就很能打。
-
实测显存需求见下表。
-
HuggingFace
transformers
正式版暂时跟不上最新接口,需要从源码装 4.49.0.dev0 左右的开发版。 -
MS-Swift(ModelScope 原生工具链)与 LLaMA-Factory(社区爆款微调框架)均已支持多模态微调,可选自己熟悉的生态;本篇给出两套命令行 & 配置模板。
-
训练数据核心是 图像/视频路径 + Prompt/Answer 的多模态对话格式,注意对齐字段、max_pixels、抽帧策略等。
-
推理环节推荐自己包一层脚本,解决路径、批处理、可视化等重复工作。
-
建议:英文 prompt + 中文输出要求,往往效果更稳定。
2. 环境准备与安装
2.1 基础环境与依赖
建议环境(亲测稳定):
-
OS:Ubuntu 20.04/22.04(Win 也能跑,但 CUDA/路径权限坑多)
-
Python:3.10(官方示例多数基于 3.10)
-
CUDA:12.4(本人使用的此版本,也可以使用11.8之类的)
-
PyTorch:2.6.0(和 CUDA 对应,好装好用)
# 1) 新建虚拟环境conda create -n qwen25-vl python=3.10 -yconda activate qwen25-vl# 2) 安装 PyTorch(任选一个 CUDA 版本)# or CUDA 12.4pip install torch==2.6.0 torchvision==0.21.0 torchaudio==2.6.0 \\ --index-url https://download.pytorch.org/whl/cu124# 3) 其他常用依赖pip install numpy==1.26.4 accelerate qwen-vl-utils==0.0.10 pillow
坑点提示:
numpy>=2.0
会让部分包崩溃(尤其老代码),这里锁定到 1.26.x。
2.2 Transformers 开发版安装
官方新接口(Qwen2_5_VLForConditionalGeneration
)目前只在 dev 版:
git clone https://github.com/huggingface/transformers.gitcd transformers# 建议固定在一个已知可用的 commit(例如 2025-02 月初的)pip install -e .
验证是否成功:
from transformers import Qwen2_5_VLForConditionalGeneration, AutoProcessor
若无红色波浪警告 / ImportError,即 OK。
2.3 模型权重下载
两种主流路径:
-
ModelScope CLI(国内快)
-
HuggingFace Hub(海外多)
示例(ModelScope):
# 3Bmodelscope download --model Qwen/Qwen2.5-VL-3B-Instruct --local_dir ./qwen25-vl-3b# 7Bmodelscope download --model Qwen/Qwen2.5-VL-7B-Instruct --local_dir ./qwen25-vl-7b# 72Bmodelscope download --model Qwen/Qwen2.5-VL-72B-Instruct --local_dir ./qwen25-vl-72b
建议:提前准备
~/.cache/modelscope
的磁盘空间(几十 GB 起)。
3. 快速推理上手
3.1 显存占用测试
import os, torchfrom transformers import Qwen2_5_VLForConditionalGeneration, AutoProcessoros.environ[\"CUDA_VISIBLE_DEVICES\"] = \"0\"model_path = \"./qwen25-vl-3b\"model = Qwen2_5_VLForConditionalGeneration.from_pretrained( model_path, torch_dtype=\"auto\", device_map=\"auto\")processor = AutoProcessor.from_pretrained(model_path)print(\"Loaded!\")
记录不同模型的首次加载显存占用(实际略有浮动):
3.2 单图问答 Demo
from transformers import Qwen2_5_VLForConditionalGeneration, AutoProcessorfrom qwen_vl_utils import process_vision_infofrom PIL import Imageimport torchmodel_path = \"./qwen25-vl-3b\"img_path = \"./demo/giraffe.jpg\"question = \"请用中文描述一下这张图片的内容,并给出主要物体。\"model = Qwen2_5_VLForConditionalGeneration.from_pretrained( model_path, torch_dtype=\"auto\", device_map=\"auto\")processor = AutoProcessor.from_pretrained(model_path)image = Image.open(img_path)messages = [{ \"role\": \"user\", \"content\": [ {\"type\": \"image\", \"image\": image}, {\"type\": \"text\", \"text\": question}, ],}]prompt = processor.apply_chat_template(messages, add_generation_prompt=True)image_inputs, video_inputs = process_vision_info(messages)inputs = processor( text=[prompt], images=image_inputs, videos=video_inputs, padding=True, return_tensors=\"pt\").to(\"cuda\")with torch.inference_mode(): out = model.generate(**inputs, max_new_tokens=128)ans = processor.batch_decode(out[:, inputs.input_ids.shape[-1]:], skip_special_tokens=True)[0]print(ans)
4. 功能实验全记录
下面所有示例我都用 3B 模型 跑过;更大模型同理(更强但更贵)。
4.1 多图联合理解
场景:一次性输入一组图片,让模型找共性、排序、或者逐张命名。
提示词技巧:英文主述,最后加上 “Answer in Chinese” 更稳。
import osfrom transformers import Qwen2_5_VLForConditionalGeneration, AutoProcessorfrom qwen_vl_utils import process_vision_infomodel_path = \"./qwen25-vl-3b\"img_dir = \"./demo/multi_images\"question = ( \"Describe the key entities in each image respectively. \" \"List them in order. Please output the entity names in Chinese.\")model = Qwen2_5_VLForConditionalGeneration.from_pretrained(model_path, torch_dtype=\"auto\", device_map=\"auto\")processor = AutoProcessor.from_pretrained(model_path)content = []for fn in sorted(os.listdir(img_dir)): if fn.lower().endswith((\".jpg\", \".jpeg\", \".png\")): content.append({\"type\": \"image\", \"image\": os.path.join(img_dir, fn)})content.append({\"type\": \"text\", \"text\": question})messages = [{\"role\": \"user\", \"content\": content}]prompt = processor.apply_chat_template(messages, tokenize=False, add_generation_prompt=True)img_inputs, vid_inputs = process_vision_info(messages)inputs = processor( text=[prompt], images=img_inputs, videos=vid_inputs, padding=True, return_tensors=\"pt\").to(\"cuda\")out = model.generate(**inputs, max_new_tokens=256)ans = processor.batch_decode(out[:, inputs.input_ids.shape[-1]:], skip_special_tokens=True)[0]print(ans)
【示意图位置:展示多张图片+模型输出的清单样式】
4.2 输出目标检测框(bbox)
只需在提示词里要求“返回坐标”,并明确格式。例如:
question = ( \"Detect all objects in the image. \" \"Return a JSON list with items like {\\\"bbox_2d\\\": [x1,y1,x2,y2], \\\"label\\\": \\\"中文类别\\\"}.\")
解析可视化示例(Pillow):
from PIL import Image, ImageDraw, ImageFontimport jsondef draw_bboxes(img_path, json_str, font_path=\"C:/Windows/Fonts/SimHei.ttf\"): img = Image.open(img_path).convert(\"RGB\") data = json.loads(json_str) draw = ImageDraw.Draw(img) font = ImageFont.truetype(font_path, 24) for item in data: x1,y1,x2,y2 = item[\"bbox_2d\"] cls = item[\"label\"] draw.rectangle([x1,y1,x2,y2], outline=\"red\", width=3) draw.text((x1, y1-24), cls, fill=\"yellow\", font=font) return img# 用 ans 中的 json 段落调用
【示意图位置:原图 + 画框后的结果对比】
4.3 OCR
提示词可用:
-
“Read all texts line by line.”
-
“请逐行输出图中出现的所有文字,不要遗漏。”
建议:默认 OCR 结果是纯文本;若想要位置、字体大小等信息,目前需要再追问或切换专门 OCR 模型。
【示意图位置:古籍/票据/手写体样例 + 输出文本】
4.4 文档结构化解析(HTML / Markdown)
让模型输出 HTML 表格非常实用,尤其是财报、表格截图。
提示词示例:
\"Convert the information in this image into a valid HTML table. Keep all numbers as they are.\"
实测模型喜欢加 ```html 代码块,可用正则截取内部再另存为 .html
。
【示意图位置:表格截图 + 浏览器渲染后的 HTML 表格对比图】
4.5 视频理解
官方接口需要把视频路径放进 messages
,并设置 max_pixels
和 fps
(抽帧率)。核心示例:
messages = [{ \"role\": \"user\", \"content\": [ {\"type\": \"video\", \"video\": \"./demo/video.mp4\", \"max_pixels\": 360*420, \"fps\": 1.0}, {\"type\": \"text\", \"text\": \"Describe this video in Chinese.\"}, ]}]
处理方式:
text = processor.apply_chat_template(messages, tokenize=False, add_generation_prompt=True)img_inputs, vid_inputs, vid_kwargs = process_vision_info(messages, return_video_kwargs=True)inputs = processor( text=[text], images=img_inputs, videos=vid_inputs, fps=vid_kwargs.get(\"fps\"), padding=True, return_tensors=\"pt\", **vid_kwargs).to(\"cuda\")
注意:
fps
需传入processor
;处理视频比图片更吃内存,做好分辨率/帧率权衡。
【示意图位置:视频关键帧拼图 + 输出摘要】
5. 批量推理脚本与常见报错定位
-
批处理脚本建议:
-
使用
argparse
解析输入目录 / 输出目录 / 模型路径 / prompt。 -
将所有推理结果(文本/JSON/HTML)统一保存为
xx.jsonl
或者分多种文件夹。 -
可加上
tqdm
做进度条,防止不知道进度卡哪了。
-
-
常见报错
-
CUDA out of memory:减小
max_pixels
、fp16
、关掉cache
、多卡分配; -
ValueError: image tensor sizes must be ...:多图/视频时,
processor
输入不一致导致,确保调用process_vision_info
; -
transformers 版本不对:确认是 dev 版并且
pip list | grep transformers
正确; -
Windows pillow font 路径问题:改用默认字体或者指定 ttf 路径。
-
6. 训练与微调实践
6.1 选择 MS-Swift 还是 LLaMA-Factory?
结论:你用哪个熟就用哪个,两者都能跑起来。下面分别给出示例。
6.2 数据格式准备
通用思路:一条样本 = (多模态输入, 文本输出)
-
图像:本地路径 / base64 皆可,但在训练时通常给路径让 dataloader 去读。
-
文本:Prompt、Answer 多轮对话(role=user/assistant)。
-
视频:建议提前抽帧,或者在 dataloader 中设定抽帧策略(统一帧数 / 间隔抽帧)。
-
Qwen 官方推荐的
messages
结构(和推理一致),训练时直接走chat_template
会更省事。
示例 JSON(单条):
{ \"id\": \"sample_0001\", \"messages\": [ { \"role\": \"user\", \"content\": [ {\"type\": \"image\", \"image\": \"/path/to/img_001.jpg\"}, {\"type\": \"text\", \"text\": \"图中有哪些动物?请用中文回答。\"} ] }, { \"role\": \"assistant\", \"content\": [ {\"type\": \"text\", \"text\": \"有两只长颈鹿和一只斑马。\"} ] } ]}
6.3 用 MS-Swift 微调
安装:
pip install ms-swift# 或者git clone https://github.com/modelscope/ms-swift.gitcd ms-swift && pip install -e .
命令行 SFT 示例(LoRA):
swift sft \\ --model Qwen/Qwen2.5-VL-3B-Instruct \\ --dataset /path/to/your_multimodal.jsonl \\ --output_dir ./outputs/qwen25vl_lora \\ --vision_tower auto \\ --max_pixels 518400 \\ --lora_rank 64 \\ --per_device_train_batch_size 1 \\ --gradient_accumulation_steps 16 \\ --fp16 \\ --num_train_epochs 3 \\ --learning_rate 1e-4 \\ --save_steps 500 \\ --logging_steps 20
要点:
-
--vision_tower auto
会自动识别视觉编码器。 -
--max_pixels
控制单图分辨率上限,别设太大容易炸显存。 -
LoRA Rank 一般 32/64 起步,结合资源调整。
-
数据集可用 JSONL,一行一个样本。
6.4 用 LLaMA-Factory 微调
安装:
git clone https://github.com/hiyouga/LLaMA-Factory.gitcd LLaMA-Factorypip install -e .
配置文件示例(qwen25vl_sft.yaml
):
model_name_or_path: ./qwen25-vl-3bstage: sftdo_train: truedataset: /data/mm_instructions.jsonltemplate: qwen2.5_vlvision_tower: automax_pixels: 518400lora_target: q_proj,v_proj # 具体看模型结构lora_rank: 64per_device_train_batch_size: 1gradient_accumulation_steps: 16learning_rate: 1e-4num_train_epochs: 3fp16: trueoutput_dir: outputs/qwen25vl_lorasave_steps: 500logging_steps: 20
命令行启动:
python src/train.py --config qwen25vl_sft.yaml
若报 vision 相关错误,确保 template 里定义了 image/video 字段处理流程。
最新的 LLaMA-Factory 分支里一般已经内置对 QwenVL 的 support,可参考其 docs/recipes。
6.5 低成本方案
-
QLoRA: 4-bit 权重量化,显存能再省一半左右。
-
冻结视觉编码器和语言头:只微调跨模态层(通常还是要放开一点点语言头),损失一点效果但速度快、省显存。
-
分布式/Deepspeed ZeRO:大模型、72B 场景必须上。
6.6 评测与导出部署
-
微调后权重合并:
swift export
或 LLaMA-Factory 的merge_lora.py
。 -
转
gguf
/turbomind
等格式部署(视情况选择)。 -
定制评测集(多模态指令、OCR 表格准确率、bbox mAP)来客观比较。
7. 资源整理 & 参考命令速查
-
官方仓库:Qwen2.5-VL GitHub / Blog / ModelScope 集合
-
多模态工具:
qwen-vl-utils
、qwen_agent
-
训练框架:MS-Swift、LLaMA-Factory、xtuner
-
冷门但好用的小工具:
-
图像批缩放:
img2dataset
/mmcv
-
视频抽帧:
ffmpeg -i in.mp4 -vf fps=1 out_%04d.jpg
-
8. 常见问题 FAQ
Q1:Windows 下 CUDA 驱动经常报错怎么办?
A:优先用 WSL2 或直接 Linux;Win 下注意显卡驱动版本和 PyTorch 匹配。
Q2:为什么建议使用英文?
A:训练数据分布所致。建议主逻辑用英文,最后补一句 “请用中文回答”。
Q3:HTML 表格输出总少几列 / 数字错位?
A:提示词里强调“保留所有列”“不要合并单元格”;必要时二次解析文本再纠错。
Q4:72B 一定要单机 8 卡吗?
A:不一定,cpu_offload
+ 4bit
+ device_map=\"auto\"
能勉强跑,但速度慢。
Q5:微调后效果不稳?
A:检查数据质量(是否多轮对齐)、超参(lr 太高/epoch 太多),以及视觉编码器是否被破坏。