
Prompt Tuning 和 P-Tuning 都属于 参数高效微调方法(PEFT, Parameter-Efficient Fine-Tuning),主要是为了避免对大模型全部参数进行训练,而是通过小规模参数(prompt embedding)来适配下游任务。但两者的实现方式和应用场景有一些区别:
提示微调 仅在输入嵌入层中加入可训练的提示向量。在离散提示方法的基础上,提示微调首先在输入端插入一组连续嵌入数值的提示词元,这些提示词元可以以自由形式 或前 缀形式 来增强输入文本,用于解决特定的下游任务。在具体实现中,只需要 将可学习的特定任务提示向量与输入文本向量结合起来一起输入到语言模型中。 P-tuning 提出了使用自由形式来组合输入文本和提示向量,通过双向 LSTM 来学习软提示词元的表示,它可以同时适用于自然语言理解和生成任务。另一种 代表性方法称为 Prompt Tuning ,它以前缀形式添加提示,直接在输入前拼 接连续型向量。在提示微调的训练过程中,只有提示的嵌入向量会根据特定任务 进行监督学习,然而由于只在输入层中包含了极少量的可训练参数,有研究工作 表明该方法的性能高度依赖底层语言模型的能力 。下图 展示了提示微调算 法的示意图。

[Prompt Embeddings] + [下游任务输入] → 模型输出。对比点 | Prompt Tuning | P-Tuning (v1) | P-Tuning v2 |
|---|---|---|---|
参数位置 | 输入层前加虚拟 embedding | 输入层前加连续 embedding | 各层 Transformer 插入虚拟 prompt |
训练参数量 | 极少 | 极少 | 较少(但比 Prompt Tuning 多) |
表达能力 | 相对较弱 | 类似 Prompt Tuning | 更强,接近全量微调 |
适用任务 | NLP下游任务(分类、生成) | NLP任务 | 小数据/复杂任务,泛化更好 |
提出方 | Google (Lester et al., 2021) | 清华 (Liu et al., 2021) | 清华 (v2, ACL 2022) |
一句话总结:
举例说明。
在 NLP 里,输入通常是离散的 token(如 "apple"、"我"、"中国")。 这些 token 会先通过 词表查找 变成向量:
例如词表大小 = 10000,embedding 维度 = 768:
scss 体验AI代码助手 代码解读复制代码"apple" → [0.12, -0.34, 0.98, ... , 0.45] (768维向量)
"中国" → [0.87, 0.22, -0.54, ... , -0.11]这些 embedding 是模型在预训练时学好的。
假设我们要做 情感分类(句子 → 积极/消极),输入是:
arduino 体验AI代码助手 代码解读复制代码"这部电影很精彩"用 Prompt Tuning 时,可以加 5 个虚拟 token:
css 体验AI代码助手 代码解读复制代码[v1][v2][v3][v4][v5] 这部电影很精彩其中 [v1]...[v5] 就是 虚拟 embedding:
css 体验AI代码助手 代码解读复制代码[v1] → [0.01, 0.77, -0.32, ...]
[v2] → [0.55, -0.88, 0.14, ...]
...这些 embedding 不属于词表,但会在训练过程中学会 如何引导模型输出“积极/消极”。
离散 prompt(人写的文字)可能是:
arduino 体验AI代码助手 代码解读复制代码"这部电影很精彩 [MASK]"连续 prompt(P-Tuning)则是:
scss 体验AI代码助手 代码解读复制代码[0.12, -0.33, 0.98, ...] (embedding1)
[0.54, 0.11, -0.66, ...] (embedding2)
[0.22, -0.77, 0.44, ...] (embedding3)
这部电影很精彩区别在于:
下边是一个 PyTorch 示例,演示如何在输入序列前面加上 虚拟/连续 embedding。
假设我们有一个简化的模型(类似 BERT),输入是 token embedding,我们想在输入前面加几个可学习的 prompt embedding。
python 体验AI代码助手 代码解读复制代码import torch
import torch.nn as nn
class SimpleModel(nn.Module):
def __init__(self, vocab_size=10000, embed_dim=16, prompt_len=5):
super().__init__()
# 普通 embedding (词表)
self.embedding = nn.Embedding(vocab_size, embed_dim)
# prompt embedding (虚拟/连续向量,不属于词表)
self.prompt_embedding = nn.Parameter(torch.randn(prompt_len, embed_dim))
# 一个简单的分类头
self.fc = nn.Linear(embed_dim, 2) # 假设2分类任务
def forward(self, input_ids):
"""
input_ids: [batch_size, seq_len] (普通输入token的id)
"""
batch_size = input_ids.size(0)
# 1. 把 token id 转换成 embedding
token_embeds = self.embedding(input_ids) # [batch, seq_len, embed_dim]
# 2. prompt embedding (复制到 batch 维度)
prompt_embeds = self.prompt_embedding.unsqueeze(0).expand(batch_size, -1, -1) # [batch, prompt_len, embed_dim]
# 3. 拼接 prompt 和原始输入
full_embeds = torch.cat([prompt_embeds, token_embeds], dim=1) # [batch, prompt_len+seq_len, embed_dim]
# 假设我们只取最后一个 token 位置做分类
last_hidden = full_embeds[:, -1, :] # [batch, embed_dim]
# 分类
logits = self.fc(last_hidden)
return logits
# ======================
# 🔹 测试
# ======================
batch_size = 2
seq_len = 4
vocab_size = 10000
embed_dim = 16
prompt_len = 5
model = SimpleModel(vocab_size, embed_dim, prompt_len)
# 模拟两个样本,每个长度为4
input_ids = torch.randint(0, vocab_size, (batch_size, seq_len))
print("输入 token ids:\n", input_ids)
logits = model(input_ids)
print("输出 logits:\n", logits)[12, 87, 325, 99] → 4个 embedding5 个虚拟 token。
[p1, p2, p3, p4, p5] → 5个 embeddingself.prompt_embedding 学习)。
本文系转载,前往查看
如有侵权,请联系 cloudcommunity@tencent.com 删除。
本文系转载,前往查看
如有侵权,请联系 cloudcommunity@tencent.com 删除。