• 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)
    
    • 1

    在实例化此预训练模型后会收到警告。这是因为 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,
    )
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    评估设置

    为了查看模型在每个训练周期结束的好坏,下面是使用**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()方法我们可以用来进行度量计算的方法:
    
    • 1
    • 2
    • 3
    • 4
    • 5

    开始训练

    只需要调用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()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

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

    trainer.train()
    
    • 1

    原生Pytorch训练方法

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

    定义OptimizerScheduler

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

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

    默认使用的学习率调度器只是从最大值 (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)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

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

    import torch
    device = torch.device("cuda") if torch.cuda.is_available() else torch.device("cpu")
    model.to(device)
    
    • 1
    • 2
    • 3

    开始训练

    为了了解训练何时结束,使用 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)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    验证集阶段

    在这里依然使用 🤗 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()
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    使用🤗 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)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31

    更改如下:

    + 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)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39

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

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

  • 相关阅读:
    【并发编程五】c++进程通信——信号量(semaphore)
    C# CodeFormer 图像修复
    python-SQLite更新的操作没有保存
    C# 写入文件比较
    详解csrf(跨站请求伪造)
    【付费推广】常见问题合集,搜索推广FAQ 1
    ELK分布式日志系统
    java基础知识点总结篇一
    SpringCloud的nacos多项目、多环境的命名空间和分组配置
    257. 二叉树的所有路径
  • 原文地址:https://blog.csdn.net/c___c18/article/details/127601097