LoRA 论文
Low-Rank Adaptation of LLMs
调整维度和秩,观察 LoRA 如何大幅减少可训练参数量。
💡 推荐: r=8~32, α=2r (缩放因子)
一句话定义:LoRA (Low-Rank Adaptation) 是一种参数高效微调技术,通过训练低秩矩阵来适配大模型,只需更新 0.1%-1% 的参数即可达到全量微调的效果。
| 全量微调 | LoRA 微调 |
|---|---|
| 重新制作一把新吉他 | 在原吉他上加个变调夹 |
| 改变整个吉他的音色 | 只调整需要的音高 |
| 成本高,耗时长 | 便宜、快速、可拆卸 |
| 每首歌一把吉他 | 一把吉他 + 多个变调夹 |
LoRA 就是给大模型加”变调夹”,轻量级地改变它的行为。
| 模型 | 参数量 | 全量微调显存 | LoRA 微调显存 |
|---|---|---|---|
| 7B | 70亿 | 56 GB (FP16) | 8 GB |
| 13B | 130亿 | 104 GB | 12 GB |
| 70B | 700亿 | 560 GB | 48 GB |
| 优势 | 说明 |
|---|---|
| 显存 | 降低 80%+ |
| 存储 | 原模型 14GB + LoRA 几十 MB |
| 多任务 | 一个基座模型 + 多个 LoRA 适配器 |
| 效果 | 接近全量微调 |
预训练权重 固定,添加可训练的低秩矩阵:
| 层 | 原始参数 | LoRA 参数 (r=8) | 压缩比 |
|---|---|---|---|
| 4096 × 4096 | 16.7M | 65K | 256x |
| 4096 × 11008 | 45M | 120K | 375x |
通常应用于 Transformer 的 Attention 层:
┌─────────────────────────────────────────┐│ Attention Layer │├─────────────────────────────────────────┤│ Q = W_q · x + B_q · A_q · x ← LoRA ││ K = W_k · x + B_k · A_k · x ← LoRA ││ V = W_v · x + B_v · A_v · x ← LoRA ││ O = W_o · attn + B_o · A_o ← LoRA │└─────────────────────────────────────────┘from transformers import AutoModelForCausalLM, AutoTokenizerfrom peft import LoraConfig, get_peft_model, TaskType
# 1. 加载基座模型model = AutoModelForCausalLM.from_pretrained( "meta-llama/Llama-2-7b-hf", torch_dtype=torch.float16, device_map="auto")
# 2. 配置 LoRAlora_config = LoraConfig( task_type=TaskType.CAUSAL_LM, r=8, # 秩 lora_alpha=32, # 缩放因子 lora_dropout=0.1, # Dropout target_modules=[ # 应用 LoRA 的层 "q_proj", "k_proj", "v_proj", "o_proj", "gate_proj", "up_proj", "down_proj" ],)
# 3. 创建 PEFT 模型model = get_peft_model(model, lora_config)model.print_trainable_parameters()# trainable params: 4,194,304 || all params: 6,742,609,920# trainable%: 0.0622%
# 4. 正常训练from transformers import Trainer, TrainingArguments
trainer = Trainer( model=model, args=TrainingArguments( output_dir="./lora-llama2", per_device_train_batch_size=4, gradient_accumulation_steps=4, num_train_epochs=3, learning_rate=2e-4, fp16=True, ), train_dataset=dataset,)trainer.train()
# 5. 保存 LoRA 权重 (仅几十 MB)model.save_pretrained("./lora-llama2")
# 6. 加载使用from peft import PeftModel
base_model = AutoModelForCausalLM.from_pretrained("meta-llama/Llama-2-7b-hf")model = PeftModel.from_pretrained(base_model, "./lora-llama2")
# 7. 合并权重 (可选,用于部署)merged_model = model.merge_and_unload()from transformers import AutoModelForCausalLM, BitsAndBytesConfigfrom peft import LoraConfig, get_peft_model, prepare_model_for_kbit_training
# 1. 4bit 量化配置bnb_config = BitsAndBytesConfig( load_in_4bit=True, bnb_4bit_quant_type="nf4", bnb_4bit_compute_dtype=torch.bfloat16, bnb_4bit_use_double_quant=True,)
# 2. 加载量化模型model = AutoModelForCausalLM.from_pretrained( "meta-llama/Llama-2-7b-hf", quantization_config=bnb_config, device_map="auto",)
# 3. 准备模型用于 k-bit 训练model = prepare_model_for_kbit_training(model)
# 4. LoRA 配置lora_config = LoraConfig( r=64, # QLoRA 推荐更高的秩 lora_alpha=16, target_modules=[ "q_proj", "k_proj", "v_proj", "o_proj", "gate_proj", "up_proj", "down_proj" ], lora_dropout=0.05, bias="none", task_type="CAUSAL_LM",)
# 5. 创建 QLoRA 模型model = get_peft_model(model, lora_config)
# 现在只需 ~6GB 显存即可微调 7B 模型!import torchimport torch.nn as nn
class LoRALayer(nn.Module): """LoRA 适配层"""
def __init__( self, in_features: int, out_features: int, rank: int = 8, alpha: float = 16, dropout: float = 0.1 ): super().__init__() self.rank = rank self.alpha = alpha self.scaling = alpha / rank
# 原始权重 (冻结) self.weight = nn.Parameter( torch.randn(out_features, in_features), requires_grad=False )
# LoRA 矩阵 self.lora_A = nn.Parameter(torch.randn(rank, in_features)) self.lora_B = nn.Parameter(torch.zeros(out_features, rank)) self.dropout = nn.Dropout(dropout)
# 初始化 nn.init.kaiming_uniform_(self.lora_A, a=math.sqrt(5)) nn.init.zeros_(self.lora_B)
def forward(self, x: torch.Tensor) -> torch.Tensor: # 原始前向 result = x @ self.weight.T
# LoRA 增量 lora_out = self.dropout(x) @ self.lora_A.T @ self.lora_B.T result += lora_out * self.scaling
return result
def merge_weights(self): """合并 LoRA 到原始权重""" self.weight.data += (self.lora_B @ self.lora_A) * self.scaling| 方法 | 特点 | 适用场景 |
|---|---|---|
| LoRA | 基础版,添加低秩矩阵 | 通用微调 |
| QLoRA | LoRA + 4bit 量化 | 显存受限 |
| DoRA | 分解权重为方向和大小 | 更接近全量微调 |
| AdaLoRA | 自适应调整秩 | 自动化调参 |
| LoRA+ | 不同学习率 for A 和 B | 更快收敛 |