Learning_RoadmapAI_Assistant_BasicsModel_Building_and_APIData_Governance_and_SecurityLLM_Book_IngestionTokenizationAttention_MechanismGPT_ArchitecturePretrainingFineTuningLoRAReasoning_Model_Book_IngestionStarTalent_ArchitectureLLM_Engineer_Handbook_CourseRAG_Building_BlocksLangChain_RunnableAI_Engineering_Chip_HuyenLLM_Ops_FinancePython_BootcampReference_Index
进阶

LoRA 从零实现 — 课程A W8

LoRA 从零实现 — 课程A W8

对应课程: course_A W8 (LoRA高效微调)

难度: ★★☆ (L2 基础)

知识基础: Build LLM from Scratch Appendix E — LoRA参数高效微调

> 高效微调的工程标准 — 审计时需要检查微调方法是否符合合规要求


1. LoRA 核心思想

不直接修改权重矩阵 W, 而是在旁边加一个低秩矩阵 B×A

$$W' = W + \Delta W = W + BA$$

其中:

参数量对比:

| 方法 | 训练参数量 (d=4096, k=4096, r=8) |

|------|-------------------------------|

| 全量微调 | $4096 \times 4096 = 16.8M$ |

| LoRA | $4096 \times 8 + 8 \times 4096 = 65.5K$ |

| 节省 | 256 倍 |


2. 手写 LoRA 层

代码
import torch
import torch.nn as nn
import torch.nn.functional as F

class LoRALayer(nn.Module):
    """LoRA 低秩适配层"""
    def __init__(self, in_dim, out_dim, rank=8, alpha=16):
        super().__init__()
        self.alpha = alpha  # 缩放因子
        self.scale = alpha / rank
        
        # LoRA 低秩矩阵
        self.A = nn.Parameter(torch.randn(in_dim, rank) * 0.01)  # ℝ^{d×r}
        self.B = nn.Parameter(torch.zeros(rank, out_dim))         # ℝ^{r×k}
        
        # 训练标识
        self.requires_grad_(True)
    
    def forward(self, x):
        # ΔW = BA, 输出 = x @ BA * scale
        return (x @ self.A @ self.B) * self.scale


class LinearWithLoRA(nn.Module):
    """带 LoRA 的 Linear 层 (原始权重冻结 + LoRA 分支)"""
    def __init__(self, linear, rank=8, alpha=16):
        super().__init__()
        self.linear = linear
        self.linear.weight.requires_grad = False  # 冻结原始权重
        self.lora = LoRALayer(
            linear.in_features, linear.out_features, rank, alpha
        )
    
    def forward(self, x):
        return self.linear(x) + self.lora(x)  # Wx + BAx

# 测试
linear = nn.Linear(64, 64)
lora_linear = LinearWithLoRA(linear, rank=8)

# 只有 LoRA 参数可训练
trainable = sum(p.numel() for p in lora_linear.parameters() if p.requires_grad)
total = sum(p.numel() for p in lora_linear.parameters())
print(f"总参数量: {total}")
print(f"可训练参数 (仅LoRA): {trainable} ({trainable/total*100:.2f}%)")
print(f"原始 Linear: {64*64} params, LoRA: {64*8 + 8*64} = {trainable} params")

3. 将 LoRA 应用到注意力层

通常只对 W_qW_v 应用 LoRA。

代码
def apply_lora_to_attention(model, rank=8, alpha=16):
    """将 GPT 模型的所有注意力层替换为 LoRA 版本"""
    for name, module in model.named_modules():
        if isinstance(module, MultiHeadAttention):  # 假设有对应的注意力类
            module.W_q = LinearWithLoRA(module.W_q, rank, alpha)
            module.W_v = LinearWithLoRA(module.W_v, rank, alpha)
            # W_k 通常不替换 (实验表明对 W_k 加 LoRA 收益不大)
    return model

print("实际使用时:")
print("model = GPTModel(cfg)")
print("model = apply_lora_to_attention(model, rank=8)")
print("# 然后只训练 LoRA 参数:")
print("optimizer = torch.optim.AdamW([p for n,p in model.named_parameters() if p.requires_grad], lr=1e-4)")

4. 使用 PEFT 库 (实际项目用)

手写 LoRA 是为了理解原理。实际项目中用 HuggingFace PEFT。

代码
try:
    import peft
    print(f"PEFT 版本: {peft.__version__}")
    print("\nLoRA 配置示例:")
    print('''
from peft import LoraConfig, get_peft_model

lora_config = LoraConfig(
    r=8,              # 秩
    lora_alpha=32,    # 缩放因子
    target_modules=["q_proj", "v_proj"],  # 作用于 Q 和 V 投影
    lora_dropout=0.05,
    bias="none",
    task_type="CAUSAL_LM"
)

model = get_peft_model(base_model, lora_config)
model.print_trainable_parameters()
# trainable params: 8,388,608 || all params: 9,012,345,678 || 0.093%
    ''')
except ImportError:
    print("PEFT 未安装。安装: pip install peft")

5. LoRA 秩的选择

| r 值 | 可训练参数量 (d=4096) | 效果 | 适用场景 |

|------|---------------------|------|---------|

| 1 | 8K | 最低 | 极低资源 |

| 4 | 33K | 一般 | 简单任务 |

| 8 | 66K | | 推荐默认值 |

| 16 | 131K | 更好 | 复杂任务 |

| 64 | 524K | 最好 | 接近全量微调 |

| full | 16.8M | 基准 | 全量微调 |

> L4 映射: LoRA 让模型版本管理变得可行—你只需要存储几 MB 的 LoRA 权重,而不是几十 GB 的完整模型。

代码
# %%ai openai-chat-custom:Qwen3.5-9B-Q4_K_M.gguf --format markdown
# 为什么 LoRA 只对 W_q 和 W_v 应用效果最好?对 W_k 和 W_o 应用会怎样?
# 在 AI 治理审计中,LoRA 微调后的模型应该被视为"全新模型"还是"原模型的变体"?

检查清单