P-tuning v1(也称为 P-tuning,由 Liu et al. 在 2021 年提出)是一种参数高效微调(Parameter-Efficient Fine-Tuning, PEFT) 方法,旨在通过优化连续提示(continuous prompts)来适配预训练语言模型到特定任务,同时保持模型原始参数不变。P-tuning v1 结合了离散提示(discrete prompts,如手动设计的文本提示)和软提示(soft prompts,如 Prompt Tuning 中的连续向量)的优点,通过引入一个小型神经网络(称为提示生成器,prompt encoder)生成任务特定的提示嵌入,从而增强提示的表达能力和任务适配性。
P-tuning v1 可以看作 Prompt Tuning 的增强版,解决了软提示初始化和表达能力不足的问题,尤其在自然语言理解(NLU)任务(如文本分类、关系抽取)中表现出色。以下是对 P-tuning v1 的详细解释:
1. P-tuning v1 的定义与原理
P-tuning v1 的核心思想是在输入序列中插入一组可训练的提示 token(prompt tokens),但这些提示的嵌入不是直接优化的连续向量,而是由一个小型神经网络(提示生成器)动态生成。这种方法结合了离散提示的语义先验和连续提示的灵活性,使模型能够更好地适配特定任务,同时保持参数效率。
工作原理:
- 插入提示 token:
- 在输入序列中插入一组虚拟的提示 token(通常 5-20 个),位置可以是输入的开头、中间或特定位置(如模板中的占位符)。
- 这些提示 token 的嵌入由提示生成器生成,而不是直接作为可训练参数。
- 提示生成器(Prompt Encoder):
- 提示生成器是一个轻量级的神经网络,通常是一个双层 MLP(多层感知机)或 LSTM,输入是提示 token 的索引或初始嵌入,输出是优化后的提示嵌入。
- 例如,对于一个长度为 k k k 的提示序列,提示生成器将生成 k k k 个嵌入向量,每个向量维度与模型的词嵌入相同。
- 提示生成器的参数是可训练的,负责捕捉任务特定的语义信息。
- 初始化与离散提示:
- P-tuning v1 通常使用离散提示(discrete prompts,如“positive”或“negative”)的词嵌入作为提示生成器的初始输入,以提供语义先验。
- 例如,在情感分类任务中,可以用“positive”和“negative”词的嵌入初始化提示 token。
- 训练:
- 冻结预训练模型的所有参数,仅优化提示生成器的参数。
- 提示生成器生成的任务特定提示嵌入与输入文本的词嵌入拼接,送入模型进行计算。
- 推理:
- 提示生成器生成提示嵌入,与输入文本一起送入模型,参与计算,推理开销极小。
数学表示:
假设输入序列为
[
x
1
,
x
2
,
…
,
x
n
]
[x_1, x_2, \dots, x_n]
[x1,x2,…,xn],P-tuning v1 在序列中插入
k
k
k 个提示 token
[
p
1
,
p
2
,
…
,
p
k
]
[p_1, p_2, \dots, p_k]
[p1,p2,…,pk]。提示嵌入由提示生成器
f
θ
f_\theta
fθ 生成:
[
p
1
,
p
2
,
…
,
p
k
]
=
f
θ
(
[
i
1
,
i
2
,
…
,
i
k
]
)
[p_1, p_2, \dots, p_k] = f_\theta([i_1, i_2, \dots, i_k])
[p1,p2,…,pk]=fθ([i1,i2,…,ik])
其中,
[
i
1
,
i
2
,
…
,
i
k
]
[i_1, i_2, \dots, i_k]
[i1,i2,…,ik] 是初始输入(如离散提示的嵌入或索引),
θ
\theta
θ 是提示生成器的可训练参数。最终输入序列为:
[
Embed
(
p
1
)
,
Embed
(
p
2
)
,
…
,
Embed
(
p
k
)
,
Embed
(
x
1
)
,
Embed
(
x
2
)
,
…
]
[\text{Embed}(p_1), \text{Embed}(p_2), \dots, \text{Embed}(p_k), \text{Embed}(x_1), \text{Embed}(x_2), \dots]
[Embed(p1),Embed(p2),…,Embed(pk),Embed(x1),Embed(x2),…]
2. P-tuning v1 的优点
-
参数效率高:
- 提示生成器的参数量极少,通常占模型总参数的 0.01%-0.1%。例如,对于一个 110M 参数的 BERT 模型,提示生成器可能只有几万到几十万参数。
- 存储需求低,适合多任务部署。
-
增强表达能力:
- 通过提示生成器(MLP 或 LSTM)生成提示嵌入,相比直接优化的软提示(Prompt Tuning),P-tuning v1 的提示更具动态性和语义表达能力。
-
结合离散提示的先验:
- 使用离散提示的嵌入初始化(如“positive”或“negative”),提供任务相关的语义信息,加速收敛并提升性能。
-
适合小数据集:
- 优化少量参数降低过拟合风险,特别适合数据量有限的场景。
-
推理开销低:
- 提示生成器仅在输入端添加少量计算,推理时延迟几乎不变。
3. P-tuning v1 的缺点
-
性能依赖任务:
- P-tuning v1 在自然语言理解任务(如分类、关系抽取)上表现优异,但在生成任务(如文本生成)上可能不如 Prefix Tuning 或 Adapter Tuning。
-
超参数敏感:
- 提示生成器的结构(MLP 或 LSTM)、提示长度、初始化方式等需要仔细调优,可能增加实验复杂性。
-
实现复杂度稍高:
- 相比 Prompt Tuning 的简单嵌入优化,P-tuning v1 需要设计和训练提示生成器,代码实现稍复杂。
-
对模型规模敏感:
- 类似 Prompt Tuning,P-tuning v1 在大模型(如 GPT-3)上效果更显著,在中小型模型(如 BERT)上可能性能略逊于 Adapter 或 LoRA。
4. P-tuning v1 的代表性实现
-
P-tuning(Liu et al., 2021):
- 最早提出 P-tuning 的方法,针对自然语言理解任务(如 SuperGLUE 基准)设计。
- 使用双层 MLP 作为提示生成器,结合离散提示初始化,在 BERT 和 GPT-2 等模型上展示了优于手动提示的性能。
-
P-tuning v2:
- 改进版本,进一步优化提示生成器的设计和初始化策略,扩展到更多任务(包括生成任务)。
- 在性能上更接近 Adapter Tuning 或 LoRA,尤其在大模型上。
5. P-tuning v1 的应用场景
P-tuning v1 特别适合以下场景:
- 自然语言理解任务:如文本分类、关系抽取、意图检测等,尤其在 SuperGLUE 等基准任务中表现优异。
- 小数据集场景:数据量有限时,通过优化提示生成器避免过拟合。
- 多任务学习:为不同任务训练独立的提示生成器,共享同一预训练模型。
- 提示工程优化:结合离散提示的先验,增强手动提示的效果。
- 资源受限环境:低存储和计算需求,适合快速部署。
6. P-tuning v1 的代码示例
以下是一个使用 Python 和 Hugging Face 的 transformers
库实现 P-tuning v1 的示例,基于 BERT 模型为 GLUE 的 SST-2(情感分类)任务进行微调。由于 Hugging Face 的 peft
库直接支持 P-tuning,我们将使用其实现。
from transformers import AutoModelForSequenceClassification, AutoTokenizer, TrainingArguments, Trainer
from peft import get_peft_model, PromptEncoderConfig
from datasets import load_dataset
import numpy as np
# 1. 加载预训练模型和分词器
model_name = "bert-base-uncased"
tokenizer = AutoTokenizer.from_pretrained(model_name)
model = AutoModelForSequenceClassification.from_pretrained(model_name, num_labels=2)
# 2. P-tuning配置
p_tuning_config = PromptEncoderConfig(
task_type="SEQ_CLS",
num_virtual_tokens=20,
encoder_hidden_size=128, # MLP的隐藏层维度
encoder_dropout=0.1,
encoder_num_layers=2, # MLP层数
)
model = get_peft_model(model, p_tuning_config)
# 3. 数据预处理
def preprocess_function(examples):
encoding = tokenizer(
examples["sentence"],
padding="max_length",
truncation=True,
max_length=128,
return_tensors="pt"
)
encoding["labels"] = examples["label"] # 添加标签字段
return encoding
dataset = load_dataset("glue", "sst2")
encoded_dataset = dataset.map(preprocess_function, batched=True)
train_dataset = encoded_dataset["train"].select(range(1000))
eval_dataset = encoded_dataset["validation"]
# 4. 调整训练参数
training_args = TrainingArguments(
output_dir="./p_tuning_output",
num_train_epochs=3,
per_device_train_batch_size=16,
per_device_eval_batch_size=16,
eval_strategy="epoch",
save_strategy="epoch",
load_best_model_at_end=True,
metric_for_best_model="accuracy", # 定义最佳模型标准
greater_is_better=True,
)
# 5. 初始化训练器(优化评估指标计算)
def compute_metrics(eval_pred):
predictions = np.argmax(eval_pred.predictions, axis=1)
return {"accuracy": (predictions == eval_pred.label_ids).mean()}
trainer = Trainer(
model=model,
args=training_args,
train_dataset=train_dataset,
eval_dataset=eval_dataset,
compute_metrics=compute_metrics,
)
# 6. 训练P-tuning
trainer.train()
# 7. 保存最终模型
model.save_pretrained("./p_tuning_model")
# 8. 推理示例
text = "This movie is fantastic!"
inputs = tokenizer(text, return_tensors="pt", padding=True, truncation=True, max_length=128).to(model.device)
outputs = model(**inputs)
prediction = outputs.logits.argmax(-1).item()
print(f"Prediction: {'Positive' if prediction == 1 else 'Negative'}")
代码说明:
- 模型和 P-tuning:使用
bert-base-uncased
模型,添加 20 个提示 token,由 MLP 提示生成器生成。 - 数据集:使用 GLUE 的 SST-2 数据集(二分类情感分析),仅用部分数据以加快训练。
- 训练:仅优化提示生成器的参数,冻结 BERT 的原始参数。
- 保存和推理:训练后的提示生成器参数保存为小文件(几十 KB),可用于情感分类推理。
- 依赖:需要安装
transformers
,peft
, 和datasets
库(pip install transformers peft datasets
)。
运行结果:
- P-tuning v1 通常只需 1-2 GB 的 GPU 内存,远低于全参数微调。
- 训练后,模型在 SST-2 数据集上可达到 ~88%-92% 的准确率,接近 Adapter Tuning 的性能。
7. 与其他 PEFT 方法的对比
方法 | 参数效率 | 推理延迟 | 性能(相对全参数微调) | 适用场景 |
---|---|---|---|---|
P-tuning v1 | 极高 | 无增加 | 接近 | NLU 任务、小数据集 |
Prompt Tuning | 极高 | 无增加 | 稍逊 | 小数据集、API 模型 |
Prefix Tuning | 极高 | 无增加 | 稍逊 | 生成任务、低资源场景 |
Adapter Tuning | 高 | 轻微增加 | 接近 | 多任务、跨语言迁移 |
LoRA | 极高 | 无增加 | 接近 | 大模型适配、个性化 |
- 与 Prompt Tuning 相比:P-tuning v1 通过提示生成器增强表达能力,性能更稳定,尤其在 NLU 任务上。
- 与 Prefix Tuning 相比:P-tuning v1 更适合理解任务(如分类),而 Prefix Tuning 更适合生成任务。
- 与 Adapter Tuning 相比:P-tuning v1 参数量更少,推理速度更快,但 Adapter Tuning 在复杂任务上可能更稳定。
- 与 LoRA 相比:P-tuning v1 实现稍复杂,但在结合离散提示的场景中更具优势。
8. 总结
P-tuning v1 是一种高效的微调方法,通过使用提示生成器优化连续提示,结合离散提示的先验,实现任务适配。它在参数效率、推理速度和自然语言理解任务中表现优异,特别适合小数据集和提示工程优化的场景。