SageMaker 分布式数据并行性库配置提示和易犯错误。 - Amazon SageMaker
Amazon Web Services 文档中描述的 Amazon Web Services 服务或功能可能因区域而异。要查看适用于中国区域的差异,请参阅 中国的 Amazon Web Services 服务入门 (PDF)

SageMaker 分布式数据并行性库配置提示和易犯错误。

在使用 Amazon SageMaker 模型并行性库之前,请查看以下提示和易犯错误。此列表包括适用于各个框架的提示。有关 TensorFlow 和 PyTorch 的具体提示,请分别参阅修改 TensorFlow 训练脚本修改 PyTorch 训练脚本

批次大小和微批次数量

  • 当批次大小增加时,库的效率最高。对于模型可以放在单个设备中、但只能用小批次训练的使用场景,在集成库后,可以而且应该增加批次大小。模型并行性可以为大型模型节省内存,使您能够使用以前无法放入内存的批次大小进行训练。

  • 选择太小或太大的微批次数量会降低性能。该库在每个设备中按顺序执行各个微批次,因此微批次大小(批次大小除以微批次数)必须足够大,才能充分利用每个 GPU。同时,管道效率会随着微批次数量的增加而提高,因此保持适当的平衡非常重要。通常,好的做法是首先尝试 2 或 4 个微批次,将批次大小增加到内存限制,然后尝试更大的批次大小和微批次数量。随着微批次数量的增加,如果使用交错管道,更大的批次大小可能会变得可行。

  • 您的批次大小必须始终可以被微批次数量整除。请注意,根据数据集的大小,有时每个纪元的最后一个批次的大小可能比其余纪元时小,并且这个较小的批次也需要能够被微批次数量整除。如果不能整除,则可以在 tf.Dataset.batch() 调用中设置 drop_remainder=True(在 TensorFlow 中),也可以在 DataLoader 中设置 drop_last=True(在 PyTorch 中),这样就不会使用最后一个小批次。如果您为数据管道使用不同的 API,则只要最后一批次无法被微批次数量整除,您就需要手动跳过最后一个批次。

管理分区

  • 如果您使用手动分区,请注意模型中多个操作和模块所使用的参数,例如转换器架构中的嵌入表。为了保证正确性,共享相同参数的模块必须放置在同一个设备中。使用自动分区时,库会自动强制执行此约束。

数据准备

  • 如果模型接受多个输入,请确保使用 smp.dp_rank(),在数据管道中将随机操作(例如随机排序)设置为种子。如果要在数据并行设备上确定性地对数据集进行分片,请确保分片按照 smp.dp_rank() 编制索引。这是为了确保在构成模型分区的所有排序上看到的数据顺序是一致的。

smp.DistributedModel 返回张量

  • smp.DistributedModel.call(对于 TensorFlow)或 smp.DistributedModel.forward(对于 PyTorch)函数返回的任何张量,都将从计算该特定张量的秩,广播到所有其他秩。因此,不应返回调用和转发方法(例如,中间激活)之外不需要的任何张量,因为这会导致不必要的通信和内存开销,并损害性能。

@smp.step 修饰器

  • 如果 smp.step 修饰的函数的张量参数没有批次维度,则调用 smp.step 时必须在 non_split_inputs 列表中提供参数名称。这可以防止库尝试将张量拆分为微批次。有关更多信息,请参阅 API 文档中的 smp.step

延迟参数初始化

对于参数超过 1000 亿的超大型模型,通过 CPU 内存进行权重初始化可能会导致内存不足错误。为了解决这个问题,库提供了 smp.delay_param_initialization 上下文管理器。在第一次执行 smp.step 修饰的函数时,这会将参数的物理分配延迟到参数移动到 GPU 中时。这样可以避免在训练初始化期间不必要地使用 CPU 内存。在创建模型对象时使用上下文管理器,如以下代码所示。

with smp.delay_param_initialization(enabled=True): model = MyModel()

PyTorch 的张量并行性

  • 如果您使用种子来获得确定性结果,请根据 smp.dp_rank() 设置种子(例如,torch.manual_seed(42 + smp.dp_rank()))。如果不这样做,nn.Parameter 的不同分区将以相同方式初始化,从而影响收敛性。

  • SageMaker 的模型并行性库使用 NCCL 来实施分发模块所需的集合体。特别是对于较小的模型,如果同时在 GPU 上计划了太多 NCCL 调用,则由于 NCCL 占用的额外空间,内存使用量可能会增加。为了抵消这种情况,smp 会限制 NCCL 的调用,使在热议给定时间的正在执行的 NCCL 操作数量小于或等于给定限制。默认限制为 8,但可以使用环境变量 SMP_NCCL_THROTTLE_LIMIT 进行调整。如果您在使用张量并行性时观察到的内存使用量超出预期,则可以尝试降低此限制。但是,选择过小的限制可能会导致吞吐量损失。要完全禁用节流,您可以设置 SMP_NCCL_THROTTLE_LIMIT=-1

  • 以下恒等式在张量并行度为 1 时成立,但当张量并行度大于 1 时不成立:smp.mp_size() * smp.dp_size() == smp.size()。这是因为张量并行组既是模型并行性组的一部分,也是数据并行性组的一部分。如果您的代码已有对 mp_rankmp_sizeMP_GROUP 等的引用,并且您只想使用管道并行组,则可能需要将这些引用替换为 smp.pp_size()。以下恒等式始终是正确的:

    • smp.mp_size() * smp.rdp_size() == smp.size()

    • smp.pp_size() * smp.dp_size() == smp.size()

    • smp.pp_size() * smp.tp_size() * smp.rdp_size() == smp.size()

  • 由于 smp.DistributedModel 包装器在启用张量并行性时会修改模型参数,因此应在调用 smp.DistributedModel 之后使用分布式参数创建优化器。例如,以下内容不起作用:

    ## WRONG model = MyModel() optimizer = SomeOptimizer(model.parameters()) model = smp.DistributedModel(model)  # optimizer now has outdated parameters! 

    而是应该改为使用参数 smp.DistributedModel 创建优化器,如下所示:

    ## CORRECT model = smp.DistributedModel(MyModel()) optimizer = SomeOptimizer(model.optimizers())
  • 当通过张量并行性将模块替换为分布式的对应模块时,分布式模块不会从原始模块继承其权重,而是初始化新的权重。举例而言,这意味着如果需要在特定调用中初始化权重(例如,通过 load_state_dict 调用),则需要在 smp.DistributedModel 调用之后,在进行了模块分布后进行初始化。

  • 直接访问分布式模块的参数时,请注意,权重的配置与原始模块不同。例如, 

    with smp.tensor_parallelism():     linear = nn.Linear(60, 60) # will pass assert tuple(linear.weight.shape) == (60, 60) distributed_linear = smp.DistributedModel(linear) # will fail. the number of input channels will have been divided by smp.tp_size() assert tuple(distributed_linear.module.weight.shape) == (60, 60)
  • 对于张量并行度,强烈建议使用 torch.utils.data.distributed.DistributedSampler。这样可以确保每个数据并行秩接收相同数量的数据样本,从而防止因不同 dp_rank 采取的不同步骤数而可能导致的挂起。

  • 如果您使用 PyTorch DistributedDataParallel 类的 join API,处理不同数据并行秩具有不同批次数量的情况,则仍需要确保相同 TP_GROUP 的秩具有相同的批次数;否则模块的分布式执行中使用的通信集合体可能会挂起。只要使用 join API,处于不同 TP_GROUP 的秩可以有不同的批次数。

  • 如果要对使用张量并行性的模型执行检查点操作,请考虑以下几点:

    • 在使用张量并行性时,为了避免在保存和加载模型时出现停滞和争用情况,请确保在缩减数据并行秩内,从以下模型和优化器状态中调用相应的函数。

    • 如果要转换现有的管道并行脚本并为脚本启用张量并行,请确保修改用于 if smp.rdp_rank() == 0 块的保存和加载的任何 if smp.dp_rank() == 0 块。否则,这可能会导致您的训练作业停滞。

    有关对使用张量并行度的模型执行检查点操作的更多信息,请参阅对分布式模型执行检查点操作