在 Hugging Face 的 PEFT(Parameter-Efficient Fine-Tuning)库中,P-tuningConfig 是用于配置 P-Tuning 方法的类。P-Tuning 是一种参数高效的微调技术,通过在输入序列中添加可训练的连续提示(continuous prompts)来调整预训练模型的行为,而不修改模型的原始权重。P-Tuning 是 Prompt Tuning 的改进版本,结合了更灵活的提示设计和优化的提示嵌入,特别适合生成任务和复杂任务。以下是对 P-tuningConfig
的详细讲解,包括其参数、用法、代码示例和注意事项。
1. P-tuningConfig 概述
P-Tuning 的核心思想是通过学习一组连续的提示嵌入(soft prompts),将其插入到输入序列中,作为任务特定的上下文。与 Prompt Tuning 相比,P-Tuning 通常使用更复杂的提示设计(例如通过 LSTM 或 MLP 建模提示嵌入),并且在生成任务(如语言模型)中表现更好。P-Tuning 的参数量极少,仅涉及提示嵌入的参数,非常适合资源受限的场景。
P-Tuning 的优势:
- 高效性:只训练少量提示参数,参数量远少于全参数微调。
- 灵活性:通过复杂的提示建模(如 LSTM 或 MLP),提高任务适配能力。
- 适用于生成任务:在因果语言模型(如 GPT)或序列到序列模型(如 T5)中效果显著。
- 兼容性:与 Hugging Face 的 Transformers 模型无缝集成。
2. P-tuningConfig 的主要参数
以下是 P-tuningConfig
中常用的参数及其说明(基于 PEFT 库的最新版本):
-
peft_type
(默认:"P_TUNING"
)- 指定 PEFT 方法类型,固定为
"P_TUNING"
。 - 通常无需手动设置,由类自动处理。
- 指定 PEFT 方法类型,固定为
-
task_type
(可选,字符串)- 指定任务类型,帮助 PEFT 确定模型结构和用途。常见选项包括:
"SEQ_CLS"
:序列分类(如情感分析)。"TOKEN_CLS"
:token 分类(如 NER)。"CAUSAL_LM"
:因果语言模型(如 GPT)。"SEQ_2_SEQ_LM"
:序列到序列模型(如 T5)。
- 推荐明确指定,尤其是生成任务使用
"CAUSAL_LM"
或"SEQ_2_SEQ_LM"
。
- 指定任务类型,帮助 PEFT 确定模型结构和用途。常见选项包括:
-
num_virtual_tokens
(整数,默认:20)- 指定添加到输入序列中的虚拟提示 token 数量。
- 典型值:10 到 100。更多的 token 增加表达能力,但也增加参数量。
- 参数量计算:依赖于提示建模方式(例如,
num_virtual_tokens * embedding_dim
或更多,如果使用 LSTM/MLL)。
-
prompt_encoder_type
(字符串,默认:"LSTM"
)- 指定提示嵌入的编码器类型,用于生成连续提示。选项包括:
"LSTM"
:使用 LSTM 建模提示嵌入(P-Tuning v2 的默认方式)。"MLP"
:使用多层感知机(MLP)建模提示嵌入。"INNER"
:直接优化嵌入(类似 Prompt Tuning)。
- LSTM 通常更适合复杂任务,但参数量稍多;MLP 更简单,参数量较少。
- 指定提示嵌入的编码器类型,用于生成连续提示。选项包括:
-
encoder_hidden_size
(整数,可选)- 指定提示编码器(LSTM 或 MLP)的隐藏层大小。
- 典型值:与模型的
embedding_dim
匹配(如 BERT 的 768,GPT 的 1024)。 - 如果不指定,默认使用模型的隐藏层大小。
-
encoder_num_layers
(整数,默认:2)- 指定提示编码器(LSTM 或 MLP)的层数。
- 典型值:1 到 4。更多层增加表达能力,但也增加参数量。
-
encoder_dropout
(浮点数,默认:0.1)- 提示编码器中的 Dropout 概率,用于正则化。
- 典型值:0.0 到 0.5。
-
modules_to_save
(列表,可选)- 指定需要全量微调的模块(不使用 P-Tuning)。例如,分类头或语言模型头可以放入此列表。
- 示例:
["classifier"]
或["lm_head"]
。
3. 使用 P-tuningConfig 的基本流程
以下是一个使用 P-tuningConfig
微调 GPT-2 模型(用于因果语言建模任务)的完整示例:
步骤 1:安装和导入库
pip install peft transformers torch datasets
from transformers import AutoModelForCausalLM, AutoTokenizer, Trainer, TrainingArguments
from peft import P-tuningConfig, get_peft_model
from datasets import load_dataset
步骤 2:加载模型和数据集
# 加载预训练模型和分词器
model_name = "gpt2"
model = AutoModelForCausalLM.from_pretrained(model_name)
tokenizer = AutoTokenizer.from_pretrained(model_name)
tokenizer.pad_token = tokenizer.eos_token # 为 GPT-2 设置 pad token
# 加载数据集(以 wikitext 为例)
dataset = load_dataset("wikitext", "wikitext-2-raw-v1")
# 数据预处理
def tokenize_function(examples):
return tokenizer(examples["text"], truncation=True, padding="max_length", max_length=128)
tokenized_dataset = dataset.map(tokenize_function, batched=True)
步骤 3:配置 P-tuningConfig
# 配置 P-Tuning
ptuning_config = P-tuningConfig(
task_type="CAUSAL_LM", # 因果语言模型任务
num_virtual_tokens=20, # 虚拟提示 token 数量
prompt_encoder_type="LSTM", # 使用 LSTM 编码器
encoder_hidden_size=1024, # 编码器隐藏层大小(匹配 GPT-2)
encoder_num_layers=2, # 编码器层数
encoder_dropout=0.1, # Dropout 概率
modules_to_save=["lm_head"] # 全量微调语言模型头
)
# 将 P-Tuning 应用到模型
peft_model = get_peft_model(model, ptuning_config)
# 查看可训练参数
peft_model.print_trainable_parameters()
输出示例:
trainable params: 2,048,000 || all params: 126,688,608 || trainable%: 1.617
这表明只有约 1.62% 的参数需要训练(LSTM 编码器参数 + 语言模型头参数)。
步骤 4:训练模型
# 配置训练参数
training_args = TrainingArguments(
output_dir="./results",
evaluation_strategy="epoch",
learning_rate=1e-3, # P-Tuning 通常需要较高学习率
per_device_train_batch_size=8,
per_device_eval_batch_size=8,
num_train_epochs=3,
weight_decay=0.01,
)
# 初始化 Trainer
trainer = Trainer(
model=peft_model,
args=training_args,
train_dataset=tokenized_dataset["train"].select(range(1000)), # 选取子集以加速示例
eval_dataset=tokenized_dataset["validation"].select(range(200)),
)
# 开始训练
trainer.train()
步骤 5:保存和加载 P-Tuning 模型
# 保存 P-Tuning 参数
peft_model.save_pretrained("./ptuning_model")
# 加载 P-Tuning 模型
from peft import PeftModel
base_model = AutoModelForCausalLM.from_pretrained(model_name)
loaded_model = PeftModel.from_pretrained(base_model, "./ptuning_model")
步骤 6:推理
# 准备输入
inputs = tokenizer("Once upon a time", return_tensors="pt")
# 推理
loaded_model.eval()
outputs = loaded_model.generate(**inputs, max_length=50)
print(tokenizer.decode(outputs[0], skip_special_tokens=True))
4. P-tuningConfig 的优化建议
-
调整
num_virtual_tokens
:- 较小的值(如 10-20)适合简单任务或快速实验。
- 较大的值(如 50-100)适合复杂生成任务,但会增加参数量。
- 参数量与
num_virtual_tokens
和编码器复杂度(LSTM/MLL)相关。
-
选择
prompt_encoder_type
:- LSTM:适合复杂任务,提供更强的提示建模能力,但参数量较多。
- MLP:适合简单任务或资源受限场景,参数量较少。
- INNER:类似 Prompt Tuning,适合快速实验。
-
配置
encoder_hidden_size
:- 通常设置为模型的
embedding_dim
(如 GPT-2 的 1024,BERT 的 768)。 - 较小的值可以减少参数量,但可能降低性能。
- 通常设置为模型的
-
调整
encoder_num_layers
:- 默认 2 层通常足够。增加到 3 或 4 层可增强表达能力,但参数量增加。
- 较小的值(1 层)适合快速实验。
-
学习率:
- P-Tuning 通常需要较高的学习率(如 1e-3 或 5e-4),因为只优化少量参数。
- 使用学习率调度器(如线性衰减)以提高稳定性。
-
任务类型:
- 确保
task_type
匹配模型类型,例如 GPT 使用"CAUSAL_LM"
,T5 使用"SEQ_2_SEQ_LM"
。
- 确保
-
内存优化:
- P-Tuning 参数量较小,但对于大模型,可以结合 4-bit 量化:
from transformers import BitsAndBytesConfig quantization_config = BitsAndBytesConfig(load_in_4bit=True) model = AutoModelForCausalLM.from_pretrained(model_name, quantization_config=quantization_config)
- P-Tuning 参数量较小,但对于大模型,可以结合 4-bit 量化:
5. P-Tuning vs. 其他 PEFT 方法
-
P-Tuning vs. Prompt Tuning:
- 结构:P-Tuning 使用复杂的编码器(LSTM/MLL)建模提示,Prompt Tuning 直接优化嵌入。
- 参数量:P-Tuning 参数量稍多,但表达能力更强。
- 适用场景:P-Tuning 更适合生成任务,Prompt Tuning 更通用。
-
P-Tuning vs. Prefix Tuning:
- 结构:P-Tuning 在输入序列添加提示,Prefix Tuning 在每层添加前缀向量。
- 参数量:P-Tuning 参数量通常少于 Prefix Tuning(取决于编码器)。
- 适用场景:两者都适合生成任务,P-Tuning 更灵活。
-
P-Tuning vs. LoRA:
- 参数量:P-Tuning 参数量通常少于 LoRA。
- 灵活性:LoRA 应用于权重矩阵,适合多种任务;P-Tuning 更专注生成任务。
- 性能:LoRA 在分类任务中可能更优,P-Tuning 在生成任务中表现更好。
6. 常见问题与解答
-
Q1:如何选择
num_virtual_tokens
?- 从 10-20 开始实验。如果生成质量不足,增加到 50 或 100。
- 注意参数量随
num_virtual_tokens
和编码器复杂度增长。
-
Q2:
prompt_encoder_type
如何选择?- LSTM:推荐用于复杂任务或大模型。
- MLP:适合简单任务或快速实验。
- INNER:类似 Prompt Tuning,适合资源极度受限的场景。
-
Q3:P-Tuning 是否支持所有模型?
- 支持所有 Hugging Face Transformers 模型,但最适合生成模型(如 GPT、LLaMA、T5)。
- 对于分类模型(如 BERT),效果可能不如 LoRA。
-
Q4:性能不佳如何优化?
- 增加
num_virtual_tokens
或encoder_num_layers
。 - 切换
prompt_encoder_type
(如从 MLP 到 LSTM)。 - 调整学习率(尝试 5e-4 到 2e-3)。
- 检查数据集质量或增加训练轮次。
- 增加
7. 进阶用法
-
多任务 P-Tuning:
- 为不同任务创建多个提示:
ptuning_config_task1 = P-tuningConfig( task_type="CAUSAL_LM", num_virtual_tokens=20, prompt_encoder_type="LSTM" ) ptuning_config_task2 = P-tuningConfig( task_type="CAUSAL_LM", num_virtual_tokens=20, prompt_encoder_type="MLP" ) peft_model = get_peft_model(model, ptuning_config_task1, adapter_name="task1") peft_model.add_adapter("task2", ptuning_config_task2) peft_model.set_adapter("task1") # 切换提示
-
保存到 Hugging Face Hub:
peft_model.push_to_hub("your-username/ptuning-model")
-
结合量化:
from transformers import AutoModelForCausalLM, BitsAndBytesConfig quantization_config = BitsAndBytesConfig(load_in_4bit=True) model = AutoModelForCausalLM.from_pretrained(model_name, quantization_config=quantization_config) peft_model = get_peft_model(model, ptuning_config)