PEFT 方法 #
PEFT 概述 #
PEFT(Parameter-Efficient Fine-Tuning)是一系列只训练少量参数就能达到接近全量微调效果的方法。
text
┌─────────────────────────────────────────────────────────────┐
│ PEFT 方法对比 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 方法 参数量 效果 训练速度 推理开销 │
│ ───────────────────────────────────────────────────────── │
│ Full FT 100% 最好 慢 无 │
│ LoRA 0.1-1% 很好 快 极小 │
│ QLoRA 0.1-1% 好 快 极小 │
│ Prefix Tuning 0.1% 好 快 有 │
│ Adapter 1-5% 好 中等 有 │
│ Prompt Tuning 0.01% 一般 最快 有 │
│ │
└─────────────────────────────────────────────────────────────┘
Prefix Tuning #
原理 #
text
┌─────────────────────────────────────────────────────────────┐
│ Prefix Tuning 原理 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 在输入序列前添加可学习的前缀向量 │
│ │
│ 输入:[prefix_1, prefix_2, ..., prefix_k, x_1, x_2, ...] │
│ │
│ 特点: │
│ ├── 只训练前缀向量 │
│ ├── 原始模型参数冻结 │
│ ├── 前缀长度通常为 10-100 │
│ └── 参数量极少 │
│ │
└─────────────────────────────────────────────────────────────┘
配置 #
python
from peft import PrefixTuningConfig, get_peft_model, TaskType
prefix_config = PrefixTuningConfig(
task_type=TaskType.CAUSAL_LM,
num_virtual_tokens=20,
prefix_projection=True,
prefix_projection_hidden_size=512,
)
model = get_peft_model(model, prefix_config)
model.print_trainable_parameters()
参数说明 #
text
num_virtual_tokens
├── 前缀长度
├── 常用值:10-100
├── 越长:效果越好,参数越多
└── 推荐:20-30
prefix_projection
├── 是否使用投影层
├── True:效果更好
└── 推荐:True
prefix_projection_hidden_size
├── 投影层隐藏维度
├── 常用值:512-1024
└── 推荐:512
Prompt Tuning #
原理 #
text
┌─────────────────────────────────────────────────────────────┐
│ Prompt Tuning 原理 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 在输入嵌入层添加可学习的软提示 │
│ │
│ 输入嵌入:[soft_prompt_1, ..., soft_prompt_k, x_1, ...] │
│ │
│ 特点: │
│ ├── 只训练软提示向量 │
│ ├── 最简单的 PEFT 方法 │
│ ├── 参数量最少 │
│ └── 效果相对较弱 │
│ │
└─────────────────────────────────────────────────────────────┘
配置 #
python
from peft import PromptTuningConfig, get_peft_model, TaskType
prompt_config = PromptTuningConfig(
task_type=TaskType.CAUSAL_LM,
num_virtual_tokens=20,
token_dim=4096,
num_transformer_submodules=1,
num_attention_heads=32,
num_layers=32,
)
model = get_peft_model(model, prompt_config)
初始化方式 #
python
from peft import PromptTuningInit
prompt_config = PromptTuningConfig(
task_type=TaskType.CAUSAL_LM,
num_virtual_tokens=20,
prompt_tuning_init=PromptTuningInit.TEXT,
prompt_tuning_init_text="分类以下文本的情感:",
tokenizer_name_or_path="Qwen/Qwen2-7B",
)
model = get_peft_model(model, prompt_config)
text
初始化方式:
RANDOM
├── 随机初始化
├── 简单直接
└── 需要更多训练
TEXT
├── 从文本初始化
├── 效果更好
└── 推荐使用
Adapter #
原理 #
text
┌─────────────────────────────────────────────────────────────┐
│ Adapter 原理 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 在 Transformer 层中插入小型神经网络 │
│ │
│ 结构: │
│ ├── 下投影:d → r │
│ ├── 激活函数 │
│ ├── 上投影:r → d │
│ └── 残差连接 │
│ │
│ 特点: │
│ ├── 只训练 Adapter 参数 │
│ ├── 原始模型参数冻结 │
│ ├── 结构简单 │
│ └── 效果稳定 │
│ │
└─────────────────────────────────────────────────────────────┘
配置 #
python
from peft import AdapterConfig, get_peft_model, TaskType
adapter_config = AdapterConfig(
task_type=TaskType.CAUSAL_LM,
r=8,
lora_alpha=16,
adapter_lora_alpha=16,
adapter_lora_dropout=0.1,
)
model = get_peft_model(model, adapter_config)
Adapter 类型 #
text
Bottleneck Adapter
├── 标准结构
├── 下投影-激活-上投影
└── 最常用
LoRA Adapter
├── 使用 LoRA 结构
├── 参数更少
└── 效果相近
Parallel Adapter
├── 并行插入
├── 推理更快
└── 效果相当
P-Tuning #
原理 #
text
┌─────────────────────────────────────────────────────────────┐
│ P-Tuning 原理 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 使用 LSTM/MLP 编码可学习的提示嵌入 │
│ │
│ 特点: │
│ ├── 比 Prompt Tuning 更强大 │
│ ├── 使用编码器生成提示 │
│ ├── 可以建模提示之间的依赖 │
│ └── 效果比 Prompt Tuning 好 │
│ │
└─────────────────────────────────────────────────────────────┘
配置 #
python
from peft import PromptEncoderConfig, get_peft_model, TaskType
p_tuning_config = PromptEncoderConfig(
task_type=TaskType.CAUSAL_LM,
num_virtual_tokens=20,
encoder_hidden_size=128,
)
model = get_peft_model(model, p_tuning_config)
IA³ #
原理 #
text
┌─────────────────────────────────────────────────────────────┐
│ IA³ 原理 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 通过可学习的缩放向量调整激活值 │
│ │
│ 公式: │
│ ├── 注意力:softmax(QK^T / √d) × V × l_v │
│ ├── FFN:FFN(x) × l_ffn │
│ └── l_v, l_ffn 是可学习的缩放向量 │
│ │
│ 特点: │
│ ├── 参数量极少 │
│ ├── 只需训练缩放向量 │
│ └── 效果出奇地好 │
│ │
└─────────────────────────────────────────────────────────────┘
配置 #
python
from peft import IA3Config, get_peft_model, TaskType
ia3_config = IA3Config(
task_type=TaskType.CAUSAL_LM,
target_modules=["k_proj", "v_proj", "down_proj"],
feedforward_modules=["down_proj"],
)
model = get_peft_model(model, ia3_config)
方法选择 #
按任务选择 #
text
文本生成:
├── 推荐:LoRA、Prefix Tuning
├── 效果好、训练快
└── 示例:对话、写作
文本分类:
├── 推荐:LoRA、Adapter
├── 效果稳定
└── 示例:情感分析、主题分类
序列标注:
├── 推荐:LoRA、Adapter
├── 适合结构化任务
└── 示例:NER、词性标注
问答系统:
├── 推荐:LoRA、Prefix Tuning
├── 理解和生成能力
└── 示例:知识问答、客服
按资源选择 #
text
极低资源(<8GB 显存):
├── QLoRA
├── Prompt Tuning
└── IA³
低资源(8-16GB 显存):
├── LoRA
├── QLoRA
└── Prefix Tuning
中等资源(16-32GB 显存):
├── LoRA(更大 rank)
├── Adapter
└── Prefix Tuning
高资源(>32GB 显存):
├── LoRA(大 rank)
├── 全量微调
└── 任何 PEFT 方法
按效果选择 #
text
追求最佳效果:
├── LoRA(大 rank)
├── 全量微调
└── 多方法组合
平衡效果和效率:
├── LoRA(中等 rank)
├── Adapter
└── Prefix Tuning
追求最快训练:
├── Prompt Tuning
├── IA³
└── 小 rank LoRA
PEFT 组合 #
多方法组合 #
python
from peft import get_peft_model, LoraConfig, PrefixTuningConfig
lora_config = LoraConfig(
r=16,
lora_alpha=32,
target_modules=["q_proj", "v_proj"],
)
model = get_peft_model(model, lora_config)
prefix_config = PrefixTuningConfig(
num_virtual_tokens=10,
prefix_projection=True,
)
model = get_peft_model(model, prefix_config)
多任务 PEFT #
python
from peft import PeftModel
base_model = AutoModelForCausalLM.from_pretrained("Qwen/Qwen2-7B")
task1_model = PeftModel.from_pretrained(base_model, "./task1-lora")
task2_model = PeftModel.from_pretrained(base_model, "./task2-lora")
def switch_task(model, task_name):
if task_name == "task1":
return task1_model
else:
return task2_model
PEFT 最佳实践 #
配置建议 #
text
LoRA:
├── rank: 16-64
├── alpha: 2 × rank
├── target: 所有注意力层
└── dropout: 0.05
Prefix Tuning:
├── num_virtual_tokens: 20-30
├── prefix_projection: True
└── hidden_size: 512
Adapter:
├── r: 8-16
├── dropout: 0.1
└── 位置: 注意力后
Prompt Tuning:
├── num_virtual_tokens: 20-50
├── init: TEXT
└── 适合简单任务
IA³:
├── target: k_proj, v_proj, down_proj
└── 极少参数
训练建议 #
text
学习率:
├── LoRA: 2e-4
├── Prefix Tuning: 1e-4
├── Adapter: 1e-4
├── Prompt Tuning: 3e-4
└── IA³: 2e-4
训练轮数:
├── 数据量大: 1-2 轮
├── 数据量中: 2-3 轮
└── 数据量小: 3-5 轮
批次大小:
├── 根据显存调整
├── 使用梯度累积
└── 等效批次 16-32
PEFT 调试 #
检查配置 #
python
def inspect_peft_config(model):
if hasattr(model, 'peft_config'):
for key, config in model.peft_config.items():
print(f"Config {key}:")
print(config)
inspect_peft_config(model)
检查可训练参数 #
python
def count_parameters_by_type(model):
params_by_type = {}
for name, param in model.named_parameters():
if param.requires_grad:
param_type = name.split('.')[0]
if param_type not in params_by_type:
params_by_type[param_type] = 0
params_by_type[param_type] += param.numel()
for param_type, count in params_by_type.items():
print(f"{param_type}: {count:,} parameters")
count_parameters_by_type(model)
常见问题 #
效果不如全量微调 #
text
解决方案:
1. 增加可训练参数
- LoRA: 增大 rank
- Prefix Tuning: 增加前缀长度
- Adapter: 增加瓶颈维度
2. 增加目标模块
- LoRA: 添加更多层
- Adapter: 插入更多位置
3. 调整学习率
- 适当提高学习率
- 使用不同的调度器
4. 增加训练数据
- 数据质量是关键
- 数据增强
推理速度慢 #
text
解决方案:
1. 合并权重
merged_model = model.merge_and_unload()
2. 选择无推理开销的方法
- LoRA: 合并后无开销
- 其他方法: 有额外开销
3. 量化推理
- 使用 4-bit/8-bit 量化
- 加速推理
下一步 #
现在你已经掌握了各种 PEFT 方法,接下来学习 性能优化,了解如何进一步优化训练性能!
最后更新:2026-04-05