PEFT documentation
前缀微调
前缀微调

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
< 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 = 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 )
这是用于存储 PrefixEncoder 配置的类。
PrefixEncoder
用于编码前缀的 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)
加载由 PEFT 保存的展平后的提示嵌入(prompt_embeddings)。
对于前缀微调,仅在 prefix_projection=False 时受支持,因为在这种情况下,学习到的参数就是 KV 前缀本身(embedding.weight 的形状为 [num_virtual_tokens, num_layers*2*token_dim])。
如果 prefix_projection=True,则参数为(虚拟 token 嵌入 + 一个 MLP),并且没有通用的方法可以反转投影,从而从扁平化的 KV 前缀中恢复这些参数。