NLP Course documentation

使用 Trainer API 微调模型

Hugging Face's logo
Join the Hugging Face community

and get access to the augmented documentation experience

to get started

使用 Trainer API 微调模型

Ask a Question Open In Colab Open In Studio Lab

🤗 Transformers提供了一个 Trainer 类来帮助您在自己的数据集上微调任何预训练模型。完成上一节中的所有数据预处理工作后,您只需要执行几个步骤来创建 Trainer .最难的部分可能是为 Trainer.train()配置运行环境,因为它在 CPU 上运行速度会非常慢。如果您没有设置 GPU,您可以访问免费的 GPU 或 TPUGoogle Colab.

下面的示例假设您已经执行了上一节中的示例。下面这段代码,概括了您需要提前运行的代码:

from datasets import load_dataset
from transformers import AutoTokenizer, DataCollatorWithPadding

raw_datasets = load_dataset("glue", "mrpc")
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)

Training

在我们定义我们的 Trainer 之前首先要定义一个 TrainingArguments 类,它将包含 Trainer用于训练和评估的所有超参数。您唯一必须提供的参数是保存训练模型的目录,以及训练过程中的检查点。对于其余的参数,您可以保留默认值,这对于基本微调应该非常有效。

from transformers import TrainingArguments

training_args = TrainingArguments("test-trainer")

💡 如果您想在训练期间自动将模型上传到 Hub,请将push_to_hub=True添加到TrainingArguments之中. 我们将在第四章中详细介绍这部分。

第二步是定义我们的模型。正如在[之前的章节](/2_Using Transformers/Introduction)一样,我们将使用 AutoModelForSequenceClassification 类,它有两个参数:

from transformers import AutoModelForSequenceClassification

model = AutoModelForSequenceClassification.from_pretrained(checkpoint, num_labels=2)

你会注意到,和第二章不一样的是,在实例化此预训练模型后会收到警告。这是因为 BERT 没有在句子对分类方面进行过预训练,所以预训练模型的头部已经被丢弃,而是添加了一个适合句子序列分类的新头部。警告表明一些权重没有使用(对应于丢弃的预训练头的那些),而其他一些权重被随机初始化(新头的那些)。最后鼓励您训练模型,这正是我们现在要做的。

一旦我们有了我们的模型,我们就可以定义一个 Trainer 通过将之前构造的所有对象传递给它——我们的modeltraining_args ,训练和验证数据集,data_collator ,和 tokenizer

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,
)

请注意,当您在这里完成tokenizer后,默认 Trainer使用 的data_collator会使用之前预定义的 DataCollatorWithPadding ,因此您可以在这个例子中跳过 data_collator=data_collator。在第 2 节中向您展示这部分处理仍然很重要!

为了让预训练模型在在我们的数据集上微调,我们只需要调用Trainertrain() 方法 :

trainer.train()

这将开始微调(在GPU上应该需要几分钟),并每500步报告一次训练损失。但是,它不会告诉您模型的性能如何(或质量如何)。这是因为:

  1. 我们没有通过将evaluation_strategy设置为“steps”(在每次更新参数的时候评估)或“epoch”(在每个epoch结束时评估)来告诉Trainer在训练期间进行评估。
  2. 我们没有为Trainer提供一个compute_metrics()函数来直接计算模型的好坏(否则评估将只输出loss,这不是一个非常直观的数字)。

评估

让我们看看如何构建一个有用的 compute_metrics() 函数并在我们下次训练时使用它。该函数必须采用 EvalPrediction 对象(带有 predictionslabel_ids 字段的参数元组)并将返回一个字符串到浮点数的字典(字符串是返回的指标的名称,而浮点数是它们的值)。我们可以使用 Trainer.predict() 命令来使用我们的模型进行预测:

predictions = trainer.predict(tokenized_datasets["validation"])
print(predictions.predictions.shape, predictions.label_ids.shape)
(408, 2) (408,)

predict() 的输出结果是具有三个字段的命名元组: predictions , label_ids , 和 metrics .这 metrics 字段将只包含传递的数据集的loss,以及一些运行时间(预测所需的总时间和平均时间)。如果我们定义了自己的 compute_metrics() 函数并将其传递给 Trainer ,该字段还将包含compute_metrics()的结果。

predict() 方法是具有三个字段的命名元组: predictions , label_ids , 和 metrics .这 metrics 字段将只包含传递的数据集的loss,以及一些运行时间(预测所需的总时间和平均时间)。如果我们定义了自己的 compute_metrics() 函数并将其传递给 Trainer ,该字段还将包含compute_metrics() 的结果。如你看到的, predictions 是一个形状为 408 x 2 的二维数组(408 是我们使用的数据集中元素的数量)。这些是我们传递给predict()的数据集的每个元素的结果(logits)(正如你在之前的章节看到的情况)。要将我们的预测的可以与真正的标签进行比较,我们需要在第二个轴上取最大值的索引:

import numpy as np

preds = np.argmax(predictions.predictions, axis=-1)

现在建立我们的 compute_metric() 函数来较为直观地评估模型的好坏,我们将使用 🤗 Evaluate 库中的指标。我们可以像加载数据集一样轻松加载与 MRPC 数据集关联的指标,这次使用 evaluate.load() 函数。返回的对象有一个 compute()方法我们可以用来进行度量计算的方法:

import evaluate

metric = evaluate.load("glue", "mrpc")
metric.compute(predictions=preds, references=predictions.label_ids)
{'accuracy': 0.8578431372549019, 'f1': 0.8996539792387542}

您获得的确切结果可能会有所不同,因为模型头的随机初始化可能会影响最终建立的模型。在这里,我们可以看到我们的模型在验证集上的准确率为 85.78%,F1 分数为 89.97。这是用于评估 GLUE 基准的 MRPC 数据集结果的两个指标。而在BERT 论文中展示的基础模型的 F1 分数为 88.9。那是 uncased 模型,而我们目前正在使用 cased 模型,通过改进得到了更好的结果。

最后将所有东西打包在一起,我们得到了我们的 compute_metrics() 函数:

def compute_metrics(eval_preds):
    metric = evaluate.load("glue", "mrpc")
    logits, labels = eval_preds
    predictions = np.argmax(logits, axis=-1)
    return metric.compute(predictions=predictions, references=labels)

为了查看模型在每个训练周期结束的好坏,下面是我们如何使用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,
)

请注意,我们设置了了一个新的 TrainingArguments 它的evaluation_strategy 设置为 epoch 并创建了一个新模型。如果不创建新的模型就直接训练,就只会继续训练之前我们已经训练过的模型。要启动新的训练运行,我们执行:

trainer.train()

这一次,它将在训练loss之外,还会输出每个 epoch 结束时的验证loss和指标。同样,由于模型的随机头部初始化,您达到的准确率/F1 分数可能与我们发现的略有不同,但它应该在同一范围内。

Trainer 将在多个 GPU 或 TPU 上开箱即用,并提供许多选项,例如混合精度训练(在训练的参数中使用 fp16 = True )。我们将在第 10 章讨论它支持的所有内容。

使用Trainer API微调的介绍到此结束。对最常见的 NLP 任务执行此操作的示例将在第 7 章中给出,但现在让我们看看如何在纯 PyTorch 中执行相同的操作。

✏️ 试试看! 使用您在第 2 节中进行的数据处理,在 GLUE SST-2 数据集上微调模型。