AI 17 分钟阅读

LoRA 微调 VLM 2B~8B:参数、显存、踩坑记录

· 更新于 2026/6/29

覆盖 Qwen2.5-VL / Qwen3-VL / InternVL 2B~8B 模型,聚焦可直接使用的参数与代码。


一、VLM 和 LLM 微调的区别

LLM 是单一模型,所有参数属于同一个 Transformer,微调时整个模型共享一套学习率和策略。VLM 则由三个功能完全不同的模块拼接而成,每个模块对学习率、冻结策略、量化方式的要求各不相同:

图像 → [Vision Encoder (ViT)] → [Projector / Merger] → [LLM 主干] → 文本输出
        预训练视觉特征提取        跨模态对齐             语言生成
        通常冻结                小数据冻结/大数据训练     LoRA 微调

这带来几个 LLM 微调中不存在的问题:

1. 模块间学习率必须分层

LLM 微调只需设一个学习率。VLM 如果三个模块用同一个 LR,ViT 的预训练特征会被破坏(视觉能力退化),而 LLM 部分又学不够快。实践中 Vision LR 必须是 LLM LR 的 1/10 以内。

2. 图像 token 占上下文但不算 loss

LLM 的每个 input token 要么参与 loss 计算,要么是被 mask 的 prompt(至少语义上有意义)。VLM 中一张图片经 ViT 编码后产生数百到数千个视觉 token(Qwen2.5-VL 一张 1080p 图 ≈ 2584 token),这些 token 全部被 mask(label=-100),不参与 loss 但占满了 context window。一条 VLM 样本真正算 loss 的 token 可能只有 12%。

3. 分辨率直接影响显存和训练速度

LLM 的序列长度由文本决定,变化范围有限。VLM 的序列长度很大程度取决于图像分辨率 —— 同一张图片,不限制分辨率 vs 压缩到 512px,视觉 token 数可以差 10 倍以上,显存需求完全不同。

4. 量化有额外限制

LLM 做 QLoRA 时整个模型统一量化到 4bit。VLM 的 ViT 不能量化 —— 量化 vision tower 会导致训练崩溃。此外,训练 embed_token 时必须同步给 lm_head 挂 LoRA,因为两者权重共享,缺一侧会训练发散。

各模块的默认策略:

模块 典型组件 默认策略 学习率
Vision Encoder(ViT) Qwen2.5-VL 675M ViT、InternViT-300M 冻结
Projector / Merger MLP、Resampler 训练(>5k 样本)/ 冻结(<5k 样本) 1e-5
LLM 主干 Qwen2、InternLM2 等 LoRA all-linear 1e-4
from peft import LoraConfig, get_peft_model

def build_vlm_lora(model, r=16, alpha=32, dropout=0.05):
    skip_keywords = ("visual", "vision", "patch_embed", "img_projection")

    linear_names = set()
    for name, module in model.named_modules():
        if any(k in name for k in skip_keywords):
            continue
        if isinstance(module, torch.nn.Linear):
            linear_names.add(name.split(".")[-1])

    config = LoraConfig(
        r=r,
        lora_alpha=alpha,
        lora_dropout=dropout,
        target_modules=list(linear_names),
        bias="none",
        task_type="CAUSAL_LM",
    )
    return get_peft_model(model, config)

二、模型选型与参数规格

2.1 Qwen 系列

Qwen2.5-VL:全系列共用 675M ViT,patch size 14×14。

模型 总参数 ViT LLM Merger 输出维度
Qwen2.5-VL-3B ~3.0B 675M 2.3B Qwen2.5 2048
Qwen2.5-VL-7B ~7.0B 675M 6.3B Qwen2.5 3584

Qwen3-VL:改用分级 ViT(SigLIP2),patch size 16×16,token 数比 Qwen2.5-VL 少 21%。

模型 总参数 ViT LLM
Qwen3-VL-2B ~2.0B SigLIP2-Large 300M 1.7B Qwen3
Qwen3-VL-4B ~4.0B SigLIP2-Large 300M 3.7B Qwen3
Qwen3-VL-8B ~8.6B SigLIP2-SO 400M 8.2B Qwen3

2.2 InternVL 系列

统一 ViT-MLP-LLM 三段式架构,1B~14B 使用 InternViT-300M。

模型 ViT LLM 总参数
InternVL2/2.5-2B InternViT-300M InternLM2-1.8B ~2.2B
InternVL2.5-4B InternViT-300M Qwen2.5-3B ~3.5B
InternVL2/2.5-8B InternViT-300M InternLM2.5-7B ~8.0B
InternVL3-2B InternViT-300M Qwen2.5-1.5B ~2.0B
InternVL3-8B InternViT-300M Qwen2.5-7B ~8.0B

2.3 LoRA 可训练参数量(all-linear,LLM 部分)

模型 hidden_dim layers r=16 参数量 r=64 参数量
Qwen2.5-VL-3B 2048 36 ~16.5M ~66M
Qwen2.5-VL-7B 3584 28 ~22.4M ~90M
Qwen3-VL-4B 2560 36 ~20.6M ~83M
Qwen3-VL-8B 4096 32 ~29.4M ~118M

三、LoRA 超参数

3.1 Rank 与 Alpha

参数 推荐值 说明
r 16(起点) 简单分类用 r=8;结构化输出/多任务用 r=32~64
alpha r 或 2r 高 rank(≥32)时 alpha/r=0.5 也常见
scaling alpha/r 本质是权重更新的缩放系数,与 LR 联动

小数据场景的过拟合规律(实验数据):

配置 总步数 最优步数 浪费比例
r=16, epochs=10 2200 1320(ep6) 40%
r=64, epochs=10 4400 1320(ep3) 70%
r=16, epochs=10,数据清洗后 4400 2200(ep5) 50%
r=64, 复杂任务 2800 930(ep5) 67%

r=64 过拟合比 r=16 严重得多。数据质量带来的收益远大于提高 rank:相同数据量清洗 5% 错误标签,任务准确率可提升 4~5 个百分点。

两条进阶选项:

  • rsLoRAuse_rslora=True):缩放改为 alpha/√r,rank ≥ 32 时建议开启,防止梯度不稳
  • DoRA:权重分解为方向+幅度,通常有 1~2pp 稳定提升,训练慢约 20%

3.2 Target Modules

只挂 q_proj, v_proj 是 LoRA 原论文写法,不是最优。推荐挂所有 linear:

target_modules = [
    "q_proj", "k_proj", "v_proj", "o_proj",  # Attention
    "gate_proj", "up_proj", "down_proj",      # FFN
]
# LLaMA-Factory 等价写法:lora_target: all

代价:可训练参数增加约 3.5 倍,显存 +15%。收益:下游指标通常提升 2~5pp。2B~4B 模型全挂 LoRA 压力不大,7B+ 建议开 QLoRA 配合。

3.3 Dropout 与正则

参数 推荐 说明
lora_dropout 0.05~0.1 epochs≤2 用 0.05;epochs=5 用 0.1
bias "none" 训练 bias 收益极小
weight_decay 0.01 HF Trainer 默认值

Dropout 和训练长度必须配套。高 LR + 长训练必须用更强正则。


四、训练超参数

4.1 学习率

LoRA 只训练低秩增量矩阵(A·B),参数量 <1%,梯度不影响预训练权重。因此 LoRA LR 可以比全参微调高 10~100 倍。

微调方式 典型 LR
Full fine-tuning 2e-6
LoRA(通用) 1e-4
QLoRA 2e-4
VeOmni (ByteDance) 3e-4(warmup=0.007)

按模型规格细分:

模型规格 LoRA LR 全参 FT LR
2B-3B 2e-4~5e-4 2e-5
4B 1e-4~3e-4 1e-5~2e-5
7B-8B 1e-4~2e-4 1e-5

更大的模型对 LR 更敏感,建议偏低选择。

4.2 Epochs 与 Warmup

  • Epochs:通用场景 2~3 epoch 足够,小数据(<5000 样本)给 5 epoch + early_stop
  • Warmup:LoRA 参数少,梯度方差天然小,warmup_ratio ≤ 0.03 足够,超过 0.05 是浪费

4.3 其他关键参数

参数 推荐值 说明
lr_scheduler cosine warmup 后余弦衰减
per_device_batch_size 1~4 VLM 瓶颈在图像 token 数量
gradient_accumulation_steps eff_bs = 16~128 视任务复杂度
optimizer adamw_torch AdamW 即可
precision bf16 A100/H100/4090 均支持
gradient_checkpointing True 显存减少 50%+,速度损失约 20%
max_grad_norm 1.0 高 LR 下防梯度爆炸
cutoff_len 2048~8192 图像 token 已占数百个

4.4 只对 Response 计算 Loss

多轮对话中,user 部分不参与 loss 计算可提升约 1% 精度并减少一半无效计算:

def mask_user_tokens(labels, input_ids, assistant_token_id):
    in_response = False
    for i, tid in enumerate(input_ids):
        if tid == assistant_token_id:
            in_response = True
        if not in_response:
            labels[i] = -100  # HF Trainer 忽略 -100
    return labels

五、显存规划

5.1 GPU 需求

模型规格 LoRA BF16(单卡) QLoRA 4bit(单卡) 推荐 batch_size 推荐 grad_accum
2B-3B 1× 24GB 1× 12GB 2-4 4-8
4B 1× 24GB 1× 16GB 1-2 8-16
7B-8B 2× 24GB 或 1× 80GB 1× 24GB 1 8-16

含 1 张 1920×1080 图像(约 2000~2600 token)的估算:

模型 LoRA BF16 训练显存 QLoRA 4bit 训练显存
2B-3B ~10-14 GB ~6-8 GB
4B ~16-20 GB ~8-12 GB
7B-8B ~28-40 GB ~10-16 GB

2B 模型单卡参考(RTX 4090 24GB):

配置 显存
batch=1, cutoff=1024, 单图 ~14 GB
batch=2, grad_accum=8, cutoff=1024 ~20 GB
全分辨率(1920×1080),batch=1, cutoff=8192 ~18 GB

5.2 QLoRA 4bit

指标 全参微调 LoRA BF16 QLoRA 4bit
7B 训练显存 ~100-120 GB ~28 GB ~8-10 GB
性能保留 100% 90-95% 80-90%
适用 GPU 8× H100 1× A100 80GB 1× 4090 24GB
from transformers import BitsAndBytesConfig
import torch

bnb_config = BitsAndBytesConfig(
    load_in_4bit=True,
    bnb_4bit_compute_dtype=torch.bfloat16,
    bnb_4bit_use_double_quant=True,
    bnb_4bit_quant_type="nf4",
)
model = AutoModelForCausalLM.from_pretrained(
    model_path, quantization_config=bnb_config, device_map="auto"
)
model = prepare_model_for_kbit_training(model)

5.3 Gradient Checkpointing

7B+ 模型几乎必需。7B BF16 + AdamW 显存分布:参数 14GB + 梯度 14GB + 优化器 56GB + 激活值 20~40GB,其中激活值可通过 gradient checkpointing 减少 50%+,训练速度损失约 20%。

args = TrainingArguments(
    gradient_checkpointing=True,
    gradient_checkpointing_kwargs={"use_reentrant": False},  # 与 PEFT 兼容
)

六、图像分辨率与 VLM 特有问题

6.1 Qwen2.5-VL 动态分辨率机制

图像以原始宽高比处理,不强制裁剪:

  1. 宽高取整为 28 的整数倍(14×14 patch 经 2×2 merge,每 token 覆盖 28×28 px)
  2. visual_tokens = (H/28) × (W/28)

preprocessor_config.json 中默认 max_pixels 是架构上限(12,845,056),不是推荐训练值。1920×1080 不限制时产生 2584 个 token,4K 图像可达 10,000+,直接 OOM。

6.2 Qwen3-VL 的变化

patch size 从 14→16,取整倍数从 28→32,每 token 覆盖 1024px(vs 784px):

系列 取整分辨率 1920×1080 视觉 Token
Qwen2.5-VL 1904×1064 68×38 = 2,584
Qwen3-VL 1920×1088 60×34 = 2,040

Qwen3-VL 相同图像少 21% token,显存和速度直接受益。

此外,Qwen3-VL 修复了 Qwen2.5-VL 的 MRoPE 频率不均衡问题(Interleaved MRoPE),长视频位置编码更稳定。

6.3 Image 参数对照

不能将 Qwen2.5-VL 的配置直接复制到 Qwen3-VL,patch size 28→32 会导致静默错误缩放:

参数 Qwen2.5-VL Qwen3-VL
image_min_pixels 256 × 28² = 200,704 256 × 32² = 262,144
image_max_pixels 1280 × 28² = 1,003,520 1280 × 32² = 1,310,720
# Qwen2.5-VL
processor.image_processor.min_pixels = 256 * 28 * 28
processor.image_processor.max_pixels = 1280 * 28 * 28

# Qwen3-VL(必须用 32)
processor.image_processor.min_pixels = 256 * 32 * 32
processor.image_processor.max_pixels = 1280 * 32 * 32

6.4 常见图像陷阱

截断图像:训练报 IOError: image file is truncated

from PIL import ImageFile
ImageFile.LOAD_TRUNCATED_IMAGES = True

# 或在数据集加载时预过滤
from PIL import Image

def filter_corrupt(data_list):
    valid = []
    for item in data_list:
        try:
            Image.open(item["image"]).verify()
            valid.append(item)
        except Exception:
            pass
    return valid

全分辨率 vs 压缩:需要坐标精度(GUI Agent、OCR 定位)必须保持全分辨率,不设 image_max_pixels 上限。纯分类任务可以压缩以节省显存。


七、基线配置

7.1 通用 VLM 微调(数据量 >3000,无坐标精度要求)

# LLaMA-Factory 配置
model_name_or_path: Qwen/Qwen2-VL-2B-Instruct

finetuning_type: lora
lora_target: all
lora_rank: 16
lora_alpha: 32
lora_dropout: 0.05

freeze_vision_tower: true
freeze_merger: false

learning_rate: 1.0e-4
vision_lr: 2.0e-6
merger_lr: 1.0e-5
lr_scheduler_type: cosine
warmup_ratio: 0.03
weight_decay: 0.01
optim: adamw_torch
max_grad_norm: 1.0

per_device_train_batch_size: 2
gradient_accumulation_steps: 8
num_train_epochs: 3
cutoff_len: 2048

bf16: true
gradient_checkpointing: true

image_max_pixels: 1003520
image_min_pixels: 200704

val_size: 0.05
eval_strategy: steps
eval_steps: 200
save_steps: 200
load_best_model_at_end: true
metric_for_best_model: eval_loss

7.2 小数据 + 全分辨率(数据量 <5000,需要坐标精度)

# 与通用配置的差异
lora_dropout: 0.1        # 训练更长,需要更强正则
freeze_merger: true       # 数据不足以安全训练 merger

num_train_epochs: 5
early_stopping: true
early_stopping_patience: 2

cutoff_len: 8192
# 不设 image_max_pixels  # 保持全分辨率

per_device_train_batch_size: 1
gradient_accumulation_steps: 8

7.3 两套配置对比

维度 通用 小数据全分辨率
数据量 >3000 <5000
图像分辨率 压缩(max_pixels 限制) 全分辨率
Epochs 3 5 + early_stop
Dropout 0.05 0.1
Merger 训练 冻结
坐标精度 无要求 需要(<30px)

八、过拟合:分析与防治

8.1 规律

小数据 VLM 微调的过拟合模式高度一致:

  • 最优点集中在 epoch 3~6,之后 eval_loss 持续上升
  • train_loss 后期降到极低(~0.001),eval_loss 反弹 30~50%
  • r=64 过拟合比 r=16 严重,且数据越少越明显
  • 最优 epoch 由数据量和任务复杂度决定,与超参关系不大

8.2 防过拟合策略组合

from transformers import TrainingArguments, EarlyStoppingCallback

args = TrainingArguments(
    num_train_epochs=5,
    eval_strategy="steps",
    eval_steps=200,
    save_steps=200,
    load_best_model_at_end=True,
    metric_for_best_model="eval_loss",
    greater_is_better=False,
    save_total_limit=3,
)
early_stopping = EarlyStoppingCallback(early_stopping_patience=2)

8.3 数据质量是核心

  • 500~5000 高质量样本通常已足够(2B~3B 模型);超过 1w 收益边际递减
  • 相同数据量,清洗 5% 的错误标签比调高 rank 的收益大
  • Response 格式、长度、JSON 结构必须统一,模型无法同时学习多种输出风格

8.4 训练稳定性信号

信号 含义 处理
train_loss < 0.1,eval_loss 反弹 过拟合 检查 early_stopping 是否生效
loss 不降 数据或格式问题 检查 <image> token 位置和 chat template
梯度爆炸 LR 过高 设 max_grad_norm=1.0,降低 LR
灾难性遗忘 垂域数据比例过高 混入 5~20% 通用 VQA 数据

九、多卡训练(FSDP)

9.1 策略选择

场景 推荐策略
LoRA,单卡放得下 DDP
LoRA,7B+ 单卡放不下 SHARD_GRAD_OP(ZeRO-2)
全参微调 FULL_SHARD(ZeRO-3)

LoRA 已大幅减少可训练参数,FULL_SHARD 的主要收益对 LoRA 场景不关键。VLM 的序列并行比参数分片更有价值。

策略 等价 ZeRO 分片内容
NO_SHARD DDP
SHARD_GRAD_OP ZeRO-2 梯度 + 优化器状态
FULL_SHARD ZeRO-3 参数 + 梯度 + 优化器
HYBRID_SHARD 多节点优化

9.2 LoRA Adapter 保存(已知 Bug)

FSDP 保存的 state_dict 中 LoRA key 不含 adapter name,PEFT 加载时期望 lora_A.default.weight 格式,导致 key 不匹配。

from transformers import TrainerCallback
import os

class PeftSavingCallback(TrainerCallback):
    def on_save(self, args, state, control, **kwargs):
        ckpt = os.path.join(args.output_dir, f"checkpoint-{state.global_step}")
        kwargs["model"].save_pretrained(ckpt)
        return control

accelerate config 必须设置:

fsdp_config:
  fsdp_use_orig_params: true       # 关键:支持 LoRA 冻结+可训练参数混合
  fsdp_sharding_strategy: SHARD_GRAD_OP
  fsdp_state_dict_type: FULL_STATE_DICT

9.3 NCCL 超时

FSDP 每步触发 all-gather + reduce-scatter,默认 30 分钟超时容易触发:

export NCCL_ASYNC_ERROR_HANDLING=1
export NCCL_TIMEOUT=1800
export TORCH_NCCL_HEARTBEAT_TIMEOUT_SEC=1800
export NCCL_P2P_LEVEL=NVL
import torch.distributed as dist
from datetime import timedelta
dist.init_process_group(backend="nccl", timeout=timedelta(seconds=3600))

Docker 启动必须加 --shm-size=10g --ulimit memlock=-1,默认 64MB shared memory 不够。


十、训练监控

10.1 解析 trainer_state.json(不需要 WandB)

HF Trainer 每个 checkpoint 自动保存 trainer_state.json,包含完整训练日志:

import json
from pathlib import Path

def show_progress(output_dir: str):
    checkpoints = sorted(
        Path(output_dir).glob("checkpoint-*"),
        key=lambda p: int(p.name.split("-")[1])
    )
    if not checkpoints:
        return

    state = json.load(open(checkpoints[-1] / "trainer_state.json"))
    logs = state["log_history"]
    trains = [l for l in logs if "loss" in l and "eval_loss" not in l]
    evals  = [l for l in logs if "eval_loss" in l]

    print(f"Progress : {state['global_step']} / {state['max_steps']}")
    print(f"Best ckpt: {state.get('best_model_checkpoint', 'N/A')}")
    print(f"Best val : {state.get('best_metric', 'N/A')}\n")

    print("Eval history:")
    for e in evals[-5:]:
        t = next((x["loss"] for x in reversed(trains) if x["step"] <= e["step"]), None)
        gap = e["eval_loss"] - t if t else float("nan")
        print(f"  step {e['step']:5d} | eval={e['eval_loss']:.4f} | gap={gap:+.4f}")

    print("Train (recent):")
    for t in trains[-3:]:
        print(f"  step {t['step']:5d} | loss={t['loss']:.4f} | lr={t.get('learning_rate', 0):.2e}")

show_progress("output/my_model")

后台训练时实时查看:

watch -n 60 python3 monitor.py output/my_model

10.2 TensorBoard(WandB 替代)

args = TrainingArguments(
    report_to="tensorboard",
    logging_dir="runs/exp1",
    logging_steps=10,
)
# 启动
tensorboard --logdir runs/ --port 6006
# 远程服务器
ssh -L 6006:localhost:6006 user@server

10.3 WandB:自定义 train/eval gap

import wandb
from transformers import TrainerCallback

class GapMonitorCallback(TrainerCallback):
    def on_log(self, args, state, control, logs=None, **kwargs):
        if not (logs and state.is_world_process_zero):
            return

        metrics = {k: v for k, v in logs.items()
                   if k in ("loss", "eval_loss", "learning_rate", "grad_norm")}

        trains = [l for l in state.log_history if "loss" in l and "eval_loss" not in l]
        evals  = [l for l in state.log_history if "eval_loss" in l]
        if trains and evals:
            metrics["train_eval_gap"] = evals[-1]["eval_loss"] - trains[-1]["loss"]

        if metrics:
            wandb.log(metrics, step=state.global_step)

十一、调参顺序与速查

11.1 实验顺序(节省 GPU 时间)

每次只动一组变量:

  1. 跑通基线:确认 loss 正常下降
  2. 数据质量 A/B:换不同数据子集,通常这步收益最大
  3. Rank 扫描:r ∈ {8, 16, 32, 64}
  4. LR 扫描:lr ∈ {5e-5, 1e-4, 2e-4, 3e-4}
  5. Target modules:Q/V only vs all-linear
  6. Merger / ViT 解冻:图像分布与自然图像差异较大时再试
  7. rsLoRA / DoRA:基线稳定后的进阶选项

11.2 综合参数速查

参数 2B-3B 4B 7B-8B
LoRA rank 16 16-32 16-32
LoRA alpha 16-32 32 32-64
LoRA target all-linear all-linear all-linear
LR 2e-4~5e-4 1e-4~3e-4 1e-4~2e-4
Warmup ratio 0.03 0.03 0.03
Batch size 2-4 1-2 1
Grad accum 4-8 8-16 8-16
Epochs 3-5 + early_stop 1-3 1-3
Gradient checkpointing 可选 推荐 必需
分布式策略 DDP DDP / SHARD_GRAD_OP SHARD_GRAD_OP
QLoRA(单卡) 12GB 16-24GB 24GB
Flash Attention 2 推荐 推荐 必需

11.3 经验法则

  1. LR:LoRA 用 1e-4,QLoRA 用 2e-4,Full FT 用 1e-5~1e-6
  2. 数据 > 参数:清洗 5% 错误标签 > 任何超参调整
  3. Rank:小数据 r=16,大数据/多任务再考虑 r=64
  4. Warmup:≤ 0.03
  5. Dropout:epochs≤2 用 0.05,epochs=5 用 0.1
  6. 冻结 ViT:分布贴近自然图像时冻结,省 40% 显存
  7. 全分辨率:坐标精度任务必须保持,纯分类可压缩
  8. Merger:数据量 >5000 训练,<5000 冻结
  9. Early stopping:必须开,patience=2 对小数据集足够

11.4 Qwen2.5-VL → Qwen3-VL 迁移检查清单

  1. 所有 28*28 改为 32*32(image_min/max_pixels)
  2. 确认 mrope_interleaved: true
  3. Qwen3-VL ViT 更小(300M vs 675M),可考虑解冻 ViT
  4. 相同图像 token 数少 21%,可适当增大 batch_size
  5. LLaMA-Factory 已支持 Qwen3-VL,检查最新版本配置格式

References


补充:多阶段 LoRA 流水线中的 Rank 选择(2026-07 消融实验)

本节基于 Qwen3-VL-8B 三阶段 LoRA 串联训练的消融实验,补充上文 LoRA rank 取 4~32 就够结论的适用边界。

背景

上文建议 8B 模型用 r=4~32,这个建议适用于单阶段任务微调。但在多阶段串联流水线中,首阶段的 rank 选择需要额外考虑。

我们的三阶段流水线:

Qwen3-VL-8B-Instruct
  -> S1 LoRA (r=64, 170M params) + merge   # 游戏 UI 理解预训练
  -> S2P1 LoRA (r=8, 21M params) + merge   # 动作类型学习
  -> S2P2 LoRA (r=8, 21M params) + merge   # 完整 Agent 能力
  = v5.1 (50px=81.2%)

消融实验结果

版本 S1 S2P1 S2P2 50px% vs v5.1
v5.1 (baseline) r=64 r=8 r=8 81.2 -
v5.1d (只 S2P2) r=8 76.2 -5.0pp
v5.1e (全 r=8) r=8 r=8 r=8 75.7 -5.5pp
v5.1c (跳 S1) r=8 r=8 75.1 -6.1pp

核心发现:S1 是领域适配预训练

S1 的表面任务是 13 类 screen_type 分类(3512 条数据),但它的真正效果是领域适配:用 170M 参数把 LLM 从通用模型改造成理解游戏 UI 的专用模型。这和 S2P1/S2P2 学习具体输出格式的任务微调本质不同。

S1 r=8 甚至不如完全跳过 S1(v5.1e 75.7% < v5.1d 76.2%),说明 r=8 的参数量不足以完成领域适配,反而引入有害的不完整知识。

串联 merge 的生存性问题

多阶段 LoRA 的独特约束:每个阶段 merge 后,后续阶段的 LoRA 会覆盖前一阶段的修改。S1 r=64 修改了 64 个独立的权重方向,后续 S2P1(r=8) 最多只能覆盖其中 8 个,剩余 56 个方向的领域知识保留下来。如果 S1 也用 r=8,那 8 个方向会被 S2P1 完全覆盖,领域知识全部丢失。

Rank 选择建议(修订)

场景 推荐 rank 原因
单阶段任务微调 r=4~32 本文上半部分的结论,依然成立
多阶段流水线 - 任务微调阶段 r=4~32 学习具体的输出格式
多阶段流水线 - 领域适配阶段 r=64+ 需要大参数量做 domain shift,且修改要能在后续 merge 中存活

总结:r=4~32 的建议适用于任务微调。当首阶段承担领域适配角色时,需要 r=64+ 以确保领域知识在串联 merge 后存活。判断依据是该阶段的目的:学新格式(低 rank)还是改造模型的领域理解(高 rank)。