Accelerate 文档

问题排查

Hugging Face's logo
加入 Hugging Face 社区

并获得增强的文档体验

开始使用

问题排查

本指南提供了一些在使用 Accelerate 时可能遇到的问题的解决方案。并非涵盖所有错误,因为 Accelerate 是一个活跃的库,不断发展,并且存在许多不同的用例和分布式训练设置。如果此处描述的解决方案无法帮助解决您的特定错误,请查看寻求帮助部分,了解在何处以及如何获得帮助。

日志记录

日志记录可以帮助您确定错误的来源。在具有多个进程的分布式设置中,日志记录可能是一个挑战,但 Accelerate 提供了 logging() 工具来确保日志同步。

要排查问题,请使用 logging() 而不是标准的 Python logging 模块。使用 log_level 参数设置详细程度级别(INFODEBUGWARNINGERRORCRITICAL),然后您可以选择

  1. log_level 导出为 ACCELERATE_LOG_LEVEL 环境变量。
  2. log_level 直接传递给 get_logger

例如,要设置 log_level="INFO"

from accelerate.logging import get_logger

logger = get_logger(__name__, log_level="DEBUG")

默认情况下,日志仅在主进程上调用。要在所有进程上调用它,请传递 main_process_only=False。如果日志应在所有进程上按顺序调用,则还需传递 in_order=True

from accelerate.logging import get_logger

logger = get_logger(__name__, log_level="DEBUG")
# log all processes
logger.debug("thing_to_log", main_process_only=False)
# log all processes in order
logger.debug("thing_to_log", main_process_only=False, in_order=True)

代码挂起和超时错误

代码挂起的原因有很多。让我们看看如何解决一些可能导致代码挂起的最常见问题。

张量形状不匹配

张量形状不匹配是一个常见问题,可能导致您的代码在分布式设置中挂起很长时间。

在分布式设置中运行脚本时,诸如 Accelerator.gather()Accelerator.reduce() 等函数是必要的,用于跨设备获取张量,以便集体对其执行操作。这些(以及其他)函数依赖于 torch.distributed 来执行 gather 操作,这要求张量在所有进程中具有完全相同的形状。当张量形状不匹配时,您的代码会挂起,并且最终会遇到超时异常。

您可以使用 Accelerate 的操作调试模式立即捕获此问题。我们建议在 accelerate config 设置期间启用此模式,但您也可以从 CLI、环境变量或手动编辑 config.yaml 文件来启用它。

CLI
环境变量
config.yaml
accelerate launch --debug {my_script.py} --arg1 --arg2

启用调试模式后,您应该会收到一个回溯,指向张量形状不匹配问题。

Traceback (most recent call last):
  File "/home/zach_mueller_huggingface_co/test.py", line 18, in <module>
    main()
  File "/home/zach_mueller_huggingface_co/test.py", line 15, in main
    broadcast_tensor = broadcast(tensor)
  File "/home/zach_mueller_huggingface_co/accelerate/src/accelerate/utils/operations.py", line 303, in wrapper
accelerate.utils.operations.DistributedOperationException:

Cannot apply desired operation due to shape mismatches. All shapes across devices must be valid.

Operation: `accelerate.utils.operations.broadcast`
Input shapes:
  - Process 0: [1, 5]
  - Process 1: [1, 2, 5]

早停

对于分布式训练中的早停,如果每个进程都有特定的停止条件(例如,验证损失),则可能无法在所有进程之间同步。因此,进程 0 上可能发生中断,但进程 1 上不会发生中断,这将导致您的代码无限期挂起,直到发生超时。

如果您有早停条件,请使用 set_triggercheck_trigger 方法来确保所有进程都正确结束。

# Assume `should_do_breakpoint` is a custom defined function that returns a conditional, 
# and that conditional might be true only on process 1
if should_do_breakpoint(loss):
    accelerator.set_trigger()

# Later in the training script when we need to check for the breakpoint
if accelerator.check_trigger():
    break

Linux 上的低内核版本

在内核版本低于 5.5 的 Linux 上,已报告进程挂起。为避免此问题,请将您的系统升级到更高版本的内核。

MPI

如果您的使用 MPI 的分布式 CPU 训练作业挂起,请确保您在节点之间设置了无密码 SSH(使用密钥)。这意味着对于主机文件中的所有节点,您应该能够从一个节点 SSH 到另一个节点,而无需提示输入密码。

接下来,尝试运行 mpirun 命令作为健全性检查。例如,以下命令应打印出每个节点的主机名。

mpirun -f hostfile -n {number of nodes} -ppn 1 hostname

内存溢出

在运行训练脚本时,最令人沮丧的错误之一是在 CUDA、XPU 或 CPU 等设备上遇到“内存溢出”。整个脚本需要重新启动,并且所有进度都会丢失。

为了解决这个问题,Accelerate 提供了 find_executable_batch_size() 工具,该工具很大程度上基于 toma。此工具会重试因 OOM(内存溢出)条件而失败的代码,并自动降低批大小。对于每个 OOM 条件,该算法会将批大小减半并重试代码,直到成功。

要使用 find_executable_batch_size(),请重构您的训练函数,使其包含一个带有 find_executable_batch_size 的内部函数,并在其中构建您的数据加载器。至少,这只需要 4 行新代码。

内部函数必须将批大小作为第一个参数,但在调用时我们不会传递一个参数给它。包装器将为您处理此问题。任何消耗设备内存并传递给 Accelerator 的对象(模型、优化器)也必须在内部函数中声明。

def training_function(args):
    accelerator = Accelerator()

+   @find_executable_batch_size(starting_batch_size=args.batch_size)
+   def inner_training_loop(batch_size):
+       nonlocal accelerator # Ensure they can be used in our context
+       accelerator.free_memory() # Free all lingering references
        model = get_model()
        model.to(accelerator.device)
        optimizer = get_optimizer()
        train_dataloader, eval_dataloader = get_dataloaders(accelerator, batch_size)
        lr_scheduler = get_scheduler(
            optimizer, 
            num_training_steps=len(train_dataloader)*num_epochs
        )
        model, optimizer, train_dataloader, eval_dataloader, lr_scheduler = accelerator.prepare(
            model, optimizer, train_dataloader, eval_dataloader, lr_scheduler
        )
        train(model, optimizer, train_dataloader, lr_scheduler)
        validate(model, eval_dataloader)
+   inner_training_loop()

不同设备设置之间结果不可复现

如果您更改了设备设置并观察到不同的模型性能,则可能是您在从一个设置移动到另一个设置时没有更新脚本。即使您使用相同的脚本和相同的批大小,在 TPU、多 GPU 和单 GPU 上,结果仍然会有所不同。

例如,如果您在单 GPU 上以批大小为 16 进行训练,并且您移动到双 GPU 设置,则需要将批大小更改为 8 才能获得相同的有效批大小。这是因为在使用 Accelerate 进行训练时,传递给数据加载器的批大小是每个 GPU 的批大小

为了确保您可以在不同设置之间复现结果,请确保使用相同的种子,相应地调整批大小,并考虑缩放学习率。

有关批大小的更多详细信息和快速参考,请查看比较不同设备设置之间的性能指南。

不同 GPU 上的性能问题

如果您的多 GPU 设置由不同的 GPU 组成,您可能会遇到一些性能问题

  • GPU 之间可能存在 GPU 内存不平衡的情况。在这种情况下,内存较小的 GPU 将限制批大小或可以加载到 GPU 上的模型大小。
  • 如果您使用的是具有不同性能配置文件的 GPU,则性能将由您使用的最慢的 GPU 驱动,因为其他 GPU 将不得不等待它完成其工作负载。

同一设置中差异巨大的 GPU 可能会导致性能瓶颈。

寻求帮助

如果此处的解决方案和建议均无法帮助您解决问题,您始终可以联系社区和 Accelerate 团队寻求帮助。

  • 在 Hugging Face 论坛上寻求帮助,方法是在 Accelerate 类别中发布您的问题。请务必撰写描述性帖子,其中包含有关您的设置和可复现代码的相关背景信息,以最大限度地提高您的问题得到解决的可能性!

  • Discord 上发布问题,让团队和社区帮助您。

  • 如果您认为您发现了与该库相关的错误,请在 Accelerate GitHub 存储库上创建一个 Issue。请包含关于该错误的背景信息以及关于您的分布式设置的详细信息,以帮助我们更好地找出问题所在以及我们如何修复它。

< > 在 GitHub 上更新