Skip to content

排查问题

本指南提供了解决在使用 Accelerate 时可能遇到的一些问题的方案。由于 Accelerate 是一个不断发展的活跃库,存在许多不同的使用场景和分布式训练设置,因此并非所有错误都涵盖在内。如果这里描述的解决方案无法解决你的特定错误,请参阅 寻求帮助 部分,了解在哪里以及如何获取帮助。

日志记录

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

要排查问题,请使用 [~accelerate.logging] 而不是标准的 Python logging 模块。通过 log_level 参数设置日志级别(INFODEBUGWARNINGERRORCRITICAL),然后你可以:

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

例如,设置 log_level="INFO"

py
from accelerate.logging import get_logger

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

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

py
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 文件来启用它。

一旦你启用调试模式,你应该会得到一个指向张量形状不匹配问题的回溯信息。

py
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_breakpointcheck_breakpoint 方法来确保所有进程都能正确结束。

py
# 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_breakpoint()

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

Linux 上的低内核版本

在 Linux 上,如果内核版本 < 5.5,已报告出现进程挂起的问题。为了避免此问题,请将系统升级到更高版本的内核。

MPI

如果你使用 MPI 的分布式 CPU 训练任务出现挂起,请确保在节点之间已设置 无密码 SSH(使用密钥)。这意味着对于主机文件中的所有节点,你应该能够从一个节点无密码地 SSH 到另一个节点。

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

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

CUDA 内存不足

在运行训练脚本时,最令人沮丧的错误之一就是遇到"CUDA 内存不足"。整个脚本需要重新启动,任何进度都会丢失。

为了解决这个问题,Accelerate 提供了 [find_executable_batch_size] 工具,该工具主要基于 toma。 这个工具会重试因 OOM(内存不足)条件而失败的代码,并自动降低批处理大小。对于每个 OOM 条件,算法会将批处理大小减半并重试代码,直到成功。

要使用 [find_executable_batch_size],请重构你的训练函数,包含一个使用 find_executable_batch_size 的内部函数,并在其中构建你的数据加载器。最少只需要添加 4 行新代码。

diff
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 可能会导致性能瓶颈。

寻求帮助

如果这里的解决方案和建议没有帮助你解决问题,你可以随时向社区和 Accelerate 团队寻求帮助。

  • 在 Hugging Face 论坛的 Accelerate 分类 中发布你的问题。确保写一篇描述性的帖子,包含有关你的配置和可复现代码的相关上下文,以最大化解决问题的可能性!

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

  • 如果你认为你发现了一个与库相关的 bug,请在 Accelerate GitHub 仓库 中创建一个 Issue。包括有关 bug 的上下文和有关你的分布式配置的详细信息,以帮助我们更好地找出问题并解决它。