公司动态

datawhale--llm-algo-leetcode[10] LoRA

📅 2026/8/31 18:30:04
datawhale--llm-algo-leetcode[10] LoRA
链接笔记10. LoRA Tutorial | LoRA 教程本章目标实现最小LoRALinear看清低秩旁路 A、B 如何构成Δ W \Delta WΔW以及为什么它能用较少的可训练参数完成微调。完成后你应该能把 LoRA 接到端到端微调实验和 QLoRA 低比特微调里。冻结主权重 → 低秩旁路 A/B → 缩放 → 合并权重 → 合并后的零额外 LoRA 推理开销本章不会重新讲优化器基础只聚焦 LoRA 的低秩更新机制和参数冻结关系。模型架构总览LoRA 在微调技术路线中的位置预训练模型权重 W_0 冻结LoRA 旁路矩阵 A (r×k), B (d×r)前向传播h W_0 x (α/r) BAx训练只更新 A 和 B合并权重W_merged W_0 (α/r) BA推理标准 Linear 层零额外开销微调后的模型与原始模型结构相同结构说明LoRA 的核心思想是冻结预训练权重只在旁边加一条低秩可训练旁路。推理时可以将旁路权重合并回主权重实现合并后的零额外 LoRA 推理开销。LoRA 前向传播数据流输入 x[B, S, k]主权重 W_0[d, k] 冻结矩阵 A[r, k] 可训练矩阵 B[d, r] 可训练缩放 α/r主分支输出W_0 x[B, S, d]LoRA 分支输出(α/r) BAx[B, S, d]最终输出h W_0 x (α/r) BAx合并后W_merged W_0 (α/r) BA本节核心概念LoRA 三要素冻结主权重W_0 不更新低秩旁路A (降维), B (升维)合并后的零额外 LoRA 推理开销合并权重本节涉及的模块模块章节形状变化核心作用输入张量#0[B, S, k]原始输入主权重 W_0#1[d, k]冻结的预训练权重矩阵 A#1[r, k]降维矩阵可训练矩阵 B#1[d, r]升维矩阵可训练LoRA 输出#2[B, S, d]低秩旁路的增量合并权重#3[d, k]W_0 (α/r) BA核心原则LoRA 不是少写几个参数而是把更新预算集中到最有效的低秩方向上。0. 学习本章前先认识这些名词名词英文简单理解LoRALow-Rank Adaptation低秩适配冻结主权重训练旁路矩阵旁路Sidecar / Adapter在主干网络旁边附加的可训练模块低秩Low-Rank矩阵的秩远小于原始维度缩放因子Scaling Factorα/r控制 LoRA 更新的影响程度合并权重Merge Weights将 LoRA 旁路合并回主权重实现合并后的零额外 LoRA 推理开销PEFTParameter-Efficient Fine-Tuning参数高效微调不需要过度强调LoRA 只用于大模型LoRA 的思想可以应用到任何线性层包括小模型和卷积层。理解低秩更新的数学本质比记住LoRA 用于 7B 模型更重要。1. 核心差异与机制1.1 全参微调 vs LoRA全参微调和 LoRA 的关键差异在于参数更新范围全参微调 (Full Fine-tuning): 给定预训练模型 更新所有参数 显存参数 梯度 优化器状态Adam 通常包含参数、梯度和一阶/二阶状态 目标重新学习所有任务相关的权重 LoRA: 给定预训练模型 更新只更新旁路矩阵 A 和 B 显存只保存 A、B 的梯度和优化器状态 目标在预训练知识基础上做小幅适配为什么 LoRA 能节省显存以 7B 模型为例方法可训练参数显存占用FP16全参微调7B~14GB仅参数LoRA (r8)约35M取决于目标层仅参数约70MB不含梯度和优化器状态LoRA (r16)~70M~140MBLoRA 的训练显存节省主要来自不需要保存主权重的梯度不需要为主权重保存优化器状态动量、方差只保存 A、B 两个小矩阵的优化器状态1.2 低秩更新的数学原理核心假设预训练权重的更新矩阵Δ W \Delta WΔW是低秩的即Δ W ≈ B A \Delta W \approx BAΔW≈BA其中B ∈ R d × r B \in \mathbb{R}^{d \times r}B∈Rd×rA ∈ R r × k A \in \mathbb{R}^{r \times k}A∈Rr×kr ≪ min ⁡ ( d , k ) r \ll \min(d, k)r≪min(d,k)为什么是低秩直觉预训练模型已经在高维空间中学习到了通用特征。微调时我们只需要在少数几个方向上做小幅调整不需要重新学习所有维度。理解重点LoRA 假设微调产生的更新矩阵可以被低秩矩阵近似实际效果需要通过具体任务实验验证。1.3 缩放因子α / r \alpha/rα/r为什么需要缩放当改变秩r rr时需要控制 LoRA 更新量的幅度。缩放因子α / r \alpha/rα/r的作用是作为一个额外缩放项帮助控制不同秩设置下的更新幅度实际效果取决于α \alphaα、r rr、学习率和训练设置通常设置α r × learning_rate_scale \alpha r \times \text{learning\_rate\_scale}αr×learning_rate_scale常见配置秩 r缩放因子 α实际缩放 α/r81621616132321646412. 代码实现框架2.1 LoRALinear 完整实现importtorchimporttorch.nnasnnimporttorch.nn.functionalasFimportmathclassLoRALinear(nn.Module):def__init__(self,in_features:int,out_features:int,r:int8,lora_alpha:int16):super().__init__()self.rr self.lora_alphalora_alpha self.scalingself.lora_alpha/self.r# 主权重冻结的预训练线性层self.linearnn.Linear(in_features,out_features,biasFalse)self.linear.weight.requires_gradFalse# LoRA 旁路矩阵self.lora_Ann.Parameter(torch.empty(r,in_features))self.lora_Bnn.Parameter(torch.empty(out_features,r))self.reset_parameters()defreset_parameters(self):按照 LoRA 论文的规则初始化# 主权重保持预训练权重的原始初始化# 实际使用预训练模型时此处应加载已有 W_0而不是重新初始化# nn.init.kaiming_uniform_(self.linear.weight, amath.sqrt(5))# LoRA AKaiming 均匀分布提供随机性nn.init.kaiming_uniform_(self.lora_A,amath.sqrt(5))# LoRA B必须初始化为 0确保初始输出等于冻结权重nn.init.zeros_(self.lora_B)defforward(self,x:torch.Tensor)-torch.Tensor: 前向传播主分支 LoRA 旁路 Args: x: [..., in_features] Returns: output: [..., out_features] # 主分支输出resultself.linear(x)# LoRA 旁路输出lora_out(x self.lora_A.T) self.lora_B.T*self.scaling# 两者相加resultlora_outreturnresultdefmerge_weights(self): 将 LoRA 旁路合并回主权重 合并后W_merged W_0 (α/r) BA 推理时与标准 Linear 层完全相同 self.linear.weight.data(self.lora_B self.lora_A)*self.scalingShape 对齐示意输入 x: [B, S, k] 主权重 W_0: [d, k] LoRA A: [r, k] LoRA B: [d, r] 主分支: x W_0.T → [B, S, d] LoRA 分支: x A.T B.T → [B, S, d] 缩放: * (α/r) 最终: 主分支 LoRA 分支 → [B, S, d]3. 动手实战下面是独立 toy 示例不依赖主线模型。3.1 TODOTODO 1初始化补全__init__中的主权重和 LoRA 矩阵初始化。TODO 2权重初始化补全reset_parameters中的 Kaiming 和 zeros 初始化。TODO 3前向传播补全forward中的主分支和 LoRA 旁路计算。TODO 4合并权重补全merge_weights中的权重合并逻辑。3.2 建议测试代码deftest_lora():in_dim,out_dim128,256batch_size,seq_len32,10layerLoRALinear(in_dim,out_dim,r8,lora_alpha16)xtorch.randn(batch_size,seq_len,in_dim)# 1. 验证初始化B 全零所以初始输出等于冻结权重的输出withtorch.no_grad():out_loralayer(x)out_baselayer.linear(x)asserttorch.allclose(out_lora,out_base),\初始化错误: lora_B 未被初始化为 0# 2. 模拟训练一步改变 B 的值layer.lora_B.data.normal_(0,0.02)out_trainedlayer(x)assertnottorch.allclose(out_trained,out_base),\前向传播错误: 旁路未能注入增量# 3. 验证合并权重的正确性layer.merge_weights()out_mergedlayer.linear(x)asserttorch.allclose(out_trained,out_merged,atol1e-5),\权重合并错误: 合并后的输出与分离时的输出不一致print(\n✅ tests passed)这些测试分别证明什么asserttorch.allclose(out_lora,out_base)证明初始化时 B 全零LoRA 旁路输出为 0总输出等于冻结权重的输出。assertnottorch.allclose(out_trained,out_base)证明 LoRA 旁路可以注入有效的增量更新。asserttorch.allclose(out_trained,out_merged,atol1e-5)证明合并后的权重与分离时的输出完全一致合并后的零额外 LoRA 推理开销成立。4. 与真实源码对应4.1 Hugging Face PEFTHugging Face 的peft库中LoRA 的实现逻辑相同frompeftimportLoraConfig,get_peft_model configLoraConfig(r8,lora_alpha16,target_modules[q_proj,v_proj],lora_dropout0.05,biasnone,task_typeCAUSAL_LM)modelget_peft_model(model,config)4.2 参数量对比以 LLaMA3 8B 为例只对 Q/V 投影应用 LoRA (r8)组件原始参数量LoRA 参数量比例Q/K/V Linear3 × 4096 × 4096 ≈ 50.3M2 × (8 × 4096 4096 × 8) ≈ 131K0.26%单层---32 层-~4.2M-5. 工程要点5.1 秩 r 的选择秩 r参数量适用场景4最少简单任务数据量小8常用大多数场景的默认选择16较多复杂任务数据量充足32很多接近全参微调收益递减经验法则从 r8 开始如果效果不足再逐步增加。r32 以上通常不如直接全参微调。5.2 目标模块的选择不是所有线性层都需要加 LoRA# 常见选择只对 Q/K/V 投影加 LoRAtarget_modules[q_proj,k_proj,v_proj]# 也可以包括 output projectiontarget_modules[q_proj,k_proj,v_proj,o_proj]# 包括 MLP参数量更大target_modules[q_proj,k_proj,v_proj,gate_proj,up_proj,down_proj]经验法则Attention 的 Q/V 投影是常见选择之一不同任务和模型结构可能需要实验比较其他目标模块。5.3 合并权重的时机训练中保持 A 和 B 分离方便继续训练或调整部署前合并权重移除 A 和 B降低推理延迟和显存多适配器可以训练多个 A/B 对推理时动态加载不同适配器5.4 常见陷阱忘记冻结主权重如果requires_grad TrueLoRA 退化为全参微调B 未初始化为 0会导致训练开始时输出与预训练模型不一致缩放因子设置错误影响训练稳定性和最终效果秩过高r 过大时 LoRA 失去意义接近全参微调6. 与 QLoRA 的关系LoRA 和 QLoRA 的关系LoRA: 参数高效微调只训练少量参数 QLoRA: LoRA 4-bit 量化进一步降低显存QLoRA 在 LoRA 的基础上将主权重量化为 4-bitLoRA 旁路保持 FP16/BF16前向传播时反量化主权重与 LoRA 旁路相加7. 本章掌握检查已掌握后应该能回答LoRA 的核心公式h W 0 x α r B A x h W_0 x \frac{\alpha}{r} BAxhW0​xrα​BAx中每个符号代表什么为什么 LoRA B 必须初始化为 0而 A 可以随机初始化合并权重后为什么推理延迟与原始模型相同缩放因子α / r \alpha/rα/r的作用是什么LoRA 和全参微调在数学上有什么区别为什么说 LoRA 不是少写几个参数而是把更新集中到低秩方向8. 本章最终带走的结构输入 x [B, S, k] ↓ 主分支x W_0.T → [B, S, d]冻结 ↓ LoRA 分支x A.T B.T * (α/r) → [B, S, d]可训练 ↓ 相加h W_0 x (α/r) BAx ↓ 训练只更新 A 和 B ↓ 合并W_merged W_0 (α/r) BA ↓ 推理标准 Linear零额外开销9. 带走的直觉LoRA 的核心是低秩假设预训练权重的更新可以用低秩矩阵近似B 初始化为 0 是关键细节确保训练开始时模型行为与预训练模型一致合并权重实现合并后的零额外 LoRA 推理开销这是 LoRA 相比 Adapter 等方法的重要优势缩放因子保持学习率稳定性改变秩时不需要重新调整学习率LoRA 是参数层面的技巧与 SFT 的 loss masking数据层面正交可以同时使用10.辅助理解