PEFT documentation
PaCA:部分连接适配
PaCA:部分连接适配
PaCA(ICLR 2025)是一种 PEFT 方法,旨在提升训练速度并降低激活内存占用。它针对每个目标预训练权重,微调其中随机选取的 r 个输入列(即“部分连接”)。唯一可训练的参数是这些列的更新量;与 LoRA 不同,PaCA 不会引入额外的低秩矩阵。
LoRA 等方法会在每个预训练层旁边运行一个小型低秩分支。该分支所需的 FLOPs 很少,但它与预训练层顺序执行,并且会为反向传播保留输入激活的独立副本。PaCA 避免了这两点:
- 每层一次 GEMM。 适配的列会在其矩阵乘法(matmul)执行前写入预训练权重,并在矩阵乘法执行后立即恢复,因此前向和反向传播各只执行一次矩阵乘法,与冻结的基础层完全一致。
- 部分激活值。适配列的梯度仅依赖于对应的
r个输入通道,因此在反向传播中只保存这些通道,而非完整的输入。
就所得权重而言,PaCA 适配器等价于秩为 r 的 LoRA 适配器,其 A 矩阵是一个固定的列选择器:W' = W + delta @ S,其中 S 用于选择 r 列。因此,可以通过 convert_to_lora() 将其无损转换为秩为 r 的 LoRA 适配器,例如用于在 LoRA 推理栈中提供服务。
何时使用 PaCA:
- 您希望达到 LoRA 级别的精度,同时拥有更快的微调步骤和更低的激活内存占用,例如以便在相同的 GPU 上处理更长的序列或更大的批次。
- 你希望适配器能够精确地合并到基础权重中(该更新是一个普通的列更新)。
当前限制:
- 仅支持
nn.Linear层(包括量化线性层)。 - PaCA 自行执行矩阵乘法,而不是调用基础层的
forward,因此注册在基础层上的前向钩子不会被调用(会发出警告)。请改为在 PaCA 层上注册钩子。 - 不支持混合适配器批次(
adapter_names参数)。 - 使用分片权重(FSDP、DeepSpeed ZeRO-3)进行训练的功能尚未进行测试。
torch.compile受支持:在编译模式下,列交换、矩阵乘法和恢复操作作为不透明的自定义算子(torch.ops.peft.paca_linear_forward/_backward)运行,因此编译器不会复制基础权重。这需要torch.library.custom_op(PyTorch >= 2.4);否则将追踪 eager 实现。paca_alpha / r会缩放列更新,因此相当于有效学习率的乘数。- 在推理阶段,请合并适配器(
merge_and_unload());未合并的前向传播在每次调用时仍会反复切换列。
用法
from transformers import AutoModelForCausalLM
from peft import PacaConfig, get_peft_model
model = AutoModelForCausalLM.from_pretrained("meta-llama/Llama-3.2-3B")
config = PacaConfig(r=16, paca_alpha=16, target_modules="all-linear", random_seed=0)
model = get_peft_model(model, config)
model.print_trainable_parameters()默认情况下,每个目标层都会微调r个随机选择的列;在论文中,这种方法的性能与按权重范数或按累积梯度选择列相当。若要自行选择列,例如尝试另一种选择策略或为每层使用不同数量的列,请传入paca_indices_pattern。其键为层名或正则表达式,与 LoRA 的rank_pattern类似:
config = PacaConfig(
r=16,
paca_alpha=16,
target_modules=["q_proj", "v_proj"],
paca_indices_pattern={"layers.0.self_attn.q_proj": [0, 2, 4, 6], "layers.1.self_attn.v_proj": list(range(32))},
)匹配某个键的层会对给定的列进行微调,其余目标层则随机选择 r 列。缩放系数为 paca_alpha / r,其中 r 取自配置中每一层的值,因此 paca_alpha 在所有层中的含义相同。
该论文的摘要如下:
先前的参数高效微调(PEFT)算法通过仅训练少量额外的适配器参数,而非整个模型,来降低大型神经网络模型微调的内存占用和计算成本。然而,PEFT 带来的计算成本降低并不必然转化为训练时间的缩短;尽管适配器层的计算成本远小于预训练层,但众所周知,这两类层在 GPU 上是顺序处理的,从而导致了显著的延迟开销。LoRA 及其变体通过在推理期间将低秩适配器矩阵与预训练权重合并来避免这种延迟开销。但是,在训练期间无法合并这些层,因为在低秩适配器矩阵持续更新的过程中,预训练权重必须保持冻结。此外,LoRA 及其变体并未减少激活内存,因为第一个低秩适配器矩阵仍然需要预训练权重的输入激活来计算权重梯度。为缓解此问题,我们提出了部分连接适配(PaCA),它通过微调预训练权重内随机选择的部分连接,而不是在模型中引入适配器层来进行微调。PaCA 不仅通过消除适配器和预训练层顺序处理带来的时间开销来提升训练速度,还通过仅存储部分激活而非完整激活用于梯度计算来减少激活内存。与 LoRA 相比,PaCA 将训练时间减少了 22%,总内存使用量减少了 16%,同时在各种微调场景(例如在 MMLU 数据集上进行微调和在 Oasst1 数据集上进行指令微调)中保持了相当的准确性。PaCA 还可以与量化结合使用,从而实现对 LLaMA3.1-70B 等大型模型的微调。此外,与 LoRA 相比,PaCA 在 NVIDIA A100 GPU 和 INTEL Gaudi2 HPU 上均支持训练长 23% 的序列,并将吞吐量提高了 16%。
基准测试概览
API
PacaConfig
类 peft.PacaConfig
< source >( task_type: Optional[Union[str, TaskType]] = Nonepeft_type: Optional[Union[str, PeftType]] = Noneauto_mapping: Optional[dict] = Nonepeft_version: Optional[str] = Nonebase_model_name_or_path: Optional[str] = Nonerevision: Optional[str] = Noneinference_mode: bool = Falser: int = 8paca_alpha: int = 8target_modules: list[str] | str | None = Noneexclude_modules: list[str] | str | None = Nonelayers_to_transform: list[int] | int | None = Nonelayers_pattern: list[str] | str | None = Nonerandom_seed: int | None = Nonepaca_indices_pattern: dict | None = <factory>init_weights: bool = Truemodules_to_save: list[str] | None = None )
参数
- r (
int,可选,默认为8) — 每个目标层中参与微调的输入列数(部分连接)。对于形状为(out_features, in_features)的权重,PaCA 会训练r * out_features个参数,其数量与秩为r的 LoRA 适配器中B矩阵的参数数量相同。该值不得超过in_features。 - paca_alpha (
int,可选,默认为8) — 缩放因子。所选列的更新量为paca_alpha / r * delta。与lora_alpha类似,它作为适配器有效学习率的乘数。 - target_modules (
Union[list[str], str], 可选) — 要替换为 PaCA 的模块名称列表或模块名称的正则表达式。例如,['q_proj', 'v_proj']或'.*decoder.*(SelfAttention|EncDecAttention).*(q|v)$'。如果指定为'all-linear',则选择除输出层外的所有线性模块。仅支持nn.Linear层。 - exclude_modules (
Union[list[str], str], 可选) — 不应用适配器的模块名称。传入字符串时,将执行正则表达式匹配。传入字符串列表时,将执行精确匹配,或检查模块名称是否以传入的任一字符串结尾。 - layers_to_transform (
Union[list[int], int], 可选) — 要转换的层索引。如果指定了此参数,PEFT 将只转换该列表中指定的层索引。如果传入单个整数,PEFT 将只转换该索引处的层。 - layers_pattern (
Optional[Union[list[str], str]],可选) — 层模式名称,仅在layers_to_transform不同于None时使用。它应指向模型的nn.ModuleList,通常命名为'layers'或'h'。 - random_seed (
int,可选,默认为None) — 用于选择部分连接的随机种子。每一层都使用由该种子和层名称派生的不同选择。如果为None,则使用全局 torch 随机数生成器。所选索引始终存储在适配器检查点中,因此加载已训练的适配器时无需提供该种子。 - paca_indices_pattern (
dict, 可选) — 一个可选的映射,将层名称或正则表达式映射到在匹配的层中用于微调的输入列索引,而不是随机选择的r个索引,例如{'^model.layers.0.self_attn.q_proj': [0, 2, 4]}。这也允许每层使用不同数量的列。某层的索引必须唯一且小于in_features。缩放因子为paca_alpha / r,其中r取自此配置,在所有层中保持一致,与给定的索引数量无关。未匹配任何键的目标层将使用随机选择的r个列。 - init_weights (
bool,可选,默认为True) — 如果为True,则选定的列的更新初始化为零,即适配模型在初始化时与基础模型完全相同。将其设置为False以使用随机值进行初始化;此选项仅用于测试。 - modules_to_save (
list[str], 可选) — 除 PaCA 层外,需要设置为可训练并在最终检查点中保存的模块列表。例如,在序列分类或标记分类任务中,最终层classifier/score是随机初始化的,因此需要可训练并保存。
这是用于存储 PacaModel 配置的类。
PaCA(Partial Connection Adaptation,https://huggingface.co/papers/2503.01905)对每个目标预训练权重中随机选取的 r 个输入列(即部分连接)进行微调。唯一可训练的参数是这些列的更新量;与 LoRA 不同,PaCA 不引入额外的低秩矩阵。在矩阵乘法执行前,将适配后的列替换到预训练权重中,并在矩阵乘法完成后立即恢复,因此前向和反向传播各只需执行一次 GEMM,且反向传播时仅在内存中保留所选的 r 个输入通道。
PacaModel
类 peft.PacaModel
< source >( modelpeft_config: Union[PeftConfig, dict[str, PeftConfig]]adapter_name: strlow_cpu_mem_usage: bool = Falsestate_dict: Optional[dict[str, torch.Tensor]] = None ) → torch.nn.Module
参数
- model (PreTrainedModel) — 要适配的模型。
- config (PacaConfig) — PaCA 模型的配置。
- adapter_name (
str) — 适配器的名称,默认为"default"。 - low_cpu_mem_usage (
bool,optional, defaults toFalse) — 在 meta 设备上创建空的适配器权重。有助于加快加载过程。
返回值
torch.nn.Module
PaCA 模型。
从预训练模型创建一个 Partial Connection Adaptation (PaCA) 模型。该方法在 https://huggingface.co/papers/2503.01905 中有详细描述。
示例:
>>> from transformers import AutoModelForCausalLM
>>> from peft import PacaConfig, get_peft_model
>>> base_model = AutoModelForCausalLM.from_pretrained("facebook/opt-125m")
>>> config = PacaConfig(r=16, paca_alpha=16, target_modules="all-linear")
>>> model = get_peft_model(base_model, config)属性:
- model (PreTrainedModel) — 要适配的模型。
- peft_config (PacaConfig): PaCA 模型的配置。