LoRA 技术 #
什么是 LoRA? #
LoRA(Low-Rank Adaptation)是一种参数高效的微调方法,通过在预训练模型的权重矩阵旁添加低秩矩阵来实现模型适配。
text
┌─────────────────────────────────────────────────────────────┐
│ LoRA 核心思想 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 传统微调:更新所有参数 │
│ W' = W + ΔW │
│ 参数量:d × k │
│ │
│ LoRA:添加低秩分解 │
│ W' = W + BA │
│ 参数量:d × r + r × k (r << min(d, k)) │
│ │
│ 示例:d=4096, k=4096, r=16 │
│ ├── 传统:16,777,216 参数 │
│ └── LoRA:131,072 参数(减少 128 倍) │
│ │
└─────────────────────────────────────────────────────────────┘
LoRA 原理 #
数学基础 #
text
假设预训练权重矩阵 W ∈ R^(d×k)
微调时的权重更新 ΔW 可以分解为:
ΔW = BA
其中:
├── B ∈ R^(d×r):降维矩阵
├── A ∈ R^(r×k):升维矩阵
└── r << min(d, k):低秩约束
前向传播:
h = Wx + ΔWx = Wx + BAx
初始化:
├── A:随机高斯初始化
└── B:零初始化(初始时 ΔW = 0)
架构图 #
text
┌─────────────────────────────────────────────────────────────┐
│ LoRA 架构 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 输入 x │
│ │ │
│ ┌───────┴───────┐ │
│ │ │ │
│ ▼ ▼ │
│ ┌───────┐ ┌───────┐ │
│ │ W │ │ A │ │
│ │(冻结) │ │ r×k │ │
│ └───┬───┘ └───┬───┘ │
│ │ │ │
│ │ ▼ │
│ │ ┌───────┐ │
│ │ │ B │ │
│ │ │ d×r │ │
│ │ └───┬───┘ │
│ │ │ │
│ └───────┬───────┘ │
│ │ │
│ ▼ │
│ 输出 h │
│ │
└─────────────────────────────────────────────────────────────┘
LoRA 配置 #
基础配置 #
python
from peft import LoraConfig, get_peft_model, TaskType
lora_config = LoraConfig(
task_type=TaskType.CAUSAL_LM,
r=16,
lora_alpha=32,
lora_dropout=0.1,
target_modules=["q_proj", "v_proj"],
bias="none",
)
参数详解 #
text
┌─────────────────────────────────────────────────────────────┐
│ LoRA 参数说明 │
├─────────────────────────────────────────────────────────────┤
│ │
│ r(rank) │
│ ├── 低秩矩阵的秩 │
│ ├── 常用值:4、8、16、32、64 │
│ ├── 越大:效果越好,参数越多 │
│ └── 推荐:16-64 │
│ │
│ lora_alpha │
│ ├── 缩放因子 │
│ ├── 实际缩放:lora_alpha / r │
│ ├── 常用值:r 的 1-2 倍 │
│ └── 推荐:32(r=16 时) │
│ │
│ lora_dropout │
│ ├── Dropout 比例 │
│ ├── 防止过拟合 │
│ ├── 常用值:0.05-0.1 │
│ └── 推荐:0.05 │
│ │
│ target_modules │
│ ├── 应用 LoRA 的模块 │
│ ├── 常用:q_proj, v_proj, k_proj, o_proj │
│ ├── 更多模块:gate_proj, up_proj, down_proj │
│ └── 推荐:["q_proj", "v_proj"] 或全部注意力模块 │
│ │
│ bias │
│ ├── 偏置项处理 │
│ ├── "none":不训练偏置 │
│ ├── "all":训练所有偏置 │
│ └── "lora_only":只训练 LoRA 偏置 │
│ │
└─────────────────────────────────────────────────────────────┘
不同任务配置 #
python
causal_lm_config = LoraConfig(
task_type=TaskType.CAUSAL_LM,
r=16,
lora_alpha=32,
target_modules=["q_proj", "k_proj", "v_proj", "o_proj"],
lora_dropout=0.05,
)
seq2seq_config = LoraConfig(
task_type=TaskType.SEQ_2_SEQ_LM,
r=8,
lora_alpha=16,
target_modules=["q", "v"],
lora_dropout=0.1,
)
classification_config = LoraConfig(
task_type=TaskType.SEQ_CLS,
r=8,
lora_alpha=16,
target_modules=["q_proj", "v_proj"],
lora_dropout=0.1,
)
LoRA 应用 #
应用到模型 #
python
from transformers import AutoModelForCausalLM
from peft import get_peft_model
model = AutoModelForCausalLM.from_pretrained(
"Qwen/Qwen2-7B",
torch_dtype=torch.float16,
device_map="auto"
)
model = get_peft_model(model, lora_config)
model.print_trainable_parameters()
输出:
trainable params: 4,194,304 || all params: 7,000,000,000 || trainable%: 0.06%
选择目标模块 #
text
常见目标模块:
注意力层:
├── q_proj:Query 投影
├── k_proj:Key 投影
├── v_proj:Value 投影
├── o_proj:Output 投影
└── 推荐:至少包含 q_proj 和 v_proj
MLP 层:
├── gate_proj:门控投影
├── up_proj:上投影
├── down_proj:下投影
└── 推荐:追求更好效果时添加
不同模型模块名称:
├── LLaMA:q_proj, k_proj, v_proj, o_proj
├── Qwen:q_proj, k_proj, v_proj, o_proj
├── Mistral:q_proj, k_proj, v_proj, o_proj
└── BERT:query, key, value
python
def find_all_linear_names(model):
cls = torch.nn.Linear
lora_module_names = set()
for name, module in model.named_modules():
if isinstance(module, cls):
names = name.split('.')
lora_module_names.add(names[0] if len(names) == 1 else names[-1])
if 'lm_head' in lora_module_names:
lora_module_names.remove('lm_head')
return list(lora_module_names)
modules = find_all_linear_names(model)
print(f"可用的线性层: {modules}")
LoRA 训练 #
训练配置 #
python
from transformers import TrainingArguments, Trainer
training_args = TrainingArguments(
output_dir="./lora-output",
num_train_epochs=3,
per_device_train_batch_size=4,
gradient_accumulation_steps=4,
learning_rate=2e-4,
weight_decay=0.01,
warmup_ratio=0.1,
lr_scheduler_type="cosine",
logging_steps=10,
save_strategy="epoch",
fp16=True,
gradient_checkpointing=True,
optim="adamw_8bit",
)
trainer = Trainer(
model=model,
args=training_args,
train_dataset=train_dataset,
eval_dataset=eval_dataset,
)
trainer.train()
保存和加载 #
python
model.save_pretrained("./my-lora-model")
from peft import PeftModel
base_model = AutoModelForCausalLM.from_pretrained(
"Qwen/Qwen2-7B",
torch_dtype=torch.float16,
device_map="auto"
)
model = PeftModel.from_pretrained(base_model, "./my-lora-model")
LoRA 合并 #
合并到基础模型 #
python
merged_model = model.merge_and_unload()
merged_model.save_pretrained("./merged-model")
tokenizer.save_pretrained("./merged-model")
多 LoRA 合并 #
python
from peft import PeftModel
base_model = AutoModelForCausalLM.from_pretrained("Qwen/Qwen2-7B")
model1 = PeftModel.from_pretrained(base_model, "./lora-1")
model2 = PeftModel.from_pretrained(base_model, "./lora-2")
merged_model = model1.merge_and_unload()
def merge_lora_weights(model1, model2, weight=0.5):
for name, param in model1.named_parameters():
if 'lora' in name:
param2 = model2.state_dict()[name]
param.data = weight * param.data + (1 - weight) * param2.data
return model1
merged = merge_lora_weights(model1, model2, weight=0.5)
QLoRA #
什么是 QLoRA? #
text
QLoRA = Quantized LoRA
核心思想:
├── 将基础模型量化为 4-bit
├── 使用 LoRA 进行微调
├── 大幅降低显存需求
└── 效果接近全量微调
优势:
├── 显存需求极低
├── 7B 模型只需 8GB 显存
├── 个人可微调大模型
└── 效果接近 LoRA
QLoRA 配置 #
python
from transformers import BitsAndBytesConfig, AutoModelForCausalLM
from peft import LoraConfig, get_peft_model
bnb_config = BitsAndBytesConfig(
load_in_4bit=True,
bnb_4bit_quant_type="nf4",
bnb_4bit_compute_dtype=torch.float16,
bnb_4bit_use_double_quant=True,
)
model = AutoModelForCausalLM.from_pretrained(
"Qwen/Qwen2-7B",
quantization_config=bnb_config,
device_map="auto"
)
lora_config = LoraConfig(
r=64,
lora_alpha=16,
target_modules=["q_proj", "k_proj", "v_proj", "o_proj"],
lora_dropout=0.05,
bias="none",
task_type="CAUSAL_LM"
)
model = get_peft_model(model, lora_config)
QLoRA 参数 #
text
┌─────────────────────────────────────────────────────────────┐
│ QLoRA 参数说明 │
├─────────────────────────────────────────────────────────────┤
│ │
│ load_in_4bit │
│ ├── 启用 4-bit 量化 │
│ └── 显存减少约 75% │
│ │
│ bnb_4bit_quant_type │
│ ├── 量化类型 │
│ ├── "nf4":NormalFloat4(推荐) │
│ └── "fp4":Float4 │
│ │
│ bnb_4bit_compute_dtype │
│ ├── 计算数据类型 │
│ ├── torch.float16(推荐) │
│ └── torch.bfloat16 │
│ │
│ bnb_4bit_use_double_quant │
│ ├── 双重量化 │
│ ├── 进一步减少显存 │
│ └── 推荐开启 │
│ │
└─────────────────────────────────────────────────────────────┘
LoRA 最佳实践 #
Rank 选择 #
text
rank 选择建议:
小数据集(<10K):
├── r = 8-16
├── 避免过拟合
└── 参数量适中
中等数据集(10K-100K):
├── r = 16-32
├── 平衡效果和参数
└── 推荐起点
大数据集(>100K):
├── r = 32-64
├── 追求更好效果
└── 可尝试更大 rank
Alpha 设置 #
text
alpha 选择建议:
标准设置:
├── lora_alpha = 2 × r
├── 例如:r=16, alpha=32
└── 最常用配置
保守设置:
├── lora_alpha = r
├── 缩放因子为 1
└── 更稳定的训练
激进设置:
├── lora_alpha = 4 × r
├── 更大的更新幅度
└── 可能更快收敛
目标模块选择 #
text
目标模块选择策略:
最小配置(快速实验):
├── ["q_proj", "v_proj"]
├── 参数最少
└── 效果基础
标准配置(推荐):
├── ["q_proj", "k_proj", "v_proj", "o_proj"]
├── 覆盖所有注意力
└── 效果好
完整配置(追求效果):
├── ["q_proj", "k_proj", "v_proj", "o_proj",
│ "gate_proj", "up_proj", "down_proj"]
├── 覆盖注意力和 MLP
└── 效果最好,参数最多
LoRA 调试 #
检查可训练参数 #
python
def print_trainable_parameters(model):
trainable_params = 0
all_param = 0
for _, param in model.named_parameters():
all_param += param.numel()
if param.requires_grad:
trainable_params += param.numel()
print(
f"trainable params: {trainable_params:,} || "
f"all params: {all_param:,} || "
f"trainable%: {100 * trainable_params / all_param:.4f}%"
)
print_trainable_parameters(model)
检查 LoRA 权重 #
python
def inspect_lora_weights(model):
for name, param in model.named_parameters():
if 'lora' in name:
print(f"{name}: {param.shape}, requires_grad={param.requires_grad}")
inspect_lora_weights(model)
常见问题 #
LoRA 效果不好 #
text
问题:LoRA 微调效果不如预期
解决方案:
1. 增加 rank
r=32 或 r=64
2. 增加目标模块
target_modules=["q_proj", "k_proj", "v_proj", "o_proj"]
3. 调整学习率
learning_rate=1e-4 到 5e-4
4. 增加训练数据
数据量和质量是关键
5. 检查数据格式
确保数据格式正确
显存仍然不足 #
text
问题:使用 LoRA 后显存仍然不足
解决方案:
1. 使用 QLoRA
load_in_4bit=True
2. 减小批次大小
per_device_train_batch_size=1
3. 增加梯度累积
gradient_accumulation_steps=16
4. 启用梯度检查点
gradient_checkpointing=True
5. 使用 8-bit 优化器
optim="adamw_8bit"
下一步 #
现在你已经掌握了 LoRA 技术,接下来学习 PEFT 方法,了解更多的参数高效微调技术!
最后更新:2026-04-05