数据截至 (上游 commit bd2a0fc7c314)
05 · PEFT:LoRA、DoRA 与 QLoRA
这一章讲什么: torchtune 没有依赖 HuggingFace 的 peft 库,
torchtune/modules/peft/四个文件就是它全部的参数高效微调实现。本章讲透五件事:LoRALinear这个替换件长什么样、builder 怎么把普通 Linear 换成它、参数冻结靠什么保证没出错、DoRA 比 LoRA 多算哪一步、QLoRA 的 4 bit 基座怎么和适配器拼在一起。
贯穿全章的主线例子: 全章只用一个输入 —— Llama-3.1-8B 的第 0 层注意力里那个 4096×4096 的
q_proj,换成rank=8、alpha=16的 LoRA 版,配置就取官方的recipes/configs/llama3_1/8B_lora_single_device.yaml。后面每一节开头的「主线例子 · 第 N 步」都是这同一条线上的一步,每步旁边写出具体的数。
1. 它要解决的小问题
先算一笔账。Llama-3.1-8B 的参数表写死在 torchtune/models/llama3_1/_model_builders.py:122-131:词表 128,256、32 层、隐藏维 4096、前馈中间维 14336。按这张表逐项加起来是 8,030,261,248 个数。
全量微调时,这 80.3 亿个数每一个都要配一份梯度(这个数该往哪个方向挪、挪多少),最常用的那个优化器 AdamW 还要再给每一个配两份历史统计量。换句话说,真正吃显存的不是模型本身,是模型的三四倍。 第 4 章讲的 optimizer-in-backward 就是在这一项上抢显存。
参数高效微调(parameter-efficient fine-tuning,业界缩写 PEFT)换了个方向:别动原来那 80.3 亿个数,只训练新加的一小撮。 原权重不训练,就不需要梯度,也不需要优化器状态,那三四倍的账直接归零。
torchtune 把这件事做成了「替换件」:模型结构一个字符不改,只把其中若干个 nn.Linear 换成一个行为兼容、但内部多挂了两片小矩阵的类。换哪些、换成什么、换完谁能训,就是这一章。