Accelerate 文档
故障排除
并获得增强的文档体验
开始使用
问题排查
本指南为使用 Accelerate 时可能遇到的一些问题提供解决方案。由于 Accelerate 是一个不断发展的活跃库,并且存在许多不同的用例和分布式训练设置,因此并非所有错误都包含在内。如果此处描述的解决方案对您的特定错误没有帮助,请参阅寻求帮助部分,了解在哪里以及如何获得帮助。
日志记录
日志记录可以帮助您识别错误的来源。在具有多个进程的分布式设置中,日志记录可能是一个挑战,但 Accelerate 提供了 logging()
工具来确保日志同步。
要排查问题,请使用 logging()
而不是标准的 Python logging
模块。使用 log_level
参数设置详细级别(INFO
、DEBUG
、WARNING
、ERROR
、CRITICAL
),然后您可以:
- 将
log_level
导出为ACCELERATE_LOG_LEVEL
环境变量。 - 将
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
文件来启用它。
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_trigger
和 check_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 团队寻求帮助。
通过在Accelerate 类别中发布您的问题,在 Hugging Face 论坛上寻求帮助。请务必撰写一篇描述性帖子,其中包含有关您的设置和可复现代码的相关上下文,以最大程度地提高问题解决的可能性!
在 Discord 上发布问题,让团队和社区帮助您。
如果您认为自己发现了与库相关的错误,请在 Accelerate GitHub 仓库中创建一个 Issue。请包含有关错误的上下文和分布式设置的详细信息,以帮助我们更好地找出问题所在以及如何修复它。