
基于Hugging Face的NLP任务微调
这种处理方法是ok的,但缺点是处理之后tokenized_dataset不再是一个dataset格式,而是返回字典(带有我们的键:input_ids、attention_mask 和 token_type_ids,对应的键值对的值)。Trainer 第一个参数是TrainingArguments类,是一个与训练循环本身相关的参数的子集,包含 Trainer中用于训练和评估的所有超参数。为了使我们的
pipeline
from transformers import pipeline
classifier([
"I've been waiting for a HuggingFace course my whole life.",
"I hate this so much!"
])
"""
[{'label': 'POSITIVE', 'score': 0.9598047137260437},
{'label': 'NEGATIVE', 'score': 0.9994558095932007}]
"""
目前可用的一些管道是:
- feature-extraction (获取文本的向量表示)
- fill-mask填充给定文本中的空白(完形填空)
- ner (named entity recognition)词性标注
- question-answering问答
- sentiment-analysis情感分析
- summarization摘要生成
- text-generation文本生成
- translation翻译
- zero-shot-classification零样本分类
这个管道将三个步骤组合在一起:预处理、通过模型传递输入和后处理:
Behind the pipeline
tokenizer预处理
与其他神经网络一样,Transformer 模型不能直接处理原始文本,因此我们管道的第一步是将文本输入转换为模型可以理解的数字。为此,我们使用了一个分词器tokenizer,它将负责:
- 将输入拆分为称为标记的单词、子词subword或符号symbols(如标点符号)
- 将每个标记映射到一个整数
- 添加可能对模型有用的其他输入
from transformers import AutoTokenizer
checkpoint = "distilbert-base-uncased-finetuned-sst-2-english"
tokenizer = AutoTokenizer.from_pretrained(checkpoint)
raw_inputs = [
"I've been waiting for a HuggingFace course my whole life.",
"I hate this so much!",
]
inputs = tokenizer(raw_inputs, padding=True, truncation=True, return_tensors="pt")
#return_tensors="pt"表示返回Pytorch张量。文本转换为数字之后必须再转换成张量tensors才能输入模型。
#padding=True表示填充输入序列到最大长度,truncation=True表示过长序列被截断
选择模型
from transformers import AutoModel
checkpoint = "distilbert-base-uncased-finetuned-sst-2-english"
model = AutoModel.from_pretrained(checkpoint)
Model heads
将隐藏状态的高维向量(也就是logits向量)作为输入,并将它们投影到不同的维度上
Transformers 中有许多不同的架构可用,每一种架构都围绕着处理特定任务而设计。 下面列举了部分Model heads:
- Model (retrieve the hidden states)
- ForCausalLM
- ForMaskedLM
- ForMultipleChoice
- ForQuestionAnswering
- ForSequenceClassification
- ForTokenClassification
- and others 🤗
以情感分类为例,我们需要一个带有序列分类的Model head(能够将句子分类为正面或负面)。 因此,我们实际上不会使用 AutoModel 类,而是使用 AutoModelForSequenceClassification:
from transformers import AutoModelForSequenceClassification
checkpoint = "distilbert-base-uncased-finetuned-sst-2-english"
model = AutoModelForSequenceClassification.from_pretrained(checkpoint)
outputs = model(**inputs)
print(outputs.logits.shape)
"""
torch.Size([2, 2])
由于我们只有两个句子和两个标签,因此我们从模型中得到的结果是 2 x 2 的形状。
"""
构建Trainer API微调预训练模型
从Hub上下载dataset
GLUE 基准是一种学术基准,用于衡量 ML 模型在 10 个不同文本分类任务中的性能。
Datasets库提供了一个非常简单的命令来下载和缓存Hub上的dataset。 我们可以像这样下载 MRPC 数据集:
from datasets import load_dataset
raw_datasets = load_dataset("glue", "mrpc")
raw_datasets
"""
DatasetDict({
train: Dataset({
features: ['sentence1', 'sentence2', 'label', 'idx'],
num_rows: 3668
})
validation: Dataset({
features: ['sentence1', 'sentence2', 'label', 'idx'],
num_rows: 408
})
test: Dataset({
features: ['sentence1', 'sentence2', 'label', 'idx'],
num_rows: 1725
})
})
"""
load_dataset 方法, 可以从不同的地方构建数据集
- from the HuggingFace Hub,
- from local files, 如CSV/JSON/text/pandas files
- from in-memory data like python dict or a pandas dataframe.
例如: datasets = load_dataset("text", data_files={"train": path_to_train.txt, "validation": path_to_validation.txt} 具体可以参考文档
可以通过设置 HF_HOME 环境变量来自定义缓存文件夹
数据集预处理
tokenized_dataset = tokenizer(
raw_datasets["train"]["sentence1"],
raw_datasets["train"]["sentence2"],
padding=True,
truncation=True,
)
这种处理方法是ok的,但缺点是处理之后tokenized_dataset不再是一个dataset格式,而是返回字典(带有我们的键:input_ids、attention_mask 和 token_type_ids,对应的键值对的值)。 而且一旦我们的dataset过大,无法放在内存中,那么这样子的做法会导致 Out of Memory 的异常。( 🤗 Datasets 库中的数据集是存储在磁盘上的 Apache Arrow 文件,因此请求加载的样本都保存在内存中)。
为了使我们的数据保持dataset的格式,我们将使用更灵活的Dataset.map 方法。此方法可以完成更多的预处理而不仅仅是 tokenization:
def tokenize_function(example):
return tokenizer(example["sentence1"], example["sentence2"], truncation=True)
在 map 调用中使用了 batched=True,因此该函数一次应用于数据集的整个batch元素,而不是分别应用于每个元素。 这样预处理速度会更快(因为 🤗 Tokenizers 库中的Tokenizer用 Rust 编写,一次处理很多输入时这个分词器可以非常快)。
tokenized_datasets = raw_datasets.map(tokenize_function, batched=True)
tokenized_datasets
使用 Trainer API 在 PyTorch 中进行微调
由于启动训练需要特别多的参数,各个nlp任务又有很多通用的参数,这些就被抽象出来了Trainer
Trainer最困难的部分可能是准备运行 Trainer.train 的环境,因为它在 CPU 上运行速度非常慢
trainer主要参数包括:
- Model:用于训练、评估或用于预测的模型
- args (TrainingArguments):训练调整的参数。如果未提供,将默认为 TrainingArguments 的基本实例
- data_collator(DataCollator,可选)– 用于批处理train_dataset 或 eval_dataset 的的函数
- train_dataset:训练集
- eval_dataset:验证集
- compute_metrics:用于计算评估指标的函数。必须传入EvalPrediction 并将返回一个字典,键值对是metric和其value。
- callbacks (回调函数,可选):用于自定义训练循环的回调列表(List of TrainerCallback)
- optimizers:一个包含优化器和学习率调整器的元组,默认优化器是AdamW,默认的学习率是线性的学习率,从5e-5 到 0
from datasets import load_dataset
from transformers import AutoTokenizer, DataCollatorWithPadding
raw_datasets = load_dataset("glue", "mrpc")#MRPC判断两个句子是否互为paraphrases
checkpoint = "bert-base-uncased"
tokenizer = AutoTokenizer.from_pretrained(checkpoint)
def tokenize_function(example):
return tokenizer(example["sentence1"], example["sentence2"], truncation=True)
tokenized_datasets = raw_datasets.map(tokenize_function, batched=True)
data_collator = DataCollatorWithPadding(tokenizer=tokenizer) # 动态填充,即将每个批次的输入序列填充到一样的长度
from transformers import TrainingArguments
training_args = TrainingArguments("test-trainer")
from transformers import AutoModelForSequenceClassification
model = AutoModelForSequenceClassification.from_pretrained(checkpoint, num_labels=2) # 标签数为2也就是二分类
from transformers import Trainer
trainer = Trainer(
model,
training_args,
train_dataset=tokenized_datasets["train"],
eval_dataset=tokenized_datasets["validation"],
data_collator=data_collator,
tokenizer=tokenizer,
)
trainer.train()
Trainer 第一个参数是TrainingArguments类,是一个与训练循环本身相关的参数的子集,包含 Trainer中用于训练和评估的所有超参数。 唯一一个必须提供的参数是:保存model或者说是checkpoint的目录,其它参数可以选取默认值(比如默认训练3个epoch等)
使用 Trainer.predict 命令获得模型的预测结果:
predictions = trainer.predict(tokenized_datasets["validation"])
predict 方法输出一个具有三个字段的元组。
- predictions: 预测值,形状为:[batch_size, num_labels], 是logits 而不是经过softmax之后的结果
- label_ids:真实的的label id
- metrics:评价指标,默认是training loss,以及一些time metrics (预测所需的总时间和平均时间)。但是一旦我们传入了 compute_metrics 函数给 Trainer,那么该函数的返回值也会一并输出
评估函数
现在看看如何构造compute_metrics 函数。这个函数:
- 必须传入 EvalPrediction 参数。 EvalPrediction是一个具有 predictions字段和 label_ids 字段的元组。
- 返回一个字典,键值对是key:metric 名字(string类型),value:metric 值(float类型)。
通过 load_metric 函数,我们可以像加载数据集一样轻松加载与 MRPC 数据集关联的metric
from datasets import load_metric
metric = load_metric("glue", "mrpc")
metric.compute(predictions=preds, references=predictions.label_ids)
"""
{'accuracy': 0.8578431372549019, 'f1': 0.8996539792387542}#模型在验证集上的准确率为 85.78%,F1 分数为 89.97
"""
将以上内容整合到一起,得到 compute_metrics 函数:
def compute_metrics(eval_preds):
metric = load_metric("glue", "mrpc")
logits, labels = eval_preds
predictions = np.argmax(logits, axis=-1)
return metric.compute(predictions=predictions, references=labels)
再设定每个epoch查看一次验证评估。所以下面就是我们设定compute_metrics参数之后的Trainer:
training_args = TrainingArguments("test-trainer", evaluation_strategy="epoch")
model = AutoModelForSequenceClassification.from_pretrained(checkpoint, num_labels=2)
trainer = Trainer(
model,
training_args,
train_dataset=tokenized_datasets["train"],
eval_dataset=tokenized_datasets["validation"],
data_collator=data_collator,
tokenizer=tokenizer,
compute_metrics=compute_metrics
)
超参数搜索
安装依赖:
!pip install optuna
!pip install ray[tune]
def model_init():
return AutoModelForSequenceClassification.from_pretrained(model_checkpoint, num_labels=num_labels)
trainer = Trainer(
model_init=model_init,
args=args,
train_dataset=encoded_dataset["train"],
eval_dataset=encoded_dataset[validation_key],
tokenizer=tokenizer,
compute_metrics=compute_metrics
)
# 这个过程可能很久,我们可以先用部分数据集进行超参搜索,再进行全量训练。 比如使用1/10的数据进行搜索
best_run = trainer.hyperparameter_search(n_trials=10, direction="maximize")
for n, v in best_run.hyperparameters.items():
setattr(trainer.args, n, v)
trainer.train()
更多推荐
所有评论(0)