HuggingFace——Trainer的简单使用

使用 Trainer API 微调模型[中文Course|API ]

Transformers提供了一个 Trainer 类来帮助在数据集上微调任何预训练模型。

在定义Trainer之前首先要定义一个TrainingArguments类。

它将包含 Trainer用于训练和评估的所有超参数。其中唯一必须提供的参数是保存训练模型的目录——output_dir( The output directory where the model predictions and checkpoints will be written.)参数。对于其余的参数,使用默认值。

定义模型

以分类句子模型为例,第二步是定义我们的模型。正如在将使用 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,
)

评估设置

为了查看模型在每个训练周期结束的好坏,下面是使用**compute_metrics()**函数定义一个新的 Trainer。现在建立compute_metric()函数来较为直观地评估模型的好坏,可以使用 Evaluate 库中的指标。

def compute_metrics(eval_preds):
    metric = evaluate.load("glue", "mrpc") # 加载与 MRPC 数据集关联的指标
    logits, labels = eval_preds
    predictions = np.argmax(logits, axis=-1)
    return metric.compute(predictions=predictions, references=labels) # 返回的对象有一个 compute()方法我们可以用来进行度量计算的方法:

开始训练

只需要调用Trainer的train() 方法 :

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,
)
trainer.train()

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

trainer.train()

原生Pytorch训练方法

定义模型等地方就不在赘述了,直接从优化器开始。

定义Optimizer和Scheduler

这里使用目前预训练语言模型常用的AdamW

from transformers import AdamW
optimizer = AdamW(model.parameters(), lr=2e-5)

默认使用的学习率调度器只是从最大值 (5e-5) 到 0 的线性衰减。 为了定义它,需要知道我们训练的次数,即所有数据训练的次数(epochs)乘以的数据量(这是所有训练数据的数量)

from transformers import get_scheduler

num_epochs = 3
num_training_steps = num_epochs * len(train_dataloader)
lr_scheduler = get_scheduler(
    "linear",
  	'''
  	可用参数
    LINEAR = "linear"
    COSINE = "cosine"
    COSINE_WITH_RESTARTS = "cosine_with_restarts"
    POLYNOMIAL = "polynomial"
    CONSTANT = "constant"
    CONSTANT_WITH_WARMUP = "constant_with_warmup"
  	'''
    optimizer=optimizer,
    num_warmup_steps=0,
    num_training_steps=num_training_steps,
)
print(num_training_steps)

定义训练物理位置(CPU/GPU)

import torch
device = torch.device("cuda") if torch.cuda.is_available() else torch.device("cpu")
model.to(device)

开始训练

为了了解训练何时结束,使用 tqdm 库,在训练步骤数上添加了一个进度条:

from tqdm.auto import tqdm

progress_bar = tqdm(range(num_training_steps))

model.train()
for epoch in range(num_epochs):
    for batch in train_dataloader:
        batch = {k: v.to(device) for k, v in batch.items()}
        outputs = model(**batch)
        loss = outputs.loss
        loss.backward()

        optimizer.step()
        lr_scheduler.step()
        optimizer.zero_grad()
        progress_bar.update(1)

验证集阶段

在这里依然使用 Evaluate 库提供的指标。其中已经了解了 metric.compute() 方法,当使用 add_batch()方法进行预测循环时,实际上该指标可以为累积所有 batch 的结果。一旦我们累积了所有 batch ,就可以使用 metric.compute() 得到最终结果 .以下是在评估循环中实现所有这些的方法:

import evaluate

metric = evaluate.load("glue", "mrpc")
model.eval()
for batch in eval_dataloader:
    batch = {k: v.to(device) for k, v in batch.items()}
    with torch.no_grad():
        outputs = model(**batch)

    logits = outputs.logits
    predictions = torch.argmax(logits, dim=-1)
    metric.add_batch(predictions=predictions, references=batch["labels"])

metric.compute()

使用 Accelerate加速训练循环

使用 Accelerate库,只需进行一些调整,就可以在多个 GPU 或 TPU 上启用分布式训练。从创建训练和验证数据加载器开始,在原生Pytorch训练方法中训练循环如下所示:

from transformers import AdamW, AutoModelForSequenceClassification, get_scheduler

model = AutoModelForSequenceClassification.from_pretrained(checkpoint, num_labels=2)
optimizer = AdamW(model.parameters(), lr=3e-5)

device = torch.device("cuda") if torch.cuda.is_available() else torch.device("cpu")
model.to(device)

num_epochs = 3
num_training_steps = num_epochs * len(train_dataloader)
lr_scheduler = get_scheduler(
    "linear",
    optimizer=optimizer,
    num_warmup_steps=0,
    num_training_steps=num_training_steps,
)

progress_bar = tqdm(range(num_training_steps))

model.train()
for epoch in range(num_epochs):
    for batch in train_dataloader:
        batch = {k: v.to(device) for k, v in batch.items()}
        outputs = model(**batch)
        loss = outputs.loss
        loss.backward()

        optimizer.step()
        lr_scheduler.step()
        optimizer.zero_grad()
        progress_bar.update(1)

更改如下:

+ from accelerate import Accelerator
  from transformers import AdamW, AutoModelForSequenceClassification, get_scheduler

+ accelerator = Accelerator()

  model = AutoModelForSequenceClassification.from_pretrained(checkpoint, num_labels=2)
  optimizer = AdamW(model.parameters(), lr=3e-5)

- device = torch.device("cuda") if torch.cuda.is_available() else torch.device("cpu")
- model.to(device)

+ train_dataloader, eval_dataloader, model, optimizer = accelerator.prepare(
+     train_dataloader, eval_dataloader, model, optimizer
+ )

  num_epochs = 3
  num_training_steps = num_epochs * len(train_dataloader)
  lr_scheduler = get_scheduler(
      "linear",
      optimizer=optimizer,
      num_warmup_steps=0,
      num_training_steps=num_training_steps
  )

  progress_bar = tqdm(range(num_training_steps))

  model.train()
  for epoch in range(num_epochs):
      for batch in train_dataloader:
-         batch = {k: v.to(device) for k, v in batch.items()}
          outputs = model(**batch)
          loss = outputs.loss
-         loss.backward()
+         accelerator.backward(loss)

          optimizer.step()
          lr_scheduler.step()
          optimizer.zero_grad()
          progress_bar.update(1)

要添加的第一行是导入Accelerator。第二行实例化一个 Accelerator对象 ,它将查看环境并初始化适当的分布式设置。 Accelerate 处理数据在设备间的传递,因此可以删除将模型放在设备上的那行代码(或者可使用 accelerator.device 代替 device )。

然后大部分工作会在将数据加载器、模型和优化器发送到的accelerator.prepare()中完成。这将会把这些对象包装在适当的容器中,以确保分布式训练按预期工作。要进行的其余更改是删除将batch放在 device 的那行代码(同样,如果想保留它,可以将其更改为使用 accelerator.device ) 并将 loss.backward() 替换为accelerator.backward(loss)

你可能感兴趣的:(#,炼丹记录,python,HuggingFace,Trainer,Pytorch)