云服务器上基于lora微调Qwen2.5-VL-7b-Instruct模型之Lora微调代码讲解
在之前的项目在云服务器上基于lora微调Qwen2.5-VL-7b-Instruct模型的train.py文件中,配置Lora的代码如下。下面对它进行讲解:
完整代码回顾
config = LoraConfig(task_type=TaskType.CAUSAL_LM,target_modules=["q_proj", "k_proj", "v_proj", "o_proj", "gate_proj", "up_proj", "down_proj"],inference_mode=False, # 训练模式r=64, # Lora 秩lora_alpha=16, # Lora alphalora_dropout=0.05, # Dropout 比例bias="none",
)
这是一段使用 peft
库(Parameter-Efficient Fine-Tuning)创建 LoRA(Low-Rank Adaptation) 微调配置的代码。它的作用是:告诉模型“我要在哪些模块上添加可训练的小型低秩矩阵”,从而实现 高效、节省显存的微调。
一、什么是 LoRA?(背景知识)
原理简述:
传统全参数微调(Full Fine-tuning)要更新所有 70 亿参数,显存消耗极大。
而 LoRA 的核心思想是:
❝ 不直接修改原始权重 WWW,而是引入一个低秩分解的增量 ΔW=A⋅B\Delta W = A \cdot BΔW=A⋅B,只训练 AAA 和 BBB ❞
数学表达:
输出=x⋅(W+ΔW)=x⋅W+x⋅(A⋅B)\text{输出} = x \cdot (W + \Delta W) = x \cdot W + x \cdot (A \cdot B) 输出=x⋅(W+ΔW)=x⋅W+x⋅(A⋅B)
其中:
- WWW: 原始冻结的大矩阵(如 attention 中的 q_proj)
- A∈Rd×r,B∈Rr×kA \in \mathbb{R}^{d \times r}, B \in \mathbb{R}^{r \times k}A∈Rd×r,B∈Rr×k:两个小矩阵,r≪dr \ll dr≪d
- rrr 就是
r=64
,称为 LoRA 秩(rank)
优势:
- 显存节省 70%+
- 训练速度快
- 可以保存多个 LoRA 适配器,切换任务灵活
二、逐参数详解
1. task_type=TaskType.CAUSAL_LM
task_type=TaskType.CAUSAL_LM
含义:
指明当前任务类型是 因果语言建模(Causal Language Modeling),也就是典型的自回归生成任务:
给定前面的词,预测下一个词(比如 GPT、Qwen 都属于这类)
为什么选这个?
因为 Qwen 是一个 decoder-only 的大模型,做的是文本生成任务(图像描述生成),所以属于 CAUSAL_LM
。
其他常见选项:
类型 | 用途 |
---|---|
SEQ_CLS | 文本分类 |
TOKEN_CLS | 命名实体识别等 token 级任务 |
QUESTION_ANS | 问答任务 |
对于多模态生成任务(图文对话),仍然是
CAUSAL_LM
,因为最终输出是自回归文本。
2. target_modules=[...]
target_modules=["q_proj", "k_proj", "v_proj", "o_proj", "gate_proj", "up_proj", "down_proj"]
含义:
指定要在哪些模块上添加 LoRA 适配层。
这些名字对应 Transformer 中的关键线性层:
模块 | 所属结构 | 功能 |
---|---|---|
q_proj , k_proj , v_proj | Self-Attention | Query, Key, Value 投影 |
o_proj | Self-Attention | Output 投影 |
gate_proj , up_proj , down_proj | MLP/Feed-Forward | Qwen 使用 GLU 结构(门控线性单元),所以有 gate 和 up/down |
🔍 Qwen 系列模型使用的是 SwiGLU 激活函数 的 FFN 层,结构如下:
FFN(x)=down_proj(Swish(gate_proj(x))⊗up_proj(x))\text{FFN}(x) = \text{down\_proj}(\text{Swish}(\text{gate\_proj}(x)) \otimes \text{up\_proj}(x)) FFN(x)=down_proj(Swish(gate_proj(x))⊗up_proj(x))
所以这三个都需要加 LoRA 才能有效微调。
为什么选这些?
实验证明:在 注意力层(QKV) 和 FFN 层(up/gate/down) 添加 LoRA,能覆盖模型最主要的参数流动路径,效果最好。
如何知道该写哪些模块?
可以打印模型结构查看:
print(model)
# 或
for name, module in model.named_modules():if "dense" in name or "proj" in name:print(name)
也可以让 PEFT 自动检测目标模块(但不推荐用于大模型):
from peft import LoraConfig, get_peft_modelconfig = LoraConfig(task_type=TaskType.CAUSAL_LM,# target_modules="all-linear", # 自动选择所有线性层(慎用!太重)...
)
注意:不要设为 "all-linear"
,否则会把 embedding、layernorm 也都加上 LoRA,浪费资源且可能不稳定。
3. inference_mode=False
inference_mode=False # 表示当前是训练模式
含义:
False
:训练模式 → 创建可训练的 LoRA 参数(A 和 B)True
:推理模式 → 不创建新参数,只加载已有的 LoRA 权重
什么时候用 True?
在测试阶段加载 .bin
权重时使用,例如你后面的代码:
val_peft_model = PeftModel.from_pretrained(model, "./output/checkpoint-124", config=val_config)
那时就可以设为 inference_mode=True
,因为不需要再训练了。
4. r=64
(LoRA 秩)
r=64
含义:
LoRA 矩阵分解的秩(rank),即中间维度大小。
比如原始 q_proj
是 [4096 -> 4096]
,LoRA 添加两个小矩阵:
- AAA:
[4096 x 64]
- BBB:
[64 x 4096]
总参数量从 40962≈16.7M4096^2 ≈ 16.7M40962≈16.7M → 4096×64×2≈0.52M4096×64×2 ≈ 0.52M4096×64×2≈0.52M,减少约 97%
如何选择 r
?
r 值 | 特点 | 推荐场景 |
---|---|---|
8~16 | 参数少、速度快、显存低 | 快速实验、小数据集 |
32~64 | 性能较好,平衡点 | 多数情况下推荐 |
128+ | 接近全量微调效果 | 数据量大、追求 SOTA |
Qwen 官方建议:r=64 或 r=128 效果较好。
选
r=64
是一个非常合理的选择,在性能和效率之间取得良好平衡。
5. lora_alpha=16
lora_alpha=16
含义:
LoRA 中的缩放系数 α\alphaα。最终的增量是:
ΔW=αr⋅A⋅B\Delta W = \frac{\alpha}{r} \cdot A \cdot B ΔW=rα⋅A⋅B
这相当于一个“学习权重的权重”。
为什么需要 alpha?
- 控制 LoRA 更新的强度
- 当
r
很小时,ΔW\Delta WΔW 可能太弱,用alpha
放大它 - 通常保持
alpha / r
比值恒定(比如 16/64 = 0.25)
推荐设置:
r | alpha | alpha/r |
---|---|---|
8 | 16 | 2.0 ❌ 太强 |
32 | 32 | 1.0 ✅ 常见 |
64 | 16 | 0.25 ✅ 保守 |
64 | 32 | 0.5 ✅ 平衡 |
64 | 64 | 1.0 ✅ 强 |
设置:r=64, alpha=16
→ 缩放因子 = 16/64 = 0.25
,属于 较保守 设置,适合防止过拟合,尤其在小数据集上更稳定。
如果发现模型学得慢或欠拟合,可以尝试调高 alpha(如 32 或 64)。
6. lora_dropout=0.05
lora_dropout=0.05
含义:
在 LoRA 的 A 矩阵输出上应用 dropout,防止过拟合。
0.05
表示 5% 的概率随机丢弃 LoRA 的更新信号- 在训练时启用,推理时自动关闭
为什么加 dropout?
虽然 LoRA 参数很少,但在小数据集上仍可能过拟合。加入轻量 dropout 可提高泛化能力。
推荐值:
- 小数据集(<1k 样本):0.1 ~ 0.3
- 中等数据集(1k~10k):0.05 ~ 0.1
- 大数据集(>10k):0.0 ~ 0.05
设置 0.05
是标准做法,很合理。
7. bias="none"
bias="none"
含义:
是否对 LoRA 中的 bias 参数进行微调。
选项:
"none"
:不训练任何 bias(最常用)"all"
:训练所有 bias"lora_only"
:只训练 LoRA 层内部的 bias
为什么选 "none"
?
- 偏置项本身参数量小
- 微调 bias 效果不明显,反而增加复杂度
- 多数论文和实践中都设为
"none"
最佳实践就是 bias="none"
,无需改动。
三、总结:你的配置为何合理?
参数 | 当前值 | 是否合理 | 原因 |
---|---|---|---|
task_type | CAUSAL_LM | ✅ | 对应生成任务 |
target_modules | Q/K/V/O + Gate/Up/Down | ✅ | 覆盖主要模块,适配 Qwen 结构 |
inference_mode | False | ✅ | 训练模式正确 |
r | 64 | ✅ | 平衡性能与效率 |
lora_alpha | 16 | ⚠️ 偏保守 | 缩放系数 0.25,可尝试 32 提速 |
lora_dropout | 0.05 | ✅ | 小幅正则化,防过拟合 |
bias | "none" | ✅ | 标准做法 |
四、常见问题与优化建议
Q1: 我能不能只微调 attention 层?(去掉 gate/up/down)
可以,但效果通常较差。
因为 Qwen 的 FFN 层(尤其是 SwiGLU)也承载了大量语义信息,必须微调。
建议:保留全部 target_modules
Q2: r=64 太大了,显存不够怎么办?
你可以尝试:
- 降为
r=32
或r=16
- 减少
target_modules
(比如只保留q_proj
,v_proj
) - 使用
gradient_checkpointing=True
(你已开启)
Q3: 如何选择最优的 r 和 alpha?
可以用 消融实验(ablation study):
实验编号 | r | alpha | 结果(BLEU/训练速度/显存) |
---|---|---|---|
Exp1 | 16 | 16 | 快但欠拟合 |
Exp2 | 32 | 32 | 平衡 |
Exp3 | 64 | 32 | 效果最好 ✅ |
推荐优先试 r=64, alpha=32
(缩放比 0.5),比你当前更强一点。
Q4: 为什么不用 IA³ 或 Adapter?
因为 LoRA 是目前最成熟、兼容性最好、性能最强的 PEFT 方法,尤其适合大模型。
Qwen 官方也推荐使用 LoRA。