Skip to content

10. 参数高效微调(PEFT)

全参微调 70B 模型需要约 1.4 TB 显存,普通研究者无法承担。PEFT(Parameter-Efficient Fine-Tuning)通过只训练少量参数大幅降低需求,是消费级 GPU 玩转大模型的关键。本章重点讲 LoRA、QLoRA、DoRA,并简要梳理 Adapter、Prefix-Tuning、(IA)³ 等其他范式。

10.1 为什么需要 PEFT:显存账本

要理解 PEFT 的价值,先要算清楚全参微调一个 7B 模型需要多少显存。

10.1.1 显存四大块

训练时的显存占用主要由以下四部分构成:

类别大小(按参数 N,BF16 + Adam)7B 模型实例
模型参数2N bytes (BF16)14 GB
梯度2N bytes (BF16)14 GB
优化器状态 (Adam: m,v)8N bytes (FP32 master + FP32 m + FP32 v)56 GB
激活值 (与 batch、seq、layer 相关)视设置20-40 GB
合计~100-120 GB

注:

  • Adam 优化器需要保存 FP32 的 master weights、动量 m、二阶动量 v,加起来每个参数 12 字节(实际上 master 4 + m 4 + v 4 = 12 字节,BF16 模型还要 2 字节,FP16 梯度也要 2 字节,加起来 16 字节)。简化估算用 16N bytes。
  • 7B 全参微调的总显存约 112 GB——单张 80GB A100 / H100 都装不下。
  • 70B 模型:约 1.12 TB,需要 14+ 张 A100 跑 ZeRO-3。

10.1.2 PEFT 的显存收益

PEFT 的核心:冻结绝大部分参数,只训练 0.1%-3% 的参数。这样:

  • 模型参数仍占满显存(base 权重还在)。
  • 梯度:只需要存可训练参数的梯度。
  • 优化器状态:只需要存可训练参数的 Adam 状态。

举例,7B 模型 + LoRA r=16(全 linear):

类别全参LoRA r=16
参数14 GB14 GB(冻结)+ ~80 MB(LoRA)
梯度14 GB~80 MB
优化器状态56 GB~320 MB
激活值30 GB30 GB(同)
合计~114 GB~44 GB

QLoRA 把 base 量化到 4-bit:base 参数从 14 GB → 4 GB,总显存可压到 ~16 GB,单张 RTX 3090(24 GB)即可微调 7B。

10.2 LoRA:Low-Rank Adaptation(Hu et al., 2021)

LoRA 是 PEFT 的事实标准。理解它需要从「权重更新的内在维度」假设出发。

10.2.1 核心数学推导

LoRA 假设:预训练权重在下游任务上的更新 ΔW 具有低内在秩(low intrinsic rank)。

冻结预训练权重 W0Rd×k,把更新 ΔW 分解为两个低秩矩阵之积:

W=W0+ΔW=W0+BA

其中:

  • BRd×r
  • ARr×k
  • rmin(d,k)

参数量从 d×k 降到 r(d+k)。例如 d=k=4096,r=8:原来 409621.68×107 参数,LoRA 仅 8×(4096+4096)=65,536减少 256 倍

前向传播变为:

h=Wx=W0x+BAx

引入 scaling 系数 α/r

h=W0x+αrBAx

其中 α 是超参(常设为 r2r)。α/r 可以理解为「LoRA 的等效学习率缩放」——改变 r 时不必重调外层 LR。

10.2.2 为什么这个分解有效?

LoRA 论文有个有趣的实证:把全参微调后的 ΔW 做 SVD,发现前几个奇异值就承载了绝大部分能量——意味着 ΔW 天然是低秩的。这从经验上支撑了「intrinsic rank 假设」。

直觉上:预训练已经把一般性的语言/世界知识压在权重里,下游任务的「调整」只是在这个高维空间里向特定方向偏移——这个偏移自然是低秩的。

10.2.3 初始化方案

  • AN(0,σ2) 或 Kaiming uniform 初始化。
  • B=0 初始化。
  • 这样 ΔW=BA=0起始时模型行为与预训练完全一致

为什么必须有一个矩阵置零?如果 A,B 都随机初始化,BA0,相当于在训练初期给模型权重加了一个随机扰动,会破坏预训练表征。让其中一个置零,则起点等价于 base model,训练才能稳定开始。

10.2.4 PyTorch 实现(从零)

python
import math
import torch
import torch.nn as nn
import torch.nn.functional as F

class LoRALinear(nn.Module):
    """把一个 nn.Linear 替换为 LoRA 形式。"""

    def __init__(self, base: nn.Linear, r: int = 8, alpha: int = 16, dropout: float = 0.0):
        super().__init__()
        self.base = base
        for p in self.base.parameters():
            p.requires_grad = False  # 冻结 base

        d_out, d_in = base.weight.shape
        self.r = r
        self.alpha = alpha
        self.scaling = alpha / r

        self.A = nn.Parameter(torch.empty(r, d_in))
        self.B = nn.Parameter(torch.zeros(d_out, r))
        nn.init.kaiming_uniform_(self.A, a=math.sqrt(5))

        self.dropout = nn.Dropout(dropout) if dropout > 0 else nn.Identity()

    def forward(self, x: torch.Tensor) -> torch.Tensor:
        base_out = self.base(x)                        # (..., d_out)
        lora_out = self.dropout(x) @ self.A.T @ self.B.T  # (..., d_out)
        return base_out + lora_out * self.scaling

    @torch.no_grad()
    def merge(self) -> nn.Linear:
        """把 LoRA 融合回 base,返回普通 Linear(推理用)。"""
        merged = nn.Linear(self.base.in_features, self.base.out_features,
                           bias=self.base.bias is not None)
        merged.weight.copy_(self.base.weight + self.scaling * self.B @ self.A)
        if self.base.bias is not None:
            merged.bias.copy_(self.base.bias)
        return merged

调用:

python
def replace_linear_with_lora(model, target_names=("q_proj", "v_proj"), r=8, alpha=16):
    for name, module in model.named_modules():
        if any(t in name for t in target_names) and isinstance(module, nn.Linear):
            parent_name, _, attr = name.rpartition(".")
            parent = model.get_submodule(parent_name)
            setattr(parent, attr, LoRALinear(module, r=r, alpha=alpha))
    return model

完整可运行代码请参考 /code/04_lora_from_scratch.py

10.2.5 秩 r 的选择

任务复杂度推荐 r备注
轻微风格调整 / 特定格式4-80.05% 参数
一般 SFT(对话、问答)8-160.1-0.5%
复杂任务(数学、代码、推理)32-640.5-1.5%
重度 domain adaptation64-256接近全参

经验:

  • r 不一定越大越好。论文实验显示 r=4 已能逼近 full FT,r>64 收益快速递减。
  • 不同模块用不同 r(rank-allocation)通常没必要——固定 r 简单可靠。

10.2.6 α 的选择

  • 默认 α=rα=2r(即 scaling = 1 或 2)。
  • rsLoRA(rank-stabilized LoRA, Kalajdzievski 2023)发现大 r 时标准 α/r 缩放过激进,建议改用 α/r,对 r>64 训练更稳定。

10.2.7 应用到哪些模块?

LoRA 论文最初建议只对 attention 的 Wq,Wv 应用(参数最少)。但后续实践(QLoRA、Tülu 等)发现:

  • 应用到所有 linearq_proj, k_proj, v_proj, o_proj, gate_proj, up_proj, down_proj)效果最好。
  • 多增加的参数仍远小于全参,性价比高。

实战默认配置:

python
target_modules = [
    "q_proj", "k_proj", "v_proj", "o_proj",       # attention
    "gate_proj", "up_proj", "down_proj",          # MLP (LLaMA / Qwen / Mistral)
]

10.2.8 推理时合并:零开销

LoRA 最大的工程优势:推理时可以把 LoRA 合并回 base

Wmerged=W0+αrBA

合并后是普通 Linear,没有任何额外计算或显存开销。这与 Adapter(推理时仍需串行执行 adapter 模块)形成鲜明对比。

python
from peft import PeftModel

base = AutoModelForCausalLM.from_pretrained("Qwen/Qwen3-7B")
lora = PeftModel.from_pretrained(base, "my-lora-adapter")
merged = lora.merge_and_unload()  # 返回普通 transformers 模型
merged.save_pretrained("qwen3-7b-merged")

10.2.9 多任务部署:Adapter 切换

同一个 base + 多个 LoRA → 多任务切换部署,每个任务只占几十 MB:

                    ┌── LoRA-medical (40 MB)

   Base (14 GB) ────┼── LoRA-legal (40 MB)

                    └── LoRA-coder (40 MB)

vLLM、SGLang 等推理引擎都原生支持 LoRA 热切换。这是 LoRA 在生产环境的另一个关键优势。

10.3 QLoRA(Dettmers et al., 2023)

QLoRA 把 LoRA 推到极致:65B 模型在单张 48GB GPU 上微调,性能匹配 16-bit 全参微调。它通过三个创新实现这一点。

10.3.1 创新 1:4-bit NormalFloat (NF4) 量化

观察:神经网络权重近似服从零均值正态分布 N(0,σ2)。NF4 把 4-bit 的 16 个量化级别放在标准正态分布的等概率分位点上——即每两个量化点之间承载的概率相同——这在信息论意义上对正态分布最优

NF4 量化级别 qi 通过正态分布的分位函数 QX 计算:

qi=12(QX(i2k1+1)+QX(i+12k1+1))

实测对比:在 OPT、BLOOM、Pythia、LLaMA 上,NF4 比 FP4、Int4 都更优,Wikitext PPL 显著下降

10.3.2 创新 2:Double Quantization

第一次量化(4-bit)需要每个 block(默认 64)一个 FP32 scale 常数,本身占 32/64=0.5 bits/param。

Double Quantization 把这些 32-bit scale 常数本身再量化为 8-bit float(block size 256),节省约 0.37 bits/param:

平均位宽=4+864+3264×2564.127 bits/param

(相比简单方案 4+32/64=4.5 bits)

7B 模型:14 GB → 3.6 GB(节省 0.65 GB)。

10.3.3 创新 3:Paged Optimizers

利用 NVIDIA Unified Memory,遇到显存峰值(如长序列梯度检查点反向传播时)把 optimizer state 暂存到 CPU 内存,避免 OOM。这相当于自动 swap——透明、稳定。

实现上是 bitsandbytesPagedAdamW8bit

python
from bitsandbytes.optim import PagedAdamW8bit
optimizer = PagedAdamW8bit(model.parameters(), lr=2e-4)

10.3.4 工作流

  1. 把 base model 量化为 NF4,冻结
  2. 在每个 linear 上挂 LoRA adapter(FP16/BF16)。
  3. 前向:每次用到 NF4 权重时,临时反量化为 BF16 再做 matmul。
  4. 反向:梯度只流到 LoRA 的 A、B(base 权重无梯度)。
python
import torch
from transformers import AutoModelForCausalLM, BitsAndBytesConfig
from peft import LoraConfig, get_peft_model, prepare_model_for_kbit_training

bnb = BitsAndBytesConfig(
    load_in_4bit=True,
    bnb_4bit_quant_type="nf4",
    bnb_4bit_use_double_quant=True,
    bnb_4bit_compute_dtype=torch.bfloat16,
)

model = AutoModelForCausalLM.from_pretrained(
    "meta-llama/Llama-2-70b-hf",
    quantization_config=bnb,
    device_map="auto",
)
model = prepare_model_for_kbit_training(model)

lora_cfg = LoraConfig(
    r=64,
    lora_alpha=16,
    lora_dropout=0.1,
    bias="none",
    task_type="CAUSAL_LM",
    target_modules=["q_proj", "k_proj", "v_proj", "o_proj",
                    "gate_proj", "up_proj", "down_proj"],
)
model = get_peft_model(model, lora_cfg)
model.print_trainable_parameters()
# trainable params: 419,430,400 || all params: 35,318,964,224 || trainable%: 1.187

QLoRA 训出的 Guanaco-65B 在 Vicuna 评测达到 ChatGPT 99.3% 的水平。

10.3.5 性能损失

QLoRA 论文做了大量对比:在 GLUE、MMLU、Vicuna-eval 上,QLoRA r=64 与 16-bit 全参微调几乎无差距(< 0.5%)。这一发现是 PEFT 历史性突破——4-bit 量化在精度损失上不再是瓶颈。

10.4 DoRA:Weight-Decomposed LoRA(Liu et al., ICML 2024 Oral)

10.4.1 动机:LoRA 与全参 FT 的更新模式不同

DoRA 作者通过权重分解分析(magnitude / direction)发现:

  • 全参微调(FT):通常磁度(magnitude)和方向(direction)的变化呈负相关
  • LoRA:倾向于同方向变化(magnitude 与 direction 同时增减)。

这一差异可能是 LoRA 性能略低于 FT 的原因之一。

10.4.2 公式

把权重 WRd×k 分解为 magnitude × direction:

W=mVVc

其中:

  • mR1×k 是每列的 magnitude vector(长度向量)。
  • VRd×k 是 direction matrix。
  • c 表示按列求 L2 范数。

DoRA 对方向部分使用 LoRA 更新,magnitude 单独训练:

W=mV0+BAV0+BAc

其中:

  • V0=W0(用预训练权重作为 direction 初值)。
  • BA 是 LoRA 更新(仍冻结 base V0)。
  • m 直接训练(额外参数仅 k 个 / 列)。

可训练参数 = LoRA r(d+k) + magnitude k,比 LoRA 多了 k 个,几乎可以忽略。

10.4.3 优势

  • 更接近 FT 行为:magnitude 和 direction 解耦更新,能产生类似 FT 的负相关模式。

  • 性能提升:在 LLaMA-7B/13B、LLaMA-2、LLaMA-3 上比 LoRA 平均高 1-3 点(commonsense reasoning、ARC、HellaSwag 等)。

  • 训练稳定:低 rank 时(r=4, 8)相比 LoRA 更稳定。

  • 推理零开销:与 LoRA 一样可合并:

    Wmerged=mW0+(α/r)BAW0+(α/r)BAc
  • 可与 QLoRA 结合 → QDoRA

10.4.4 PyTorch 实现

python
import math
import torch
import torch.nn as nn
import torch.nn.functional as F

class DoRALayer(nn.Module):
    def __init__(self, base: nn.Linear, r: int = 8, alpha: int = 16):
        super().__init__()
        self.base = base
        for p in self.base.parameters():
            p.requires_grad = False

        d_out, d_in = base.weight.shape
        self.r = r
        self.scaling = alpha / r

        # magnitude: 每列的 L2 范数,作为可训练参数
        self.m = nn.Parameter(base.weight.norm(p=2, dim=0, keepdim=True))

        # direction: LoRA
        self.A = nn.Parameter(torch.empty(r, d_in))
        self.B = nn.Parameter(torch.zeros(d_out, r))
        nn.init.kaiming_uniform_(self.A, a=math.sqrt(5))

    def forward(self, x: torch.Tensor) -> torch.Tensor:
        # 重建 direction
        adapted = self.base.weight + self.scaling * (self.B @ self.A)
        norm = adapted.norm(p=2, dim=0, keepdim=True) + 1e-6
        weight = self.m * adapted / norm
        return F.linear(x, weight, self.base.bias)

注意:DoRA 计算量比 LoRA 多一次 norm,训练吞吐略低(约 80-90% LoRA 速度)。HuggingFace PEFT 的实现通过梯度优化把开销降到 ~10%。

PEFT 库使用 DoRA:

python
lora_cfg = LoraConfig(
    r=16, lora_alpha=32,
    use_dora=True,             # 关键
    target_modules=[...],
    task_type="CAUSAL_LM",
)

10.5 Adapter(Houlsby et al., ICML 2019)

PEFT 鼻祖,比 LoRA 早三年。在每层 Transformer 中插入两个瓶颈结构

x~=x+ReLU(xWdown)Wup

其中 WdownRd×mWupRm×dmd(如 m=64)。

   ┌─────────────────────┐
   │   FFN (frozen)       │
   └──────────┬──────────┘

          ┌───▼───────────┐
          │ down: d → m    │
          │     ↓ ReLU     │
          │ up:   m → d    │
          └───┬───────────┘
              │ residual

            output
  • 一般在 attention 之后和 FFN 之后各放一个。
  • 仅训练 adapter + LayerNorm + classification head。
  • 仅 3.6% 参数量即接近 full FT 性能(GLUE 基准)。

缺点

  • 推理时多了串行计算——adapter 不能合并回原权重,必须实际执行 down/up projection。
  • 这导致推理延迟增加 5-15%

变体:

  • Pfeiffer adapter:仅在 FFN 后插入一个,参数减半。
  • Parallel adapter(He et al., 2021):与原模块并行而非串行,可部分合并。
  • AdapterFusion:训练多个领域 adapter 后用 attention 融合。

10.6 Prefix-Tuning(Li & Liang, ACL 2021)

冻结 LM,在每层注意力的 key/value 前面拼接一段可训练的「虚拟 token」(prefix):

Attention(Q,[PK;K],[PV;V])

其中 PK,PVRl×d 是 prefix 长度为 l 的可学习参数(l 通常 5-20)。

直觉:每个后续 token 在 self-attention 时可以「关注」prefix,相当于一段对模型生成行为的软指令——比离散 prompt 表达力更强。

10.6.1 重参数化技巧

直接训练 PK,PV 数值不稳定。论文提出用一个小 MLP 把短向量 PRl×d 映射到 PK,PV

PK,PV=MLP(P),dd

训练完后丢弃 MLP,只保留映射后的 PK,PV

10.6.2 性能

  • 仅训练 0.1% 参数即接近 FT。
  • 低数据场景 (< 1K 样本) 甚至超过 FT。
  • 缺点:占用 context 长度(每层 l 个虚拟 token);多任务部署时 prefix 不能合并。

10.6.3 衍生方法

  • Prompt-Tuning(Lester et al., 2021):只在 input embedding 层加 soft prompt,更简单,但需 10B+ 模型才有效。

  • P-Tuning v2(Liu et al., 2022):每层都加(即 prefix-tuning 的回归),性能稳定。

  • (IA)³(Liu et al., 2022):用三个学习向量 lK,lV,lff 缩放 K、V、FFN:

    K=lKK,V=lVV,FFN(x)=lffFFN(x)

    仅 0.01% 参数,极小但效果略低于 LoRA。

10.7 LoRA+ 与其他 LoRA 改进

2023-2024 年涌现了一批 LoRA 改进方法:

10.7.1 LoRA+(Hayou et al., 2024)

观察:LoRA 中 AB 的最优学习率不应相同。具体地,B 的学习率应该比 A(论文给出 ηB/ηA=16)。

直观解释:B 初始为 0,更新空间巨大;A 初始已有信号,需要更小步长。

实现仅几行:

python
opt = torch.optim.AdamW([
    {"params": [p for n, p in model.named_parameters() if "lora_A" in n], "lr": 1e-4},
    {"params": [p for n, p in model.named_parameters() if "lora_B" in n], "lr": 1.6e-3},
])

实测在数学、代码任务上 LoRA+ 比 LoRA 提升 1-2 点。

10.7.2 rsLoRA:Rank-Stabilized

把 scaling 改为 α/r 而非 α/r,使大 r 训练稳定。

10.7.3 PiSSA

用 base 权重 SVD 的 Top-r 主成分初始化 A,B(不再 B=0 起步),收敛更快。

10.7.4 VeRA

多个 LoRA 层共享同一个随机的 A,B,每层只训练两个对角缩放向量。极致压缩参数。

这些改进各有适用场景。对大多数项目,朴素 LoRA / DoRA + QLoRA 已经够用——不要过早优化。

10.8 PEFT 方法对比

方法可训参数 %显存(vs FT)性能推理延迟备注
Full FT100%100%上限基线0黄金标准
Adapter (Houlsby)0.5-3%~50%接近 FT+5-15%串行结构
Prefix-Tuning0.1%~30%占 contextKV 拼接
Prompt-Tuning0.01%~25%大模型才稳占 contextembedding 层
(IA)³0.01%~25%略低0极小参数
LoRA (r=16)0.1-1%~35%接近 FT0(合并后)主流
QLoRA (r=64)同 LoRA + 4-bit base~12%接近 16-bit FT065B/48GB
DoRA比 LoRA 略多 m~38%> LoRA0(合并后)新主流
QDoRADoRA + 4-bit base~14%略 > QLoRA0极致省显存
LoRA+同 LoRA同 LoRA> LoRA0改 LR
PiSSA同 LoRA同 LoRA收敛快0改初始化

10.8.1 实战选择决策树

┌─ 单卡显存 < 24 GB? ─── Yes ──→ QLoRA / QDoRA (r=32-64)
│                              如果还放不下 → 减小 batch / max_len

├─ 显存 24-80 GB? ─── Yes ──→ LoRA r=16-64 / DoRA r=16
│                            或 7B-13B 全参 SFT (注意激活值)

└─ 显存 > 80 GB (多卡) ── ┬─ 模型 < 13B → 全参 SFT

                          ├─ 13B-70B → FSDP + LoRA / DoRA
                          │           或 ZeRO-3 + 全参

                          └─ > 70B → QLoRA + FSDP

经验法则

  • 显存充足、追求性能:DoRA r=64 > LoRA r=64 > LoRA r=16
  • 显存极紧(消费级 24/48 GB):QLoRA / QDoRA
  • 多任务部署:LoRA(adapter 切换)
  • 研究 / 快速实验:LoRA r=8, α=16, dropout=0.05

10.9 完整 LoRA 训练样例

下面是一个完整可运行的 LoRA SFT 脚本(HuggingFace PEFT + TRL):

python
import torch
from datasets import load_dataset
from transformers import AutoModelForCausalLM, AutoTokenizer
from peft import LoraConfig, get_peft_model, TaskType
from trl import SFTTrainer, SFTConfig

MODEL_ID = "Qwen/Qwen3-7B"

tokenizer = AutoTokenizer.from_pretrained(MODEL_ID)
tokenizer.padding_side = "right"

model = AutoModelForCausalLM.from_pretrained(
    MODEL_ID,
    torch_dtype=torch.bfloat16,
    attn_implementation="flash_attention_2",
)

# LoRA 配置
peft_cfg = LoraConfig(
    r=16,
    lora_alpha=32,
    lora_dropout=0.05,
    bias="none",
    task_type=TaskType.CAUSAL_LM,
    target_modules=[
        "q_proj", "k_proj", "v_proj", "o_proj",
        "gate_proj", "up_proj", "down_proj",
    ],
    use_dora=False,  # 改 True 即 DoRA
)
model = get_peft_model(model, peft_cfg)
model.print_trainable_parameters()
# trainable params: 40,370,176 || all params: 7,656,360,448 || trainable%: 0.527

ds = load_dataset("HuggingFaceH4/ultrachat_200k", split="train_sft[:20000]")

config = SFTConfig(
    output_dir="qwen3-7b-lora",
    num_train_epochs=2,
    per_device_train_batch_size=4,
    gradient_accumulation_steps=4,
    gradient_checkpointing=True,
    learning_rate=2e-4,         # LoRA 用更高 LR
    lr_scheduler_type="cosine",
    warmup_ratio=0.05,
    bf16=True,
    max_seq_length=2048,
    assistant_only_loss=True,
    logging_steps=10,
    save_strategy="epoch",
    optim="adamw_torch_fused",
)

trainer = SFTTrainer(model=model, args=config, train_dataset=ds, tokenizer=tokenizer)
trainer.train()

# 保存 adapter(仅 ~80 MB)
trainer.model.save_pretrained("qwen3-7b-lora/final")

# 合并并保存为完整模型(~14 GB)
merged = trainer.model.merge_and_unload()
merged.save_pretrained("qwen3-7b-merged")
tokenizer.save_pretrained("qwen3-7b-merged")

QLoRA 版本只需把 from_pretrained 部分换成:

python
from transformers import BitsAndBytesConfig
from peft import prepare_model_for_kbit_training

bnb = BitsAndBytesConfig(
    load_in_4bit=True,
    bnb_4bit_quant_type="nf4",
    bnb_4bit_use_double_quant=True,
    bnb_4bit_compute_dtype=torch.bfloat16,
)
model = AutoModelForCausalLM.from_pretrained(
    MODEL_ID, quantization_config=bnb, device_map="auto",
)
model = prepare_model_for_kbit_training(model)

# LoRA r 可以更大(QLoRA 论文推荐 64)
peft_cfg.r = 64
peft_cfg.lora_alpha = 16

10.10 本章小结

  • PEFT 的核心:冻结大部分参数,只训练 0.1-3% 的「适配器」参数,把显存需求从 100 GB 降到 10-50 GB。
  • LoRA 假设权重更新低秩:ΔW=BAB 初始化为 0 保证训练稳定起步。
  • QLoRA 三大创新(NF4 + Double Quantization + Paged Optimizers)让 65B 模型能在单张 48 GB GPU 上微调。
  • DoRA 把权重分解为 magnitude × direction,对方向用 LoRA,更接近全参 FT 的更新模式。
  • 推理零开销是 LoRA/DoRA 相对 Adapter/Prefix 的关键优势——可合并回 base,部署无延迟。
  • 实战默认配置:r=16, α=32, dropout=0.05, 全 linear 模块,bf16;显存紧时改 QLoRA。

思考题

  1. LoRA 假设权重更新 ΔW 是低秩的,这一假设在什么情况下会失效? 设计一个实验:训练同一个任务的 LoRA r=4, 16, 64, 256,比较收敛性能。如果 r=256 显著优于 r=16,意味着什么?

  2. QLoRA 论文声称 4-bit NF4 量化几乎无精度损失,但仅在 SFT 任务上验证。在 RLHF(PPO / DPO)阶段使用 QLoRA 会有什么风险? 提示:考虑梯度数值稳定性、reward signal 的细粒度。

  3. 如果你要在生产环境同时部署 50 个不同领域的 LoRA adapter(医疗、法律、金融、各种工具),你会如何设计推理架构? 比较以下方案的优劣:(a) 50 个独立合并的全模型;(b) 1 个 base + 50 个未合并 LoRA 的动态切换;(c) 用 S-LoRA / Punica 等 LoRA serving 框架批量服务。

基于 MIT 协议发布