SilverIce Toolbox
Back to course

Stage 6 / Chapter 27

第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 FT100%0%最好
LoRA0.1~1%~70%接近全量
Prompt Tuning<0.01%~90%一般
Adapter1~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

特性RLHFDPO
需要 Reward Model
需要 PPO
稳定性较低较高
效果接近或更好
训练速度
DPO 已成为许多开源模型(如 Zephyr、Llama-2-Chat)的首选对齐方法。

English Explanation

DPO: directly optimize from preference data without explicit Reward Model.

---

27.5 完整实现:LoRA

代码案例

python
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

  1. LoRA 秩实验:同一任务上对比 r=1, 4, 8, 16, 64 的效果,绘制效果-参数量曲线。
  1. QLoRA 了解:了解 4-bit 量化 + LoRA 的组合,如何在 24GB 显存上微调 65B 模型。
  1. Reward Model 实现:实现一个简化版奖励模型,用排序损失训练。
  1. DPO 实现:实现 DPO 损失函数,对比与 RLHF 的代码复杂度。
  1. 权重合并:实现 LoRA 权重合并到主权重的功能,对比合并前后的推理速度。
  1. 多任务 LoRA:了解如何在不同任务间切换 LoRA 权重(如 LoRA Hub)。
  1. 过度优化:研究 Reward Hacking 现象,为什么模型可能欺骗 Reward Model。