0. 系列闭环

本篇位置 上游 本篇产出 下游
第 3/10 篇 第 02 篇数据进入模型 理解 r/alpha/target_modules 第 05–06 篇 Trainer 配置 · 第 10 篇 vLLM --max-lora-rank

读本篇后再打开 train_lora_single.py,看到 LoraConfig 不应再是「调参黑盒」。


1. 要解决的实际问题

全量微调 Qwen3.5-4B(约 4,216,368,128 参数)在单卡 V100 上:

  • 优化器状态占用巨大,即使用 bf16 也紧张
  • 每次实验保存 8GB+ checkpoint,迭代慢
  • 1000 条对话数据相对 42 亿参数极易过拟合全量权重

LoRA 的核心承诺:只学「任务增量」ΔW,且 ΔW 低秩可分解,参数量降到千万级。

本项目实测(all_logs.log 第 34 行):

1
trainable params: 10,616,832 || all params: 4,216,368,128 || trainable%: 0.2518

2. 实现位置

文件 内容
LoRA_Demo/train_lora_single.py LORA_RLORA_ALPHALoraConfig(...)
LoRA_Demo/output/.../final_lora/adapter_config.json 训练落盘后的 r=8, alpha=16, target_modules 列表
LoRA_Demo/output/.../final_lora/adapter_model.safetensors 可训练权重本体(~41 MB)

注意:LoRA 在 SFTTrainer(..., peft_config=lora_config) 创建时才注入,不是在 LoraConfig(...) 定义时。


3. 数学形式(对照代码)

对某线性层,原前向:

[
y = W x
]

LoRA(peft 默认):

[
y = W x + \frac{\alpha}{r} B A x
]

  • (W):冻结,来自 AutoModelForCausalLM.from_pretrained
  • (A \in \mathbb{R}^{r \times k}),(B \in \mathbb{R}^{d \times r}):训练
  • 代码里 LORA_R = 8 → (r=8)
  • LORA_ALPHA = 16 → 缩放 (\alpha/r = 2)
1
2
3
4
5
6
7
8
flowchart LR
x[输入 x] --> W[冻结 W]
x --> A[可训练 A]
A --> B[可训练 B]
W --> add((+))
B --> scale["× α/r"]
scale --> add
add --> y[输出 y]

为什么低秩够用: 风格 SFT 改的是「怎么说」的条件分布,相对原模型是低维偏移;不需要动满秩 (W)。


4. 三个超参在本项目中的取值

4.1 LORA_R = 8

r 参数量 适用
4 更少 极窄任务、防过拟合
8 本项目 1000 条风格 SFT 平衡点
16+ 更多 更复杂行为/多域,小数据慎用

vLLM 部署时必须 --max-lora-rank 8 与训练 r 一致(第 10 篇)。

4.2 LORA_ALPHA = 16

控制 LoRA 分支对输出的有效步长。不是越大越好:过大易震荡,过小学不动。16/8=2 是常见经验起点。

4.3 LORA_DROPOUT = 0.05

仅作用在 LoRA 分支。1000 条数据有重复句式,轻微 dropout 减轻死记。


5. target_modules:为什么在 attention + FFN 全注入

train_lora_single.py 第 189–192 行:

1
2
3
4
target_modules=[
"q_proj", "k_proj", "v_proj", "o_proj",
"gate_proj", "up_proj", "down_proj",
],

Qwen3.5 每个 Transformer 层:

1
2
Self-Attention: q_proj, k_proj, v_proj, o_proj
FFN (SwiGLU): gate_proj, up_proj, down_proj
模块组 影响
Q/K/V/O 注意力模式:关注 user 情绪词 vs 事实词
gate/up/down 前馈非线性:用词习惯、句式节奏

只注入 q_proj,v_proj 也能训,但风格迁移通常弱于** attention+FFN 全注入**。代价是参数量从百万级到千万级——对本项目仍仅 0.25%。

落盘验证(adapter_config.json):

1
2
3
4
5
"target_modules": ["v_proj", "k_proj", "up_proj", "down_proj", "q_proj", "gate_proj", "o_proj"],
"r": 8,
"lora_alpha": 16,
"lora_dropout": 0.05,
"bias": "none"

6. 与 SFTTrainer 的衔接(易错点)

正确写法

1
2
3
4
5
trainer = SFTTrainer(
model=model,
peft_config=lora_config, # 只在这里注入
...
)

错误写法

1
2
model = get_peft_model(model, lora_config)  # 手动注入
trainer = SFTTrainer(model=model, peft_config=lora_config, ...) # 重复

重复注入会导致不可预期行为或报错。脚本注释(第 184、224 行)明确写了「勿重复 get_peft_model」。

训练前 trainer.model.print_trainable_parameters() 必须看到 **~0.25%**。若是 0% 或 100%,立刻停训查配置。


7. 有效 batch 与 LoRA 无关但决定步数

1
2
3
BATCH_SIZE = 2
GRADIENT_ACCUMULATION_STEPS = 2
# 有效 batch = 4

总步数:

[
\text{steps} = \lceil 1000 / 4 \rceil \times 3 = 750
]

与 LoRA 无直接关系,但决定每条样本被看几遍、学习率调度长度。改 batch 不改 LoRA,却改训练动态。


8. 踩坑

坑 1:改 r 后不重新训练 adapter
旧 adapter 的 adapter_config.json 里 r=8,若手改脚本 r=16 再加载旧权重,shape 对不上。

坑 2:target_modules 拼写错一个字母
例如 q_projj,PEFT 静默不注入该层,trainable% 下降但不一定报错。

坑 3:以为 LoRA 省显存 = 可以任意增大 seq_len
LoRA 主要省可训练参数和优化器;前向仍要跑完整 4B 基座,512 token 的激活显存仍在。OOM 先减 BATCH_SIZEMAX_SEQ_LEN(第 06 篇)。

坑 4:vLLM --max-lora-rank 小于训练 r
启动失败或静默降级;部署参数必须读 adapter_config.json 对齐。


9. 小结

  1. LoRA 冻结 (W),训练低秩 (BA),本项目仅 0.2518% 参数。
  2. r=8, alpha=16, dropout=0.05 写在脚本顶部,落盘在 adapter_config.json
  3. 7 个 target_modules 覆盖 attention + FFN,适合风格 SFT。
  4. 只通过 SFTTrainer(peft_config=...) 注入,不要重复 get_peft_model
  5. 部署时 vLLM 的 max-lora-rank 必须与 r 一致。

附录:LoraConfig 字段对照

1
2
3
4
5
6
7
8
9
10
# LoRA_Demo/train_lora_single.py 第 186-196 行

lora_config = LoraConfig(
r=LORA_R, # 秩 r,决定 A/B 形状
lora_alpha=LORA_ALPHA, # α,有效缩放 α/r
target_modules=[...], # 模块名必须与模型层名完全一致
lora_dropout=LORA_DROPOUT,
bias="none", # 不训 bias,再省参数
task_type="CAUSAL_LM", # 因果 LM,与 SFT 一致
)

系列导航

篇目 链接
上一篇 02 · 训练集设计
下一篇 04 · 环境搭建
索引 README

← 返回 LoRA 老年陪伴专题