PEFT 文档

RandLora: 对大型模型进行全秩参数高效微调

Hugging Face's logo
加入 Hugging Face 社区

并获得增强的文档体验

开始使用

RandLora: 对大型模型进行全秩参数高效微调

RandLora 是一种参数高效微调技术,与 LoRAVeRA 类似,但它执行全秩更新以提高性能。当需要将大型模型适配到需要复杂更新的困难任务时,RandLora 特别有用,同时还能保持 LoRA 的参数效率。RandLora 的全秩更新是通过线性缩放随机基来实现的。这些随机基是多个低秩矩阵的集合,它们的秩之和大于或等于参数矩阵的全秩。RandLora 的可训练参数是两个对角矩阵(向量),它们与右侧的低秩随机基相乘,方式类似于 VeRA 的更新。为了保持低内存使用,RandLora 使用了一个自定义函数,防止在内存中为反向传播存储不必要的基。

RandLora 的一个显著区别是,与其他 LoRA 类的 PEFT 算法相反,增加 RandLora 的随机基的秩会增加可训练参数的数量。因为在 RandLora 中,基的数量 x 基的秩是恒定的,所以减小秩会增加随机基的数量,从而增加与基相关的可训练对角基的数量。

由于降低 RandLora 随机基的秩会增加其数量,因此对于非常小的秩,RandLora 的训练速度可能比 LoRA 慢,通常秩低于 4 会导致训练时间大幅增加。然而,这不影响推理,因为 RandLora 适配器可以合并到预训练的权重矩阵中。

RandLora 还支持使用稀疏的三元随机基(只包含 -1、0 和 1)进行训练。这些基如 Bingham 等人Ping 等人的研究所述,理论上可以通过执行聚合而不是矩阵乘法来创建权重更新,从而减少计算需求。目前尚不支持此功能。尽管目前并未减少计算量,但在某些情况下,在 RandLora 中使用稀疏随机基可以减少过拟合。对于有兴趣使用稀疏三元基的用户,建议使用 `sparse` 选项,而非可能降低性能的 `very_sparse` 选项。

与 VeRA 类似,在保存 RandLora 的参数时,可以通过在 `VeraConfig` 中设置 `save_projection=False` 来避免存储低秩矩阵。在这种情况下,这些矩阵将根据 `projection_prng_key` 参数的固定随机种子进行恢复。这减少了检查点的大小,但我们不能保证在所有设备和所有未来版本的 PyTorch 上的可复现性。如果你想确保可复现性,请设置 `save_projection=True`(这是默认值)。

与 Vera 类似,为了处理不同形状的适配层,RandLora 使用每个维度的最大所需大小来初始化共享的 A 和 B 矩阵。在前向传播期间,会从这些共享矩阵中切出特定层的子矩阵 A 和 B,并按论文所述使用。例如,适配两个形状为 (100, 20) 和 (80, 50) 的线性层,将创建形状分别为 (rank, 50) 和 (100, rank) 的 A 和 B 矩阵。然后,为了适配一个形状为 (100, 20) 的层,将提取形状为 (rank, 20) 和 (100, rank) 的子矩阵 A 和 B。

RandLora 目前有以下限制

  • 仅支持 `nn.Linear` 层。

论文摘要如下:

低秩自适应(LoRA)及其变体在减少大型 Transformer 网络的可训练参数数量和内存需求方面取得了令人瞩目的成果,同时保持了微调性能。然而,权重更新的低秩特性固有地限制了微调模型的表示能力,从而可能在复杂任务上影响性能。这引出了一个关键问题:当观察到 LoRA 与标准微调之间的性能差距时,这是由于可训练参数数量减少还是秩不足所致?本文旨在通过引入 RandLora 来回答这个问题,这是一种参数高效的方法,它使用低秩、不可训练的随机矩阵的习得线性组合来执行全秩更新。我们的方法通过将优化限制在应用于固定随机矩阵的对角缩放矩阵上,从而限制了可训练参数的数量。这使我们能够有效地克服低秩限制,同时在训练期间保持参数和内存效率。通过在视觉、语言和视觉-语言基准上进行广泛的实验,我们系统地评估了 LoRA 和现有随机基方法的局限性。我们的发现表明,全秩更新在单独的视觉和语言任务中是有益的,而在视觉-语言任务中更是如此,其中 RandLora 显著减少——有时甚至消除——了标准微调与 LoRA 之间的性能差距,证明了其有效性。

RandLoraConfig

class peft.RandLoraConfig

< >

( task_type: typing.Union[str, peft.utils.peft_types.TaskType, NoneType] = None peft_type: typing.Union[str, peft.utils.peft_types.PeftType, NoneType] = None auto_mapping: typing.Optional[dict] = None base_model_name_or_path: typing.Optional[str] = None revision: typing.Optional[str] = None inference_mode: bool = False r: int = 32 target_modules: typing.Union[list[str], str, NoneType] = None projection_prng_key: int = 0 save_projection: bool = True sparse: bool = False very_sparse: bool = False randlora_dropout: float = 0.0 fan_in_fan_out: bool = False randlora_alpha: int = 640 bias: str = 'none' modules_to_save: typing.Optional[list[str]] = None init_weights: bool = True layers_to_transform: typing.Union[list[int], int, NoneType] = None layers_pattern: typing.Optional[str] = None )

参数

  • r (int, 可选, 默认为 32) — RandLora 的随机基秩维度。与 LoRA 相反,此参数与可训练参数的数量成反比,因为减小它会增加可训练参数。
  • target_modules (Union[list[str], str]) — 要应用 RandLora 的模块名称。仅支持线性层。
  • projection_prng_key (int) — RandLora PRNG 初始化密钥。用于为新模型初始化 basis_A 和 basis_B,或在加载未包含这些投影的检查点时使用。默认为 `0`。
  • save_projection (bool) — 是否在状态字典中保存全局 basis_A / basis_B 随机基,以及每层的 lambda / gamma 对角矩阵。这会增加检查点的大小,但能保证我们可以在所有系统配置上重新加载检查点。默认为 `True`。
  • sparse (bool) — 是否使用 RandLora 论文中描述的稀疏随机基。这些基是三元稀疏基(仅包含-1、0和1),其中-1和1的分配概率为1/6,0的分配概率为2/3。这些稀疏矩阵旨在未来用于免矩阵乘法计算,请参阅 https://huggingface.co/papers/2406.02528v1。然而,目前的实现是一个概念验证,其中稀疏性并未用于提高速度或减少内存使用。使用稀疏矩阵通常不会降低性能,甚至可能有助于减少过拟合。默认为 `False`。
  • very_sparse (bool) — 是否使用 RandLora 论文中描述的高度稀疏的随机基。极稀疏基是三元稀疏基(仅包含-1、0和1),对于一个最小维度为D的矩阵,-1和1的分配概率为1/√D,0的分配概率为1-2/√D。使用这些稀疏矩阵可以比 `sparse` 选项进一步减少过拟合,但很可能会因此降低性能。请谨慎使用。默认为 `False`。
  • randlora_dropout (float) — RandLora 层的 dropout 概率。
  • randlora_alpha (float) — RandLora 层的缩放系数,通常是秩的 20 倍。由于 `randlora_alpha` 系数默认较大,可能导致数值不稳定,尤其是在学习率较高时。如果训练不稳定,请考虑降低学习率或 `randlora_alpha` 系数。
  • fan_in_fan_out (bool) — 如果要替换的层存储权重的方式是 (fan_in, fan_out),则设置为 True。例如,gpt-2 使用 `Conv1D`,其权重存储方式为 (fan_in, fan_out),因此应设置为 `True`。
  • bias (str) — 偏置类型。可以是 'none'、'all' 或 'randlora_only'。如果为 'all' 或 'randlora_only',则相应的偏置将在训练期间更新。请注意,这意味着即使禁用适配器,模型的输出也不会与未进行适配的基础模型相同。
  • modules_to_save (list[str]) — 除了 RandLora 层之外,需要设置为可训练并在最终检查点中保存的模块列表。
  • init_weights (bool) — 是否使用默认初始化来初始化 RandLora 层的权重。除非你确切知道自己在做什么,否则不要更改此设置。
  • layers_to_transform (Union[list[int],int]) — 要转换的层索引,如果指定此参数,它将在指定列表中的层索引上应用 RandLora 转换。如果传递单个整数,它将在此索引的层上应用 RandLora 转换。
  • layers_pattern (str) — 层模式名称,仅在 `layers_to_transform` 不为 `None` 且层模式不在常见层模式中时使用。

这是用于存储 RandLoraModel 配置的配置类。

论文:https://huggingface.co/papers/2502.00987

RandLoraModel

class peft.RandLoraModel

< >

( model peft_config: Union[PeftConfig, dict[str, PeftConfig]] adapter_name: str low_cpu_mem_usage: bool = False state_dict: Optional[dict[str, torch.Tensor]] = None ) torch.nn.Module

参数

  • model (PreTrainedModel) — 要适配的模型。
  • config (RandLoraConfig) — RandLora 模型的配置。
  • adapter_name (str) — 适配器的名称,默认为 `"default"`。
  • low_cpu_mem_usage (bool, 可选, 默认为 `False`) — 在元设备上创建空的适配器权重。有助于加快加载过程。

返回

torch.nn.Module

RandLora 模型。

从预训练的 transformers 模型创建 RandLoRA 模型。

示例

>>> from transformers import AutoModelForCausalLM
>>> from peft import RandLoraConfig, get_peft_model

>>> base_model = AutoModelForCausalLM.from_pretrained("facebook/opt-125m")
>>> config = RandLoraConfig(r=32)
>>> model = get_peft_model(base_model, config)

属性:

  • model (PreTrainedModel) — 需要被适配的模型。
  • peft_config (RandLoraConfig):RandLora 模型的配置。

delete_adapter

< >

( adapter_name: str )

参数

  • adapter_name (str) — 要删除的适配器名称。

删除一个现有的适配器。

merge_and_unload

< >

( progressbar: bool = False safe_merge: bool = False adapter_names: Optional[list[str]] = None )

参数

  • progressbar (bool) — 是否显示一个进度条来指示卸载和合并过程
  • safe_merge (bool) — 是否激活安全合并检查,以检查适配器权重中是否存在任何潜在的 Nan 值
  • adapter_names (list[str], 可选) — 应该合并的适配器名称列表。如果为 None,所有活动的适配器都将被合并。默认为 `None`。

此方法将 RandLora 层合并到基础模型中。如果有人想将基础模型用作独立模型,则需要此方法。

示例

>>> from transformers import AutoModelForCausalLM
>>> from peft import PeftModel

>>> base_model = AutoModelForCausalLM.from_pretrained("tiiuae/falcon-40b")
>>> peft_model_id = "smangrul/falcon-40B-int4-peft-lora-sfttrainer-sample"
>>> model = PeftModel.from_pretrained(base_model, peft_model_id)
>>> merged_model = model.merge_and_unload()

unload

< >

( )

通过移除所有 RandLora 模块而不进行合并,恢复基础模型。这将返回原始的基础模型。

< > 在 GitHub 上更新