第27章:微调、LoRA 与 RLHF | Chapter 27: Fine-tuning, LoRA & RLHF
阶段定位 | Stage: 第六阶段 — 无监督学习与生成式 AI 预计学时 | Duration: 4~5 小时
---
学习目标 | Learning Objectives
中文:
- 理解全量微调与参数高效微调(PEFT)的区别
- 掌握 LoRA 的数学原理:W = W₀ + BA
- 理解为什么低秩更新足够有效(内在维度假说)
- 掌握 RLHF 的三步流程:SFT → Reward Model → PPO
- 能在 PyTorch 中实现 LoRA 层并对比参数量
- 了解 DPO 作为 RLHF 的替代方案
English:
- Understand full fine-tuning vs parameter-efficient fine-tuning (PEFT)
- Master LoRA math: W = W₀ + BA
- Understand why low-rank updates are effective (intrinsic dimension hypothesis)
- Master RLHF three-step pipeline: SFT → Reward Model → PPO
- Implement LoRA layer in PyTorch and compare parameters
- Know DPO as RLHF alternative
---
27.1 微调策略 | Fine-tuning Strategies
中文解释
全量微调(Full Fine-tuning)
更新预训练模型的所有参数:
参数更新: ΔW = W_new - W_pretrained
训练数据: 数万~数十万条
显存需求: 2× 模型大小(参数 + 梯度 + 优化器状态)对于 70B 参数的模型,全量微调需要数百 GB 显存,消费级 GPU 不可能实现。
参数高效微调(PEFT)
只更新一小部分参数:
LoRA: 只训练低秩矩阵 B, A
Prompt Tuning: 只训练输入前缀的嵌入
Adapter: 在层间插入小网络,只训练小网络| 方法 | 更新参数 | 显存节省 | 效果 |
|---|---|---|---|
| Full FT | 100% | 0% | 最好 |
| LoRA | 0.1~1% | ~70% | 接近全量 |
| Prompt Tuning | <0.01% | ~90% | 一般 |
| Adapter | 1~5% | ~60% | 较好 |
English Explanation
Full FT: updates all parameters, requires massive memory. PEFT: updates small fraction, feasible on consumer GPUs.
---
27.2 LoRA | Low-Rank Adaptation
中文解释
核心公式
h = W₀x + ΔWx = W₀x + BAx- W₀:预训练权重(冻结,不参与梯度更新)
- B, A:可训练的低秩矩阵
- r:秩,通常 4~64,远小于 d
为什么有效?
内在维度假说(Intrinsic Dimension Hypothesis):
尽管模型有数十亿参数,但适配下游任务所需的"有效参数更新"位于一个低维子空间中。
实验表明,r=8 或 r=16 就能达到接近全量微调的效果。
初始化策略
- A:随机高斯初始化
- B:零初始化
为什么 B 零初始化?
训练初期,BA = 0,模型输出与预训练模型完全一致。这确保:
- 训练开始时不会破坏预训练知识
- 优化过程逐渐学习适配
扩展到所有层
在实际应用中,LoRA 应用于:
- Attention 的 Q、V 投影矩阵(效果最好的位置)
- 可选:K、O 投影矩阵、FFN 层
English Explanation
W = W₀ + BA. B zero-initialized to preserve pretrained knowledge at start.
Intrinsic dimension hypothesis: task adaptation lives in a low-dimensional subspace.
---
27.3 RLHF 详解 | RLHF Deep Dive
中文解释
第一步:SFT(监督微调)
数据: 高质量 (prompt, response) 对话对
目标: 让模型学会对话格式和基本指令遵循
损失: 标准语言建模交叉熵第二步:Reward Model(奖励模型)
数据: 同一 prompt 的多个回答 + 人类排序
训练: 让模型学会预测人类偏好
损失: 排序损失(pairwise ranking loss)例如:对于回答 A 和 B,人类更喜欢 A:
loss = -log σ(RM(A) - RM(B))第三步:PPO(近端策略优化)
目标: 最大化 Reward Model 的评分
约束: KL 散度惩罚(不要偏离 SFT 模型太远)PPO 的巧妙之处:用重要性采样和裁剪目标稳定训练,防止策略更新过大。
English Explanation
SFT: learn dialogue format. Reward Model: predict human preferences. PPO: optimize policy with KL constraint.
---
27.4 DPO:RLHF 的简化版 | DPO
中文解释
问题
RLHF 流程复杂:需要训练 Reward Model + 运行 PPO(不稳定、超参敏感)。
DPO 的洞察
可以直接从人类偏好数据优化语言模型,不需要显式的 Reward Model。
loss = -log σ(β * log(P_winner / P_loser))P_winner:模型对更好回答的概率P_loser:模型对更差回答的概率β:控制与参考模型的偏离程度
DPO vs RLHF
| 特性 | RLHF | DPO |
|---|---|---|
| 需要 Reward Model | 是 | 否 |
| 需要 PPO | 是 | 否 |
| 稳定性 | 较低 | 较高 |
| 效果 | 好 | 接近或更好 |
| 训练速度 | 慢 | 快 |
DPO 已成为许多开源模型(如 Zephyr、Llama-2-Chat)的首选对齐方法。
English Explanation
DPO: directly optimize from preference data without explicit Reward Model.
---
27.5 完整实现:LoRA
代码案例
import torch
import torch.nn as nn
import math
class LoRALinear(nn.Module):
"""带 LoRA 适配的线性层: h = W₀x + BAx"""
def __init__(self, in_features, out_features, rank=4, lora_alpha=1, dropout=0.0):
super().__init__()
self.rank = rank
self.scaling = lora_alpha / rank
# 冻结的预训练权重
self.weight = nn.Parameter(torch.zeros(out_features, in_features), requires_grad=False)
self.bias = nn.Parameter(torch.zeros(out_features), requires_grad=False) if False else None
# LoRA 可训练参数
self.lora_A = nn.Parameter(torch.randn(in_features, rank) / math.sqrt(in_features))
self.lora_B = nn.Parameter(torch.zeros(rank, out_features))
self.lora_dropout = nn.Dropout(p=dropout) if dropout > 0 else nn.Identity()
def forward(self, x):
original = torch.nn.functional.linear(x, self.weight, self.bias)
lora = self.lora_dropout(x) @ self.lora_A @ self.lora_B * self.scaling
return original + lora
def num_lora_params(self):
return self.lora_A.numel() + self.lora_B.numel()
def num_original_params(self):
return self.weight.numel()
def merge_weights(self):
"""将 LoRA 权重合并回主权重(推理时加速)"""
self.weight.data += (self.lora_A @ self.lora_B).T * self.scaling
return self
# ========== 对比参数量 ==========
print("=" * 50)
print("LoRA 参数量对比")
print("=" * 50)
configs = [
(768, 768, 4), # BERT-base
(768, 768, 8), # BERT-base + rank=8
(4096, 4096, 16), # LLaMA-7B
(4096, 4096, 64), # LLaMA-7B + rank=64
]
for in_f, out_f, rank in configs:
lora = LoRALinear(in_f, out_f, rank=rank)
original_params = lora.num_original_params()
lora_params = lora.num_lora_params()
ratio = lora_params / original_params
print(f"\nd={in_f}, rank={rank}:")
print(f" 原始参数: {original_params:,}")
print(f" LoRA 参数: {lora_params:,}")
print(f" 比例: {ratio:.4%} ({1/ratio:.0f}x 减少)")
# ========== 前向传播验证 ==========
lora_layer = LoRALinear(768, 768, rank=8)
x = torch.randn(2, 10, 768)
out = lora_layer(x)
print(f"\n前向传播验证:")
print(f" 输入: {x.shape}")
print(f" 输出: {out.shape}")
print(f" 训练初期输出 ≈ 原始输出(因为 B=0): {torch.allclose(out, torch.nn.functional.linear(x, lora_layer.weight), atol=1e-6)}")输出:
==================================================
LoRA 参数量对比
==================================================
d=768, rank=4:
原始参数: 589,824
LoRA 参数: 6,144
比例: 1.0417% (96x 减少)
d=768, rank=8:
原始参数: 589,824
LoRA 参数: 12,288
比例: 2.0833% (48x 减少)
d=4096, rank=16:
原始参数: 16,777,216
LoRA 参数: 131,072
比例: 0.7813% (128x 减少)
d=4096, rank=64:
原始参数: 16,777,216
LoRA 参数: 524,288
比例: 3.1250% (32x 减少)
前向传播验证:
输入: torch.Size([2, 10, 768])
输出: torch.Size([2, 10, 768])
训练初期输出 ≈ 原始输出(因为 B=0): True---
27.6 常见误区 | Common Pitfalls
1. LoRA 秩越大越好?
不是。r=8 通常就能达到 r=64 的 90% 效果。过大的 r 增加参数量但收益递减,还可能过拟合。
2. RLHF 让模型"更聪明"?
RLHF 不改变模型的知识量,只改变回答风格(更有帮助、更无害、更诚实)。知识来自预训练,对齐来自 RLHF。
3. DPO 完全替代 RLHF?
DPO 更简单高效,但在复杂任务上 RLHF(PPO)可能更稳定。两者都是活跃研究方向。
---
本章总结 | Chapter Summary
中文:
- 全量微调需要巨大显存,PEFT 让消费级 GPU 微调大模型成为可能
- LoRA: W = W₀ + BA,只训练低秩矩阵,减少 50~100 倍参数
- B 零初始化确保训练初期不破坏预训练知识
- RLHF: SFT → Reward Model → PPO,让模型对齐人类偏好
- PPO 用 KL 散度约束防止模型偏离太远
- DPO 直接优化偏好数据,无需 Reward Model 和 PPO,更简单高效
- 对齐只改变风格,不改变知识
English:
- Full FT requires massive memory; PEFT enables consumer GPU fine-tuning
- LoRA: W = W₀ + BA, 50~100× parameter reduction
- B zero-initialized preserves pretrained knowledge
- RLHF: SFT → Reward Model → PPO for human alignment
- PPO uses KL constraint
- DPO: direct preference optimization without RM/PPO
- Alignment changes style, not knowledge
---
课后练习 | Homework
- LoRA 秩实验:同一任务上对比 r=1, 4, 8, 16, 64 的效果,绘制效果-参数量曲线。
- QLoRA 了解:了解 4-bit 量化 + LoRA 的组合,如何在 24GB 显存上微调 65B 模型。
- Reward Model 实现:实现一个简化版奖励模型,用排序损失训练。
- DPO 实现:实现 DPO 损失函数,对比与 RLHF 的代码复杂度。
- 权重合并:实现 LoRA 权重合并到主权重的功能,对比合并前后的推理速度。
- 多任务 LoRA:了解如何在不同任务间切换 LoRA 权重(如 LoRA Hub)。
- 过度优化:研究 Reward Hacking 现象,为什么模型可能欺骗 Reward Model。