PEFT 框架全景:从生态定位到代码架构的整体观
PEFT 框架全景:从生态定位到代码架构的整体观
篇 1/3 · 整体观
"先见森林,后见树木。"本篇只回答一个问题:PEFT 是什么、长什么样、如何与生态协作。读完本篇,你会拿到一张地图,知道每一块代码该去哪里找。论文公式与算法细节留给篇 2,设计哲学留给篇 3。
目录
- 导言:站在 HuggingFace 生态的肩上
- 第一章:生态定位 —— PEFT 在 HF 宇宙中的坐标
- 第二章:代码地图 —— 38+ 方法的统一布局
- 第三章:架构分层 —— 三层抽象的递进
- 第四章:注册机制 —— 38+ 方法如何被一键接入
- 第五章:核心 API —— 用户视角的全景
- 使用指南与典型案例
- 总结:把地图装进脑子里
导言:站在 HuggingFace 生态的肩上
0.1 大模型微调的两堵墙
当模型从 7B 涨到 70B、175B,全量微调开始撞上两堵墙:
| 墙 | 表现 | 数字 |
|---|---|---|
| 存储墙 | 每个下游任务都要存一份完整副本 | GPT-3 175B 全量微调 = 350 GB / 任务 |
| 显存墙 | 训练时需要同时存权重、梯度、Adam 二阶矩 | Llama-65B 全量微调 > 780 GB GPU 内存 |
更可怕的是:百万用户 × 1 个任务 / 用户 × 350 GB / 任务 = 350 PB。这在工程上是不可行的。
0.2 PEFT 的"四两拨千斤"哲学
Parameter-Efficient Fine-Tuning(PEFT) 的核心命题是:
在冻结大模型 99%+ 参数的前提下,仅训练极少额外参数(0.01%~1%),即可逼近全量微调的效果。
PEFT 库([huggingface/peft](file:///workspace/README.md))就是这一哲学的工程化落地:它把过去十年所有"参数高效微调"的研究成果(LoRA、Prefix-Tuning、IA³、AdaLoRA、DoRA、VeRA、OFT、BOFT、FourierFT……)统一封装成一个可插拔的 Python 库,让任何一个 transformers 模型都能用 5 行代码完成 PEFT 化改造。
0.3 三层认知的递进
| 层级 | 篇 | 你能回答的问题 |
|---|---|---|
| L1 用 | 篇 1(本篇) | PEFT 长什么样?怎么用? |
| L2 改 | 篇 2 | 这个公式怎么来的?代码怎么对应? |
| L3 创 | 篇 3 | 为什么这样设计?怎么造一个新方法? |
读完本篇,你会拿到一张"地图";篇 2 给你"放大镜"看公式细节;篇 3 给你"哲学刀"剖析设计本质。
0.4 与生态的协作概览
PEFT 从不孤立运行,它生于 HuggingFace 生态、长于生态。下面这张图(配图 1-1:HuggingFace 生态协作图)展示了 PEFT 与其他四大件的关系:
图 1-1 说明:用户代码同时引用 transformers 和 peft;peft 通过 get_peft_model 把基础模型包成 PeftModel(保留 base_model 冻结 + 注入 adapter 可训练)。训练时 accelerate 负责 FSDP/DeepSpeed 分布式,diffusers 在 SD/Flux 场景复用同一套 LoRA 适配器,trl 的 SFTTrainer 内置 PEFT 集成。PEFT 是"插件",不是"主干"——主干始终是 transformers。
第一章:生态定位 —— PEFT 在 HF 宇宙中的坐标
1.1 PEFT 解决的核心痛点
| 痛点 | 传统解法 | PEFT 解法 |
|---|---|---|
| 显存不够 | 8 比特 Adam / 梯度累加 | LoRA + QLoRA:4 bit 量化 + LoRA 适配器 |
| 多任务存储爆炸 | 每任务一份完整模型 | 共享 base + 每任务一份 adapter(MB 级) |
| 部署推理慢 | Adapter 层增加延迟 | LoRA 训练后可 merge 回原模型,零推理开销 |
| 灾难性遗忘 | EWC / L2 正则 | OFT 正交变换保留神经元间角度(保语义) |
| 任务切换成本高 | 重载整个模型 | set_adapter 一行切换 |
1.2 显存对比表(A100 80GB)
来自 [/workspace/README.md](file:///workspace/README.md) 的真实数据:
| 模型规模 | 全量微调 | LoRA | LoRA + DS CPU offloading |
|---|---|---|---|
| 3B | 60 GB | 24 GB | 4 GB |
| 7B | 120 GB | 32 GB | 8 GB |
| 12B | 192 GB | 48 GB | 12 GB |
关键观察:LoRA + DeepSpeed CPU offloading 让 12B 模型能在 12 GB 显存上训练——这是消费级 GPU(RTX 4090 24GB)完全跑得动的量级。
1.3 与 transformers 的接口契约
PEFT 与 transformers 的契约面只有两个:
# 契约 1:训练前
from peft import LoraConfig, get_peft_model
from transformers import AutoModelForCausalLM
base = AutoModelForCausalLM.from_pretrained("Qwen/Qwen2.5-3B")
peft_model = get_peft_model(base, LoraConfig(...))
# 契约 2:推理时
from peft import AutoPeftModelForCausalLM
model = AutoPeftModelForCausalLM.from_pretrained("path/to/adapter")
实现细节在 [peft_model.py](file:///workspace/src/peft/peft_model.py) 第 422 行 from_pretrained 和 [mapping_func.py](file:///workspace/src/peft/mapping_func.py) 的 get_peft_model。
1.4 与 accelerate 的协作:DeepSpeed / FSDP
peft 不重写分布式训练逻辑,所有 ZeRO-3 / FSDP / Megatron TP 都委托给 accelerate。以 [examples/sft/run_peft_qlora_deepspeed_stage3.sh](file:///workspace/examples/sft/run_peft_qlora_deepspeed_stage3.sh) 为例:
accelerate launch train.py \
--config configs/deepspeed_config_z3_qlora.yaml \
--use_peft --lora_r 16 --lora_alpha 32
peft 内部通过 [peft/utils/integrations.py](file:///workspace/src/peft/utils/integrations.py) 的 hook 在 accelerate 启动时注入 LoRA 的 offload 行为。
1.5 与 diffusers 的协作:Stable Diffusion LoRA
diffusers 的 UNet / DiT 模型可以原样塞进 get_peft_model,peft 会自动识别 CrossAttention 的 to_q / to_k / to_v / to_out 模块并注入 LoRA。典型场景见 [examples/lora_dreambooth/train_dreambooth.py](file:///workspace/examples/lora_dreambooth/train_dreambooth.py)。
1.6 与 trl 的协作:SFT 流水线
trl 的 SFTTrainer 直接接受 peft_config 参数,内部完成 get_peft_model 包装。完整示例见 [examples/sft/train.py](file:///workspace/examples/sft/train.py):
from trl import SFTTrainer
from peft import LoraConfig
trainer = SFTTrainer(
model=base_model,
peft_config=LoraConfig(r=16, lora_alpha=32, target_modules="all-linear"),
...
)
第二章:代码地图 —— 38+ 方法的统一布局
2.1 src/peft/ 顶层结构
图 1-2 说明:src/peft/ 是一个"小而美"的包——核心模块只有 5 个文件,工具集只有 11 个文件,但 tuners/ 下塞了 38+ 个独立子包。这种"瘦核心、胖周边"的结构是 PEFT 可扩展性的根:新增一个方法只需在 tuners/ 下加一个文件夹,不需要动核心。
2.2 tuners/ 的"三件套"约定
每个 tuner 文件夹几乎都遵循同一种"三件套"结构:
| 文件 | 角色 | 内容 |
|---|---|---|
config.py | 配置类 | 继承 PeftConfig,定义该方法的所有超参 |
layer.py | 层实现 | 继承 BaseTunerLayer,实现具体的 forward / merge / init |
model.py | 模型实现 | 继承 BaseTuner,实现 _create_and_replace 决定何时注入 |
举例:LoRA 的三件套是 [lora/config.py](file:///workspace/src/peft/tuners/lora/config.py)(373 行 LoraConfig)、[lora/layer.py](file:///workspace/src/peft/tuners/lora/layer.py)(103 行 LoraLayer + 814 行 Linear)、[lora/model.py](file:///workspace/src/peft/tuners/lora/model.py)(88 行 LoraModel)。
LoRA 比其他方法多了一堆 backend 文件(bnb.py、gptq.py、awq.py、aqlm.py、hqq.py、eetq.py、inc.py、torchao.py、te.py),用于支持各种量化 backend——这是 LoRA 作为"母体"的特权。
2.3 utils/ 工具集
| 文件 | 用途 |
|---|---|
[peft_types.py](file:///workspace/src/peft/utils/peft_types.py) | PeftType 枚举(44 种)+ TaskType + register_peft_method 装饰器 |
[save_and_load.py](file:///workspace/src/peft/utils/save_and_load.py) | get_peft_model_state_dict / set_peft_model_state_dict |
[loftq_utils.py](file:///workspace/src/peft/utils/loftq_utils.py) | LoftQ 初始化(同时量化权重 + 初始化 A/B) |
[quantization_utils.py](file:///workspace/src/peft/utils/quantization_utils.py) | 量化后端分发(bnb / gptq / awq / eetq / hqq / inc / torchao) |
[merge_utils.py](file:///workspace/src/peft/utils/merge_utils.py) | 适配器合并(用于多 adapter 加权混合) |
[hotswap.py](file:///workspace/src/peft/utils/hotswap.py) | 运行时热替换适配器(不重启进程切换 adapter) |
[incremental_pca.py](file:///workspace/src/peft/utils/incremental_pca.py) | 增量 PCA,用于 EVA 初始化 |
[other.py](file:///workspace/src/peft/utils/other.py) | prepare_model_for_kbit_training 等杂项 |
[transformers_weight_conversion.py](file:///workspace/src/peft/utils/transformers_weight_conversion.py) | 适配器权重与 transformers 原生格式互转 |
2.4 optimizers/:两个特殊优化器
| 文件 | 方法 | 论文 | 原理 |
|---|---|---|---|
[lorafa.py](file:///workspace/src/peft/optimizers/lorafa.py) | LoRA-FA | “LoRA-FA: Memory Efficient Low-Rank Adaptation” | 冻结 A 的梯度,只反传到 B,省一半显存 |
[loraplus.py](file:///workspace/src/peft/optimizers/loraplus.py) | LoRA+ | Hayou et al. 2024 | 给 A、B 设不同学习率(λ_B = 16 × λ_A) |
2.5 测试、示例、文档的三角
tests/ ← 38+ 个 test_<method>.py,每种方法独立测试
examples/ ← 60+ 个示例目录,每种方法都有可运行 demo
docs/source/ ← Sphinx 文档树,方法页 + API 页 + 开发指南
method_comparison/ ← 横向对比所有方法在 MetaMathQA/Flux 上的效果
这种"三角"保证了:每个新方法落地时,测试、示例、文档、横向对比必须同步更新,PR 才会被合并(见 [/workspace/.ai/AGENTS.md](file:///workspace/.ai/AGENTS.md))。
第三章:架构分层 —— 三层抽象的递进
PEFT 的精髓在于把"如何注入适配器"这件事抽象成了三层。这一层抽象是 PEFT 能容纳 44 种方法的根。
3.1 三层抽象的层次关系
图 1-3 说明:用户面对 PeftModel(外壳),它持有一个 BaseTuner 实例(骨架);BaseTuner 在 inject_adapter 中遍历模型,把每个匹配的 nn.Module 替换成一个 BaseTunerLayer 实例(细胞)。三层各司其职:外壳管生命周期、骨架管注入流程、细胞管具体前向。
3.2 第一层:PeftModel —— 用户面向的"壳"
定义在 [peft_model.py](file:///workspace/src/peft/peft_model.py) 第 94 行:
class PeftModel(PushToHubMixin, torch.nn.Module):
def __init__(self, model, peft_config, adapter_name="default", ...):
super().__init__()
self.active_adapter = adapter_name
self.peft_type = peft_config.peft_type
self.special_peft_forward_args = {"adapter_names", "alora_offsets"}
self._is_prompt_learning = peft_config.is_prompt_learning
if self._is_prompt_learning:
self._peft_config = {adapter_name: peft_config}
self.base_model = model
self.add_adapter(adapter_name, peft_config, ...)
else:
self._peft_config = None
cls = PEFT_TYPE_TO_TUNER_MAPPING[peft_config.peft_type]
self.base_model = cls(model, {adapter_name: peft_config}, adapter_name)
关键设计:PeftModel 不继承 PreTrainedModel,而是把原模型作为 base_model 持有。这保证了 PEFT 是"包装器"而非"继承者",原模型的 forward/generate 等方法透明保留(通过 __getattr__ 转发,见 peft_model.py:989-996)。
PeftModel 还有 6 个任务子类,按 task_type 自动分发:
| 子类 | 行号 | 任务 |
|---|---|---|
PeftModelForSequenceClassification | 1829 | 文本分类 |
PeftModelForCausalLM | 2053 | 因果 LM(最常用) |
PeftModelForSeq2SeqLM | 2197 | Seq2Seq |
PeftModelForTokenClassification | 2348 | Token 分类 |
PeftModelForQuestionAnswering | 2612 | QA |
PeftModelForFeatureExtraction | 2844 | 特征抽取 |
3.3 第二层:BaseTuner —— 方法级的"骨架"
定义在 [tuners_utils.py](file:///workspace/src/peft/tuners/tuners_utils.py) 第 236 行。每个具体方法(如 LoraModel)必须:
-
声明三个类级属性:
class LoraModel(BaseTuner): prefix: str = "lora_" # 方法专属前缀 tuner_layer_cls = LoraLayer # 对应的层类型 target_module_mapping = TRANSFORMERS_MODELS_TO_LORA_TARGET_MODULES_MAPPING # 自动推断 target_modules -
实现
_create_and_replace(抽象方法):决定如何把目标模块替换为 PEFT 层。 -
可选覆写
_prepare_adapter_config、_prepare_model、_check_merge_allowed等 hook。
BaseTuner 的真正"大招"是 [inject_adapter](file:///workspace/src/peft/tuners/tuners_utils.py) 方法(行 753-1070):它实现了"扫描模型 → 匹配模块 → 调子类创建 → 错误检查 → housekeeping"的完整流程,是 PEFT 可扩展性的真正引擎。
3.4 第三层:BaseTunerLayer —— 层级的"细胞"
定义在 [tuners_utils.py](file:///workspace/src/peft/tuners/tuners_utils.py) 第 1408 行。每个具体层(如 LoraLayer)必须:
- 声明
adapter_layer_names(如("lora_A", "lora_B"))和other_param_names,这些是nn.ModuleDict,每个 adapter 一个 key。 - 实现
forward(x)(抽象方法):决定 PEFT 增量如何叠加到 base_layer 输出。 - 实现
merge/unmerge:把 ΔW 加到 / 减回 base_layer.weight。
BaseTunerLayer 提供了所有"通用层级行为":set_adapter、delete_adapter、enable_adapters、get_base_layer、_freeze_non_trainable_peft_weights。这些行为对所有方法都一样,所以抽象到基类。
3.5 注入引擎 inject_adapter 的四阶段流程
图 1-4 说明:inject_adapter 是 PEFT 可扩展性的真正核心——它把"扫描 + 匹配 + 创建 + 检查"四步模板化,让每个新方法只需关注 _create_and_replace 这一个 hook。
阶段 A:准备(tuners_utils.py:783-864)
- transformers v5 兼容:自动转换
target_modules以匹配新架构名 - 多 adapter 冲突检查
target_modules=None时用target_module_mapping自动推断'all-linear'简写展开为所有Linear/Conv1D模块名- 大量 target_modules 时的最小化优化
阶段 B:匹配 & 创建(tuners_utils.py:866-961)
- 遍历
named_modules - 对每个 key 调
check_target_module_exists(支持精确匹配、后缀匹配、正则匹配) - 命中后调子类
_create_and_replace,由它实例化BaseTunerLayer并替换原模块 - 已存在的 PEFT 层走
update_layer复用(添加新 adapter 而非替换)
阶段 C:错误检查(tuners_utils.py:963-1047)
- 全部 excluded → “Check your target_modules, exclude_modules and modules_to_save”
- 全部 unmatched 且无
target_modules→ “No target_modules passed…” - 部分 matched 部分 excluded → “No modules were targeted…”
阶段 D:housekeeping(tuners_utils.py:1049-1070)
set_adapter重设激活,防止新 adapter 在它独占的层上意外被激活_mark_only_adapters_as_trainable:除prefix开头的参数外,全部冻结set_additional_trainable_modules:处理modules_to_save(如分类头)
第四章:注册机制 —— 38+ 方法如何被一键接入
4.1 注册表中枢:四张映射表
定义在 [mapping.py](file:///workspace/src/peft/mapping.py) 第 30-33 行:
PEFT_TYPE_TO_CONFIG_MAPPING = {}
PEFT_TYPE_TO_TUNER_MAPPING = {}
PEFT_TYPE_TO_PREFIX_MAPPING = {}
PEFT_TYPE_TO_MIXED_MODEL_MAPPING = {}
每张表都是 dict[PeftType, ...],键是枚举值,值是对应的类。这四张表是整个框架"按 peft_type 分发"的中枢。
4.2 PeftType 枚举
定义在 [utils/peft_types.py](file:///workspace/src/peft/utils/peft_types.py) 第 19-100 行,继承 str, enum.Enum(故 PeftType.LORA == "LORA" 为真,便于 JSON 序列化):
class PeftType(str, enum.Enum):
LORA = "LORA"
ADALORA = "ADALORA"
IA3 = "IA3"
PROMPT_TUNING = "PROMPT_TUNING"
P_TUNING = "P_TUNING"
PREFIX_TUNING = "PREFIX_TUNING"
# ... 共 44 种
4.3 register_peft_method 装饰器
定义在 [peft_types.py](file:///workspace/src/peft/utils/peft_types.py) 第 125-199 行。这是第三方注册新 PEFT 方法的统一入口。下面用代码注释展示完整流程:
图 1-5 说明:注册机制是"模板方法 + 注册表"模式——开发者只需在 PeftType 枚举里加一项,写好 FooConfig/FooModel/FooLayer 三件套,调用 @register_peft_method,整个 get_peft_model / PeftModel.from_pretrained / inject_adapter_in_model 全部自动可用。
4.4 实战:注册一个虚构的 FooTuner
# Step 1: 在 PeftType 枚举加一项
class PeftType(str, enum.Enum):
FOO = "FOO"
# Step 2: 写三件套
@dataclass
class FooConfig(PeftConfig):
rank: int = 8
def __post_init__(self):
self.peft_type = PeftType.FOO
class FooLayer(BaseTunerLayer):
adapter_layer_names = ("foo_A", "foo_B")
def forward(self, x):
result = self.base_layer(x)
for active in self.active_adapters:
result += self.foo_B[active](self.foo_A[active](x)) * self.scaling[active]
return result
class FooModel(BaseTuner):
prefix = "foo_"
tuner_layer_cls = FooLayer
target_module_mapping = {"llama": ["q_proj", "v_proj"]}
def _create_and_replace(self, ...):
new_module = FooLayer(target, adapter_name=adapter_name, ...)
self._replace_module(parent, target_name, new_module, target)
# Step 3: 注册
@register_peft_method(name="foo", prefix="foo_")
def _register():
return FooConfig, FooModel
之后用户代码完全无需改动:
from peft import get_peft_model
model = get_peft_model(base, FooConfig(rank=16))
# 自动调用 FooModel,自动注入 FooLayer
第五章:核心 API —— 用户视角的全景
5.1 一行魔法:get_peft_model
from peft import LoraConfig, get_peft_model
config = LoraConfig(
r=8, lora_alpha=16, lora_dropout=0.05,
target_modules=["q_proj", "v_proj"],
task_type="CAUSAL_LM"
)
peft_model = get_peft_model(model, config)
get_peft_model 内部做三件事:
- 从
PEFT_TYPE_TO_TUNER_MAPPING取出对应 tuner 类(如LoraModel) - 实例化
LoraModel(model, config, adapter_name="default"),触发inject_adapter - 包成
PeftModel(按task_type选子类)
5.2 加载时序:PeftModel.from_pretrained
图 1-6 说明:from_pretrained 的关键设计是"先注入随机权重 → 再覆盖真实权重"——这保证模型结构与 checkpoint 完全匹配,避免了直接 load_state_dict 时结构不一致的问题。
5.3 多适配器管理
PEFT 支持在同一 PeftModel 上挂多个 adapter:
# 添加 adapter
peft_model.add_adapter("adapter_b", LoraConfig(...))
# 加载已有 adapter 权重
peft_model.load_adapter("path/to/adapter_b", "adapter_b")
# 切换激活 adapter
peft_model.set_adapter("adapter_b")
# 删除 adapter
peft_model.delete_adapter("adapter_b")
实现细节:
- [
add_adapter](file:///workspace/src/peft/peft_model.py)(行 1092-1168):异常时回滚,避免脏状态 - [
set_adapter](file:///workspace/src/peft/peft_model.py)(行 1578-1601):逐层调用module.set_adapter(name),改requires_grad与_active_adapter - [
delete_adapter](file:///workspace/src/peft/peft_model.py)(行 1170-1187):仅删权重,不删 PEFT 层壳;彻底卸载用unload()
5.4 合并与卸载:merge_and_unload
# 训练后合并到 base,得到一个普通 nn.Module(推理零开销)
merged_model = peft_model.merge_and_unload()
实现见 [peft_model.py:696](file:///workspace/src/peft/peft_model.py),委托给 _unload_and_optionally_merge(行 641-694)。对每个 PEFT 层调 target.merge() 把 ΔW 加到 base_layer.weight,再用 get_base_layer() 替换回原模块。
5.5 上下文管理器:disable_adapter
# 临时关闭 adapter,跑 base_model 原始 forward
with peft_model.disable_adapter():
outputs = peft_model(inputs)
# 自动恢复
这个上下文管理器在调试、对照实验、EBM 训练中极其有用。
使用指南与典型案例
案例 1:用 LoRA 微调 Qwen2.5-3B 做 SFT
from transformers import AutoModelForCausalLM, AutoTokenizer, TrainingArguments
from peft import LoraConfig, get_peft_model
from datasets import load_dataset
from trl import SFTTrainer
# 1. 加载 base 模型
model = AutoModelForCausalLM.from_pretrained(
"Qwen/Qwen2.5-3B-Instruct", torch_dtype="auto", device_map="auto"
)
tokenizer = AutoTokenizer.from_pretrained("Qwen/Qwen2.5-3B-Instruct")
# 2. 配置 LoRA
peft_config = LoraConfig(
r=8, lora_alpha=16, lora_dropout=0.05,
target_modules=["q_proj", "k_proj", "v_proj", "o_proj"],
bias="none", task_type="CAUSAL_LM"
)
# 3. 包成 PeftModel
model = get_peft_model(model, peft_config)
model.print_trainable_parameters()
# 输出: trainable params: 4,194,304 || all params: 3,081,287,680 || trainable%: 0.1361%
# 4. 训练
dataset = load_dataset("tatsu-lab/alpaca", split="train")
trainer = SFTTrainer(model=model, train_dataset=dataset, tokenizer=tokenizer)
trainer.train()
# 5. 保存
trainer.save_model("./qwen-lora-alpaca")
关键观察:3B 模型,0.1361% 参数可训练。这就是 PEFT 的魔法。
案例 2:用 QLoRA 在单卡 24GB 上微调 Llama-3-8B
from transformers import BitsAndBytesConfig, AutoModelForCausalLM
from peft import LoraConfig, get_peft_model, prepare_model_for_kbit_training
# 1. 4 bit 量化加载
bnb_config = BitsAndBytesConfig(
load_in_4bit=True,
bnb_4bit_quant_type="nf4", # NormalFloat 4 (QLoRA 论文核心)
bnb_4bit_compute_dtype="bfloat16", # 反量化后的计算精度
bnb_4bit_use_double_quant=True, # Double Quantization
)
model = AutoModelForCausalLM.from_pretrained(
"meta-llama/Meta-Llama-3-8B",
quantization_config=bnb_config,
device_map="auto"
)
# 2. 准备 kbit 训练(冻结所有参数、启用梯度检查点、把 LayerNorm 转 fp32)
model = prepare_model_for_kbit_training(model)
# 3. 注入 LoRA
config = LoraConfig(
r=16, lora_alpha=32, lora_dropout=0.05,
target_modules="all-linear", # 简写:所有 Linear 层
bias="none", task_type="CAUSAL_LM"
)
model = get_peft_model(model, config)
# Llama-3-8B 现在可以在 24GB 显卡上训练
关键观察:8B 模型 + QLoRA = 单卡 24GB 可训练。这是 democratize LLM 微调的关键技术。
案例 3:多适配器权重混合(add_weighted_adapter)
# 已有三个 LoRA adapter:style_a, style_b, style_c
peft_model.add_adapter("style_a", LoraConfig(...))
peft_model.add_adapter("style_b", LoraConfig(...))
peft_model.add_adapter("style_c", LoraConfig(...))
# 加权混合(不需要训练)
peft_model.add_weighted_adapter(
adapters=["style_a", "style_b", "style_c"],
weights=[0.5, 0.3, 0.2],
adapter_name="mixed"
)
peft_model.set_adapter("mixed")
# 现在 forward 用的是 0.5 A + 0.3 B + 0.2 C 的混合 LoRA
案例 4:把 LoRA 合并回 base(merge_and_unload)
# 训练 + 验证后,合并以部署
merged_model = peft_model.merge_and_unload()
merged_model.save_pretrained("./merged-model")
# 现在 ./merged-model 是普通 transformers 模型
# 推理时无任何 adapter 开销,与全量微调后的模型结构完全一样
from transformers import AutoModelForCausalLM
inference_model = AutoModelForCausalLM.from_pretrained("./merged-model")
案例 5:使用 disable_adapter 做对照实验
# 同一 batch 内对比 LoRA vs 无 adapter
import torch
with torch.no_grad():
# 启用 adapter
outputs_peft = peft_model(inputs)
# 禁用 adapter(用 base 原始权重)
with peft_model.disable_adapter():
outputs_base = peft_model(inputs)
# 差异 = adapter 的贡献
delta = outputs_peft.logits - outputs_base.logits
总结:把地图装进脑子里
回到导言的问题:PEFT 是什么、长什么样、如何与生态协作?
我们用了五个章节回答:
- 生态定位:PEFT 是 HuggingFace 生态的"参数高效微调中央仓库",与 transformers / accelerate / diffusers / trl 协作
- 代码地图:
src/peft/瘦核心 +tuners/胖周边,每个方法"三件套"约定 - 架构分层:
PeftModel(壳)→BaseTuner(骨架)→BaseTunerLayer(细胞),inject_adapter是统一注入引擎 - 注册机制:四张映射表 +
register_peft_method装饰器,5 步注册一个新方法 - 核心 API:
get_peft_model/from_pretrained/add_adapter/set_adapter/merge_and_unload/disable_adapter
现在你已经有了一张地图。但地图只是起点——篇 2 会带你拿放大镜去看每个公式的具体推导,篇 3 会带你拿哲学刀去剖析设计本质。
篇 1 一句话总结:
PEFT 是 HuggingFace 用"四张映射表 + 三层抽象 + 一个注入引擎"装下了 44 种微调方法的工程奇迹——加新方法只需要在
tuners/下加一个文件夹。
下一篇 《PEFT 算法原理:从论文公式到代码实现的具体观》,我们将从 LoRA 论文公式 h = W₀x + BAx 开始,逐行对照代码。
更多推荐
所有评论(0)