基座模型冻结语义规范与反向梯度传播路径物理隔离
Agent 辅助工程检查
前置要求
第 64 天 LoRA 前向公式与 PyTorch 自动微分机制。
学习目标
透彻掌握 LoRA 训练下的计算图拓扑分支结构:探究基座权重为何不分配梯度内存,而适配器矩阵 A 与 B 能够精准接收反向导数更新。
操作指引
1. 写出含两条路径的前向:y = W0 @ x + (alpha/r) * B @ A @ x。第一条路径只用冻结的 W0;第二条用可训练的 A(r x d_in)和 B(d_out x r)。
2. 在前向之前用 requires_grad_(False) 冻结 W0。这样 autograd 不会记录通向 W0 的梯度边,也不会分配 W0.grad。
3. 推导适配器梯度:dL/dB = (alpha/r) * (dL/dy) @ (A @ x)^T,dL/dA = (alpha/r) * B^T @ (dL/dy) @ x^T。注意形状。
4. 预测第 0 步(B 初始化为零)会发生什么:两个梯度中哪一个恰好为零?
5. 执行一次反向传播,断言题目中的三个梯度事实,再检验第 4 步的预测。
6. 计算省下的梯度显存:冻结参数数量 × 2 字节(bf16)或 4 字节(fp32)。
时间安排(120 分钟):阅读 25 + 练习 65 + 检查 30。 阅读:Hu et al. 第 4.1 节(LoRA 更新及其初始化)和 PEFT 的 LoRA 概念指南。练习:用参考模型完成第 1-6 步。检查:不看笔记解释为什么第 0 步 lora_A.grad 为零。
自主演练与记录
实例化包含冻结线性层与可学习 LoRA 适配器的模型,执行前向反向传播。严密断言 model.linear.weight.grad 严格为 None,而 lora_A.grad 与 lora_B.grad 为非空有效张量。
遍历 model.named_parameters() 审查 param.grad 状态。
import torch
import torch.nn as nn
class TinyLoRA(nn.Module):
def __init__(self, d_in=8, d_out=4, r=2, alpha=4):
super().__init__()
self.linear = nn.Linear(d_in, d_out, bias=False)
self.linear.weight.requires_grad_(False) # freeze W0
self.lora_A = nn.Parameter(torch.randn(r, d_in) * 0.01)
self.lora_B = nn.Parameter(torch.zeros(d_out, r)) # B = 0, so the adapter starts as a no-op
self.scale = alpha / r
def forward(self, x):
return self.linear(x) + self.scale * (x @ self.lora_A.T @ self.lora_B.T)
torch.manual_seed(0)
model = TinyLoRA()
x, target = torch.randn(5, 8), torch.randn(5, 4)
loss = nn.functional.mse_loss(model(x), target)
loss.backward()
assert model.linear.weight.grad is None # no .grad buffer was ever allocated
assert model.lora_A.grad is not None
assert model.lora_B.grad is not None
# Surprise worth recording: at step 0, dL/dA = scale * B.T @ dL/dy @ x is exactly zero because
# B = 0, while dL/dB = scale * dL/dy.T @ (x @ A.T) is not. A receives signal only after B moves.
assert torch.count_nonzero(model.lora_A.grad) == 0
assert torch.count_nonzero(model.lora_B.grad) > 0
trainable = [n for n, p in model.named_parameters() if p.requires_grad]
frozen = sum(p.numel() for p in model.parameters() if not p.requires_grad)
print(trainable, frozen) # ['lora_A', 'lora_B'] 32
# Saving: 32 frozen weights x 4 bytes = 128 bytes of fp32 gradient that is never allocated;
# for a 7B base in bf16 the same rule saves 7e9 x 2 bytes = 14 GB.
能准确分析并断言基座与适配器在自动求导图中的物理隔离属性。
权威源资源与权利证明
LoRA: Low-Rank Adaptation of Large Language Models
对应章节 / 时间戳: Section 4.2 Applying LoRA to Transformer (frozen W0, VRAM and checkpoint-size reduction)
验证版本与日期: ICLR 2022 / arXiv:2106.09685v2 (Verified: 2026-10-30)
Primary literature introducing Low-Rank Adaptation, establishing parameter decomposition W = W0 + (alpha/r)*BA, initialization strategies, and zero inference latency merging.
Matrix factorization, rank, linear layers, and transformer attention.
Complete matrix derivations, tensor dimensions, and pure PyTorch LoRALinear modules are implemented directly in lesson code.
对比候选资源与选型分析 (2)
Introduces bottleneck adapter layers in series, which introduces unavoidable inference latency overhead; LoRA's parallel formulation merges with zero latency.
Prepends virtual prompt tokens; reduces available context length, unlike LoRA which preserves full sequence capacity.
PEFT: State-of-the-art Parameter-Efficient Fine-Tuning Methods
对应章节 / 时间戳: README 'Quickstart' (LoraConfig, get_peft_model, print_trainable_parameters)
验证版本与日期: PEFT 0.10.x / GitHub (Verified: 2026-10-30)
Widely adopted open-source library implementing LoRA configuration classes (LoraConfig), get_peft_model, and adapter serialization.
PyTorch nn.Module, Hugging Face transformers, and LoRA equations.
Standalone, zero-dependency PyTorch implementations of LoRALinear layers and save/reload logic are provided in curriculum scripts.
对比候选资源与选型分析 (2)
Original reference code by Hu et al.; PEFT expands this to broader multi-model ecosystem integration.
High-performance clean implementation; compared alongside PEFT for modular code architecture.