06-LoRA参数高效微调
阶段 6LoRA 参数高效微调教学目标理解 LoRALow-Rank Adaptation的核心原理和数学推导掌握 LoRA 的实现方式A、B 矩阵分解理解 LoRA 与全量微调的区别和各自适用场景能够运行 MiniMind 的原生 LoRA 训练无需 peft 依赖掌握 LoRA 权重合并与模型导出流程课时安排理论讲解1 课时LoRA 原理 数学推导实验对比1 课时LoRA vs 全量微调实验6.1 LoRA 的动机6.1.1 全量微调的挑战挑战说明显存需求大需要存储所有参数的梯度64M 模型需要约 0.5GB 额外显存存梯度训练速度慢所有参数都需要更新存储成本高每个任务微调后需要保存完整的模型副本灾难性遗忘全量微调可能导致模型忘记预训练知识6.1.2 LoRA 的核心假设关键假设预训练模型在适配下游任务时权重的变化量ΔW是低秩的。即虽然模型参数矩阵 W 很大但在微调过程中的变化量 ΔW 可以用两个小矩阵的乘积近似ΔW W_new - W_original ≈ B × A 其中: W ∈ ℝ^{d×d} 原始权重矩阵d768 A ∈ ℝ^{d×r} 降维矩阵r d B ∈ ℝ^{r×d} 升维矩阵r d r LoRA rank 秩通常 4-646.1.3 LoRA 参数量对比全量微调 可训练参数 d × d 768 × 768 589,824每层一个线性层 LoRA 微调rank8 可训练参数 d × r r × d 768 × 8 8 × 768 12,288每层 节省比例12,288 / 589,824 ≈ 2.1%仅 2% 的参数需要训练6.2 LoRA 数学原理6.2.1 线性层的 LoRA 改造原始线性层y W × x W ∈ ℝ^{out×in}加入 LoRA 后y W × x (B × A) × x (W B × A) × x 其中: A ∈ ℝ^{r×in} 初始化为随机高斯分布 B ∈ ℝ^{out×r} 初始化为零矩阵 因此训练开始时 y W × x 0 × x W × x 与原始模型行为完全一致关键设计B 初始化为零确保 LoRA 训练开始时模型行为不变实现安全启动。6.2.2 LoRA 在 MiniMind 中的应用位置LoRA 通常应用于 Attention 层的 Q、K、V、O 投影和 MLP 层的 W₁、W₂、W₃ 投影原始 Attention 层: Q x × W_Q → Q x × W_Q x × (B_Q × A_Q) K x × W_K → K x × W_K x × (B_K × A_K) V x × W_V → V x × W_V x × (B_V × A_V) O attn × W_O → O attn × W_O attn × (B_O × A_O) 原始 MLP 层: h1 x × W₁ → h1 x × W₁ x × (B₁ × A₁) h2 x × W₂ → h2 x × W₂ x × (B₂ × A₂)6.2.3 LoRA 代码实现importtorchimporttorch.nnasnnclassLoRALinear(nn.Module):def__init__(self,original_linear,rank8,alpha16):super().__init__()self.originaloriginal_linear self.rankrank self.alphaalpha in_dimoriginal_linear.in_features out_dimoriginal_linear.out_features# 冻结原始权重self.original.weight.requires_gradFalse# LoRA 矩阵self.lora_Ann.Parameter(torch.randn(rank,in_dim)*0.01)self.lora_Bnn.Parameter(torch.zeros(out_dim,rank))# 缩放因子self.scalingalpha/rankdefforward(self,x):# 原始输出 LoRA 输出original_outputself.original(x)lora_output(x self.lora_A.T self.lora_B.T)*self.scalingreturnoriginal_outputlora_output6.2.4 缩放因子Scaling FactorLoRA 输出 (B × A) × x × (α / r) 其中: α (alpha) 缩放超参数通常设为 rank 的 1-2 倍 r (rank) LoRA 秩 作用 当增大 rank 时B×A 的原始幅度也会增大。 通过 α/r 的缩放保持 LoRA 贡献的相对稳定。 这样调整 rank 时不需要重新调整学习率。6.3 MiniMind 的 LoRA 实现6.3.1 原生实现无 peft 依赖MiniMind从零实现 LoRA不依赖 HuggingFace 的peft库。这样做的好处代码透明学生可以完全理解每一步无额外依赖与 MiniMind 的架构完美适配6.3.2 LoRA 配置参数参数默认值说明lora_rank8LoRA 秩lora_alpha16缩放因子lora_dropout0.05LoRA Dropoutlora_target_modules[“wq”, “wk”, “wv”, “wo”, “w1”, “w2”, “w3”]应用 LoRA 的模块6.3.3 运行 LoRA 微调cdminimind/trainer# 从 SFT 模型开始 LoRA 微调python train_lora.py# 指定 LoRA rankpython train_lora.py--lora_rank16# 指定 LoRA alphapython train_lora.py--lora_alpha326.3.4 LoRA 训练输出out/ ├── full_sft_768.pth # 基础模型权重冻结 ├── lora_pretrain_768.pth # LoRA 适配器权重仅 A、B 矩阵 └── ...LoRA 权重文件非常小仅包含 A、B 矩阵参数LoRA 权重大小 ≈ num_layers × num_targets × 2 × rank × dim ≈ 8 × 7 × 2 × 8 × 768 ≈ 688KB 对比全量模型64M × 4 bytes ≈ 256MB 节省256MB / 688KB ≈ 370 倍6.4 LoRA 权重合并6.4.1 为什么需要合并训练好的 LoRA 适配器是独立于基础模型的推理时需要分别加载。为了部署方便可以将 LoRA 权重合并回基础模型。6.4.2 合并过程合并前 推理: y W × x (B × A) × x 需要分别加载 W 和 B、A 合并后 W_merged W B × A 推理: y W_merged × x 只需加载一个权重矩阵6.4.3 运行权重合并cdminimind/scripts# 合并 LoRA 权重到基础模型python convert_model.py# 输出: out/merged_model_768.pth包含合并后的完整权重6.4.4 模型格式转换MiniMind 支持在多种格式间转换cdscripts# torch 格式 → transformers 格式可被 vLLM、ollama 使用python convert_model.py--formattransformers--input_dir../minimind-3# transformers 格式 → GGUF 格式可被 llama.cpp 使用# 需要安装 llama-cpp-python 或使用 llama.cpp 的 convert脚本6.5 LoRA vs 全量微调对比实验6.5.1 实验设计对比维度全量微调LoRA 微调可训练参数64M全部1.4M2%训练速度基准更快反向传播计算量小显存占用基准更低不需存储全部梯度效果基准通常接近rank 足够时存储成本每任务 256MB每任务 ~1MB6.5.2 实验步骤# 实验1: 全量微调已在阶段5完成python train_full_sft.py--max_epochs5# 实验2: LoRA rank4python train_lora.py--lora_rank4# 实验3: LoRA rank8python train_lora.py--lora_rank8# 实验4: LoRA rank16python train_lora.py--lora_rank16# 实验5: LoRA rank32python train_lora.py--lora_rank32# 对比各组在测试集上的 Loss 和回答质量6.5.3 预期结果效果趋势通常 全量微调 LoRA(r32) ≈ LoRA(r16) LoRA(r8) LoRA(r4) 但当 r ≥ 16 时LoRA 效果通常非常接近全量微调。6.6 LoRA 变体与扩展知识6.6.1 常见 LoRA 变体变体核心思想与标准 LoRA 的区别QLoRA量化 LoRA将基础模型量化为 4-bitLoRA 部分保持 bf16LoRA自适应学习率为 A 和 B 设置不同的学习率DoRA权重分解将权重分解为方向和幅度仅对方向做 LoRAVeRA共享随机矩阵多个 LoRA 层共享相同的 A、B 随机矩阵6.6.2 如何选择 LoRA rankrank参数量适用场景1-4极少简单任务如风格迁移8-16较少通用微调推荐起点32-64中等复杂任务如多轮对话、推理128较多接近全量微调的效果经验法则从 rank8 开始如果效果不够好逐步增大到 16、32。实践任务任务 6.1运行 LoRA 微调必做使用 SFT 模型作为基础运行 LoRA 微调记录训练 Loss 曲线合并 LoRA 权重使用合并后的模型进行对话测试# 完整流程cdtrainerpython train_lora.py--lora_rank8cd../scriptspython convert_model.pycd..python eval_llm.py--load_from./out/merged_model_768.pth任务 6.2LoRA rank 对比实验必做rank训练 Loss测试集回答质量参数量训练时间481632思考题rank 增大时Loss 是否一定更低有没有边际效应递减的点LoRA 模型在哪些类型的问题上表现与全量微调差距最大任务 6.3LoRA 权重分析选做# 分析 LoRA 矩阵的奇异值分布importtorch# 加载 LoRA 权重lora_weightstorch.load(out/lora_pretrain_768.pth)# 对每层的 A、B 矩阵做 SVDforname,paraminlora_weights.items():iflora_Ainnameorlora_Binname:U,S,Vhtorch.linalg.svd(param)print(f{name}: top-3 singular values {S[:3].tolist()})# 观察奇异值分布判断 LoRA 是否充分利用了其秩容量挑战任务选做实现一个简化版的 QLoRA4-bit 量化 LoRA对比 LoRA 应用于不同层仅 Attention vs AttentionMLP的效果差异下一阶段预告在阶段 7 中我们将学习**强化学习对齐RLHF/RLAIF**技术。理解 DPO、PPO、GRPO、CISPO 等算法的原理和区别让模型学会区分好回答和坏回答进一步对齐人类偏好。