PEFT documentation
OFT
OFT

正交微调(OFT)和 OFTv2 是一种用于适配文本到图像扩散模型的方法。它通过用正交矩阵对预训练权重矩阵进行重参数化,以保留预训练模型中的信息。为了减少参数量,OFT 在正交矩阵中引入了块对角结构。该方法主要关注在微调后的模型中保留预训练模型的生成性能。它试图保持层内所有成对神经元之间相同的余弦相似度(超球面能量),因为这能更好地捕捉神经元之间的语义信息。这意味着 OFT 在保留主体方面更具能力,并且更适合可控生成(类似于 ControlNet)。
该论文的摘要如下:
大型文本到图像扩散模型在根据文本提示生成照片级真实感图像方面具有令人印象深刻的能力。如何有效地引导或控制这些强大的模型以执行不同的下游任务,成为一个重要的开放问题。为了应对这一挑战,我们提出了一种有原则的微调方法——正交微调(OFT),用于将文本到图像扩散模型适配到下游任务。与现有方法不同,OFT 可被证明能够保留超球面能量,该能量刻画了单位超球面上神经元之间的成对关系。我们发现,这一特性对于保留文本到图像扩散模型的语义生成能力至关重要。为了提高微调的稳定性,我们进一步提出了约束正交微调(COFT),它对超球面施加了额外的半径约束。具体而言,我们考虑了两个重要的文本到图像微调任务:主体驱动生成,其目标是在给定主体的少量图像和文本提示的情况下生成特定主体的图像;以及可控生成,其目标是使模型能够接收额外的控制信号。我们通过实验证明,我们的 OFT 框架在生成质量和收敛速度上均优于现有方法。
OFT 通过学习正交变换来保持超球面能量,从而保持神经元之间的余弦相似度不变,这可能导致对先前学习到的知识遗忘更少。在实践中,这意味着将正交矩阵与预训练权重矩阵相乘。然而,为了实现参数高效,正交矩阵被表示为具有秩为 r 的块的块对角矩阵。LoRA 通过低秩结构减少可训练参数的数量,而 OFT 则通过稀疏的块对角矩阵结构来减少可训练参数的数量。
基准测试概览
将 OFT 权重合并到基础模型
与 LoRA 类似,OFT 学习到的权重可以通过 ~OFTModel.merge_and_unload() 函数整合到预训练权重矩阵中。该函数将适配器权重与基础模型合并,从而允许您将新合并的模型作为独立模型有效使用。
OFT 使用示例
要在 TRL 中使用 OFT 进行量化微调,以执行 SFT、PPO 或 DPO 微调,请遵循以下大纲:
from transformers import AutoTokenizer, AutoModelForCausalLM, BitsAndBytesConfig
from trl import SFTTrainer
from peft import OFTConfig
if use_quantization:
bnb_config = BitsAndBytesConfig(
load_in_4bit=True,
bnb_4bit_quant_type="nf4",
bnb_4bit_compute_dtype=torch.bfloat16,
bnb_4bit_use_double_quant=True,
bnb_4bit_quant_storage=torch.bfloat16,
)
model = AutoModelForCausalLM.from_pretrained(
"model_name",
quantization_config=bnb_config
)
tokenizer = AutoTokenizer.from_pretrained("model_name")
# Configure OFT
peft_config = OFTConfig(
oft_block_size=32,
use_cayley_neumann=True,
target_modules="all-linear",
bias="none",
task_type="CAUSAL_LM"
)
trainer = SFTTrainer(
model=model,
train_dataset=ds['train'],
peft_config=peft_config,
processing_class=tokenizer,
args=training_arguments,
data_collator=collator,
)
trainer.train()API
OFTConfig
类 peft.OFTConfig
< 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 = 0oft_block_size: int = 32module_dropout: float = 0.0target_modules: Optional[Union[list[str], str]] = Nonefan_in_fan_out: bool = Falsebias: Literal['none', 'all', 'oft_only'] = 'none'exclude_modules: Optional[Union[list[str], str]] = Noneinit_weights: bool = Truelayers_to_transform: Optional[Union[list[int], int]] = Nonelayers_pattern: Optional[Union[list[str], str]] = Nonemodules_to_save: Optional[list[str]] = Nonecoft: bool = Falseeps: float = 6e-05block_share: bool = Falseuse_cayley_neumann: bool = Truenum_cayley_neumann_terms: int = 5 )
参数
- r (
int) — OFT 秩,每个注入层中 OFT 块的数量。较大的r值会生成更多稀疏更新矩阵,从而减少可训练参数。您只能指定r或oft_block_size其中之一,而不能同时指定两者,因为r×oft_block_size= 层维度。为简化起见,我们允许您指定r或oft_block_size中的任意一个,并自动推断另一个。默认设置为r = 0,建议用户设置oft_block_size以获得更好的清晰度。 - oft_block_size (
int) — 不同层之间的 OFT 块大小。较大的oft_block_size会导致更新矩阵更密集,从而拥有更多可训练参数。请选择能被层的输入维度(in_features)整除的oft_block_size,例如 4、8、16。由于r×oft_block_size= 层维度,因此您只能指定r或oft_block_size其中之一,而不能同时指定两者。为了简化操作,我们允许您指定r或oft_block_size中的任意一个,并自动推断另一个。默认设置为oft_block_size = 32。 - use_cayley_neumann (bool) — 指定是否使用 Cayley-Neumann 参数化(高效但为近似解)或原始 Cayley 参数化(精确但计算开销大,因为涉及矩阵求逆)。我们建议将其设置为
True以获得更好的效率,但由于近似误差,性能可能会略有下降。请根据您的具体需求测试两种设置(True和False)。默认值为False。 - module_dropout (
float) — 乘性 dropout 概率,通过在训练期间将 OFT 块设置为恒等变换来实现,类似于 LoRA 中的 dropout 层。 - target_modules (
Optional[Union[list[str], str]]) — 要应用适配器(adapter)的模块名称。如果指定了此参数,则只有名称匹配的模块会被替换。传入字符串时,将执行正则表达式匹配。传入字符串列表时,将执行精确匹配,或检查模块名称是否以列表中的任一字符串结尾。如果指定为 ‘all-linear’,则选择所有线性模块,但排除输出层。如果未指定,将根据模型架构选择模块。如果架构未知,则会引发错误——在这种情况下,应手动指定目标模块。 - fan_in_fan_out (
bool) — 如果待替换的层以 (fan_in, fan_out) 的形式存储权重,请将此项设置为 True。 - bias (
str) — OFT 的偏置类型。可以是 ‘none’、‘all’ 或 ‘oft_only’。如果设置为 ‘all’ 或 ‘oft_only’,对应的偏置将在训练期间被更新。请注意,这意味着即使禁用了适配器,模型的输出也不会与未进行适配的基础模型相同。 - exclude_modules (
Optional[Union[List[str], str]]) — 不应用适配器的模块名称。传入字符串时,将执行正则表达式匹配。传入字符串列表时,将执行精确匹配,或检查模块名称是否以传入的任一字符串结尾。 - init_weights (
bool) — 是否执行 OFT 权重的初始化。 - layers_to_transform (
Union[List[int], int]) — 要转换的层索引。如果传入一个整数列表,则会将适配器应用于该列表中指定的层索引。如果传入单个整数,则会对该索引处的层应用转换。 - layers_pattern (
Optional[Union[List[str], str]]) — 层模式名称,仅在layers_to_transform不同于None时使用。它应指向模型的nn.ModuleList,该列表通常命名为'layers'或'h'。 - modules_to_save (
List[str]) — 除适配器层外,需要设置为可训练并在最终检查点中保存的模块列表。 - coft (
bool) — 是否使用 OFT 的约束变体,默认关闭。 - eps (
float) — COFT 的控制强度。旋转的自由度。仅当coft设置为 True 时生效。 - block_share (
bool) — 是否在各块之间共享 OFT 参数。默认值为False。
这是用于存储 OFTModel 配置的类。
检查 kwargs 对于该配置是否有效。
OFTModel
类 peft.OFTModel
< 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 (
torch.nn.Module) — 将附加适配器调优层的模型。 - config (OFTConfig) — OFT 模型的配置。
- adapter_name (
str) — 适配器的名称,默认为"default"。 - low_cpu_mem_usage (
bool,optional, defaults toFalse) — 在 meta 设备上创建空的适配器权重。有助于加快加载过程。
返回值
torch.nn.Module
OFT 模型。
从预训练模型创建正交微调模型。该方法在 https://huggingface.co/papers/2306.07280 中有详细描述。
示例:
>>> from diffusers import StableDiffusionPipeline
>>> from peft import OFTConfig, get_peft_model
>>> config_te = OFTConfig(
... oft_block_size=32,
... target_modules=["k_proj", "q_proj", "v_proj", "out_proj", "fc1", "fc2"],
... module_dropout=0.0,
... init_weights=True,
... )
>>> config_unet = OFTConfig(
... oft_block_size=32,
... target_modules=[
... "proj_in",
... "proj_out",
... "to_k",
... "to_q",
... "to_v",
... "to_out.0",
... "ff.net.0.proj",
... "ff.net.2",
... ],
... module_dropout=0.0,
... init_weights=True,
... )
>>> model = StableDiffusionPipeline.from_pretrained("runwayml/stable-diffusion-v1-5")
>>> model.text_encoder = get_peft_model(model.text_encoder, config_te)
>>> model.unet = get_peft_model(model.unet, config_unet)属性:
- model (
~torch.nn.Module) — 要适配的模型。 - peft_config (OFTConfig): OFT 模型的配置。