Pytorch
TensorFlow

#
使用 Trainer API 微调模型

🤗 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 通过将之前构造的所有对象传递给它——我们的model 、training_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 节中向您展示这部分处理仍然很重要！

为了让预训练模型在在我们的数据集上微调，我们只需要调用Trainer的train() 方法 ：

    trainer.train()

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

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

###
评估

让我们看看如何构建一个有用的 compute_metrics() 函数并在我们下次训练时使用它。该函数必须采用 EvalPrediction 对象（带有 predictions 和 label_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 数据集上微调模型。