LLM 后训练实践
第1课:后训练概述与SFT基础

1.3 参数高效微调

掌握 LoRA 和 QLoRA 的原理与实践,理解参数高效微调的必要性和显存优化策略

为什么需要参数高效微调

全参数微调的显存瓶颈

Qwen3-8B 为例,全参数微调(Full Fine-Tuning)的显存需求远超单张 GPU 容量:

组件计算方式显存需求
模型参数(FP16)8B × 2 bytes~16 GB
梯度(FP16)8B × 2 bytes~16 GB
优化器状态(AdamW, FP32)8B × 8 bytes(动量 + 方差)~64 GB
激活值(估计)取决于 batch size 和 seq len~4–16 GB
总计~100–112 GB

单张 A100-80G 无法完成 8B 模型的全参数微调!即便是 A100-80G 也仅有 80GB 显存,而全参数微调需要约 100GB。这使得全参数微调对于大多数研究者来说是不切实际的。

显存构成分析

训练时的显存主要由四部分构成:

Memorytotal=Memoryparams+Memorygradients+Memoryoptimizer+Memoryactivations\text{Memory}_{\text{total}} = \text{Memory}_{\text{params}} + \text{Memory}_{\text{gradients}} + \text{Memory}_{\text{optimizer}} + \text{Memory}_{\text{activations}}

其中 优化器状态 是最大的开销——AdamW 需要为每个参数维护一阶动量(momentum)和二阶动量(variance),各占 FP32(4 bytes),共 8 bytes/参数。

参数高效微调(Parameter-Efficient Fine-Tuning, PEFT) 的核心思路:冻结大部分原始参数,仅训练少量新增参数,从而大幅降低梯度和优化器状态的显存需求。


LoRA:低秩适配

核心思想

LoRA(Low-Rank Adaptation,Hu 等,2021)基于一个关键假设:预训练模型在微调时的权重更新具有低秩结构

对于预训练权重矩阵 W0Rd×kW_0 \in \mathbb{R}^{d \times k},LoRA 不直接更新 W0W_0,而是将权重更新分解为两个低秩矩阵的乘积:

W=W0+ΔW=W0+BAW' = W_0 + \Delta W = W_0 + BA

其中:

  • BRd×rB \in \mathbb{R}^{d \times r}ARr×kA \in \mathbb{R}^{r \times k}
  • rmin(d,k)r \ll \min(d, k) 是秩(rank),通常取 16–64
  • W0W_0冻结,不参与梯度计算
  • AABB 参与训练

前向传播

LoRA 修改后的前向传播:

h=W0x+ΔWx=W0x+αrBAxh = W_0 x + \Delta W x = W_0 x + \frac{\alpha}{r} BAx

其中 α\alpha 是缩放因子(scaling factor),αr\frac{\alpha}{r} 控制了 LoRA 更新的幅度。

输入 x ─────┬──────── W₀x ──────────┬──── 输出 h
             │                       │
             └── A ──→ B ──→ (α/r)BAx ┘
                 r×k    d×r
              (可训练)  (可训练)

参数效率

以 Qwen3-8B 中一个典型的线性层(d=4096,k=4096d = 4096, k = 4096)为例:

方法训练参数量相对占比
全参数微调d×k=16,777,216d \times k = 16,777,216100%
LoRA(r=16)d×r+r×k=131,072d \times r + r \times k = 131,0720.78%
LoRA(r=64)d×r+r×k=524,288d \times r + r \times k = 524,2883.13%

关键超参数

秩 r 决定了 LoRA 的表达能力:

  • r 太小(如 4-8):模型表达能力受限,可能欠拟合
  • r 太大(如 128-256):接近全参数微调,失去效率优势
  • 推荐范围:16-64,通常 r=32 是一个好的起点

经验法则:

  • 简单任务(风格调整):r=8-16
  • 中等任务(指令跟随):r=32
  • 复杂任务(领域适配):r=64

alpha (α) 与 rank 共同决定 LoRA 更新的缩放:

实际缩放=αr\text{实际缩放} = \frac{\alpha}{r}

常见设置:

  • alpha = 2 * r:缩放因子为 2,更新幅度较大(Qwen3 推荐)
  • alpha = r:缩放因子为 1,更新幅度适中
  • alpha = r / 2:缩放因子为 0.5,更新幅度较小

实践建议:先固定 alpha = 2 * rank,通过调整学习率来控制训练强度。

LoRA 可以应用于 Transformer 中的不同线性层:

说明优先级
q_projQuery 投影
k_projKey 投影
v_projValue 投影
o_projOutput 投影
gate_projFFN 门控层
up_projFFN 上投影
down_projFFN 下投影

推荐:应用到所有线性层(all linear layers),即同时覆盖注意力层和 FFN 层。Qwen3 的实验表明,覆盖所有线性层的效果最好。

from peft import LoraConfig

lora_config = LoraConfig(
    r=32,
    lora_alpha=64,
    target_modules=[
        "q_proj", "k_proj", "v_proj", "o_proj",
        "gate_proj", "up_proj", "down_proj"
    ],
    lora_dropout=0.05,
    bias="none",
    task_type="CAUSAL_LM"
)

初始化策略

LoRA 的初始化确保训练开始时不改变原始模型行为:

  • 矩阵 A:使用 Kaiming 均匀分布初始化
  • 矩阵 B:初始化为全零

这保证了 ΔW=BA=0\Delta W = BA = 0,即训练开始时 W=W0W' = W_0


QLoRA:量化低秩适配

核心思想

QLoRA(Dettmers 等,2023)在 LoRA 基础上增加了 4-bit NormalFloat(NF4)量化,将基座模型的权重从 FP16(16 bit)量化到 4 bit,进一步将显存需求减半。

NF4 量化原理

NormalFloat 量化基于一个关键观察:预训练模型的权重近似服从正态分布。NF4 将正态分布的分位数作为量化级别,使得量化误差在信息论意义上最优。

WNF4=QuantizeNF4(W0)W0W_{\text{NF4}} = \text{Quantize}_{\text{NF4}}(W_0) \approx W_0

QLoRA 还引入了双重量化(Double Quantization):对量化常数本身再做一次量化,额外节省约 0.4 bit/参数。

QLoRA 训练流程

                冻结 (4-bit NF4)          可训练 (FP16/BF16)
                ┌──────────┐            ┌──────────────┐
输入 x ────────→│  W₀(NF4)  │───────────→│              │
                │  ↑反量化↑  │            │    加法求和   │──→ 输出 h
                │  W₀(FP16) │            │              │
                └──────────┘            └──────────────┘

                ┌──────────┐                  │
输入 x ────────→│  BA(FP16) │─────── (α/r) ───┘
                │  可训练    │
                └──────────┘
  • 前向传播:W0W_0 从 NF4 反量化到 FP16 后参与计算
  • LoRA 部分(BB, AA)始终保持 FP16 精度
  • 反向传播仅经过 LoRA 参数,W0W_0 不计算梯度

BitsAndBytesConfig 配置

from transformers import BitsAndBytesConfig
import torch

bnb_config = BitsAndBytesConfig(
    load_in_4bit=True,                    # 启用 4-bit 加载
    bnb_4bit_quant_type="nf4",            # 使用 NF4 量化
    bnb_4bit_compute_dtype=torch.bfloat16, # 计算时使用 BF16
    bnb_4bit_use_double_quant=True,       # 启用双重量化
)

显存对比

Qwen3-8B 模型为例,不同微调方法的显存对比:

方法模型加载可训练参数优化器状态总显存GPU 需求
全参数微调(FP16)16 GB8B (16 GB)~64 GB~100 GB2× A100-80G
LoRA(FP16, r=32)16 GB~40M (80 MB)~320 MB~20 GB1× A100-40G
QLoRA(NF4, r=32)5 GB~40M (80 MB)~320 MB~10 GB1× RTX 4090

显存经验法则(7B-8B 模型):

  • 全参数微调:~100 GB(需多卡)
  • LoRA(FP16):~20 GB(A100-40G 或 RTX 4090)
  • QLoRA(NF4):~10 GB(T4 16GB 勉强可用)

QLoRA 使大模型微调真正走向了消费级 GPU!


近期进展简介

DoRA:权重分解 LoRA

DoRA(Weight-Decomposed Low-Rank Adaptation,2024)将权重矩阵分解为幅度(magnitude)和方向(direction)两个分量,LoRA 仅作用于方向分量:

W=mW0+BAW0+BAcW' = m \cdot \frac{W_0 + BA}{\|W_0 + BA\|_c}

其中 mm 是可学习的幅度向量。DoRA 在多项基准上略优于标准 LoRA,但增加的计算开销很小。

Spectrum:基于信噪比的层选择

Spectrum(2024)提出了一种更智能的层选择策略:

  • 计算每一层的信噪比(Signal-to-Noise Ratio)
  • 只对信噪比高的层应用 LoRA,跳过信噪比低的层
  • 效果:在相同参数预算下,Spectrum 优于对所有层统一应用 LoRA

这种方法的直觉是:不同层在微调中的贡献是不均等的,集中资源到"关键层"可以获得更好的效果。


实践建议总结

决策推荐选择说明
全参数 vs. PEFTPEFT(LoRA/QLoRA)除非资源充裕且数据极多
LoRA vs. QLoRAQLoRA(显存有限时)A100-40G 上 LoRA 亦可
秩 r32通用起点,可按需调整
Alpha2 × r如 r=32 则 alpha=64
目标层所有线性层q/k/v/o_proj + gate/up/down_proj
Dropout0.05防止过拟合
计算精度BF16比 FP16 更稳定,A100 原生支持

本节小结

概念要点
全参数微调瓶颈8B 模型需 ~100GB 显存,单卡无法完成
LoRA冻结 W0W_0,训练低秩 BABA,仅 ~0.2-1% 参数
LoRA 公式W=W0+αrBAW' = W_0 + \frac{\alpha}{r}BA
QLoRALoRA + NF4 4-bit 量化,显存再减半
显存对比全参 ~100GB → LoRA ~20GB → QLoRA ~10GB
DoRA权重分解,幅度+方向,略优于 LoRA
Spectrum信噪比层选择,集中资源到关键层