对应 Maps to:CMU 10-423 / 10-623 / 10-723 Generative AI(Spring 2026;Aran Nayebi、Matt Gormley),Lecture 10 · Mon 16-Feb · Parameter-efficient fine tuning(slides:lecture10-peft)。 阅读材料:Houlsby et al. (2019), Parameter-Efficient Transfer Learning for NLP(adapter);Li & Liang (2021), Prefix-Tuning;Hu et al. (2021), LoRA: Low-Rank Adaptation of Large Language Models;Li et al. (2018), Measuring the Intrinsic Dimension of Objective Landscapes;Aghajanyan, Zettlemoyer & Gupta (2020), Intrinsic Dimensionality Explains the Effectiveness of Language Model Fine-Tuning;He et al. (2022), Towards a Unified View of Parameter-Efficient Transfer Learning。 依据说明:本讲按原课第 10 讲公开的 slides 展开:少样本场景下的两条路(监督微调 vs 上下文学习)、微调和上下文学习的比较;PEFT 的目标和四类做法(只调一部分参数、adapter、前缀微调、LoRA);只调顶层;adapter 的结构与结果;前缀微调;LLM 有多大、GPT-3 的线性层有多大;内在维度(Li et al. 2018、Aghajanyan et al. 2020);LoRA 的三个动机、结构、初始化、热切换、偏置、加在哪些矩阵上、结果;PEFT 的统一视角;LoRA 为什么省显存;PEFT 的计算时间;ViT 上的 PEFT。论文里的数字写成”某某报告”。所有实验是本讲义自己设计的,不代表课堂上讲了什么。 配套代码 Code:
code/lecture10/(6 个脚本 + 1 个模块;01约 10 秒,05约 2 分钟,02、06约 2.5 分钟,04约 3.5 分钟,03约 4.5 分钟;长的都有--quick) 预计阅读时间:3.5 小时(含运行代码)
0. 本讲导航 · How to read this lecture
0.1 学习目标 Learning goals
学完这一讲,你应该能够:
- 说出少样本场景下”监督微调”和”上下文学习”各自的优缺点,以及全量微调贵在哪里(显存、存储)。
- 描述四类 PEFT:只调顶层、adapter、前缀微调、LoRA——各自改了模型的哪一部分、加了多少参数、推理时有没有额外开销。
- 解释”内在维度”:它怎么测、为什么预训练会让它变小、它和 LoRA 的关系。
- 写出 LoRA 的前向公式、参数量、初始化(为什么 B = 0)、缩放 α/r、合并与拆回;说出它加在哪些矩阵上最划算。
- 给一个训练配置算显存账:权重、梯度、优化器状态、激活值各占多少,PEFT 省的是哪几项。
- 根据”任务离预训练有多远”判断 PEFT 够不够用、该选多大的秩。
0.2 先修与本讲范围 Prerequisites & scope
| 默认你已经会 Assumed | 本讲只给直觉,后面再展开 Previewed only |
|---|---|
| 第 2 讲:GPT 的结构(W_q、W_k、W_v、W_o、前馈层、残差、LayerNorm) | 上下文学习、提示、指令微调(第 11 讲) |
| 第 4 讲:预训练 + 微调;线性探针有时比全量微调还好 | 多卡训练、ZeRO 的显存切分(第 17 讲) |
| 第 5 讲:ViT;前置讲 P1:浮点格式与每个数占几个字节 | 量化 + LoRA(QLoRA)只在延伸阅读里提到 |
0.3 本讲地图 Map
§1 为什么需要 PEFT:微调 vs 上下文学习;全量微调贵在哪里 ← 代码 01(第 4 部分)
§2 PEFT 的目标与四类做法;本讲的实验设定
§3 只调一部分参数:只调顶层 ← 代码 03
§4 Adapter ← 代码 03、05
§5 前缀微调 ← 代码 05
§6 内在维度:微调到底需要多少自由度 ← 代码 02
§7 LoRA:结构、初始化、合并、加在哪里、秩多大 ← 代码 01、04
§8 主实验:七种做法、两个任务、三种数据量;遗忘 ← 代码 03
§9 显存与计算时间 ← 代码 01、05
§10 ViT 上的 PEFT ← 代码 06
§11–15 小结、误区、练习、延伸阅读、下一讲
1. 为什么需要 PEFT · Why parameter-efficient fine-tuning
1.1 少样本场景下的两条路
slides 开头的设定:你有一个很小的数据集 ,和一个很大的预训练语言模型(几十亿参数)。怎样让模型”学会”这个任务?
| A. 监督微调(supervised fine-tuning) | B. 上下文学习(in-context learning,第 11 讲) | |
|---|---|---|
| 做法 | 用标准的监督目标、反向传播、你喜欢的优化器(如 Adam),在训练数据上更新模型参数 | 把训练样本拼进提示,让模型在推理(解码)时从中找规律,取它接着写出来的内容当预测 |
| 优点 | 就是标准的机器学习流程;N 很大时照样能用 | 不需要反向传播,每个例子只”过”一遍;不需要模型权重,有 API 就行 |
| 缺点 | 反向传播要大约 3 倍于前向的显存和计算;可能根本拿不到权重(模型不开源) | Transformer 处理长度为 N 的提示要 的时间和空间;提示可能超出最大上下文 |
本讲回答 A 这一边的问题:怎样更高效地微调一个非常大的基础模型?
1.2 既然这么贵,为什么还要微调?
slides 的回答:因为即使对很大的模型,微调也常常比上下文学习好。
- LoRA 论文的表里,GPT-3 在 MNLI-m 上微调的结果比少样本好得多。slides 的问答点出原因:GPT-3 的上下文只有 2048 个 token,MNLI-m 的少样本设定一共只放得下 6 个例子;而微调用了全部 39.3 万个训练样本、训练 2 个 epoch——两边用的信息量根本不在一个级别。
- Mosbach et al. (2023) 做了”公平比较”(同样的模型、同样少的样本):在 RTE 和 MNLI 上,大多数模型尺寸下微调仍然胜过上下文学习。
- slides 加了一句注脚:这是 2023 年的普遍看法;2024 年以后上下文越来越长,可以塞进成百上千个例子(“多样本上下文学习”),故事可能不同了——第 19 讲(长上下文)再谈。
1.3 全量微调贵在哪里
两笔账(代码 01 第 2、4 部分):
训练时的显存。 用混合精度 + Adam 训练时,每个可训练参数要存:fp16 权重 2 字节 + fp16 梯度 2 字节 + fp32 主权重 4 字节 + Adam 的一阶动量 4 字节 + 二阶动量 4 字节 = 16 字节(ZeRO 论文的记账方式;前置讲 P1 讲过每种浮点数占几个字节)。冻结的参数只需要那 2 字节的 fp16 权重。
| 模型 | 参数 | 全量微调 | LoRA 的可训练参数 | LoRA | 省了 |
|---|---|---|---|---|---|
| Llama-2 7B | 6.7B | 108 GB | W_q、W_v,r = 8:4.2M | 13.5 GB | 8.0× |
| GPT-3 175B | 175B | 2,800 GB | W_q、W_v,r = 4:18.9M | 350.3 GB | 8.0× |
(不含激活值。两个模型都省了约 16/2 = 8 倍:冻结的权重占了大头,LoRA 自己的参数几乎不占地方。LoRA 论文报告 GPT-3 175B 的训练显存从 1.2 TB 降到 350 GB;具体倍数取决于优化器状态怎么存,但量级一致。)
每个任务的存储。 全量微调每做一个任务就要存一份完整的模型:GPT-3 用 fp16 存是 350 GB。如果你要为一百个客户各微调一个版本,就是 35 TB。LoRA(W_q、W_v,r = 4)每个任务只要存 18,874,368 个数,fp16 下 37.7 MB,差 9,272 倍(LoRA 论文报告”350 GB → 35 MB,约 1 万倍”)。
2. PEFT 的目标与四类做法 · Goal and approaches
slides 的定义:参数高效微调(PEFT)的目标是只微调少得多的参数,却在下游任务上达到和全量微调相当的效果。 四类做法:
- 只调一部分参数(subset):比如一个 K + L 层的网络只调最上面 K 层。
- Adapter:在网络里插入几层参数很少的小网络,只训练它们,其余全部固定。
- 前缀微调(prefix tuning):对 Transformer 语言模型,假装你的序列前面还有很多 token,只调这些 token 对应的 key / value。
- LoRA:给每个参数矩阵学一个小的增量,增量被限制成低秩的。
slides 里有一张总图(取自 He et al., 2022):把三种”加东西”的做法画在同一个 Transformer 层里——adapter 串在注意力和前馈子层后面,前缀 拼在注意力的 K、V 前面,LoRA 并联在 旁边。He et al. 的观点是:它们都可以看成”在某个位置给隐藏状态加一个修正量 “,区别只在修正量从哪里算、加在哪里、形式是什么。
2.1 本讲的实验设定
我们没有几十亿参数的模型,但 PEFT 的机制和规模无关。预训练模型用前面讲过的两个:
- 第 2 讲的农场语言 GPT(111,360 个参数,2 层、d = 64),在 2 万句农场语言上预训练过。
- 第 5 讲的 ViT(约 10 万参数),只学过”是哪种形状”(4 类)。
农场 GPT 的两个下游任务(各 1000 句训练数据,peft_lab.make_task):
| 任务 | 例句 | 和预训练比 |
|---|---|---|
| 冬天的农场 | the cows eat hay in the barn . | 语法、用词完全不变,只是动物的习性变了:大多待在谷仓,牛和马主要吃干草。离预训练近 |
| 时间前置的方言 | at night the dogs chase the cats . | 句末的时间短语挪到句首。语序变了,离预训练远 |
两个任务的真实分布都已知(方言是一一对应的变换,真实概率等于原句),所以我们可以算超出真实分布的损失(nats/词)= (模型的 NLL − 真实分布的 NLL) / 词数——0 表示和真实分布一样好。不微调时,预训练模型在冬天任务上是 0.183,在方言上是 5.84(它认为句子几乎不可能以 at / in / every 开头)。
公平起见,所有做法都从同一个预训练模型出发、训练同样的步数,用验证集早停、从两个候选学习率里挑一个(PEFT 的参数少、初始化在 0 附近,通常需要更大的学习率)。
3. 只调一部分参数:只调顶层 · Fine-tuning the top layers only
最简单的基线:冻结除最上面 K 层以外的全部参数。slides 的图:一个 BERT 式分类器,在第 K 层下面画一道”停止梯度”的线,误差不再往下传。
为什么省显存? 梯度只需要传过 K 层:更低的层既不需要存”参数的梯度”(slides 称为 adjoint,即损失对每个参数的导数),也不需要为反向传播保存激活值。代码 05 实测(批大小 32):只调顶层存的激活值是 3,069 KB,全量微调 5,695 KB——下面那一层根本不需要反向传播。
为什么它能行? slides 的问答:难道不会拟合不好吗?答:低层捕捉通用的语言特征,高层捕捉和任务相关的特征。
我们的实验(代码 03,2 层的模型只调第 2 层 + 输出层,占 48% 的参数)给出一个更细的回答:
| 任务 | 不微调 | 只调顶层 | 全量微调 |
|---|---|---|---|
| 冬天的农场 | 0.1832 | 0.0181 | 0.0144 |
| 时间前置的方言 | 5.8352 | 0.0932 | 0.0264 |
冬天任务只改了”习性”,需要的是一种新的读出方式——顶层够用;方言改了语序,最底层的计算也得改——只动上面一层,用了一半的参数,还比只有 9,216 个参数的 LoRA(0.0276)差 3 倍多。slides 的解释(低层通用、高层任务相关)适用于”特征够用、只差读出方式”的任务。
4. Adapter
4.1 结构
slides:adapter 层就是一个只有一个隐藏层的前馈网络,加一个残差连接。输入 d 维,输出也是 d 维,但中间压到一个低维度 r:
Houlsby et al. (2019) 在每个 Transformer 层里插两个:一个在注意力子层之后、一个在前馈子层之后(slides 的图)。实践中 r 取得让 adapter 只占全部参数的 0.5%–8%;训练时只更新 adapter,其余全部固定。slides 说”有意思的是:灰色的模块都固定不动,它照样有效!”
peft_lab.Adapter 把 初始化成 0:插进去的一刻 adapter 是恒等映射,模型的输出逐位不变——微调从预训练模型本身出发(LoRA 的 B = 0 是同一个道理)。
4.2 结果
- slides 引用 Houlsby et al. 的 BERT-Large 实验:adapter 用少得多的参数,几乎达到全量微调的效果(论文摘要:GLUE 上差距在 0.4% 以内,每个任务只多 3.6% 的参数),有时还更好;对照的基线”只调最上面 K 层”需要多得多的参数才能接近。
- 我们的 adapter(r = 8,4,384 个参数,占 3.9%):冬天任务 0.0117,比全量微调(0.0144)还好;方言 0.0778,是全量的 3 倍。瓶颈宽度 r = 2 / 8 / 32(参数占 1.2% / 3.9% / 15.1%)时方言是 0.159 / 0.078 / 0.050(代码
05)。
4.3 代价:推理延迟
adapter 是串在网络里的额外层,推理时永远要多算一次,没法去掉。代码 05 实测一次前向(批大小 32):adapter 是基准的 1.10 倍 ⏱。在大模型、小批量的在线推理里,这种”每层多一个小算子”的延迟更明显(GPU 上每个算子都有启动开销)。这是 LoRA 论文的动机 3。
5. 前缀微调 · Prefix tuning
5.1 思路
slides 的四步:
- 在真实 token 前面注入一些虚拟的前缀 token,下标集合记作 ;
- 第 i 个前缀 token 的激活直接由可训练参数给出:;
- 实际上令 ——让 的维度比 低,训练更稳定;
- 训练时 Transformer 的参数全部固定,只训练 θ。
对 Transformer 来说,“token 在某一层的激活”就是它的 key 和 value:。所以前缀微调 = 在每一层的注意力里,给 K、V 前面拼上 n 个可训练的向量 (slides 的图)。每个真实 token 的查询都能看到它们,就像序列前面多了一段”学出来的上下文”。
这和第 11 讲的”提示”是亲戚:提示是在输入层放几个真实的词;前缀微调是在每一层放连续的向量,不受”必须是词表里的词”的限制。
5.2 实验(代码 05,时间前置的方言)
peft_lab.PrefixAttention 实现了带掩码的拼接:前 n 列(前缀)任何位置都能看,后面照常是因果掩码。三个发现:
(1) 改装后、训练前,输出就变了。 多出来的 n 个 key 会分走一部分注意力(softmax 的分母多了 n 项),所以和 LoRA、adapter 的零初始化不同,前缀一加上模型就不是原来的模型了(表里”训练前”一列:5.79–5.94,原模型 5.835)。Li & Liang 发现随机初始化不稳定,用真实词的激活初始化更好。
(2) 前缀长度:加多了不一定更好。
| 做法 | n = 1 | n = 4 | n = 16 | n = 64 |
|---|---|---|---|---|
| MLP 重参数化(lr 1e-2,两个种子) | 0.239 / 0.278 | 0.148 / 0.155 | 0.137 / 0.186 | 0.192 / 0.134 |
| 直接训练 P(lr 1e-2) | 0.501 | 0.246 | 0.238 | 0.208 |
| 直接训练 P(lr 3e-2) | 0.402 | 0.203 | 0.146 | 0.128 |
重参数化版本两个种子的平均是 0.259 / 0.151 / 0.161 / 0.163:n = 4 以后就不再变好。LoRA 论文的动机 2 说的正是这件事——“直接优化提示(前缀)时,效果随参数增多而不单调”,而我们希望”参数越多、效果越好”。不过在我们这里,n ≥ 4 之后的差别比两个种子之间的差别(平均 0.038)还小,更准确的说法是”饱和了”。
(3) 重参数化的作用。 同样的学习率 1e-2,重参数化在 4 个长度上都比直接训练 P 好;但把直接训练的学习率提到 3e-2,它反而随 n 单调变好,n = 64 时 0.128 是这张表里最好的。所以 MLP 重参数化更像是”换了一种更好调的参数化”(相当于给 P 换了有效学习率和更新方向),而不是凭空多出来的能力。注意它训练时的参数(约 1.9 万)比最后要存的 P(n = 16 时 4,096 个)多得多——MLP 训练完就扔掉。
在主实验(§8)里,前缀微调(n = 16)在两个任务上都排在 LoRA 和 adapter 后面。
6. 内在维度 · Intrinsic dimensionality
6.1 一个奇怪的现象
slides 先回顾 LLM 有多大(GPT-2 15 亿 → GPT-3 1750 亿 → PaLM 5400 亿 → Llama-3 4050 亿),然后问:GPT-3 里一个线性层就是 12k × 12k,约 1.5 亿个参数。用几千个样本去微调它,不加任何正则化,为什么不严重过拟合?
slides 给出的假设:它们的内在维度很低。
6.2 怎么测内在维度
Li et al. (2018) 的想法:参数个数也许并不能很好地衡量”学会这个问题真正需要多少自由度”。测法:
- 冻结初始参数 ,只允许参数在一个随机的 d 维子空间里移动:, 是固定的随机矩阵,只训练 ;
- 从小到大试 d,第一次达到完整训练效果 90% 时的 d 叫作内在维度 。
slides 的例子:MNIST 上的全连接网络有 D = 199,210 个参数, 却只有 750。
Aghajanyan et al. (2020) 把这个测法用到预训练语言模型的微调上,发现:微调任务的内在维度很低(论文摘要:在 MRPC 上,只训练 200 个随机投影回全空间的参数,就能让 RoBERTa 达到全参数微调 90% 的水平);而且预训练越充分,下游任务的内在维度越低,更大的模型内在维度反而更低。
6.3 我们的实验(代码 02)
peft_lab.Subspace 用稀疏随机投影实现 (每列 64 个非零元素、归一化成单位长度),用 torch.func.functional_call 拿 θ 跑前向,梯度自动传回 。
(1) 预训练 vs 随机初始化(ViT,新任务”形状在哪个象限”,1000 张图)。 每一列都另外完整训练新的输出层(260 个参数):
| 起点 | 只训练输出层 | d = 10 | 30 | 100 | 300 | 1000 | 全部参数 |
|---|---|---|---|---|---|---|---|
| 预训练的 ViT | 0.415 | 0.534 | 0.696 | 0.794 | 0.900 | 0.911 | 0.928 |
| 随机初始化 | 0.282 | 0.278 | 0.286 | 0.243 | 0.245 | 0.435 | 0.944 |
预训练的 ViT: = 300,只占主干参数 D = 104,448 的 0.29%。随机初始化:d = 1000 还只有 0.435(全部参数 0.944), > 1000,至少是预训练的 3.3 倍。和 Aghajanyan et al. 的结论一致:预训练把模型放到了一个”只需要很少自由度就能适应新任务”的位置。(注意随机初始化用全部参数训练反而略好——这个任务从零学也不难;内在维度说的是”需要多少自由度”,不是”能达到多好”。)
(2) 农场 GPT 的两个任务(测试超额损失):
| 做法 | 可训练参数 | 冬天的农场 | 时间前置的方言 |
|---|---|---|---|
| 不微调 | 0 | 0.1832 | 5.8352 |
| 随机子空间 d = 10 | 10 | 0.1649 | 2.1452 |
| 随机子空间 d = 100 | 100 | 0.0428 | 0.4780 |
| 随机子空间 d = 1000 | 1,000 | 0.0151 | 0.0331 |
| 随机子空间 d = 3000 | 3,000 | 0.0221 | 0.0316 |
| LoRA r = 1(W_q、W_v) | 512 | 0.0263 | 0.1800 |
| 全量微调 | 111,360 | 0.0144 | 0.0264 |
d = 1000 只占 D 的 0.9%,两个任务的损失分别是全量微调的 1.05 倍和 1.25 倍。而且内在维度是任务相对于预训练模型的属性:冬天任务 d = 100 时已经降到 0.043,方言要 d = 1000 才追上来。
这就是 LoRA 的动机 1。一个有意思的对照:参数一样少的 LoRA r = 1(512 个)在方言上不如 d = 1000 的随机子空间。随机方向会同时动到很多层的很多参数,LoRA 的增量却只能落在”每个矩阵的一个秩 1 方向”上。那为什么实践中用 LoRA 而不用随机子空间?随机子空间训练时,每一步都要把 投影成完整的 D 维更新,反向传播也要先得到全部 D 个参数的梯度再投影回去——显存里仍要放一份完整大小的梯度,而这正是 PEFT 想省的东西。LoRA 的梯度只有 个数。
7. LoRA · Low-Rank Adaptation
7.1 三个动机
slides 列出 LoRA 论文的三个动机:
- 内在维度:“我们受 Li et al. (2018a)、Aghajanyan et al. (2020) 启发,他们表明学到的过参数化模型其实位于一个低内在维度上。“LoRA 进一步假设:微调时权重的变化量 ΔW 也有低的”内在秩”。
- 前缀微调的问题:直接优化提示,效果随参数增多不单调变化(§5.2)——我们希望参数越多效果越好。
- adapter 的问题:会带来不可忽略的推理延迟(§4.3)。
7.2 结构
普通线性层 ,,,。LoRA 线性层:
- 冻结 ,只学增量 。 的秩最多是 r。
- 参数量从 降到 。代码
01:64×64 的层,r = 1 / 4 / 16 时是 128 / 512 / 2,048 个参数(3.1% / 12.5% / 50%),算出来的 恰好是 1 / 4 / 16——低秩是结构上保证的。GPT-3 的一个 (150,994,944 个参数)加 r = 4 的 LoRA 只要 98,304 个(0.065%)。 - 前向时不用真的算出 的 :先算 (r 维)再乘 B,计算量 。
缩放 α/r。 LoRA 论文还把增量乘上 :,α 是一个不随 r 变的常数。论文的理由:用 Adam 时调 α 大致等于调学习率,乘上 以后换 r 时不用重新调学习率,于是”把 α 设成第一次试的 r,之后就不调了”。代码 04 第 4 部分试了一下(方言任务,三个学习率):不缩放时 r = 1 和 r = 16 最好的学习率是 3e-2 和 1e-2;乘 α/r(α = 4)后是 1e-2 和 3e-2——在我们的设定里,这个缩放并没有让最好的学习率不随 r 变(只试了三个学习率,结论比较粗)。后来有工作(rsLoRA, Kalajdzievski 2023)认为应该乘 。实践中记住一点:改 r 时,留意 α 和学习率是不是也要跟着改。
7.3 初始化:为什么 B = 0
slides:,。这保证微调开始时参数就是预训练的值:,。
(为什么不是 A = 0?如果 A 和 B 都是 0,梯度 、,永远学不动。必须有一个非零,另一个为零。)
代码 01、04:B = 0 时改装后的输出和原层逐位相同(差 0);A、B 都随机时,一个层的输出一开始就偏了 163%。在整个模型上(LoRA r = 4,全部线性层),冬天任务训练前的验证损失从 1.075 变成 3.775——模型先被”弄坏”了;最终测试超额损失冬天 0.0157 vs 0.0265、方言 0.0276 vs 0.0486,都更差。
7.4 热切换:合并与拆回
和 形状相同,所以可以把 LoRA 参数”换进换出”(slides 称为 hot swapping):
- 得到一个包含 LoRA 微调的普通线性层:;
- 从这个层里去掉 LoRA:;
- 在任务 1 上训练了 、任务 2 上训练了 ,就可以在两者之间来回切换。
代码 01 第 3 部分在农场 GPT 上做了一遍(LoRA r = 4,加在全部 12 个线性层上,可训练参数 9,216):
| 当前的权重 | 冬天的农场 | 时间前置的方言 | 原来的语言 |
|---|---|---|---|
| (预训练模型) | 0.1832 | 5.8352 | 0.0166 |
| (合并冬天的 LoRA) | 0.0157 | 5.2327 | 0.1330 |
| (换成方言的 LoRA) | 2.1915 | 0.0276 | 2.1397 |
| 再拆掉,回到 | 0.1832 | 5.8352 | 0.0166 |
合并前()和合并后()的 logits 最大差 4.5e-6,拆回后的权重和 差 3.0e-8——都是 float32 加减的舍入。每个任务的 LoRA 存成 float32 只有 36 KB(整个模型 435 KB)。合并以后就是一个普通的线性层,推理没有任何额外计算——这是 LoRA 相对 adapter 的关键优势。
7.5 偏置
slides 的细节:LoRA 本身不碰偏置。偏置只有 d 个数,“已经是秩 1 了”,没有可以再压缩的;大多数 LoRA 实现提供”顺便也微调偏置”的选项,不需要什么特别的机制。
7.6 加在哪里
LoRA 线性层可以替换 Transformer 层里的每一个线性层,但原论文只把它加在注意力的权重上;对 GPT-3,他们发现只加在 和 上最划算(slides)。训练时只微调新加的 LoRA 参数,其余全部固定。
代码 04 第 2 部分在方言任务上复现了 LoRA 论文表 5 的实验:固定参数预算(每层 1024 个),分配到不同的矩阵上:
| 加在哪里 | r | 测试超额损失 |
|---|---|---|
| 8 | 1.3028 | |
| 8 | 1.2786 | |
| 8 | 0.0855 | |
| 8 | 0.0852 | |
| 4 | 1.2690 | |
| 4 | 0.0510 | |
| 2 | 0.0319 | |
| 前馈层 up、down | 2 | 0.1160(参数多 25%) |
两个规律和 LoRA 论文在 GPT-3 上的结论一致:(1) 分散到多个矩阵、每个用小的 r,比集中在一个矩阵上用大的 r 好;(2) 单独加在 或 上远好于单独加在 或 上。在方言任务上差别极端:只改”看哪里”()几乎学不会,改”取回什么、怎样写回残差流”()就行——语序变了,模型不需要换一种注意力模式,而是要在同样的位置上输出不同的东西。(§10 会看到 ViT 的象限任务上情况又不同:先想一想为什么。)
7.7 秩要多大
slides 总结 LoRA 在 GPT-3 上的结果:几乎和全量微调一样好、参数少得多;有些任务还超过全量微调;有些数据集 r = 1 就够了;数据集大或小都表现不错。
代码 04 第 1 部分:
| 任务 | 加在哪里 | r = 1 | r = 4 | r = 16 |
|---|---|---|---|---|
| 冬天的农场 | 0.0259 | 0.0195 | 0.0223 | |
| 时间前置的方言 | 0.1498 | 0.0510 | 0.0428 | |
| 时间前置的方言 | 全部 6 种 | 0.0597 | 0.0276 | 0.0265 |
r 从 1 加到 16,冬天任务只改善了 1.2 倍(r = 4 以后不再变好),方言改善了 3.5 倍。“r = 1 就够”只对离预训练很近的任务成立——和 §6 的内在维度是同一个结论。
7.8 全量微调的 ΔW 真的是低秩的吗
LoRA 的假设是”微调时的权重变化 ΔW 有低的内在秩”。我们可以直接检验:拿全量微调前后的权重相减,做奇异值分解(代码 04 第 5 部分,12 个块内矩阵的平均):
| 任务 | ΔW 前 1 个方向的能量 | 前 4 个 | 同形状随机矩阵:前 1 个 | 前 4 个 |
|---|---|---|---|---|
| 冬天的农场 | 31.9% | 71.7% | 5.0% | 18.2% |
| 时间前置的方言 | 29.6% | 67.4% | 5.0% | 18.2% |
再把每个 ΔW 截断到前 k 个奇异方向、加回 (嵌入、LayerNorm、输出层保持全量微调的值):
| 任务 | k = 0 | k = 1 | k = 4 | k = 16 | 完整的 ΔW |
|---|---|---|---|---|---|
| 冬天的农场 | 0.1253 | 0.0304 | 0.0136 | 0.0142 | 0.0144 |
| 时间前置的方言 | 3.3276 | 1.3140 | 0.0439 | 0.0260 | 0.0264 |
全量微调的更新确实集中在少数几个方向上:只保留每个矩阵的前 4 个方向,冬天任务甚至比完整的 ΔW 还好一点点,方言也保住了绝大部分效果。这就是 LoRA 能行的根本原因。
8. 主实验:七种做法 · The main comparison
8.1 两个任务、1000 句数据
代码 03(每种做法、每个任务从两个学习率里按验证集挑;1500 步,早停):
| 做法 | 可训练参数 | 占比 | 冬天的农场 | 时间前置的方言 |
|---|---|---|---|---|
| 不微调 | 0 | 0% | 0.1832 | 5.8352 |
| 全量微调 | 111,360 | 100% | 0.0144 | 0.0264 |
| 只调顶层(第 2 块 + 输出层) | 53,696 | 48.2% | 0.0181 | 0.0932 |
| Adapter r = 8 | 4,384 | 3.9% | 0.0117 | 0.0778 |
| 前缀微调 n = 16 | 19,328(存 4,096) | 3.7% | 0.0195 | 0.1372 |
| LoRA r = 1() | 512 | 0.46% | 0.0259 | 0.1498 |
| LoRA r = 4() | 2,048 | 1.8% | 0.0195 | 0.0510 |
| LoRA r = 4(全部 6 种线性层) | 9,216 | 8.3% | 0.0147 | 0.0276 |
- 离预训练近的任务(冬天):五种 PEFT 全都在全量微调的 2 倍以内,adapter 甚至更好;LoRA r = 1 只用 512 个参数(0.46%)就从 0.183 降到 0.026。
- 离预训练远的任务(方言):只有”LoRA 加在全部线性层上”追上了全量微调;只加在 上差 2 倍,r = 1 差 6 倍,前缀微调差 5 倍。任务离预训练越远,需要的容量越多,PEFT 之间的差距也越大。
8.2 数据的多少
| 任务 | 训练句数 | 全量微调 | Adapter r = 8 | LoRA r = 1 | LoRA r = 4(全部) |
|---|---|---|---|---|---|
| 冬天的农场 | 100 | 0.0310 | 0.0389 | 0.0454 | 0.0330 |
| 冬天的农场 | 1,000 | 0.0144 | 0.0117 | 0.0259 | 0.0147 |
| 冬天的农场 | 10,000 | 0.0058 | 0.0077 | 0.0222 | 0.0080 |
| 时间前置的方言 | 100 | 0.1151 | 0.2298 | 0.2803 | 0.1612 |
| 时间前置的方言 | 1,000 | 0.0264 | 0.0778 | 0.1498 | 0.0276 |
| 时间前置的方言 | 10,000 | 0.0088 | 0.0669 | 0.1822 | 0.0162 |
我们原以为数据很少(100 句)时 PEFT 会因为”不容易过拟合”而胜出——并没有:有早停保护,全量微调在 100 句时仍然最好。数据多(10,000 句)时,全量微调多出来的容量更有用;LoRA r = 1 几乎不再随数据变好(0.022 / 0.18),它的容量已经到顶了。PEFT 的主要价值是省显存、省存储、可热切换,而不是”效果一定更好”。
8.3 遗忘
微调完方言以后,模型对原来的语言还剩多少本事?直接量会被一个”必然的冲突”干扰:方言里句子从不以 the 开头,一个完美的方言模型必然认为原句”开头就错了”。所以我们去掉第一个词的预测,只看”<s> the 之后怎么接”——这一部分方言数据完全没有约束,模型还记得多少才是真正的遗忘(peft_lab.excess_nll(..., skip_first=True))。
| 做法 | 方言(越小越好) | 遗忘:原来的语言,去掉首词(越小越好) |
|---|---|---|
| 不微调 | 5.8352 | 0.0181 |
| 全量微调 | 0.0264 | 1.206 |
| 只调顶层 | 0.0932 | 1.374 |
| Adapter r = 8 | 0.0778 | 2.380 |
| 前缀微调 n = 16 | 0.1372 | 1.765 |
| LoRA r = 1() | 0.1498 | 1.786 |
| LoRA r = 4() | 0.0510 | 2.391 |
| LoRA r = 4(全部) | 0.0276 | 1.612 |
文献里有一个流行的说法:“LoRA 学得少、也忘得少”(Biderman et al., 2024,在代码和数学数据上微调 Llama-2 后测量其他能力)。我们的结果正好相反:全量微调忘得最少(1.206),LoRA 只加在 上忘得最多(2.391)。
遗忘出在哪里?我的第一个猜测是”容量小的做法把方言的’宾语后面就是句号’学成了一条全局规则”。03 的第一版量了”原来的句子里、宾语刚说完时模型给句号的概率”:全量微调 0.79、LoRA(全部)0.997、adapter 0.68……它和遗忘的相关系数只有 0.19——猜错了(这个诊断已经从脚本里换掉,见 README)。于是把原来的句子按位置分类,看每一类位置上的 NLL 比微调前多了多少(nats/句):
| 做法 | 主语 | 句子中间 | 宾语之后 | 时间短语其余 | 句末的 ”.” | 合计 |
|---|---|---|---|---|---|---|
| 全量微调 | −0.03 | +0.06 | +4.62 | +0.13 | +7.75 | 12.53 |
| 只调顶层 | +0.01 | +3.63 | +1.16 | +2.32 | +7.17 | 14.30 |
| Adapter r = 8 | +0.02 | +5.84 | +3.54 | +4.60 | +10.92 | 24.92 |
| 前缀微调 n = 16 | +0.41 | +1.52 | +6.14 | +5.44 | +4.93 | 18.43 |
| LoRA r = 1() | +4.54 | +0.76 | +1.34 | +1.82 | +10.14 | 18.60 |
| LoRA r = 4() | +7.40 | +0.58 | +5.12 | +2.78 | +9.02 | 24.91 |
| LoRA r = 4(全部) | +0.19 | +1.59 | +8.97 | +1.00 | +5.06 | 16.82 |
- 后三类”交界处”平均占了增量的 81%(最少的也有 68%)。方言里宾语后面是句号、时间短语后面是 the;原来的语言正好相反。两种场合只要看”句子是不是以时间短语开头”就能区分,但各种做法都把方言的这几条局部规则推广到了所有场合。增量最大的通常是句末的 ”.”:原句的时间短语说完以后,微调过的模型期待的是 the。
- 每种做法”溢出”的地方不一样。LoRA r = 4()在主语上多了 7.40 nats:输入”
<s> the”后,微调前模型预测的是名词(foxes 0.10、hens 0.09、hen 0.08),微调后是 chase 0.39、behind 0.13、beside 0.11——这些是原来的语言里主语名词后面才会出现的词,好像把 the 本身当成了主语。我们没有进一步追究这种位置混淆的机制。
教训:“遗忘”取决于新旧数据怎样重叠,以及每种做法在”新数据没覆盖到的地方”有什么归纳偏好;“参数改得少就忘得少”不是定律。
9. 显存与计算时间 · Memory and compute
9.1 LoRA 为什么省显存
slides 的问答:训练时我们仍然需要全部预训练参数,LoRA 为什么还省显存?答:
- 内存里的参数个数其实比原来多了(多了 A、B);
- 但需要的梯度少得多;
- 所以需要的优化器状态也少得多(slides 引用 ZeRO 论文的图:Adam 的状态是权重的好几倍)。
代码 05 第 3 部分逐项量(批大小 32,float32):
| 做法 | 可训练参数 | 梯度 + Adam 状态 | 保存的激活值 | 每步 ⏱ |
|---|---|---|---|---|
| 全量微调 | 111,360 | 1,305 KB | 5,695 KB | 4.30 ms |
| 只调顶层 | 53,696 | 629 KB | 3,069 KB | 2.79 ms |
| Adapter r = 8 | 4,384 | 51 KB | 3,377 KB | 3.32 ms |
| 前缀微调 n = 16 | 19,328 | 226 KB | 3,798 KB | 3.80 ms |
| LoRA r = 4() | 2,048 | 24 KB | 3,444 KB | 3.61 ms |
| LoRA r = 4(全部) | 9,216 | 108 KB | 5,496 KB | 4.65 ms |
“保存的激活值”用 torch.autograd.graph.saved_tensors_hooks 量:前向时凡是反向需要的张量都会被存起来,我们把它们的字节数加起来(模型参数本身不算)。两个值得注意的地方:
- LoRA 不一定减少激活值。 加在全部线性层上时(5,496 KB)几乎和全量微调一样:每个带 LoRA 的线性层都要存输入 x 来算 A 的梯度,而且梯度要一路传到最底层的 LoRA。只加在 上时少了 40%: 对 x 求导只需要 W,对 W 求导才需要 x——冻结的线性层不必存输入。
- 激活值和批大小、序列长度成正比;在大模型上它常常比权重还大,这时要靠激活检查点(activation checkpointing,第 17 讲)——反向时重新算一遍前向,用时间换显存。
9.2 PEFT 为什么(不一定)更快
slides:“人们用 PEFT 的一个主要原因是训练更快,但为什么会更快,其实很微妙。“计算时间有三种变化:
- 额外的计算让它变慢(LoRA、adapter、前缀微调都有;只调顶层没有);
- 需要算的梯度少了,让它变快(四种都有);
- 显存省下来,可以用更大的批,让它变快(四种都有)。
哪个占上风”并不显然,甚至可能取决于 PEFT 方法的超参数(比如批大小)“。slides 引用 Anyscale 在 Llama-2-7B 上的实测:LoRA 的吞吐量随批大小的变化并不简单。LoRA 论文报告在 GPT-3 175B 上训练快了约 25%,因为不用为绝大多数参数算梯度。
我们的小模型上(代码 05,⏱ 每次运行略有不同):LoRA(全部)每步 4.65 ms,比全量微调(4.30 ms)还慢——在这么小的模型上,多出来的那些小矩阵乘法比省下的权重梯度更费时;只调顶层最快(2.79 ms),因为下面一层连反向传播都不用做。
9.3 推理时
代码 05 第 4 部分(一次前向,批大小 32,⏱):
| 模型 | 相对基准 |
|---|---|
| 预训练模型 | 1.00× |
| LoRA(未合并) | 1.25× |
| LoRA(合并后) | 1.02× |
| Adapter r = 8 | 1.10× |
| 前缀微调 n = 16 | 1.14× |
合并后的 LoRA 就是原来的网络(输出差 2e-5)。前缀微调的推理也要付代价:每层的注意力多了 n 个 key。
10. ViT 上的 PEFT · PEFT for Vision Transformers
slides:“ViT 就是另一个 Transformer 模型,所以可以直接给它加 LoRA。“slides 引用的一篇论文里的 LNLoRA 是把 LayerNorm 也放进 LoRA 模块的变体。在 VTAB-1k(19 个视觉任务,每个只有 1000 张训练图)上,参数高效的迁移学习(PETL)有时比全量微调还好;FacT(Jie & Deng, 2023)是另一种低秩方法,能把调的参数压到极少。
代码 06:第 5 讲预训练的 ViT(只学过 4 种形状)→ 两个新任务,各 1000 张训练图(“VTAB-1k 的缩小版”);学习率按验证集挑,测试集只用来报告:
| 做法 | 可训练参数 | 形状 × 大小(8 类) | 象限(4 类) |
|---|---|---|---|
| 只训练输出层(线性探针) | 520 | 0.803 | 0.466 |
| 全量微调 | 104,968 | 0.850 | 0.941 |
| 只调顶层(第 2 块) | 50,504 | 0.872 | 0.725 |
| LoRA r = 4() | 2,568 | 0.867 | 0.887 |
| LoRA r = 4(全部线性层) | 9,736 | 0.883 | 0.892 |
| Adapter r = 8 | 4,904 | 0.858 | 0.655 |
| 前缀微调 n = 8 | 19,592 | 0.812 | 0.540 |
- 形状 × 大小:预训练的形状特征直接有用,只差把”大小”也读出来。线性探针已经 0.803,四种 PEFT(只调顶层、两种 LoRA、adapter)都比全量微调好——和 slides 里 VTAB-1k 的现象一样:数据少时,少改参数不容易过拟合。
- 象限:预训练时位置是无关信息,[CLS] 里几乎没有,线性探针只有 0.466。LoRA 追到 0.89,adapter 只有 0.655,前缀微调 0.540。
为什么象限任务上 adapter 和前缀微调差这么多?我原本的解释是:要读出位置,[CLS] 得改变”看哪些块”,也就是注意力的模式,而 LoRA 直接改 。代码 06 的补充实验不支持这个解释:
| LoRA(r = 4)只加在 | 可训练参数 | 象限 |
|---|---|---|
| (决定”看哪里”) | 2,308 | 0.887 |
| (决定”交出什么”) | 2,308 | 0.892 |
| 前馈层 up、down(逐 token) | 5,380 | 0.813 |
改变每个块”交出什么”同样能把位置信息带进 [CLS];就连只改前馈层(和 adapter 一样是逐 token 的变换)也有 0.813。那 adapter(4,904 个参数)为什么只有 0.655?我们没有找到确定的原因——可能和它串在子层输出之后的位置、以及零初始化的上投影有关,留作练习 6 的延伸。另一个对照:农场方言上 远比 重要(§7.6),这里两者差不多——“加在哪里最好”取决于任务。
两个任务对预训练模型来说”难度”完全不同:一个只差读出方式,几乎什么方法都行;另一个要改变内部的计算,方法之间就拉开了差距——和农场语言上”冬天的农场 vs 时间前置的方言”是同一个规律。
11. 小结 · Summary
- 全量微调贵在两处:训练时每个可训练参数约 16 字节(梯度 + 优化器状态),每个任务存一份完整模型。PEFT 只训练、只存很少的参数。
- 四类 PEFT:只调顶层(省激活值,但只能改读出方式)、adapter(串联的瓶颈小网络,有推理延迟)、前缀微调(每层可训练的 K、V;训练前就改变了输出,效果随长度饱和)、LoRA(并联的低秩增量 ,B = 0 初始化,可合并、可热切换,推理零开销)。
- 内在维度:在随机的 d 维子空间里训练,d 很小(我们的例子里不到参数的 1%)就能接近全量微调;预训练让下游任务的内在维度变小。全量微调的 ΔW 本身就集中在少数几个奇异方向上——这是 LoRA 的依据。
- LoRA 的设计选择:预算固定时分散到多个矩阵比集中好; 往往比 重要(但取决于任务);r 要多大取决于任务离预训练多远。
- PEFT 和全量微调的差距取决于任务:离预训练近时几乎没有差距,远时需要更大的容量。PEFT 不一定更快、不一定忘得更少;它确定的好处是省显存、省存储、可热切换。
12. 常见误区 · Common pitfalls
- “LoRA 省显存是因为参数少了。” 内存里的参数其实更多了;省的是梯度和优化器状态。激活值不一定少(加在全部线性层上时几乎不少)。
- “r = 1 就够了。” 只对离预训练很近的任务成立(冬天的农场);语序变了的方言,r = 1 比全量微调差 6 倍。
- “LoRA 的 A、B 都初始化成 0 最安全。” 那样梯度永远是 0,学不动。必须一个随机、一个为 0。
- “adapter 和 LoRA 只是位置不同。” adapter 串在网络里,推理时永远多一层;LoRA 并联、可合并,合并后推理没有任何额外开销。
- “前缀越长越好。” 我们的实验里 n = 4 以后就饱和了;LoRA 论文把”效果随参数增多不单调”列为前缀微调的缺点。
- “PEFT 训练一定更快。” 小模型上,多出来的小矩阵乘法可能比省下的梯度更费时;大模型上的加速很多来自省下显存后能用更大的批。
- “PEFT 一定忘得更少。” 遗忘取决于新旧数据怎样重叠、方法在”新数据没覆盖的地方”有什么归纳偏好;我们的方言实验里全量微调忘得最少。
- “只调顶层是最便宜的 PEFT。” 它省激活值、不加推理开销,但可训练参数往往最多,而且改不了底层的计算。
13. 练习 · Exercises
练习 1(LoRA 的参数量)。 Llama-2 7B 有 32 层、。只给 加 r = 8 的 LoRA,有多少可训练参数?占 67.4 亿的百分之几?如果改成给每层全部 4 个注意力矩阵加 r = 4 呢?
参考答案
每个矩阵 ,每层 2 个、32 层:(01 输出的 4.2M),约 0.062%。全部 4 个、r = 4:——参数一样多(这正是 §7.6 那张”固定预算”表的设计)。
练习 2(显存账)。 用混合精度 + Adam(每个可训练参数 16 字节、冻结参数 2 字节)估算:全量微调一个 13B 模型需要多少显存(不含激活值)?LoRA 可训练参数 1000 万时呢?一块 80 GB 的 GPU 放得下哪个?
参考答案
全量: GB,放不下(第 17 讲用 ZeRO 把它切到多块卡上)。LoRA: GB,放得下,剩下的显存给激活值。
练习 3(初始化)。 证明:如果 LoRA 的 A、B 都初始化成 0,则对任意损失 L, 和 在初始时都是 0。再说明为什么 B = 0、A 随机时 B 能学动。
参考答案
。记 。,。A = B = 0 时两者都是 0,梯度下降永远停在原地。A 随机、B = 0 时 ,B 第一步就能动;B 动了之后 A 的梯度也不再是 0。
练习 4(合并)。 一个 LoRA 线性层有偏置 b:。写出合并后的普通线性层的权重和偏置。为什么合并后推理不比原模型慢?合并以后还能继续训练 A、B 吗?
参考答案
,偏置不变仍为 b。合并后就是一个形状和原来一样的 矩阵乘法,算子个数、计算量都和原模型相同。合并后 A、B 不再参与前向,梯度传不到它们;要继续训练得先拆回(peft_lab.LoRALinear 在合并状态下前向不加增量)。
练习 5(内在维度)。 一个模型 D = 10⁶,在随机 1000 维子空间里训练,前向时要做什么额外的计算?反向时为什么”显存里仍要放一份完整大小的梯度”?和 LoRA 比,哪种方法每个任务要存的东西更少?
参考答案
前向:,要把 1000 维投影到 维(稀疏或 Fastfood 投影)。反向:链式法则先得到 ( 维),再算 得到 1000 维的梯度——中间那个 维的梯度必须存在。存储上随机子空间更省:只要存 (1000 个数)和生成 P 的随机种子;LoRA 要存 A、B。实践中选 LoRA 是因为训练时更省显存、更快,且不依赖”重新生成同一个随机矩阵”。
练习 6(加在哪里)。 代码 04 里,方言任务上只改 几乎学不会,只改 或 就可以。用”注意力 = 看哪里 + 取回什么”解释这个结果。你能设计一个反过来的任务(只改 就行、只改 不行)吗?
参考答案
方言里每个词出现在新的位置,但”预测下一个词需要的信息”(主语是谁、动词是什么)仍然可以用原来的注意力模式找到;需要改的是拿到这些信息之后输出什么(宾语后接句号而不是时间短语、句首接时间短语),这由 和后面的层决定。反过来的任务需要”看别的地方”:例如让模型预测前面第二个名词而不是最近的名词——信息还是那些词的身份(V 不用变),但要改变注意力的指向(Q、K 要变)。§10 的象限任务也要求 [CLS] 改变”看哪些块”。
练习 7(遗忘的度量)。 为什么量方言模型的”遗忘”时要去掉第一个词的预测?如果不去掉,一个完美的方言模型在原来的语言上的超额损失是多少?
参考答案
完美的方言模型给”句子以 the 开头”的概率是 0,原句第一个词的损失是 ——这是两种语言本来就冲突的地方,任何模型都不可能同时满足,不应该算作”忘了”。去掉之后,“<s> the 之后怎么接”这部分方言数据完全没有约束,才是真正的遗忘。
练习 8(PEFT 的选择)。 你要为 200 个客户各定制一个 70B 模型的版本,每个客户有约 5000 条数据,推理时要求延迟尽量低、同一台服务器同时服务多个客户。在全量微调、adapter、前缀微调、LoRA 中你选哪个?为什么?
参考答案
LoRA:每个客户只存几十 MB;推理时可以合并(单客户部署零开销),或者在同一个 上按请求挂不同的 (多客户同时服务,增量的计算很小,实践中有专门的批量化实现)。全量微调要存 200 份 70B 模型;adapter 有推理延迟;前缀微调的效果对长度敏感、还占用上下文。如果某些客户的任务离预训练很远,可以给他们更大的 r 或加在更多矩阵上。
14. 延伸阅读 · Further reading
- Adapter 与前缀:Houlsby et al. (2019);Li & Liang (2021), Prefix-Tuning;Lester, Al-Rfou & Constant (2021), The Power of Scale for Parameter-Efficient Prompt Tuning(只在输入层加可训练向量);Ben Zaken, Ravfogel & Goldberg (2022), BitFit(只调偏置)。
- LoRA 及其变体:Hu et al. (2021), LoRA;Dettmers et al. (2023), QLoRA(把冻结的权重量化到 4 比特再加 LoRA,论文报告单张 48 GB 的 GPU 就能微调 65B 模型);Kalajdzievski (2023), rsLoRA;Liu et al. (2024), DoRA。
- 统一视角与比较:He et al. (2022), Towards a Unified View of Parameter-Efficient Transfer Learning;Biderman et al. (2024), LoRA Learns Less and Forgets Less。
- 内在维度:Li et al. (2018);Aghajanyan, Zettlemoyer & Gupta (2020)。
- 微调 vs 上下文学习:Mosbach et al. (2023), Few-shot Fine-tuning vs. In-context Learning: A Fair Comparison and Evaluation。
- 视觉 PEFT:Jia et al. (2022), Visual Prompt Tuning;Jie & Deng (2023), FacT;Zhai et al. (2019), VTAB。
- 显存:Rajbhandari et al. (2020), ZeRO(混合精度 Adam 每个参数 16 字节的记账方式)。
15. 下一讲预告 · Next lecture
本讲一直站在 §1.1 表格的左边:更新参数。第 11 讲走到右边:不改任何参数,只把例子写进提示——上下文学习。它为什么会出现?为什么第 9 讲农场语言的小 GPT 怎么也学不会?然后从”会续写”走到”会听指令”:指令微调(HW3 要用 LoRA 做的那件事)和 RLHF 的第一步——从人类偏好里学一个奖励模型。