PEFT documentation

前缀微调

You are viewing main version, which requires installation from source. If you'd like regular pip install, checkout the latest stable version (v0.21.0).
Hugging Face's logo
Join the Hugging Face community

and get access to the augmented documentation experience

to get started

前缀微调

Optimize the prefix parameters for each task (image source).

Prefix tuning 在输入序列前添加一系列可学习的任务特定向量,同时保持预训练模型冻结。前缀参数被插入到模型的所有层中。

该论文的摘要如下:

微调是实际中利用大型预训练语言模型执行下游任务的主要方式。然而,它会修改语言模型的所有参数,因此需要为每个任务存储一份完整的模型副本。在本文中,我们提出了前缀微调(prefix-tuning),这是一种用于自然语言生成任务的轻量级微调替代方案。它保持语言模型参数冻结,但优化一个小型的连续任务特定向量(称为前缀)。前缀微调从提示(prompting)中汲取灵感,允许后续 token 关注该前缀,就好像它是“虚拟 token”一样。我们将前缀微调应用于 GPT-2 进行表格到文本生成,并应用于 BART 进行摘要。我们发现,仅学习 0.1\% 的参数,前缀微调在全数据设置下即可获得与微调相当的性能,在低数据设置下优于微调,并且对训练期间未见过的主题示例具有更好的外推能力

注意 对于编码器-解码器模型(seq2seq),前缀仅应用于解码器,这与论文中的规范不一致(参见例如 Figure 2)。前缀调优仍然可以在这些模型架构上进行微调,但性能可能不佳;对于编码器-解码器模型,建议考虑使用其他 PEFT 方法。

前缀微调(Prefix tuning)与提示微调(prompt tuning)非常相似。主要区别在于,前缀参数被插入到模型的所有层中,而提示微调仅将提示参数添加到模型输入嵌入中。此外,前缀参数通过一个独立的前馈网络(FFN)进行优化,而不是直接在软提示上进行训练,因为后者会导致不稳定性并损害性能。在更新软提示后,该 FFN 会被丢弃。

因此,作者发现,尽管前缀微调的参数数量比全量微调少 1000 倍,但其性能与全量微调相当,并且在低数据场景下表现甚至更好。

基本用法

创建一个 PrefixTuningConfig,指定任务类型以及要添加和学习的虚拟 token 数量。

from peft import PrefixTuningConfig, get_peft_model

peft_config = PrefixTuningConfig(task_type="CAUSAL_LM", num_virtual_tokens=20)
model = get_peft_model(model, peft_config)
model.print_healthcheck()

可能的初始化

默认情况下,前缀微调使用随机初始化的虚拟标记。也可以选择将向量初始化为接近无操作的状态(初始化为零,但仍会略微改变概率分布)。这意味着注入的前缀对 KV 缓存的影响从一开始就更小,从而降低了训练性能的方差。

PEFT 还提供了用于从现有的 KV 缓存前缀(例如,来自提示/语料库的前 p 个 token)初始化前缀调适器的实用工具。这仅在 prefix_projection=False(默认值)时受支持,因为在这种情况下,学习到的参数就是 KV 前缀本身。

from transformers import AutoModelForCausalLM, AutoTokenizer

from peft import PrefixTuningConfig, get_peft_model, initialize_kv_prefix_from_text

base = AutoModelForCausalLM.from_pretrained("gpt2")
tok = AutoTokenizer.from_pretrained("gpt2")

peft_cfg = PrefixTuningConfig(task_type="CAUSAL_LM", num_virtual_tokens=20, prefix_projection=False)
model = get_peft_model(base, peft_cfg)

initialize_kv_prefix_from_text(
    model,
    tok,
    text="...a long context with at least num_virtual_tokens tokens...",
    use_chat_template=False,
)m peft import PrefixTuningConfig, get_peft_model, initialize_kv_prefix_from_text

base = AutoModelForCausalLM.from_pretrained("gpt2")
tok = AutoTokenizer.from_pretrained("gpt2")

peft_cfg = PrefixTuningConfig(task_type="CAUSAL_LM", num_virtual_tokens=20, prefix_projection=False)
model = get_peft_model(base, peft_cfg)

initialize_kv_prefix_from_text(
    model,
    tok,
    text="...a long context with at least num_virtual_tokens tokens...",
    use_chat_template=False,
)

请确保文本足够长,以生成至少 num_virtual_tokens 个 token,否则初始化将会失败。

作为参考:

  • 从一个中性的起始序列开始,使用 initialize_kv_prefix_from_text,它可以是一个非常短的字符串,例如 “Question: ”
  • 如果这没有帮助,请使用与任务更相关的更长序列(即经过设计的提示词),这样你可以获得更长的虚拟 token 来填充,但同时也能对模型进行更多的引导
  • 如果无法使用初始化文本,或者您想快速检查前缀微调是否可行, 请使用不带投影的零初始化

基准测试概览

API

PrefixTuningConfig

类 peft.PrefixTuningConfig

< >

( 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 = Falsenum_virtual_tokens: int = Nonetoken_dim: int = Nonenum_transformer_submodules: Optional[int] = Nonenum_attention_heads: Optional[int] = Nonenum_layers: Optional[int] = Nonemodules_to_save: Optional[list[str]] = Noneinit_weights: typing.Literal['zero'] | None = Noneencoder_hidden_size: int = Noneprefix_projection: bool = False )

参数

  • init_weights (Optional[str]) — 如果未设置,权重将随机初始化;如果设置为 “zero”,则权重会被初始化为使激活值成为无操作(零)。
  • encoder_hidden_size (int) — 提示编码器的隐藏层大小。
  • prefix_projection (bool) — 是否对前缀嵌入进行投影。

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

PrefixEncoder

类 peft.PrefixEncoder

< >

( config )

参数

用于编码前缀的 torch.nn 模型。

示例:

>>> from peft import PrefixEncoder, PrefixTuningConfig

>>> config = PrefixTuningConfig(
...     peft_type="PREFIX_TUNING",
...     task_type="SEQ_2_SEQ_LM",
...     num_virtual_tokens=20,
...     token_dim=768,
...     num_transformer_submodules=1,
...     num_attention_heads=12,
...     num_layers=12,
...     encoder_hidden_size=768,
... )
>>> prefix_encoder = PrefixEncoder(config)

属性:

  • 嵌入 (torch.nn.Embedding) — 前缀编码器的嵌入层。
  • transform (torch.nn.Sequential) — 当 prefix_projection 为 True 时,用于转换前缀嵌入的两层 MLP。
  • prefix_projection (bool) — 是否对前缀嵌入进行投影。

输入形状: (batch_size, num_virtual_tokens)

输出形状: (batch_size, num_virtual_tokens, 2*layers*hidden)

load_prompt_embeddings

< >

( prompt_embeddings: Tensor )

加载由 PEFT 保存的展平后的提示嵌入(prompt_embeddings)。

对于前缀微调,仅在 prefix_projection=False 时受支持,因为在这种情况下,学习到的参数就是 KV 前缀本身(embedding.weight 的形状为 [num_virtual_tokens, num_layers*2*token_dim])。

如果 prefix_projection=True,则参数为(虚拟 token 嵌入 + 一个 MLP),并且没有通用的方法可以反转投影,从而从扁平化的 KV 前缀中恢复这些参数。

Update on GitHub