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