Press "Enter" to skip to content

介绍 🤗 加速

🤗 加速

在任何设备上运行你的原始 PyTorch 训练脚本。

大多数高级库都支持分布式训练和混合精度,但是它们引入的抽象需要用户学习新的 API,如果他们想要自定义底层的训练循环。🤗 加速是为 PyTorch 用户设计的,他们希望对自己的训练循环拥有完全控制,但又不愿意编写(和维护)使用分布式训练(多个 GPU 在一个或多个节点上,TPU,…)或混合精度训练所需的样板代码。未来的计划包括支持 fairscale、deepseed、AWS SageMaker 特定的数据并行和模型并行。

它提供了两个东西:一个简单一致的 API,抽象了那些样板代码,以及一个启动命令,可以在各种设置上轻松运行这些脚本。

简单集成!

让我们先看一个例子:

  import torch
  import torch.nn.functional as F
  from datasets import load_dataset
+ from accelerate import Accelerator

+ accelerator = Accelerator()
- device = 'cpu'
+ device = accelerator.device

  model = torch.nn.Transformer().to(device)
  optim = torch.optim.Adam(model.parameters())

  dataset = load_dataset('my_dataset')
  data = torch.utils.data.DataLoader(dataset, shuffle=True)

+ model, optim, data = accelerator.prepare(model, optim, data)

  model.train()
  for epoch in range(10):
      for source, targets in data:
          source = source.to(device)
          targets = targets.to(device)

          optimizer.zero_grad()

          output = model(source)
          loss = F.cross_entropy(output, targets)

-         loss.backward()
+         accelerator.backward(loss)

          optimizer.step()

只需向任何标准的 PyTorch 训练脚本中添加五行代码,现在你可以在任何类型的分布式设置上运行该脚本,以及进行混合精度训练与否。🤗 加速甚至为你处理设备放置,所以你甚至可以进一步简化上面的训练循环:

  import torch
  import torch.nn.functional as F
  from datasets import load_dataset
+ from accelerate import Accelerator

+ accelerator = Accelerator()
- device = 'cpu'

- model = torch.nn.Transformer().to(device)
+ model = torch.nn.Transformer()
  optim = torch.optim.Adam(model.parameters())

  dataset = load_dataset('my_dataset')
  data = torch.utils.data.DataLoader(dataset, shuffle=True)

+ model, optim, data = accelerator.prepare(model, optim, data)

  model.train()
  for epoch in range(10):
      for source, targets in data:
-         source = source.to(device)
-         targets = targets.to(device)

          optimizer.zero_grad()

          output = model(source)
          loss = F.cross_entropy(output, targets)

-         loss.backward()
+         accelerator.backward(loss)

          optimizer.step()

相比之下,使此代码在分布式训练中运行所需的更改如下:

+ import os
  import torch
  import torch.nn.functional as F
  from datasets import load_dataset
+ from torch.utils.data import DistributedSampler
+ from torch.nn.parallel import DistributedDataParallel

+ local_rank = int(os.environ.get("LOCAL_RANK", -1))
- device = 'cpu'
+ device = device = torch.device("cuda", local_rank)

  model = torch.nn.Transformer().to(device)
+ model = DistributedDataParallel(model)  
  optim = torch.optim.Adam(model.parameters())

  dataset = load_dataset('my_dataset')
+ sampler = DistributedSampler(dataset)
- data = torch.utils.data.DataLoader(dataset, shuffle=True)
+ data = torch.utils.data.DataLoader(dataset, sampler=sampler)

  model.train()
  for epoch in range(10):
+     sampler.set_epoch(epoch)  
      for source, targets in data:
          source = source.to(device)
          targets = targets.to(device)

          optimizer.zero_grad()

          output = model(source)
          loss = F.cross_entropy(output, targets)

          loss.backward()

          optimizer.step()

这些更改将使你的训练脚本适用于多个 GPU,但你的脚本将停止在 CPU 或一个 GPU 上运行(除非你开始在各处添加 if 语句)。更令人讨厌的是,如果你想要在 TPUs 上测试你的脚本,你需要更改不同的代码行。混合精度训练也是如此。🤗 加速的承诺是:

  • 尽量减少对训练循环的更改,使你需要学习的尽可能少。
  • 使同样的函数适用于任何分布式设置,只需学习一个 API。

它是如何工作的?

为了了解库在实践中是如何工作的,让我们来看看需要添加到训练循环中的每一行代码。

accelerator = Accelerator()

除了提供您将使用的主要对象之外,此行还将从环境中分析分布式训练运行的类型并执行必要的初始化。您可以通过传递 cpu=Truefp16=True 来强制在 CPU 上进行训练或进行混合精度训练。这两个选项也可以使用脚本的启动器进行设置。

model, optim, data = accelerator.prepare(model, optim, data)

这是 API 的主要部分,它将准备三种主要类型的对象: 模型 ( torch.nn.Module )、优化器 ( torch.optim.Optimizer ) 和数据加载器 ( torch.data.dataloader.DataLoader )。

模型

模型的准备工作包括将其包装在适当的容器中(例如 DistributedDataParallel )并将其放在适当的设备上。与常规的分布式训练一样,您需要解包您的模型以进行保存,或者访问其特定的方法,可以使用 accelerator.unwrap_model(model) 来完成。

优化器

优化器也被包装在一个特殊的容器中,它将执行必要的操作以使混合精度工作。如果状态字典非空或从检查点加载,则它还会正确处理状态字典的设备放置。

数据加载器

这是大部分魔法隐藏的地方。正如您在代码示例中看到的那样,该库不依赖于 DistributedSampler ,实际上它可以使用您传递给数据加载器的任何采样器来工作(如果您曾经需要编写自定义采样器的分布式版本,现在不再需要!)。数据加载器被包装在一个容器中,该容器仅获取采样器中当前进程相关的索引(或者如果您使用 IterableDataset ,则跳过其他进程的批次)并将批次放置在适当的设备上。

为了使其正常工作,Accelerate 提供了一个实用函数,用于在分布式训练期间同步每个进程上的随机数生成器。默认情况下,它仅同步采样器的 generator ,因此您的数据增强在每个进程上会有所不同,但随机洗牌将保持相同。当然,如果需要,您可以使用此实用程序来同步更多的 RNG。

accelerator.backward(loss)

这最后一行添加了反向传播的必要步骤(主要用于混合精度,但其他集成可能需要在此处进行一些自定义行为)。

评估方面怎么样?

评估可以在所有进程上正常运行,或者如果您只希望在主进程上运行评估,您可以使用方便的测试:

if accelerator.is_main_process():
    # 评估循环

但是您也可以很容易地使用 Accelerate 运行分布式评估,以下是您需要添加到评估循环中的内容:

+ eval_dataloader = accelerator.prepare(eval_dataloader)
  predictions, labels = [], []
  for source, targets in eval_dataloader:
      with torch.no_grad():
          output = model(source)

-     predictions.append(output.cpu().numpy())
-     labels.append(targets.cpu().numpy())
+     predictions.append(accelerator.gather(output).cpu().numpy())
+     labels.append(accelerator.gather(targets).cpu().numpy())

  predictions = np.concatenate(predictions)
  labels = np.concatenate(labels)

+ predictions = predictions[:len(eval_dataloader.dataset)]
+ labels = label[:len(eval_dataloader.dataset)]

  metric_compute(predictions, labels)

与训练一样,您需要添加一行代码来准备评估数据加载器。然后,您只需使用 accelerator.gather 来跨进程聚集预测和标签的张量。最后一行代码将预测和标签截断为数据集中的示例数,因为准备好的评估数据加载器将返回更多的元素,以确保每个进程的批次大小相同。

一个启动器管理一切

使用Accelerate的脚本将与您的传统启动器完全兼容,例如torch.distributed.launch。但是,记住它们的所有参数有点烦人,当您使用4个GPU设置实例时,您将在大多数训练中使用全部GPU。Accelerate提供了一个方便的CLI,分为两个步骤:

accelerate config

这将触发一个关于您设置的简短问卷调查,该问卷将创建一个配置文件,您可以在其中编辑所有训练命令的默认值。然后

accelerate launch path_to_script.py --args_to_the_script

将使用这些默认值启动您的训练脚本。您唯一需要做的就是提供训练脚本所需的所有参数。

为了使这个启动器更加强大,您可以使用它来生成一个使用SageMaker的AWS实例。请查看这个指南以了解更多!

如何参与?

要开始,请只需pip install accelerate或查看文档以获取更多安装选项。

Accelerate是一个完全开源的项目,您可以在GitHub上找到它,查看其文档或浏览我们的基本示例。如果您有任何问题或希望支持该库的功能,请告诉我们。有关所有问题,请查看论坛!

对于更复杂的示例,您可以查看官方的Transformers示例。每个文件夹都包含一个run_task_no_trainer.py,它利用了Accelerate库!

Leave a Reply

Your email address will not be published. Required fields are marked *