这次的教程我们将使用官方的指定demo数据来进行qwen3-vl的微调,并且将lora得到的内容进行合并,使用新的模型来进行推理验证。
由于官方库有些内容没有实现,所以这里fork了一个新的仓库来实现一下管理,
git clone https://github.com/chan-yuu/Qwen3-VL.git
在正式微调之前,我们先来认识一下这里的模型的架构是什么样的。

主要分为了三个部分,第一个是interleaved-MRoPE,是一个随时间、宽度和通过位置嵌入确定的高度,增强长视距的视频推理。第二个是DeepStack,深度堆栈,融合了多级ViT特征提取来捕捉细粒度细节和锐化的图文对齐。第三个是图像时间戳对齐,超越T-RoPE来精确的,基于时间戳事件定位用于更强的视频时序建模。
其实我还是没有看懂,但是我们可以求助一下llm。在主干大模型中,dense/moe,说明qwen3-vl既有稠密版,也有混合专家版。家族包括dense的2B/4B/8B/32B,以及MoE的30B-A3B、235B-A22B。它与底下的vision encoder的关系在于:vision encoder负责把图像视频变为模型能看懂的视觉token,LM decoder:把图像和文本token统一放进大模型里推理并生成答案。
下方,图片和视频先进入vision encoder,其中的图片包括了:
- Picture 1
- Picture 2
- Picture 3
- Video 1
这些都是视觉输入,先进入了encoder中,官方说明中指出,qwen3-vl的一项结构升级就是通过视觉侧和语言侧更紧的对齐,并使用多层视觉特征,而不是只拿最后一层视觉表示。图中的native resolution input表示输入图像尽量按原始分辨率处理,而不是一开始就强行缩到固定最小尺寸。这个也是qwen-vl系列强调的提升对文档、细粒度细节、复杂版面的感知能力。所以刚才的的三点总结就是在说它有更强的OCR、更强长文档结构解析和更细细粒度视觉理解。
小方块表示的就是encoder输出的token序列,这些token序列送进decoder中,具体来说,分成了文本token和vision token,也就是一种interleaved multimodal inputs,即文本图片视频交错混合输入。
我们还可以看到一个有意思的现象是,各个图片的token相差很大:
- Picture 1:11427 tokens
- Picture 2:8 tokens
- Picture 3:1125 tokens
这是因为不同分辨率、不同尺寸、不同视觉压缩结果,对应的视觉token数量相差很多。native resolution就是原始分辨率,说明图像本身是按照自己的分辨率进行原始输入的,这样的token也就是表现他们的特点。
在视频部分中,有一串连续帧,包括了时间序列,它是把多帧送入了vision encoder,再变成视觉token,时间信息不只靠视觉位置编码,而是把时间戳以文本形式更明确的对齐到视频内容上,提升第几秒发生了什么的定位能力,这项升级,就是从T-RoPE走向更精确的timestamp-grounded event localization。总的来说,就是视频帧提供视觉内容,文本时间戳提供更明确的时间锚点,二者一起进入大模型中。
deepstack在右边,vision encoder部分,这是最关键的创新点,也不容易看懂,很多VLM的做法是图片→vision encoder,只取最后一层视觉特征,变成了视觉token,一次性送给LLM,但是deep stack不同,不只使用视觉编码器的最后输出,还把多个层级的视觉特征注入到了LLM的不同层里。官方的描述是DeepStack fuses multi-level ViT features,技术报告摘要则说它 effectively leverages multi-level ViT features to tighten vision-language alignment。也就是浅层视觉特征:偏向于局部纹理、边缘、字形、小细节,中层视觉特征:更偏区域、部件、局部结构,高层视觉特征:更偏语义、物体、整体理解。deep stack的作用就是把这些分层的信息,分层的送进LLM。
最后总结一下,图片/视频先经过 Vision Encoder,变成视觉 token;文本 token 和视觉 token 交错组成统一上下文,送入 Qwen3 的语言解码器;同时,多层视觉特征通过 DeepStack 持续注入到 LLM 的不同层,以增强细节感知、OCR、空间理解和视频时序理解。
接下来我们就是对模型进行微调了,这里使用的是80G显存的设备进行微调,不过3B的模型应该本地也可以尝试一下LoRa微调。

我们主要是通过sft(supervised fine-tuning)监督微调来实现,所以需要准备QA问答对,在https://github.com/chan-yuu/Qwen3-VL/tree/main/qwen-vl-finetune/demo文件夹下,含有对应的json文件和图片文件,因为是一个视觉模型的识别,所以是需要传入实际的图像进行编码的。其中的json文件指向的是图片,对话两个部分。

这里我们就主要使用这个json来进行训练。当然要注意,数据比较少的情况下无法看出实际的效果,这里我们主要是跑通流程,具体的数据集准备这里就不赘述。不过需要注意的是,https://github.com/chan-yuu/Qwen3-VL/blob/main/qwen-vl-finetune/tools/process_bbox.ipynb中专门指出了如何通过已有的标记框来进行处理得到大模型需要的编码信息。同时,在https://github.com/chan-yuu/Qwen3-VL/blob/main/qwen-vl-finetune/README.md中也说明了ground任务的数据集结构
{
"image": "demo/COCO_train2014_000000580957.jpg",
"conversations": [
{
"from": "human",
"value": "<image>\nLocate house in this image and output the bbox coordinates in JSON format."
},
{
"from": "gpt",
"value": "{\n"bbox_2d": [135, 114, 1016, 672]\n}"
}
]
}
但是实测下来这里还有不足,也就是应该同时标准类别和位置信息才是完整的gpt的输出,这里可能得进行额外的测试才能得到更加合理的数据集结构。并且要特别说明的就是这个COCO_train2014_000000580957.jpg,与demo中的图片不是一个。
接下来进入云端服务器来实操,官方的python推荐版本没有明确给出,我们使用python3.12即可,通过conda创建虚拟环境
conda create -n qwen3_vl python=3.12
然后进入环境中,下载相关的依赖项:
conda activate qwen3_vl
pip install -r requirements_web_demo.txt
然后再安装一些额外的依赖项:
torch==2.6.0torchvision==0.21.0transformers==4.57.0.dev0deepspeed==0.17.1triton==3.2.0accelerate==1.7.0torchcodec==0.2peft==0.17.1flash_attn==2.7.4.post1
这里我们先安装前面的内容,最后一个flash_attn比较大,经常会因为网络原因无法安装,所以我们这里手动进行安装,前往github的下载页,一定要找对

其余的基本都是无法与torch.2.6.0匹配的。以上搞定之后,环境就可以使用了,我们还需要去下载基础模型来调用,这里我们通过脚本来直接下载即可:
from modelscope import snapshot_download
# 指定模型的下载路径
cache_dir = 'xxx/model' # 补充实际的下载目录
# 调用 snapshot_download 函数下载模型
model_dir = snapshot_download('Qwen/Qwen3-VL-2B-Instruct', cache_dir=cache_dir)
print(f"模型已下载到: {model_dir}")
在微调之前,需要先准备数据集并进行处理,准备好的数据集就是一个图片文件夹以及一个json文件,我们在https://github.com/chan-yuu/Qwen3-VL/blob/main/qwen-vl-finetune/qwenvl/data/init.py __init.py__文件中添加对应的数据集信息:

这样注册之后,微调时就可以正常通过my_dataset来访问数据集了。注意给对路径,这里我们可以考虑直接给全局路径。最后就是直接进行微调了,他准备了一个一键微调的脚本,我们修改上面的一些配置就可以训练了:
#!/bin/bash
# ======================
# Distributed Configuration
# ======================
MASTER_ADDR="127.0.0.1" # [Required] Master node IP for multi-GPU training
MASTER_PORT=$(shuf -i 20000-29999 -n 1) # Random port to avoid conflicts
NPROC_PER_NODE=$(nvidia-smi --list-gpus | wc -l) # Automatically detects available GPUs
# ======================
# Path Configuration
# ======================
MODEL_PATH="/mnt/project_modelware/zhaojian/cyun/qwen3-vl/model/Qwen/Qwen3-VL-2B-Instruct" # [ModelArguments] Pretrained model path
OUTPUT_DIR="./checkpoints" # Directory for saving checkpoints
CACHE_DIR="./cache" # [TrainingArguments] Cache directory for models
# ======================
# Model Configuration
# ======================
DATASETS="my_dataset%100" # [DataArguments] Dataset with sampling rate
# ======================
# Training Hyperparameters
# ======================
torchrun --nproc_per_node=$NPROC_PER_NODE \
--master_addr=$MASTER_ADDR \
--master_port=$MASTER_PORT \
/mnt/project_modelware/zhaojian/cyun/qwen3-vl/Qwen3-VL/qwen-vl-finetune/qwenvl/train/train_qwen.py \
--model_name_or_path $MODEL_PATH \
--tune_mm_llm True \
--tune_mm_vision False \
--tune_mm_mlp False \
--dataset_use $DATASETS \
--output_dir $OUTPUT_DIR \
--cache_dir $CACHE_DIR \
--bf16 \
--per_device_train_batch_size 4 \
--gradient_accumulation_steps 4 \
--learning_rate 2e-7 \
--mm_projector_lr 1e-5 \
--vision_tower_lr 1e-6 \
--optim adamw_torch \
--model_max_length 4096 \
--data_flatten True \
--data_packing True \
--max_pixels 451584 \
--min_pixels 12544 \
--video_fps 2 \
--video_max_frames 8 \
--video_min_frames 4 \
--video_max_pixels 1306112 \
--video_min_pixels 200704 \
--num_train_epochs 100 \
--warmup_ratio 0.03 \
--lr_scheduler_type "cosine" \
--weight_decay 0.01 \
--logging_steps 10 \
--save_steps 500 \
--save_total_limit 3 \
--lora_enable True \
--lora_r 8 \
--lora_alpha 16 \
--lora_dropout 0.0 \
--deepspeed zero3.json
参考我的配置即可,进行训练之前,我们需要先登录一下wandb来记录训练的细节信息,也就是
wandb login
然后通过wandb的密钥进行登录认证,认证完成之后需要再wandb login验证一下,如果出现了个人信息那就表明成功了。
最后直接运行
bash sft_3.14.sh
注意,训练之前需要保证当前文件夹下没有checkpoints文件夹(他也是模型的保存文件夹),否则无法训练


可以看到这里的损失其实是不好计算的,不是文本而是token的训俩。接下来需要使用训俩好的模型进行推理。
我们先通过未合并的模型来测试,此时的结果是:
python inference.py --model_path /mnt/project_modelware/zhaojian/cyun/qwen3-vl/model/Qwen/Qwen3-VL-2B-Instruct --checkpoint_path /mnt/project_modelware/zhaojian/cyun/qwen3-vl/Qwen3-VL/qwen-vl-finetune/scripts/checkpoints --image /mnt/project_modelware/zhaojian/cyun/qwen3-vl/Qwen3-VL/qwen-vl-finetune/demo/images/10095.png --prompt "Is the value of Favorable 38 in 2015?"
结果是:
当然,从效果来说的话,这点数据最终的效果其实是一般的:


然后我们使用那一张grouding的数据来训俩,得到的内容依然是无法准确的定位。

除了inference.py之外,tools中还可以使用merge_lora,它可以把训俩好的模型嵌入到原本的模型中得到最终的版本,可以直接通过这个来和模型进行对话。

162

被折叠的 条评论
为什么被折叠?



