一.soft-prompt
训练大型预训练语言模型非常耗时且计算密集。随着模型规模的不断扩大,研究者们越来越关注更有效的训练方法,如提示(Prompt)。提示通过在输入中添加描述任务的文本提示,甚至通过提供任务示例,来为特定下游任务准备冻结的预训练模型。通过使用提示,您可以避免为每个下游任务训练一个单独的模型,而是通过同一个冻结的预训练模型来完成多个任务。这比为每个任务训练一个单独的模型要高效得多,因为您只需要训练和存储一个较小的提示参数集,而不是训练整个模型的参数。
提示方法可以分为两类:
- 硬提示:手动创建的带有离散输入标记的文本提示。缺点是创建一个好的提示需要付出很多努力。
- 软提示:与可以针对数据集优化的输入嵌入连接的可学习张量。缺点是软提示不是人类可读的,因为这些“虚拟标记”没有与真实单词的嵌入对应。
本概念指南简要概述了 PEFT(Prompt Engineering and Fine-Tuning)中包含的软提示方法:提示调优、前缀调优、P调优和多任务提示调优。
# pip install -q peft transformers datasets
from datasets import load_dataset
ds = load_dataset("ought/raft", "twitter_complaints")
classes = [k.replace("_", " ") for k in ds["train"].features["Label"].names]
ds = ds.map(
lambda x: {"text_label": [classes[label] for label in x["Label"]]},
batched=True,
num_proc=1,
)
# ds["train"][0]
# {"Tweet text": "@HMRCcustomers No this is my first job", "ID": 0, "Label": 2, "text_label": "no complaint"}
数据集:
from transformers import AutoTokenizer
tokenizer = AutoTokenizer.from_pretrained("bigscience/bloomz-560m")
if tokenizer.pad_token_id is None:
tokenizer.pad_token_id = tokenizer.eos_token_id
target_max_length = max([len(tokenizer(class_label)["input_ids"]) for class_label in classes])
print(target_max_length)
import torch
max_length = 64
def preprocess_function(examples, text_column="Tweet text", label_column="text_label"):
batch_size = len(examples[text_column])
inputs = [f"{text_column} : {x} Label : " for x in examples[text_column]]
targets = [str(x) for x in examples[label_column]]
model_inputs = tokenizer(inputs)
labels = tokenizer(targets)
classes = [k.replace("_", " ") for k in ds["train"].features["Label"].names]
for i in range(batch_size):
sample_input_ids = model_inputs["input_ids"][i]
label_input_ids = labels["input_ids"][i]
model_inputs["input_ids"][i] = [tokenizer.pad_token_id] * (
max_length - len(sample_input_ids)
) + sample_input_ids
model_inputs["attention_mask"][i] = [0] * (max_length - len(sample_input_ids)) + model_inputs[
"attention_mask"
][i]
labels["input_ids"][i] = [-100] * (max_length - len(label_input_ids)) + label_input_ids
model_inputs["input_ids"][i] = torch.tensor(model_inputs["input_ids"][i][:max_length])
model_inputs["attention_mask"][i] = torch.tensor(model_inputs["attention_mask"][i][:max_length])
labels["input_ids"][i] = torch.tensor(labels["input_ids"][i][:max_length])
model_inputs["labels"] = labels["input_ids"]
return model_inputs
processed_ds = ds.map(
preprocess_function,
batched=True,
num_proc=1,
remove_columns=ds["train"].column_names,
load_from_cache_file=False,
desc="Running tokenizer on dataset",
)
from torch.utils.data import DataLoader
from transformers import default_data_collator
train_ds = processed_ds["train"]
eval_ds = processed_ds["test"]
batch_size = 16
train_dataloader = DataLoader(train_ds, shuffle=True, collate_fn=default_data_collator, batch_size=batch_size, pin_memory=True)
eval_dataloader = DataLoader(eval_ds, collate_fn=default_data_collator, batch_size=batch_size, pin_memory=True)
from transformers import AutoModelForCausalLM
model = AutoModelForCausalLM.from_pretrained("bigscience/bloomz-560m")
1.p-tune
P调优(P-Tuning)是一种为自然语言理解(NLU)任务和所有语言模型设计的方法,它是软提示方法的另一种变体。P调优通过添加一个可训练的嵌入张量来优化提示,以找到更好的提示,并使用提示编码器(例如双向长短期记忆网络 LSTM)来优化提示参数。与前缀调优不同,P调优的特点是:
- 提示令牌可以插入到输入序列的任何位置,而不仅仅是输入的开头。
- 提示令牌只添加到输入中,而不是添加到模型的每一层。
- 引入锚令牌(anchor tokens)可以改善性能,因为锚令牌能够表示输入序列中某一组件的特征。
研究结果表明,P调优比手动设计提示更高效,并且使得像 GPT 这样的模型能够在自然语言理解任务上与 BERT 等模型相竞争。
P-tuning 添加了一个可训练的嵌入张量,其中提示标记可以添加到输入序列中的任何位置。创建一个 PromptEncoderConfig,其中包含任务类型、要添加和学习的虚拟令牌数量以及用于学习提示参数的编码器的隐藏大小
from peft import PromptEncoderConfig, get_peft_model
peft_config = PromptEncoderConfig(task_type="CAUSAL_LM", num_virtual_tokens=20, encoder_hidden_size=128)
model = get_peft_model(model, peft_config)
model.print_trainable_parameters()
"trainable params: 300,288 || all params: 559,514,880 || trainable%: 0.05366935013417338"
PromptEncoderConfig
class peft.PromptEncoderConfig:
peft_type: Union = None
auto_mapping: Optional = None
base_model_name_or_path: Optional = None
revision: Optional = None
task_type: Union = None
inference_mode: bool = False
num_virtual_tokens: int = None
token_dim: int = None
num_transformer_submodules: Optional = None
num_attention_heads: Optional = None
num_layers: Optional = None
encoder_reparameterization_type: Union = <PromptEncoderReparameterizationType.MLP: 'MLP'>
encoder_hidden_size: int = None
encoder_num_layers: int = 2
encoder_dropout: float = 0.0
参数说明:
encoder_reparameterization_type
:指定要使用的重新参数化类型。encoder_hidden_size
:提示编码器的隐藏层大小。encoder_num_layers
:提示编码器的层数。encoder_dropout
:提示编码器的丢弃概率。- 这是用于存储 PromptEncoder 配置
PromptEncoder
class peft.PromptEncoder:
config: PromptEncoderConfig
参数说明:
config
:提示编码器的配置。- 提示编码器网络用于生成 P-tuning 的虚拟令牌嵌入。
示例代码:
from peft import PromptEncoder, PromptEncoderConfig
config = PromptEncoderConfig(
peft_type="P_TUNING",
task_type="SEQ_2_SEQ_LM",
num_virtual_tokens=20,
token_dim=768,
num_transformer_submodules=1,