一张显卡,怎么微调大模型

全量微调一个 8B 模型要 130 GB 以上的显存。LoRA 和 QLoRA 是怎么把它压到一张卡上的。

目录
  1. 训练一步,要存什么
  2. LoRA:只训一个差值
  3. 一层 LoRA 怎么算
  4. 插拔:一份底座,很多个任务
  5. 加在哪,秩取多少
  6. QLoRA:把冻住的底座压成 4 bit
  7. 别的省法
  8. 喂什么数据
  9. SFT 之后:教它分好坏
  10. 面试常问
  11. 参考资料

一个 8B 的模型,按 BF16 存,权重 16 GB,一张 24 GB 的显卡跑推理绰绰有余。想在这张卡上接着训它,也就是微调,要 130 GB 往上。

多出来的一百多 GB 是什么?LoRA、QLoRA 这些名字,都是在压这笔账:

每个参数占几个字节,激活另算

训练一步,要存什么

微调和预训练是同一套算法,只是起点换成了训练好的权重,数据换成了自己的。目标是让标准答案每个 token 的概率尽量高,写成损失就是交叉熵:

L(θ)=−1T∑t=1Tlog⁡pθ(yt∣y<t)\mathcal{L}(\theta)=-\frac{1}{T}\sum_{t=1}^{T}\log p_\theta(y_t\mid y_{<t})

θ\theta 是全部参数,TT 是算损失的 token 数。训练一步分三段:前向算出 L\mathcal{L},反向算出每个参数的梯度 g=∇θLg=\nabla_\theta\mathcal{L},再顺着梯度把参数挪一点。

挪法几乎都是 AdamW。它给每个参数多记两个数,梯度的滑动平均 mm 和梯度平方的滑动平均 vv:

m←β1m+(1−β1) g,v←β2v+(1−β2) g2,θ←θ−η m^v^+ϵ.\begin{aligned} m&\leftarrow\beta_1m+(1-\beta_1)\,g,\\ v&\leftarrow\beta_2v+(1-\beta_2)\,g^2,\\ \theta&\leftarrow\theta-\eta\,\frac{\hat m}{\sqrt{\hat v}+\epsilon}. \end{aligned}

m^\hat m、v^\hat v 是偏差修正后的 mm、vv,η\eta 是学习率。除以 v^\sqrt{\hat v} 等于给每个参数单独定步长,结果是每一步都挪 η\eta 左右,跟梯度本身多大关系不大。讲 LoRA 的 α\alpha 时会用到这一点。

混合精度训练时,每个参数要存这些:

存的东西精度字节
权重,前向和反向用BF162
梯度BF162
主权重,更新用FP324
Adam 的 mm、vvFP328

一共 16 字节,8B 的模型就是 128 GB。推理只要第一行。

为什么要一份 FP32 的主权重? BF16 的有效精度只有 8 个二进制位。一个 0.02 左右的权重,离它最近的可表示的数要差 0.0001 量级;Adam 一步大约挪 10−510^{-5},加上去一舍入,原样退了回来。更新只能在 FP32 上累加,算完再转一份 BF16 去做前向。

为什么是 BF16,不是 FP16?

格式指数位尾数位最大值
FP328233.4×10383.4\times10^{38}
FP165106550465504
BF16873.4×10383.4\times10^{38}

BF16 拿精度换范围,指数位和 FP32 一样多。FP16 范围太窄,小梯度会下溢成 0,得配 loss scaling:先把损失乘一个大数再反向,更新前再除回来。BF16 不用操这个心。

激活。 反向传播要用到前向每一层的中间结果,前向时就得先存着。这块跟 batch 大小乘序列长度成正比,跟用哪种微调方法无关。标准的省法是梯度检查点:前向只留每层的输入,反向时把这一层重算一遍,多花一次前向的算力,换掉大半激活。

多卡。 普通的数据并行,每张卡都存完整的 16 字节,卡再多也不省。DeepSpeed 的 ZeRO 把这些状态切开分给各张卡,设参数量为 Φ\Phi、卡数为 NN:

每张卡存的字节
数据并行16Φ16\Phi
ZeRO-1:切优化器状态4Φ+12Φ/N4\Phi+12\Phi/N
ZeRO-2:再切梯度2Φ+14Φ/N2\Phi+14\Phi/N
ZeRO-3:连权重也切16Φ/N16\Phi/N

ZeRO-3 的代价是通信:每层算之前,都得先从各张卡把权重收齐。PyTorch 的 FSDP 是同一个思路。

不过这只是把账摊开,总数一分没少。LoRA 换了个思路:大部分参数,干脆不训。

LoRA:只训一个差值

微调前后,权重从 W0W_0 变成 W0+ΔWW_0+\Delta W。全量微调学的就是 ΔW\Delta W:一个 4096×40964096\times4096 的矩阵,1,677 万个自由参数。

可微调的数据常常只有几千条,用不上这么多自由度。2020 年有个实验:把 RoBERTa 的全部更新限制在一个随机的 200 维子空间里,在 MRPC 上就能拿到全量微调 90% 的效果。预训练过的模型换任务时,要动的方向其实很少。

LoRA 假设每个矩阵的 ΔW\Delta W 都是低秩的,直接写成两个瘦矩阵相乘:

ΔW=BA,B∈Rd×r,A∈Rr×k,r≪min⁡(d,k).\Delta W=BA,\qquad B\in\mathbb{R}^{d\times r},\quad A\in\mathbb{R}^{r\times k},\quad r\ll\min(d,k).

W0W_0 冻住,只训 AA、BB,参数从 dkdk 降到 r(d+k)r(d+k)。d=k=4096d=k=4096、r=16r=16 时,不到原来的 1%。

秩为 1 的矩阵就是一列乘一行,像一块格子布;秩为 rr,就是 rr 块叠起来。下面的 ΔW\Delta W 是构造的,三个主方向加一点噪声:

图里的 BABA 是对目标做 SVD、只留前 rr 个奇异值得到的。Eckart–Young 定理说,秩不超过 rr 的矩阵里它离目标最近,所以这是 LoRA 的上限。

切到满秩就没有捷径了:rr 拖到 12,两条瘦矩阵已经和整块一样大,误差还剩一半。LoRA 好使,前提是任务要的改动本来就低秩。换说话格式、学回答风格,多半满足;灌大量新知识,就未必。2024 年的 LoRA Learns Less and Forgets Less 测过:在代码和数学上,全量微调学到的 ΔW\Delta W,秩要高出 10 到 100 倍。LoRA 学得少,忘得也少。

一层 LoRA 怎么算

h=W0x+αrBAxh=W_0x+\frac{\alpha}{r}BAx

先算 AxAx,得到一个 rr 维的小向量,再乘 BB,全程不出现 d×kd\times k 的大矩阵。

初始化。 AA 随机,BB 全零。一开始 BA=0BA=0,模型和原模型一模一样。为什么不两个都设成 0?记 δ=∂L/∂h\delta=\partial\mathcal{L}/\partial h:

∂L∂B=αr δ (Ax)⊤,∂L∂A=αr B⊤δ x⊤.\frac{\partial\mathcal{L}}{\partial B}=\frac{\alpha}{r}\,\delta\,(Ax)^\top,\qquad \frac{\partial\mathcal{L}}{\partial A}=\frac{\alpha}{r}\,B^\top\delta\,x^\top.

BB 的梯度里有 AA,AA 的梯度里有 BB,两个都是 0 就永远动不了。BB 为 0、AA 随机时,BB 先拿到梯度,离开 0 以后 AA 再跟上。反过来让 AA 为 0 也行,只是实验上前一种能用更大的学习率,效果更好。

缩放。 rr 翻倍,BABA 叠的项多一倍,α/r\alpha/r 正好减半,换 rr 时学习率不用大动。又因为 Adam 会把梯度的整体大小除掉,调 α\alpha 约等于调学习率,所以常见做法是把 α\alpha 定成 rr 或 2r2r,只调学习率。rsLoRA 认为 1/r1/r 压过了头,大秩被压得学不动,改成了 α/r\alpha/\sqrt r。

合并。 训完把 W0+αrBAW_0+\frac{\alpha}{r}BA 算出来存成普通矩阵,推理和原模型一样快。训练时是两条旁路,部署时什么都不剩,这是 LoRA 最漂亮的一笔。也可以不合并,下一节说。

省了什么。 冻住的参数不要梯度,也不要 Adam 状态和 FP32 主权重,16 字节降到 2 字节。省不下的是激活:最前面那层的 LoRA 也要梯度,反向传播照样得穿过每一层。算力也只省了对冻结权重求梯度那一份,大约是全量的三分之二。

插拔:一份底座,很多个任务

LoRA 训完只存 AA 和 BB。用 PEFT 训,就是两个文件:

adapter_config.json 底座是谁、r、alpha、挂在哪些层
adapter_model.safetensors 每一层的 A 和 B

通常几十 MB,底座一个字节都不用再存。于是 LoRA 像插件,底座加载一次,换任务就换一组:

from peft import PeftModel
model = PeftModel.from_pretrained(base, "lora/support", adapter_name="support")
model.load_adapter("lora/translate", adapter_name="translate")
model.set_adapter("translate") # 换成翻译那一组
with model.disable_adapter(): # 临时拔掉,跑原模型
...

线上服务更进一步:同一个 batch 里,这个请求要客服、那个要翻译、第三个只要原模型。这时就不能合并了,一份合并后的权重只装得下一个任务:

不合并时,整批先一起乘 W0W_0,再分组乘各自的 AA、BB 加回去。多出的计算很少,麻烦在于又小又碎。Punica、S-LoRA 专门写了 kernel,把它们攒成一次算完;S-LoRA 还把暂时不用的 LoRA 放在内存里,按需搬进显存,一张卡能挂上千个。vLLM 的 --enable-lora 也是这么干的。

插件还能叠:W0+ΔW1+ΔW2W_0+\Delta W_1+\Delta W_2,一组管风格,一组管格式,只是叠在一起可能互相打架。另外插件认底座:AA、BB 学的是相对某个 W0W_0 的差,换了底座就对不上了。

加在哪,秩取多少

一层 Transformer 有 7 个线性层:注意力的 q、k、v、o,MLP 的 gate、up、down。LoRA 原论文只挂了注意力;后来 QLoRA 和 Thinking Machines 的 LoRA Without Regret 都发现,要追平全量微调得全部挂上,MLP 最要紧,这比 rr 取多少重要得多。

rr 常取 8 到 64。它对显存几乎没影响,管的是容量:数据少、任务窄就用小的。学习率要比全量微调大一个数量级,全量常用 10−510^{-5} 上下,LoRA 常用 10−410^{-4} 上下。

词表里加了新 token 时,embedding 和 lm_head 里的新行是随机初始化的,这两层要整个放开训(PEFT 里写进 modules_to_save)。

QLoRA:把冻住的底座压成 4 bit

LoRA 之后,最大的一块是冻住的底座。它只被读、不被改,那就压缩着存,算到哪层解压哪层:底座存成 4 bit,LoRA 照常用 16 位训。

4 bit 只有 16 个格子。absmax 量化先把一组数除以其中绝对值最大的 cc,缩到 [−1,1][-1,1],再归到最近的格子,只存编号:

w^=c⋅qj,j=arg⁡min⁡i ∣ qi−wc ∣.\hat w=c\cdot q_j,\qquad j=\arg\min_i\,\Big|\,q_i-\frac{w}{c}\,\Big|.

格子怎么摆是关键。INT4 等距地摆;NF4 按正态分布的分位数摆,因为训练好的权重大致就是正态分布,这样每个格子分到的数差不多一样多:

同样是 4 bit 的编号,NF4 平均能用出 3.9 bit 的信息量,INT4 只有 3.5 bit。

点“混进一个离群值”:cc 被撑大,其余的数全挤进中间几格。所以 QLoRA 每 64 个权重分一块,各算各的 cc,离群值只祸害自己那一块。

双重量化。 每块一个 32 位的 cc,摊下来每个参数多 0.5 bit。把这些 cc 再按 256 个一组量化成 8 bit,就降到了

864+3264×256≈0.127 bit.\frac{8}{64}+\frac{32}{64\times256}\approx0.127\ \text{bit}.

分页优化器。 长样本让显存突然冲高时,把优化器状态换页到 CPU 内存,免得 OOM。

存 4 bit,算 16 bit。 每层计算前先解压回 BF16:

h=dequant⁡(W0NF4) x+αrBAx.h=\operatorname{dequant}\big(W_0^{\text{NF4}}\big)\,x+\frac{\alpha}{r}BAx.

所以 QLoRA 省显存、不省算力,还比 LoRA 慢一点。论文用它在一张 48 GB 的卡上微调了 65B 的模型,效果追平 16 位微调。

合并有个坑:LoRA 补的是量化后底座的差。合并到原始权重上,底座变了;合并后再量化,又多挨一次误差。LoftQ 的办法是量化时就让 BABA 先补上量化误差。

别的省法

LoRA 不是第一个省参数的。几种方法的可训练部分,分别放在一层的哪里:

Adapter 是串在主路上的瓶颈小网络,每个 token 都得多穿过它,中间还夹着非线性,没法并回权重。LoRA 也是瓶颈,但它是并联的、线性的:W0x+BAx=(W0+BA)xW_0x+BAx=(W_0+BA)x,所以能并回去。

Prompt Tuning 只在输入前拼几个可训练的向量;Prefix Tuning 在每层注意力的 K、V 前面都拼几列。参数极少,但占上下文,训练也不太稳,模型不够大时追不上全量微调。(IA)³ 只学几个向量,逐元素缩放 K、V 和 MLP 的中间激活,同样能并回权重。

LoRA 的常见变体:

变体改了什么
rsLoRA缩放从 α/r\alpha/r 换成 α/r\alpha/\sqrt r
LoRA+BB 的学习率设成 AA 的十几倍
DoRA把权重拆成长度和方向,方向交给 LoRA
PiSSA用 W0W_0 最大的几个奇异方向初始化 AA、BB
AdaLoRA按重要性在不同矩阵之间分配秩
LoftQ量化时初始化 AA、BB,补上量化误差
VeRA各层共用冻住的随机矩阵,只训缩放向量
GaLore全参数训练,把梯度投影到低秩空间来省优化器状态

喂什么数据

SFT 的数据是一问一答,训练时要用模型自己的聊天模板拼成文档,和推理时一模一样:

<|im_start|>user
给我的橘猫起个名字,两个字。<|im_end|>
<|im_start|>assistant
橘座<|im_end|>

损失只算回答。提问部分的标签设成 -100,PyTorch 的交叉熵会跳过它:

prompt = tok.apply_chat_template(messages[:-1], add_generation_prompt=True)
full = tok.apply_chat_template(messages)
labels = [-100] * len(prompt) + full[len(prompt):]

回答末尾的 <|im_end|> 一定要算进去,它是“说完了”的信号;漏掉它,模型学会了答,却学不会停。常见的坑是 pad token 和结束符设成同一个,mask pad 的时候把结束符也一起 mask 掉了。

几条短样本可以拼进同一条序列(packing),但要用块对角的注意力掩码,别让后一条看见前一条。

数据贵在质量。LIMA 只用 1,000 条精选数据微调 65B 的 LLaMA,效果就很能打。他们的“表面对齐假说”认为,知识和能力几乎都来自预训练,微调教的是用什么格式和口吻说出来。反过来,靠微调灌新知识效果往往不好:新事实学得慢,学进去以后模型还更爱胡编。新知识交给 RAG,微调管格式、风格和行为。

在窄数据上训久了,原来的能力会退化,这叫灾难性遗忘。混些通用数据、降低学习率、少训几轮,或者用 LoRA,都能缓解。轮数一般 1 到 3 轮,验证集损失开始回升就该停了。

SFT 之后:教它分好坏

SFT 只能模仿示范。很多时候写不出标准答案,却能判断两个回答哪个更好:同一个提问 xx,好回答 ywy_w,差回答 yly_l。

RLHF。 先训奖励模型。为了不和秩 rr 撞名,奖励记作 R(x,y)R(x,y)。按 Bradley–Terry 模型:

P(yw≻yl∣x)=σ(R(x,yw)−R(x,yl)).P(y_w\succ y_l\mid x)=\sigma\big(R(x,y_w)-R(x,y_l)\big).

再用 PPO 让模型多拿奖励,同时不许离 SFT 模型 πref\pi_{\text{ref}} 太远:

max⁡π Ex, y∼π(⋅∣x)[R(x,y)]−β DKL(π(⋅∣x) ∥ πref(⋅∣x)).\max_{\pi}\ \mathbb{E}_{x,\,y\sim\pi(\cdot\mid x)}\big[R(x,y)\big]-\beta\,\mathbb{D}_{\mathrm{KL}}\big(\pi(\cdot\mid x)\,\big\|\,\pi_{\text{ref}}(\cdot\mid x)\big).

KL 项是缰绳,防止模型钻奖励模型的空子,也就是 reward hacking。这一套要同时摆四个模型:策略、参考、奖励、critic。

DPO。 上面这个目标的最优解可以直接写出来:

π∗(y∣x)=1Z(x) πref(y∣x)exp⁡(R(x,y)β).\pi^*(y\mid x)=\frac{1}{Z(x)}\,\pi_{\text{ref}}(y\mid x)\exp\Big(\frac{R(x,y)}{\beta}\Big).
为什么最优解长这样

第三行用到了 π∗\pi^* 的定义:

Ey∼π[R(x,y)]−β DKL(π ∥ πref)=−β Ey∼π[log⁡π(y∣x)πref(y∣x)−R(x,y)β]=−β Ey∼π[log⁡π(y∣x)π∗(y∣x)]+βlog⁡Z(x)=−β DKL(π ∥ π∗)+βlog⁡Z(x).\begin{aligned} &\mathbb{E}_{y\sim\pi}\big[R(x,y)\big]-\beta\,\mathbb{D}_{\mathrm{KL}}(\pi\,\|\,\pi_{\text{ref}})\\ &=-\beta\,\mathbb{E}_{y\sim\pi}\Big[\log\frac{\pi(y\mid x)}{\pi_{\text{ref}}(y\mid x)}-\frac{R(x,y)}{\beta}\Big]\\ &=-\beta\,\mathbb{E}_{y\sim\pi}\Big[\log\frac{\pi(y\mid x)}{\pi^*(y\mid x)}\Big]+\beta\log Z(x)\\ &=-\beta\,\mathbb{D}_{\mathrm{KL}}(\pi\,\|\,\pi^*)+\beta\log Z(x). \end{aligned}

Z(x)Z(x) 跟 π\pi 无关,KL 散度最小是 0,当且仅当 π=π∗\pi=\pi^*。

倒过来,用策略表示奖励:

R(x,y)=βlog⁡π∗(y∣x)πref(y∣x)+βlog⁡Z(x).R(x,y)=\beta\log\frac{\pi^*(y\mid x)}{\pi_{\text{ref}}(y\mid x)}+\beta\log Z(x).

代回 Bradley–Terry,βlog⁡Z(x)\beta\log Z(x) 一减就没了,奖励模型也跟着消失。把 π∗\pi^* 换成要训练的 πθ\pi_\theta,就是 DPO 的损失:

LDPO=−log⁡σ(βlog⁡πθ(yw∣x)πref(yw∣x)−βlog⁡πθ(yl∣x)πref(yl∣x)).\mathcal{L}_{\text{DPO}}=-\log\sigma\Big(\beta\log\frac{\pi_\theta(y_w\mid x)}{\pi_{\text{ref}}(y_w\mid x)}-\beta\log\frac{\pi_\theta(y_l\mid x)}{\pi_{\text{ref}}(y_l\mid x)}\Big).

只要策略和参考两个模型,不用采样,也不用 critic。βlog⁡πθπref\beta\log\frac{\pi_\theta}{\pi_{\text{ref}}} 就是论文标题里说的隐式奖励。求梯度时,每条样本带一个权重 σ(R^l−R^w)\sigma(\hat R_l-\hat R_w):好坏已经分对的几乎不学,分反的学得最用力。它的毛病是只管拉开差距,好回答的概率也可能跟着往下掉。

GRPO。 DeepSeekMath 提出、DeepSeek-R1 拿来训推理的方法。还是强化学习,但扔掉了 critic:同一题采样 GG 个回答,用组内奖励的均值和标准差当基线:

A^i=Ri−mean⁡(R1,…,RG)std⁡(R1,…,RG).\hat A_i=\frac{R_i-\operatorname{mean}(R_1,\dots,R_G)}{\operatorname{std}(R_1,\dots,R_G)}.

奖励可以直接是规则,比如答案对不对、代码能不能跑过测试。

数据训练时要的模型在线采样
SFT标准答案策略不要
PPO偏好对,先训成奖励模型策略、参考、奖励、critic要
DPO偏好对策略、参考不要
GRPO打分规则策略、参考要,每题一组

这些方法都能和 LoRA 一起用。DPO 的参考模型就是没挂 LoRA 的底座,关掉 LoRA 跑一遍就有,又省一份显存。

面试常问

全量微调一个 7B 模型,大概要多少显存?

混合精度加 Adam,每个参数 16 字节:BF16 的权重和梯度各 2,FP32 的主权重、mm、vv 各 4。7B 约 112 GB,激活另算;推理只要 14 GB。

混合精度训练为什么要留一份 FP32 的主权重?

BF16 精度太低,10−510^{-5} 量级的更新加上去会被舍入掉,只能在 FP32 上累加。

BF16 和 FP16 有什么区别?

BF16 是 8 位指数、7 位尾数,范围和 FP32 一样;FP16 是 5 位指数、10 位尾数,范围窄,小梯度会下溢,要配 loss scaling。

梯度检查点是什么?

前向只存每层的输入,反向时重算这一层。多花一次前向的算力,换掉大部分激活显存。

ZeRO-1、2、3 分别切了什么?

依次切优化器状态、梯度、参数,每张卡分别存 4Φ+12Φ/N4\Phi+12\Phi/N、2Φ+14Φ/N2\Phi+14\Phi/N、16Φ/N16\Phi/N 字节。ZeRO-3 通信最多,FSDP 的 FULL_SHARD 对应它。

LoRA 的原理是什么?为什么有效?

冻住 W0W_0,把更新写成 ΔW=BA\Delta W=BA,参数从 dkdk 降到 r(d+k)r(d+k)。有效的前提是微调需要的更新本身是低秩的。

A 和 B 怎么初始化?为什么不能都是 0?

AA 随机、BB 为 0,起点就是原模型。都为 0 时,两者的梯度互相依赖,全是 0,训不动。

alpha 和 r 是什么关系?

LoRA 的输出乘 α/r\alpha/r,换 rr 时不用重调学习率;用 Adam 时,调 α\alpha 约等于调学习率。常设 α=r\alpha=r 或 2r2r;rsLoRA 改用 α/r\alpha/\sqrt r。

LoRA 推理时有额外延迟吗?

合并后没有。不合并时每层多两次小矩阵乘法,换来随时插拔。

多个任务的 LoRA 怎么部署?为什么线上一般不合并?

底座常驻显存,每个任务只存自己的 AA、BB。同一批请求挂着不同的 LoRA,合并后的权重只装得下一个任务,所以整批先乘 W0W_0,再分组乘各自的 AA、BB。

一组 LoRA 能换个底座用吗?

不能。它学的是相对某一个 W0W_0 的差,换了底座就对不上了。

LoRA 一般加在哪些层?

现在一般加全部线性层,MLP 最重要。原论文只加了 q、v。

秩 r 怎么选?

常见 8 到 64。它对显存影响很小,管的是容量;挂在哪些层比 rr 更重要。

LoRA 省了什么,没省什么?

省了冻结参数的梯度、优化器状态和 FP32 主权重。激活一点没省,算力只省了大约三分之一。

LoRA 和全量微调效果差多少?

中小规模的 SFT 基本打平。数据量很大、要学很多新东西时,LoRA 会落后,但原来的能力忘得少。

LoRA 的学习率该怎么设?

比全量微调大一个数量级:全量 10−510^{-5} 量级,LoRA 10−410^{-4} 量级。

QLoRA 做了哪几件事?

NF4 存底座、双重量化、分页优化器。计算时把权重解压回 BF16,LoRA 照常训练。

NF4 为什么比 INT4 好?

权重近似正态分布。NF4 按分位数放格子,每格分到的数差不多,4 bit 的信息量用得足,误差也小;它还保留了一个精确的 0。

为什么要分块量化?

缩放系数由最大值决定,一个离群值会把整组数挤进中间几格。分块以后,离群值只影响自己那一块;QLoRA 每 64 个一块。

双重量化省了多少?

缩放系数从每个参数 0.5 bit 降到约 0.127 bit。

QLoRA 比 LoRA 训练快吗?

更慢,每层计算前都要多一次解压。它省的是显存。

QLoRA 训完的 LoRA 怎么合并?

把底座解压回 BF16 再合并。LoRA 补的是量化底座的差,合并到原始权重、或者合并后再量化,都会有偏差。

Adapter、Prefix Tuning、Prompt Tuning 和 LoRA 有什么区别?

Adapter 串联在主路上,有延迟,不能合并;Prefix、Prompt 改的是输入,占上下文;LoRA 并联、线性,可以合并。

SFT 的损失怎么算?

提问部分的标签设为 -100,不算损失;回答末尾的结束符必须算,不然模型学不会停。

微调能让模型学会新知识吗?

很难。新知识学得慢,学进去以后还更容易胡编。新知识交给 RAG。

SFT 数据要多少?

质量比数量重要。LIMA 用一千条精选数据就有不错的效果。

灾难性遗忘怎么缓解?

混入通用数据、降低学习率、少训几轮,或者用 LoRA。

有了 SFT,为什么还要 RLHF?

SFT 只能模仿示范。偏好数据更容易拿到,还能告诉模型什么不该说。

PPO 训练要几个模型?KL 项有什么用?

策略、参考、奖励、critic 四个,其中策略和 critic 要训练。KL 项防止策略离参考模型太远,去钻奖励模型的空子。

DPO 的原理是什么?比 PPO 好在哪?

KL 约束下的最优策略有闭式解。用它把奖励写成策略和参考模型的对数概率比,代入 Bradley–Terry 后归一化常数抵消,得到只依赖偏好对的损失。只要两个模型,不用采样,也不用 critic。

DPO 里的 beta 起什么作用?

它是 KL 约束的强度。大了保守,离参考模型近;小了走得远,也更容易过拟合偏好数据。常用 0.1 左右。

GRPO 和 PPO 有什么不同?

GRPO 不要 critic,用同一题一组回答的奖励均值和标准差当基线,适合答案能自动判对错的任务。

参考资料