LLM 课程文档
使用 Trainer API 微调模型
并获得增强的文档体验
开始使用
使用 Trainer API 微调模型
🤗 Transformers 提供了 Trainer
类,可帮助您使用现代最佳实践在自己的数据集上微调其提供的任何预训练模型。完成上一节中的所有数据预处理工作后,您只需几个步骤即可定义 Trainer
。最困难的部分可能是准备运行 Trainer.train()
的环境,因为它在 CPU 上运行会非常慢。如果您没有设置 GPU,可以在 Google Colab 上获得免费的 GPU 或 TPU。
📚 训练资源:在深入训练之前,请熟悉全面的 🤗 Transformers 训练指南,并在 微调操作指南 中探索实际示例。
下面的代码示例假设您已经执行了上一节中的示例。以下是您需要的回顾摘要:
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)
训练
在定义 Trainer
之前,第一步是定义一个 TrainingArguments
类,其中包含 Trainer
用于训练和评估的所有超参数。您必须提供的唯一参数是保存训练模型以及沿途检查点的目录。对于其余所有参数,您可以保留默认值,这对于基本微调应该非常有效。
from transformers import TrainingArguments
training_args = TrainingArguments("test-trainer")
如果您想在训练期间自动将模型上传到 Hub,请在 TrainingArguments
中传递 push_to_hub=True
。我们将在第 4 章中了解更多信息。
🚀 高级配置:有关所有可用训练参数和优化策略的详细信息,请查看 TrainingArguments 文档 和 训练配置操作指南。
第二步是定义我们的模型。与上一章中一样,我们将使用 AutoModelForSequenceClassification
类,带有两个标签。
from transformers import AutoModelForSequenceClassification
model = AutoModelForSequenceClassification.from_pretrained(checkpoint, num_labels=2)
您会注意到,与第 2 章不同,在实例化此预训练模型后,您会收到警告。这是因为 BERT 尚未在分类句子对上进行预训练,因此预训练模型的头部已被丢弃,并添加了一个适合序列分类的新头部。警告表明某些权重未使用(对应于丢弃的预训练头部),而另一些权重是随机初始化的(对应于新头部)。它最后鼓励您训练模型,这正是我们现在要做的。
有了模型后,我们可以通过将所有已构建的对象(model
、training_args
、训练和验证数据集、我们的 data_collator
以及我们的 processing_class
)传递给它来定义 Trainer
。processing_class
参数是最近添加的一个参数,它告诉 Trainer 使用哪个分词器进行处理。
from transformers import Trainer
trainer = Trainer(
model,
training_args,
train_dataset=tokenized_datasets["train"],
eval_dataset=tokenized_datasets["validation"],
data_collator=data_collator,
processing_class=tokenizer,
)
当您将分词器作为 processing_class
传递时,Trainer
使用的默认 data_collator
将是 DataCollatorWithPadding
。在这种情况下,您可以跳过 data_collator=data_collator
这行代码,但我们在此处包含它是为了向您展示处理管道的这一重要部分。
📖 了解更多:有关 Trainer 类及其参数的全面详细信息,请访问 Trainer API 文档,并在 训练操作指南食谱 中探索高级使用模式。
要对我们的数据集进行模型微调,我们只需调用 Trainer
的 train()
方法。
trainer.train()
这将开始微调(在 GPU 上应该需要几分钟)并每 500 步报告训练损失。但是,它不会告诉您模型表现如何(或多差)。这是因为:
- 我们没有通过将
TrainingArguments
中的eval_strategy
设置为"steps"
(每eval_steps
评估一次)或"epoch"
(每个 epoch 结束时评估一次)来告知Trainer
在训练期间进行评估。 - 我们没有为
Trainer
提供compute_metrics()
函数来在评估期间计算指标(否则评估只会打印损失,这不是一个非常直观的数字)。
评估
让我们看看如何构建一个有用的 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
字段将仅包含传递的数据集上的损失,以及一些时间指标(总共和平均预测所需时间)。一旦我们完成 compute_metrics()
函数并将其传递给 Trainer
,该字段还将包含 compute_metrics()
返回的指标。
如您所见,predictions
是一个形状为 408 x 2 的二维数组(408 是我们用于预测的数据集中的元素数量)。这些是传递给 predict()
的数据集中每个元素的对数(如您在上一章中看到的那样,所有 Transformer 模型都返回对数)。为了将它们转换为可以与标签进行比较的预测,我们需要在第二个轴上取最大值的索引。
import numpy as np
preds = np.argmax(predictions.predictions, axis=-1)
我们现在可以将这些 preds
与标签进行比较。为了构建我们的 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}
在 🤗 Evaluate 文档中了解不同的评估指标和策略。
您获得的确切结果可能会有所不同,因为模型头部的随机初始化可能会改变其实现的指标。在这里,我们可以看到我们的模型在验证集上的准确率达到 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)
要查看它在每个 epoch 结束时报告指标的实际应用,我们这样定义一个新的 Trainer
,并传入这个 compute_metrics()
函数:
training_args = TrainingArguments("test-trainer", eval_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,
processing_class=tokenizer,
compute_metrics=compute_metrics,
)
请注意,我们创建了一个新的 TrainingArguments
,并将其 eval_strategy
设置为 "epoch"
,以及一个新模型——否则,我们将只是继续训练我们已经训练过的模型。要启动新的训练运行,我们执行:
trainer.train()
这次,除了训练损失之外,它还将在每个 epoch 结束时报告验证损失和指标。同样,您获得的确切准确率/F1 分数可能与我们发现的略有不同,这是由于模型头部随机初始化造成的,但应该在大致相同的范围内。
高级训练功能
Trainer
附带许多内置功能,使现代深度学习的最佳实践易于访问:
混合精度训练:在训练参数中使用 fp16=True
以加快训练速度并减少内存使用。
training_args = TrainingArguments(
"test-trainer",
eval_strategy="epoch",
fp16=True, # Enable mixed precision
)
梯度累积:当 GPU 内存有限时,有效扩大批次大小。
training_args = TrainingArguments(
"test-trainer",
eval_strategy="epoch",
per_device_train_batch_size=4,
gradient_accumulation_steps=4, # Effective batch size = 4 * 4 = 16
)
学习率调度:Trainer 默认使用线性衰减,但您可以自定义。
training_args = TrainingArguments(
"test-trainer",
eval_strategy="epoch",
learning_rate=2e-5,
lr_scheduler_type="cosine", # Try different schedulers
)
🎯 性能优化:有关包括分布式训练、内存优化和硬件特定优化在内的更高级训练技术,请探索 🤗 Transformers 性能指南。
Trainer
可以在多个 GPU 或 TPU 上开箱即用,并提供许多分布式训练选项。我们将在第 10 章中详细介绍其支持的所有功能。
本节结束了使用 Trainer
API 进行微调的介绍。在第 7 章中将给出针对大多数常见 NLP 任务的示例,但现在让我们看看如何使用纯 PyTorch 训练循环来做同样的事情。
📝 更多示例:查看 🤗 Transformers 笔记本的综合集合。
章节测验
测试您对 Trainer API 和微调概念的理解。
1. <code> processing_class </code> 参数在 Trainer 中的作用是什么?
2. 哪个 TrainingArguments 参数控制训练期间评估的频率?
3. TrainingArguments 中的 <code> fp16=True </code> 启用了什么?
4. <code> compute_metrics </code> 函数在 Trainer 中的作用是什么?
5. 当您不向 Trainer 提供 <code> eval_dataset </code> 时会发生什么?
6. 什么是梯度累积,以及如何启用它?
💡 要点:
Trainer
API 提供了一个高级接口,可以处理大多数训练的复杂性。- 使用
processing_class
指定分词器,以正确处理数据。 TrainingArguments
控制训练的所有方面:学习率、批处理大小、评估策略和优化。compute_metrics
允许自定义训练损失之外的评估指标。- 混合精度(
fp16=True
)和梯度累积等现代功能可以显著提高训练效率。