PEFT documentation
PSOFT
PSOFT
PSOFT 是一种基于正交微调(OFT)的参数高效微调方法,它在保持预训练权重列向量几何关系的同时,实现了性能与多维效率(包括参数量、内存占用和计算成本)之间的平衡。通过将正交变换限制在从预训练权重中导出的低秩主子空间内,PSOFT 弥合了 LoRA 与 OFT 之间的差距,兼具理论保证和实际适应性。其在 GLUE、VTAB-1K、GSM8K、MATH 以及常识推理等多种基准上的广泛评估验证了该方法的有效性。
- 仅支持
nn.Linear层。 - 不支持量化层。
该论文的摘要如下:
随着模型参数规模的快速增长,参数高效微调(PEFT)已成为在受限计算资源下将大模型适配到多样化下游任务的关键。在这一范式下,正交微调及其变体能够保留预训练模型的语义表示,但在参数量、内存和计算方面难以同时兼顾表达能力与效率。为克服这一局限,我们提出了基于主子空间适配的高效正交微调(PSOFT),该方法将正交变换限制在预训练权重的主子空间内。具体而言,PSOFT 通过矩阵分解构建该子空间以实现兼容的变换,建立了一个严格的理论条件以维持该子空间的几何结构从而确保关键语义的保留,并引入了高效的可调向量,在训练过程中逐步放松正交性以增强模型的适应性。在四个代表性模型上针对 35 个 NLP 和 CV 任务的广泛实验表明,PSOFT 提供了一种实用且可扩展的解决方案,能够在 PEFT 中同时实现语义保留、表达能力以及多维度的效率。
PSOFT 的工作原理
PSOFT 将每个权重矩阵 分解为 和 使用 SVD:
主子空间 由前 $r$ 个奇异分量构造而成:
(PSOFT-SO:具有严格正交性的 PSOFT
(PSOFT-RO:放宽正交性约束的 PSOFT
在训练过程中,$A$、$B$ 和 被冻结,只有 (或 带有 和 $\beta$) 是可训练的。
为了与 PEFT 框架(该框架期望使用加性权重更新)兼容,PSOFT 以以下加性形式实现:
可训练参数
应用 PSOFT 后:
- 原始模型权重($A$、$B$ 和 $W_{\text{res}}$)被冻结。
- 仅正交矩阵 (以及可选的 $\alpha$、$\beta$ 是可训练的。
- 不会引入额外的偏置参数。
基本用法
import torch
from transformers import AutoModelForCausalLM, AutoTokenizer
from peft import PsoftConfig, get_peft_model
# Load base model
model_id = "facebook/opt-125m"
model = AutoModelForCausalLM.from_pretrained(model_id)
# Configure PSOFT
config = PsoftConfig(
r=32, # the dimension of trainable matrix R,
psoft_alpha=32, # scaling factor (typically set to r in PSOFT),
target_modules=["q_proj", "v_proj"], # target attention projection layers
ab_svd_init="psoft_init", # principal subspace initialization
psoft_svd="full", # SVD method
psoft_orth=True, # enable orthogonal R (Cayley parameterization)
psoft_mag_a=True, # enable tunable vector alpha
psoft_mag_b=True, # enable tunable vector beta
use_cayley_neumann=False, # disable Cayley–Neumann approximation
num_cayley_neumann_terms=5, # number of Neumann series terms
cayley_neumann_eps=None, # improve numerical stability
)
# Apply PSOFT
model = get_peft_model(model, config)
model.train()
tokenizer = AutoTokenizer.from_pretrained(model_id)
tokenizer.pad_token = tokenizer.eos_token
# Train
inputs = tokenizer("Hello world", return_tensors="pt", padding=True)
loss = model(**inputs, labels=inputs["input_ids"]).loss
loss.backward()
trainable = [p for p in model.parameters() if p.requires_grad]
optimizer = torch.optim.AdamW(trainable, lr=5e-4)
optimizer.step()
optimizer.zero_grad(set_to_none=True)配置选项
不同模式
(PSOFT-SO:具有严格正交性的 PSOFT)
config = PsoftConfig(psoft_orth=True,psoft_mag_a=False,psoft_mag_b=False)(PSOFT-RO:放宽正交性的 PSOFT)
config = PsoftConfig(psoft_orth=True,psoft_mag_a=True,psoft_mag_b=True)最佳实践
- 秩的选择:较小的秩(例如
32–128)适用于较简单的任务,而较大的秩(例如64–256)能为更复杂的任务提供更高的表达能力,但代价是参数量和计算量的增加。 - Scaling Factor: 缩放因子通常设置为 (在 PSOFT 中)。
- 学习率:使用标准学习率(例如
1e-4到5e-3)以实现稳定训练。 - SVD 初始化:
lowrank选项比full更节省内存和计算资源,因此更适合大型模型。 - Cayley–Neumann 近似:当秩较大时,启用 Cayley–Neumann 近似可以显著提升计算效率,而对于较小的秩,其收益则不那么明显。在实践中,使用少量 Neumann 级数项(通常为
5)通常能在精度和效率之间取得良好的平衡。
基准测试概览
API
PsoftConfig
类 peft.PsoftConfig
< 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 = 32target_modules: Optional[Union[list[str], str]] = Noneexclude_modules: Optional[Union[list[str], str]] = Nonepsoft_alpha: int = 32psoft_dropout: float = 0.0fan_in_fan_out: bool = Falseab_svd_init: Literal['psoft_init', 'pissa_init'] = 'psoft_init'psoft_svd: Literal['full', 'lowrank'] = 'full'psoft_svd_lowrank_niter: int = 10random_seed: int = 0psoft_orth: bool = Truepsoft_mag_b: bool = Truepsoft_mag_a: bool = Trueuse_cayley_neumann: bool = Falsenum_cayley_neumann_terms: int = 5cayley_neumann_eps: Optional[float] = Nonemodules_to_save: Optional[list[str]] = Noneinit_weights: bool = Truelayers_to_transform: Optional[Union[list[int], int]] = Nonelayers_pattern: Optional[Union[list[str], str]] = None )
参数
- r (
int) — 默认值为 32。PSOFT 秩 (r) 通过 r*r 变换 R 控制适配器容量。对于简单任务,较小的秩 32-128 通常已足够;对于更复杂的任务,使用 64-256 可能更有益,这以增加额外参数和计算量为代价来提高表达能力。请参阅论文中经过实证验证的设置:https://openreview.net/forum?id=FSHrinMArK。 - target_modules (
Optional[Union[List[str], str]]) — 要应用适配器(adapter)的模块名称。如果指定了此参数,则只有名称匹配的模块会被替换。传入字符串时,将执行正则表达式匹配。传入字符串列表时,将执行精确匹配,或检查模块名称是否以列表中的任一字符串结尾。如果指定为 ‘all-linear’,则选择所有 linear/Conv1D 模块(如果模型是 PreTrainedModel,则排除输出层)。如果未指定,将根据模型架构选择模块。如果架构未知,则会引发错误——在这种情况下,应手动指定目标模块。 - exclude_modules (
Optional[Union[List[str], str]]) — 不应用适配器的模块名称。传入字符串时,将执行正则表达式匹配。传入字符串列表时,将执行精确匹配,或检查模块名称是否以传入的任一字符串结尾。 - psoft_alpha (
int) — 默认值为 32。它控制 PSOFT 缩放因子。语义与 LoRA alpha 相同。 - psoft_dropout (
float) — 默认值为 0.0。PSOFT 路径的 Dropout。语义与 LoRA dropout 相同。 - fan_in_fan_out (
bool) — 如果待替换的层以 (fan_in, fan_out) 的形式存储权重,请将此项设置为 True。例如,gpt-2 使用的Conv1D以 (fan_in, fan_out) 的形式存储权重,因此应将其设置为True。 - ab_svd_init (
Literal["psoft_init", "pissa_init"]) — 默认为 ‘psoft_init’。用于在 PSOFT 中构建主子空间时,A 和 B 的初始化策略。‘psoft_init’:基于 SVD 的初始化,A 行正交,确保严格正交性(PSOFT)。‘pissa_init’:基于 SVD 的初始化,A 和 B 对称(标准 PiSSA)。 - psoft_svd (
Literal["full", "lowrank"]) — 默认为 ‘full’。用于初始化的 SVD 后端:‘full’ 使用 torch.linalg.svd;‘lowrank’ 使用 torch.svd_lowrank。 - psoft_svd_lowrank_niter (
int) — 仅在 psoft_svd=‘lowrank’ 时使用。默认值为 10。当 psoft_svd=‘lowrank’ 时,torch.svd_lowrank 使用的幂迭代次数。 - psoft_orth (
bool) — 默认为 ‘True’。如果为 True,则通过 Cayley 参数化约束 R 为正交矩阵,从而保持预训练权重向量各列之间的几何关系。如果为 False,则 R 是一个没有正交性约束的自由矩阵。 - psoft_mag_b (
bool) — 默认为 ‘True’。如果为 True,则在 R 的 ‘output’ 端学习一个对角缩放向量。通常与 psoft_mag_a 搭配使用,以提高任务适应性,但会对预训练几何结构造成轻微扭曲。 - psoft_mag_a (
bool) — 默认为 ‘True’。如果为 True,则在 R 的 ‘input’ 端学习一个对角缩放向量。通常与 psoft_mag_b 搭配使用,以提高任务适应性,但会对预训练几何结构造成轻微扭曲。 - use_cayley_neumann (
bool) — 默认为 ‘False’。是否使用 PSOFT 的 Cayley-Neumann 形式。设置为 True 可提高计算效率,但代价是正交性的近似误差更大。 - num_cayley_neumann_terms (
int) — 默认为 5。仅在 use_cayley_neumann=True 时使用。要使用的 Cayley-Neumann 项数。数值越高,正交性的近似误差越小。 - cayley_neumann_eps (
optional[float]) — 默认为 ‘None’。仅在 use_cayley_neumann=True 时使用。Cayley-Neumann 近似中生成矩阵 Q 的可选 Frobenius 范数上界。如果为 None(默认值),则不进行重新缩放。如果设置为 (0, 1) 之间的值(例如 0.9),则当 ||Q||_F 超过该阈值时,会对 Q 进行重新缩放以提高数值稳定性。详情请参见 https://spherelab.ai/oftv2/。 - init_weights (
bool) — 默认为 ‘True’。是否使用默认初始化方式初始化 PSOFT 层的权重。除非您完全清楚自己在做什么,否则请勿更改此设置。 - modules_to_save (
List[str]) — 除适配器层外,需要设置为可训练并在最终检查点中保存的模块列表。 - layers_to_transform (
Union[List[int], int]) — 要转换的层索引。如果传入一个整数列表,则会将适配器应用于该列表中指定的层索引。如果传入单个整数,则会对该索引处的层应用转换。 - layers_pattern (
Optional[Union[List[str], str]]) — 层模式名称,仅在layers_to_transform不同于None时使用。它应指向模型的nn.ModuleList,该列表通常命名为'layers'或'h'。
PSOFT(基于主子空间自适应的高效正交微调)的配置。
PSOFT 在低秩矩阵 A 和 B 之间插入一个 r*r 正交变换 R,因此低秩更新为 ΔW = B @ (R-I) @ A。仅训练 R(以及可选的可调向量);A 和 B 通过 psoft_init (基于 SVD,A 行正交)进行初始化并被冻结。
PsoftModel
类 peft.PsoftModel
< source >( modelpeft_config: Union[PeftConfig, dict[str, PeftConfig]]adapter_name: strlow_cpu_mem_usage: bool = Falsestate_dict: Optional[dict[str, torch.Tensor]] = None )
PSOFT(基于主子空间自适应的高效正交微调)模型。
在低秩矩阵 A 和 B 之间插入一个 r*r 正交(或缩放)变换 R:ΔW = B @ (R-I) @ A。使用 ab_svd_init="psoft_init" 从 SVD 初始化 A/B 并将其冻结,仅训练 R(以及可选的幅度 向量)。