对应课程: course_A W8 (LoRA高效微调)
难度: ★★☆ (L2 基础)
知识基础: Build LLM from Scratch Appendix E — 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 倍 |
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")通常只对 W_q 和 W_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)")手写 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")| 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 微调后的模型应该被视为"全新模型"还是"原模型的变体"?