前两篇分别写了 Program-as-Weights 和 Parametric Skills。
读到这里会自然出现一个更底层的问题:
既然它们都把函数 / 技能内化成 LoRA,
那 LoRA 到底是什么?
它又是怎么和大模型内部协作的?
这篇专门回答这个问题。
先给结论:
LoRA 不是一个外挂 prompt,也不是另一个小模型。它是在 Transformer 内部的若干线性层旁边,加上一条低秩增量支路。大模型 forward 时,原始权重和 LoRA 增量一起参与计算,所以模型行为会被这个 adapter 持续调制。
如果用软件类比:
大模型原始权重 = 操作系统 / 通用运行时
LoRA adapter = 可插拔插件 / 技能补丁
一次 forward = 原始能力 + 插件增量共同执行
PAW 把“函数”编译成这种插件;Parametric Skills 把“技能”编译成这种插件。
1. 先从一个线性层说起
大模型内部有大量矩阵乘法。最简单的线性层可以写成:
y = xW
这里:
x = 当前 token 的 hidden state
W = 模型训练好的权重矩阵
y = 输出 hidden state
假设 hidden size 是 4096,那么一个方阵线性层可能是:
W: 4096 × 4096
这一个矩阵就有:
4096 × 4096 = 16,777,216 个参数
Transformer 里这种矩阵很多。attention 里有 Q/K/V/O projection,MLP 里有 up/gate/down projection。模型的“知识”和“行为倾向”,很大一部分都编码在这些矩阵里。
如果要让模型更擅长某个任务,最直接的方法是 full fine-tuning:
把 W 本身改掉
但这很重:
需要训练所有参数
需要保存一份完整新模型
容易破坏原模型能力
部署时切换成本高
LoRA 的想法是:不要直接改 W,只给 W 加一个小增量。
2. LoRA 的核心公式
LoRA 把原来的线性层:
y = xW
改成:
y = xW + xΔW
其中:
W = 原始模型权重,冻结不动
ΔW = 任务/技能带来的权重增量
关键是,LoRA 不直接保存完整的 ΔW,而是把它拆成两个小矩阵:
ΔW = A B
所以实际计算是:
y = xW + xAB
如果 W 是 4096 × 4096,完整 ΔW 也会是 4096 × 4096,还是 1677 万参数。
但如果 rank 取 16:
A: 4096 × 16
B: 16 × 4096
参数量变成:
4096 × 16 + 16 × 4096 = 131,072
约等于原矩阵的 1/128。
这就是 Low-Rank Adaptation 的意思:用一个低秩矩阵近似表达“该如何调整原模型”。
3. 为什么低秩增量够用?
直觉上,很多任务不需要重塑整个模型,只需要把模型行为往某个方向推一点。
比如:
更倾向输出 JSON
更关注 error log 里的根因
更稳定地遵守某种代码迁移步骤
更偏向把“紧急邮件”识别出来
更懂某个公司项目里的命名习惯
这些变化不是“从零学习语言”,而是在已有能力上加一层偏置。原始大模型已经会读文本、写代码、分类、推理。LoRA 只需要告诉它:
在这个任务里,哪些注意力模式更重要
哪些中间表示应该被放大
哪些输出分布应该被压低或抬高
这类行为调整往往可以用低维方向表达。
可以类比成调音台:
原模型 = 一首已经混好的歌
LoRA = 对若干频段做小幅调节
不是重新录一首歌,而是在原有基础上调出某种风格。
4. LoRA 插在 Transformer 的哪里?
一个 decoder-only Transformer block 大致长这样:
input hidden state
→ self-attention
→ q_proj
→ k_proj
→ v_proj
→ o_proj
→ MLP
→ gate_proj
→ up_proj
→ down_proj
→ output hidden state
这些 *_proj 基本都是线性层,也就是矩阵乘法。
LoRA 通常会插在这些 projection 上:
q = xWq + xAqBq
k = xWk + xAkBk
v = xWv + xAvBv
o = xWo + xAoBo
up = xWup + xAupBup
gate = xWgate + xAgateBgate
down = xWdown + xAdownBdown
不同系统会选择不同插入点。有的只插 Q/V,有的插 attention 全部 projection,有的连 MLP 也插。
这意味着 LoRA 会影响两类东西:
4.1 影响 attention:模型看哪里
插在 Q/K/V/O projection 上,会影响 attention 的行为。
attention 的核心是:
query 和 key 算相似度
根据相似度加权 value
再通过 output projection 回到 hidden state
LoRA 如果改了 Q/K/V/O 的变换,就会改变模型:
更关注哪些 token
哪些 token 之间更容易建立联系
哪些上下文信息会被带到下一层
这对 skill 很关键。比如一个 debugging skill 可能会让模型更关注:
错误日志里的 stack trace
测试失败的 assertion
最近修改过的函数
配置文件里的版本号
4.2 影响 MLP:模型如何加工中间特征
MLP 层更像每个 token 上的特征变换器。它把 hidden state 投到更高维空间,经过非线性,再压回来。
插在 MLP 上,会影响模型:
如何抽取局部模式
如何强化某类概念
如何压制某些错误倾向
如何把中间表示映射到输出倾向
对于格式修复、分类、代码迁移、风格控制等任务,MLP LoRA 往往也很有用。
5. LoRA 和 prompt 的本质差异
Prompt 是运行时输入。
你把说明书放在上下文里,
模型要读它、理解它、记住它、执行它。
LoRA 是模型计算图的一部分。
你把说明书对应的行为增量装进模型层里,
模型每一层 forward 都受到它影响。
可以对比一下:
| 维度 | Prompt / SKILL.md | LoRA adapter |
|---|---|---|
| 存在位置 | 上下文窗口 | 模型层内部 |
| 工作方式 | 模型阅读文本后遵守 | 每层矩阵乘法加入增量 |
| 成本 | 每次调用都占 token | 加载后不占上下文 |
| 可读性 | 人类可直接审查 | 需要测试和工具解释 |
| 适合场景 | 一次性、低频、需人工可控 | 高频、可复用、行为稳定化 |
| 失败方式 | 读到了但没照做 | adapter 偏置错误或过强 |
这也是为什么 Parametric Skills 说文本 skill 的问题不是“没有知识”,而是“模型在长上下文里未必能稳定利用知识”。
LoRA 不让模型临时读说明书,而是把说明书的一部分变成行为倾向。
6. 加载 LoRA 时模型内部发生了什么?
从工程实现看,加载 LoRA 大概有两种方式。
6.1 运行时旁路计算
不改原始权重 W,每次 forward 额外算:
base = xW
lora = xAB * scale
y = base + lora
这种方式 adapter 可以随时切换:
load_adapter("json-repair")
unload_adapter()
load_adapter("flaky-test-debugging")
适合多租户、多任务、动态 skill。
6.2 合并进原权重
也可以预先计算:
W' = W + AB * scale
然后 forward 时直接用:
y = xW'
这叫 merge adapter。好处是推理时更简单,坏处是切换不如旁路灵活,而且要小心多个 adapter 的合并和回滚。
PAW 和 Parametric Skills 更强调 adapter 的可加载、可组合、可版本化,所以概念上更接近第一种动态加载方式。
7. 为什么 PAW 可以把“函数”变成 LoRA?
PAW 的函数不是传统 Python 函数,而是 fuzzy function:
判断日志是否该报警
判断邮件是否紧急
修复半坏 JSON
给搜索结果打相关性标签
这类函数的难点不是控制流,而是语义判断。大模型本来就会做这些判断,只是每次直接 prompt 大模型太贵。
PAW 做的是:
自然语言 spec
→ compiler
→ pseudo-program + LoRA
→ frozen interpreter 执行
这里的 LoRA 承载的是:
这个函数该关注哪些输入特征
输出格式是什么
边界样例怎么处理
哪些情况应该判 YES / NO
哪些情况应该拒绝或保守处理
如果不用 LoRA,只靠 prompt,解释器每次都要重新读 spec。用了 LoRA,函数行为的一部分直接进入模型层的增量计算。
所以 PAW 里的 LoRA 更像:
per-function behavior patch
它不是存储所有输入输出表,而是改变模型处理这类输入时的内部表示和输出倾向。
8. 为什么 Parametric Skills 可以把“技能”变成 LoRA?
Skill 比函数更复杂。它包含:
触发条件
做事步骤
常见错误
验证方法
工具使用习惯
多轮任务里的注意事项
文本 skill 的问题是,模型可能在上下文里看到了,但执行时忘了。
Parametric Skills 的做法是:
SKILL.md / agent 轨迹经验
→ hypernetwork
→ LoRA
→ base model 加载 skill adapter
这里的 LoRA 承载的是:
遇到这类任务时应该先查什么
哪些证据比其他信息更重要
不要犯哪些常见错误
什么时候该运行测试
失败后该如何回退和重新定位
它更像:
per-skill behavior patch
和 PAW 的区别是粒度:
PAW LoRA:让模型像一个具体函数
Parametric Skills LoRA:让模型像掌握了一套做事方法
9. LoRA 为什么能组合?
如果两个 adapter 都是对原模型权重的增量:
W + ΔW1
W + ΔW2
那么多 adapter 合并的直觉就是:
W + αΔW1 + βΔW2
这就是为什么大家会研究 adapter merge、adapter routing、rank-concat 等方法。
但组合不是免费午餐。两个 skill 可能语义冲突:
skill A:尽量最小修改
skill B:优先大规模重构
skill A:输出严格 JSON
skill B:输出详细解释
参数空间里相加,不等于语义上无冲突。
所以真正成熟的系统需要三层机制:
选择:这次任务该加载哪些 adapter
组合:多个 adapter 如何合并或路由
验证:加载后结果是否真的更好
这也是 Parametric Skills 里多 skill merging 和 self-evolving 很重要的原因。
10. LoRA 和大模型“内核”的协作方式
如果把大模型看成一个固定内核,LoRA 的协作方式可以分成四步:
10.1 内核提供通用能力
原始大模型已经学会:
语言理解
代码模式
常识推理
格式生成
基本工具使用
LoRA 不重新发明这些能力。
10.2 Adapter 提供任务方向
LoRA 提供的是“这次应该往哪里偏”的信号:
更像分类器
更像 JSON 修复器
更像 debugging specialist
更像 API migration assistant
10.3 Forward 时二者逐层相加
每一层里:
hidden state
→ 原始权重变换
→ LoRA 增量变换
→ 相加
→ 传给下一层
所以 adapter 不是只影响最后输出,而是在多层 hidden state 演化过程中持续参与。
10.4 输出仍然由同一个模型生成
最后 logits 还是由同一个模型头产生。LoRA 只是改变了前面 hidden state 的轨迹。
这点很重要:LoRA 不是外部规则引擎,它不绕过模型。它是模型内部计算的一部分。
11. 为什么这比“长期 prompt”更像软件工件?
一个 prompt 很难像二进制一样管理。
你当然可以版本化 prompt 文本,但它运行时仍然依赖:
上下文顺序
模型是否注意到
历史消息干扰
token budget
远程模型版本
LoRA adapter 则更像一个部署工件:
文件大小明确
版本明确
hash 明确
依赖的 base model 明确
可以加载 / 卸载 / 合并 / 回滚
这就是 PAW 和 Parametric Skills 想抓住的工程价值。
未来一个 Agent 系统可能不只有 prompt library,而是有 adapter registry:
json-repair@1.2.0.lora
log-triage@0.4.1.lora
flaky-test-debugging@2.0.0.lora
api-migration-react18@1.1.0.lora
company-style-guide@3.5.2.lora
每个 adapter 都有:
source spec / skill text
compiler version
base model version
eval report
known failure cases
rollback policy
这就从 prompt 管理,走向了神经插件管理。
12. LoRA 的风险:内化不等于可控
LoRA 很适合内化能力,但它也带来新问题。
12.1 不透明
Prompt 可以直接读。LoRA 不能直接读。
你可以知道它来自哪份 spec 或 skill,但很难回答:
这个 adapter 为什么在这个输入上失败?
是哪一层的哪个增量导致的?
两个 adapter 冲突在哪里?
所以必须保留源文本、训练记录和评估报告。
12.2 可能过拟合
如果 adapter 来自少量样本,它可能学到表面模式。
比如一个日志报警 adapter 可能过度依赖某几个关键词,而不是真正理解严重性。
12.3 可能破坏原能力
LoRA 虽然小,但如果 scale 太大、插入层太多、训练数据偏,仍然可能让模型在其他能力上退化。
所以 adapter 要做回归测试:
目标任务是否提升
非目标任务是否明显下降
输出格式是否稳定
安全边界是否仍然存在
12.4 多 adapter 组合会产生干扰
多个 LoRA 同时加载时,不只是“能力相加”。它们的增量可能把 hidden state 推向不同方向。
这需要 routing、gating、merge calibration 和任务级选择。
12.5 依赖 base model
LoRA 是相对于某个 base model 训练或生成的。
qwen3-0.6b adapter
不一定能直接挂到另一个结构不同的模型上
这和传统软件插件不同。它高度绑定模型结构、层名、hidden size、projection shape。
13. 一个更准确的心智模型
不要把 LoRA 想成:
一个知识文件
一个提示词压缩包
一个独立小模型
更准确的心智模型是:
LoRA 是一组分布在模型多层线性变换旁边的低秩行为增量。
它的效果不是“记住一段文字”,而是改变模型在某类输入上的计算路径:
关注哪里
强化什么特征
压制什么倾向
更容易输出什么结构
更容易遵守什么过程
这也是为什么它能承载函数、技能、领域、风格。
14. 三篇文章连起来看
现在这三篇可以组成一条完整链路。
第一篇 PAW 回答:
能不能把一个 fuzzy function 编译成权重?
答案是:可以,用 compiler 生成 per-function LoRA,让小解释器执行。
第二篇 Parametric Skills 回答:
能不能把 Agent skill 编译成权重?
答案是:可以,用 hypernetwork 生成 per-skill LoRA,让模型更稳定地利用技能。
这篇回答:
为什么 LoRA 能承担“函数”和“技能”?
答案是:因为 LoRA 不是外部文本,而是参与模型内部矩阵乘法的低秩增量。它不替换大模型,只调制大模型。
三句话总结:
PAW:函数规格 → LoRA → 可复用 fuzzy function
Parametric Skills:技能文档 → LoRA → 可加载 agent skill
LoRA 机制:原始权重 + 低秩增量 → 行为被调制的大模型
15. 最后一句话
LoRA 之所以适合承载 PAW 和 Parametric Skills,不是因为它“神奇地存储了知识”,而是因为它以很低成本进入了大模型的内部计算路径。
Prompt 是让模型读一份说明书;LoRA 是给模型装一个行为补丁。
这就是“能力从上下文窗口迁移到参数模块”的底层机制。