PEFT 框架全景:从生态定位到代码架构的整体观

篇 1/3 · 整体观

"先见森林,后见树木。"本篇只回答一个问题:PEFT 是什么、长什么样、如何与生态协作。读完本篇,你会拿到一张地图,知道每一块代码该去哪里找。论文公式与算法细节留给篇 2,设计哲学留给篇 3。


目录


导言:站在 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 与其他四大件的关系:

HF Ecosystem

User Code

transformers
AutoModel

peft
get_peft_model

Base Model
(frozen)

Adapter
(trainable)

accelerate
Trainer / FSDP / DS

diffusers
SD/Flux LoRA

trl
SFTTrainer

图 1-1 说明:用户代码同时引用 transformerspeftpeft 通过 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) 的真实数据:

模型规模全量微调LoRALoRA + DS CPU offloading
3B60 GB24 GB4 GB
7B120 GB32 GB8 GB
12B192 GB48 GB12 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_modelpeft 会自动识别 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 流水线

trlSFTTrainer 直接接受 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/ 顶层结构

src/peft/

核心模块

tuners/
38+ 方法

utils/
工具集

optimizers/
自定义优化器

peft_model.py
PeftModel 主类

config.py
PeftConfig 基类

mapping.py
4 张注册表

auto.py
AutoPeftModel

mixed_model.py
PeftMixedModel

lora/

adalora/

ia3/

vera/

...共 38 个

save_and_load.py

loftq_utils.py

quantization_utils.py

merge_utils.py

hotswap.py

lorafa.py

loraplus.py

图 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.pygptq.pyawq.pyaqlm.pyhqq.pyeetq.pyinc.pytorchao.pyte.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 三层抽象的层次关系

持有

继承

继承

实例化

«user-facing»

PeftModel

+base_model: BaseTuner

+peft_config: dict

+forward(*args)

+from_pretrained(model_id)

+save_pretrained(save_dir)

+add_adapter(name, config)

+set_adapter(name)

+merge_and_unload()

«abstract»

BaseTuner

+prefix: str

+tuner_layer_cls: type

+target_module_mapping: dict

+inject_adapter(model, name)

+_create_and_replace()

+_mark_only_adapters_as_trainable()

«abstract»

BaseTunerLayer

+adapter_layer_names: tuple

+merged: bool

+_active_adapter: list

+merge()

+unmerge()

+set_adapter(name)

+delete_adapter(name)

+forward(x)

LoraModel

+prefix = "lora_"

+tuner_layer_cls = LoraLayer

+_create_and_replace()

LoraLayer

+lora_A: ModuleDict

+lora_B: ModuleDict

+scaling: dict

+update_layer(name, r, alpha)

图 1-3 说明:用户面对 PeftModel(外壳),它持有一个 BaseTuner 实例(骨架);BaseTunerinject_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 自动分发:

子类行号任务
PeftModelForSequenceClassification1829文本分类
PeftModelForCausalLM2053因果 LM(最常用)
PeftModelForSeq2SeqLM2197Seq2Seq
PeftModelForTokenClassification2348Token 分类
PeftModelForQuestionAnswering2612QA
PeftModelForFeatureExtraction2844特征抽取

3.3 第二层:BaseTuner —— 方法级的"骨架"

定义在 [tuners_utils.py](file:///workspace/src/peft/tuners/tuners_utils.py) 第 236 行。每个具体方法(如 LoraModel)必须:

  1. 声明三个类级属性:

    class LoraModel(BaseTuner):
        prefix: str = "lora_"                    # 方法专属前缀
        tuner_layer_cls = LoraLayer              # 对应的层类型
        target_module_mapping = TRANSFORMERS_MODELS_TO_LORA_TARGET_MODULES_MAPPING  # 自动推断 target_modules
    
  2. 实现 _create_and_replace(抽象方法):决定如何把目标模块替换为 PEFT 层。

  3. 可选覆写 _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)必须:

  1. 声明 adapter_layer_names(如 ("lora_A", "lora_B"))和 other_param_names,这些是 nn.ModuleDict,每个 adapter 一个 key。
  2. 实现 forward(x)(抽象方法):决定 PEFT 增量如何叠加到 base_layer 输出。
  3. 实现 merge / unmerge:把 ΔW 加到 / 减回 base_layer.weight。

BaseTunerLayer 提供了所有"通用层级行为":set_adapterdelete_adapterenable_adaptersget_base_layer_freeze_non_trainable_peft_weights。这些行为对所有方法都一样,所以抽象到基类。

3.5 注入引擎 inject_adapter 的四阶段流程

命中

排除

未命中

inject_adapter(model, adapter_name)

阶段 A: 准备

v5 兼容性转换

_check_new_adapter_config
_check_tied_modules

_prepare_adapter_config
(推断 target_modules)

_prepare_model
(layer_replication 等)

_maybe_include_all_linear
(展开 all-linear)

阶段 B: 匹配 & 创建

遍历 named_modules

check_target_module_exists?

_get_submodules
(parent, target, target_name)

_create_and_replace
(调子类)

加入 excluded_modules

加入 unmatched_modules

阶段 C: 错误检查

targeted_module_names 为空?

抛出精确错误

阶段 D: housekeeping

set_adapter(active)

_mark_only_adapters_as_trainable

set_additional_trainable_modules
(处理 modules_to_save)

注入完成

图 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:housekeepingtuners_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 方法的统一入口。下面用代码注释展示完整流程:

第三方开发者

@register_peft_method
(name='foo', prefix='foo_')

name 合法?
小写、不以_结尾

报错

PeftType.FOO 存在?

需先在枚举里加

写 PEFT_TYPE_TO_CONFIG_MAPPING

写 PEFT_TYPE_TO_TUNER_MAPPING

写 PEFT_TYPE_TO_PREFIX_MAPPING

is_mixed_compatible?

写 PEFT_TYPE_TO_MIXED_MODEL_MAPPING

完成

用户:get_peft_model(model, FooConfig())
自动可用

图 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 内部做三件事:

  1. PEFT_TYPE_TO_TUNER_MAPPING 取出对应 tuner 类(如 LoraModel
  2. 实例化 LoraModel(model, config, adapter_name="default"),触发 inject_adapter
  3. 包成 PeftModel(按 task_type 选子类)

5.2 加载时序:PeftModel.from_pretrained

BaseTunerLayer BaseTuner Tuner Mapping PeftConfig PeftModel.from_pretrained User BaseTunerLayer BaseTuner Tuner Mapping PeftConfig PeftModel.from_pretrained User loop [每个目标模块] from_pretrained(adapter_path) _get_peft_type(adapter_config.json) PeftType.LORA from_pretrained() 反序列化 LoraConfig PEFT_TYPE_TO_TUNER_MAPPING[LORA] LoraModel LoraModel(model, config, "default") inject_adapter(model) _create_and_replace update_layer (初始化 A/B) 注入完成 (随机权重) load_adapter (加载真实权重) set_peft_model_state_dict 权重已写入 返回 PeftModel

图 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 是什么、长什么样、如何与生态协作?

我们用了五个章节回答:

  1. 生态定位:PEFT 是 HuggingFace 生态的"参数高效微调中央仓库",与 transformers / accelerate / diffusers / trl 协作
  2. 代码地图src/peft/ 瘦核心 + tuners/ 胖周边,每个方法"三件套"约定
  3. 架构分层PeftModel(壳)→ BaseTuner(骨架)→ BaseTunerLayer(细胞),inject_adapter 是统一注入引擎
  4. 注册机制:四张映射表 + register_peft_method 装饰器,5 步注册一个新方法
  5. 核心 APIget_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 开始,逐行对照代码。

Logo

北京人形旗下天工造物具身智能开源社区,聚焦具身天工与慧思开物两大平台

更多推荐