从裸机到70B大模型②:基础设施设置与脚本

15d9d11fb9f021933c83d6e9eea77998.jpeg

作者 | IMBUE团队

OneFlow编译

翻译 | 刘乾裕、林心宇

题图由Siliconcloud平台生成

今年6月,大模型公司Imbue依托它们自主构建的基础设施,从零起步,成功训练出一个70亿参数的模型,其在相关推理任务上的卓越表现远零样本GPT-4o。

在《70B大模型训练秘方① :数据集创建与评估》一文中,他们分享了用于模型评估的数据集,包含11个公共数据集的高质量子集,以及一套用于代码理解的原始问题。重点分享了为什么选择这些特定的数据集,以及数据创建过程和实际数据集的详细信息。

本文是Imbue团队训练70B大模型三部曲系列的第二篇。在本文中,他们将分享一套适用于搭建所需基础设施的端到端指南:从搭建初始集群、安装操作系统,直至训练过程中实现错误的自动纠正。在每个步骤中,他们都详细说明了遇到的挑战,并分享了解决方案。

除了经验教训外,他们还分享了为确保主机健康而自主开发的大量基础设施脚本,旨在帮助其他团队更便捷地为自己的模型训练创建稳定的基础设施。

随着他们给出的详细流程,他们还推出了:

  • 主机级健康性检查(https://github.com/imbue-ai/cluster-health/tree/master/health_checks):用于确保特定主机无已知错误的脚本

  • NVIDIA集群通信库(NCCL) 补丁,优化错误和延迟的日志记录

  • 压力测试,确认GPU能够分配大型张量并执行标准操作(https://github.com/imbue-ai/cluster-health/tree/master/gpu_stress_test)

  • 网络测试(https://github.com/imbue-ai/cluster-health/tree/master/host_validation) ,以检查特定机器上的GPU能否相互通信(通过 NVLink),并且是否能够与其他机器上的GPU进行通信(通过 InfiniBand)

  • 用于解析统一网络管理器(UFM)事件日志的脚本(https://github.com/imbue-ai/cluster-health/tree/master/host_validation) ,检查相关事件,并确定哪些网络端口应被禁用

  • 一个用于为InfiniBand网络结构生成全面预热工作负载的脚本,目的是测试每一个可用的链路。

(本文由OneFlow编译发布,转载请联系授权。来源:https://imbue.com/research/70b-infrastructure/)

1

背景:如何实现这一运作

我们的计算目标在于实现大规模语言模型的快速实验。为此,我们需要大量高速GPU,并确保它们能够以高速相互通信。


本文重点介绍一个由511台服务器构成的集群,其中部署了4088枚H100 GPU,均匀分布在每台服务器上,每台服务器搭载8个GPU。考虑到统一网络结构管理节点对连接的需求,保留了部分连接并且用于管理 InfiniBand 网络。

在这511台配置了GPU的主机中,每个GPU均直接连接到一块 ConnectX-7 卡。这块卡具备高效的数据交换能力,通过自身的 ConnectX-7 卡,能够以400 Gbps的速度向InfiniBand网络中的其他任何GPU发送和接收数据,从而确保了集群内部的高速数据流通。

我们的InfiniBand网络拓扑被称为“完全非阻塞(fully non-blocking)”,因为理论上每个GPU均能实现与其他GPU的最大速率通信。这是通过三层InfiniBand网络架构实现的:当正确连接时,三层InfiniBand交换机能够在整个网络上实现这种高水平的吞吐量。请参阅下方的InfiniBand网络概述:

e943e5500bf3ec29ddc444484c7291f5.png


请注意,训练网络的通信是通过InfiniBand而非以太网进行的。尽管这些计算机也连接到了以太网,但该网络主要用于传输数据集、检查点和其他数据。如果通过以太网发送数据将会非常缓慢,因为数据需要首先从GPU传输到CPU,然后再通过一根100 Gbps的以太网卡发送。虽然理论上可以通过称为融合以太网的RDMA(RoCE)技术在以太网上进行训练,但在硬件和软件两方面都需要大量额外工作,并且通常比InfiniBand更不可靠(详情参见论文(https://arxiv.org/pdf/2402.15627),其中概述了整体流程)。

此外,还存在一个专门用于配置和管理的辅助以太网络,它允许访问基本输入/输出系统(BIOS)、电源供应以及其他底层计算机接口的控制界面。如果没有这个管理网络,我们就必须手动使用USB驱动器、键盘和显示器来设置我们的节点,这对于成百上千台机器而言,显然不是一个高效且可持续的解决方案。


使用我们的集群进行高性能训练意味着每个组件——InfiniBand、以太网、GPU以及节点本身——都必须近乎完美地协同工作。如果超过12000个连接中的任何一个稍微不稳定,都可能拖慢整个训练过程的进度。本文剩余部分将详细阐述实现所有组件完美运行的过程,并确保其持续稳定运行的方法。

2

流程:如何从裸机蜕变为高效运转集群

预配单台机器

在成功建立管理网络与集群之间的初始以太网络连接后,我们获得了对主板管理控制器(BMC)的访问权限。BMC作为一种专业化服务处理器,能够对主机系统进行实时监控,并通常与一个独立网络相连接。这使得我们能够如同身临其境般地操控每一台计算机,同时提供了丰富的API接口,涵盖了硬件健康状况的监控、BIOS配置的调整以及电源管理的全面控制。有了这些组件,我们卷起袖子开始设置集群。

步骤 0:获取一台预配机器

我们首先使用iDRAC(戴尔的主板管理控制器)在一台服务器上安装Ubuntu 22.04,该服务器将用于设置其他所有内容。除此之外,iDRAC还允许我们从本地计算机挂载并从ISO镜像启动,浏览器中提供了一个虚拟控制台。理想状态下,这将是此过程中唯一的手动安装步骤。

步骤 1:在每台机器上安装操作系统

在完成首台目标服务器的部署后,我们继续安装Ubuntu的Metal-as-a-Service(MAAS)软件,以便配置剩余的服务器。通过PXE(预启动执行环境)启动机制和iDRAC的自动化工具,我们通过指令让所有服务器从网络启动,并确保MAAS能够响应PXE启动请求。在执行初始网络启动时,服务器无需在本地存储上安装任何软件,即可通过DHCP(动态主机配置协议)从MAAS获取IP地址并加载初始内核。随后,这个基础系统环境自动用于执行持久的操作系统安装。理论上,我们只需等待首次启动,所有配置工作即可自动完成。然而,在实际操作中,由于MAAS与BMC的集成不够稳定,我们不得不提前通过iDRAC API收集每台服务器的MAC地址(作为唯一的硬件标识符),以便进行后续的管理。

在整个训练周期中,MAAS作为堆栈的组成部分,整体表现稳定可靠。不过,初期阶段,我们在配置上遇到了一些特定障碍。例如,在最初的配置尝试中,时间同步偏差异常严重,导致HTTPS证书验证失败,从而阻碍了通过apt进行软件安装。

进一步来说,MAAS服务器需承担多重角色——从DHCP和DNS服务,到HTTP代理、NTP服务、cloud-init配置管理,以及维护MAC地址与IP、主机名和自定义元数据之间关联的基础真实数据库——这使得我们难以定位问题的根本源头。

此外,MAAS的生命周期管理学习曲线同样陡峭,因为它旨在处理全新部署的复杂性,支持节点的逐步迁移,并应对各种调试/不健康的中间状态。

步骤 2:诊断故障机器

在设置大型GPU集群的过程中,我们发现大约有10%的机器无法启动,这主要是由于服务器物理问题造成的。我们遇到的一些问题包括:未连接或接线错误的以太网电缆、iDRAC硬件故障、损坏的电源单元、不良的NVME(非易失性内存表达式)驱动器、缺失的内部连线,以及网络卡或GPU无法识别。

我们对这些问题进行了自动化检查,将一些机器送回戴尔进行重新测试,并为数据中心工作人员提交了相应的工单。自己搭建集群的优势在于,我们能够立即将健康的机器投入使用,同时等待其他机器维护工作的完成。

步骤 3:最小可观测机器

我们在每一台服务器上都设置了以下内容:

  1. Docker(更轻松地运行服务和训练任务)

  2. 数据中心GPU驱动程序

  3. Prometheus节点导出器(导出硬件/操作系统的稳定指标流)

  4. DCGM导出器(NVIDIA 的额外GPU 状态/时钟/利用率指标)

  5. 在所有非操作系统驱动器上配置RAIDZ ZFS存储池(这使机器能够在一块驱动器故障的情况下继续运行,同时提供免费的透明压缩功能,这对于纯文本数据集和重复性日志特别有用,使我们能够常规性地利用比原本多约10倍的空间)。

随后,我们执行了基本的GPU诊断以确认GPU的总体功能状态——那些功能不正常的GPU通常会在数小时内出现硬件问题。

在此期间,我们尝试对所有400个节点并行部署软件包时,遭遇了带宽瓶颈。同时,这也是我们首次在数据中心部署期间接收到关于各组件的高温警报。针对首batch出现的散热问题,我们主要通过固件升级的方式进行了有效解决。

步骤 4:单节点GPU训练

下一步是确保每台机器都能独立处理真实的GPU工作负载。许多机器无法做到这一点,原因如下:

1.针对GPU相关的错误,我们主要通过重新插卡进行了修复:首先,物理地将重达200磅的服务器从机架上滑出,接着拆除了盖板与GPU之间的所有连线,随后将GPU从插槽中取出并重新安装,最后恢复所有连线并重新将服务器放回机架。

2.根据Ubuntu服务器日志显示,GPU与PCIe(外围组件互连Express)总线或网络卡之间的数条连线出现了“带宽限制:x4 < x16”的提示。在完成PCIe交换总线固件更新后,我们发现在集群中约有四分之一的主机需要重新配置内部PCIe总线。这一现象可能是因为这些较为脆弱的连线被安置在机箱与GPU之间,导致在执行GPU维护操作时,这些连线容易受到震动或被意外拔除。

3.多起零星故障影响了少数主机。戴尔公司协助我们通过固件升级解决了这些问题:

  • NVMe驱动器未显示故障,但一旦触碰机器,整个系统都会被锁定。

  • 在Linux系统中,硬盘驱动以随机顺序出现,这一现象导致MAAS服务混淆,导致操作系统被安装到了错误的驱动上。

  • 错误的温度读数,导致风扇一直以100%的功率旋转。此问题部分源于所使用的NVIDIA驱动程序存在缺陷。我们已通过降级至先前的驱动程序版本,成功解决了驱动相关的问题。

  • CPU的动态频率调整功能失控,导致活跃core的频率限制在2 GHz。

  • 直接GPU-GPU 通信(GDR,或 GPUDirect RDMA Peer Memory Client)无法成功应用。

预置InfiniBand

步骤0:安装通用固件模块(UFM)

InfiniBand的优势在于集中式架构,这意味着整个网络由一个控制中心管理。因此,我们仅需与单一实体进行交互,即可管理和配置320个网络交换机。我们的首要任务是识别每个交换机和与其连接的设备,将这一信息与布线图相匹配,并依据其实际物理位置对交换机进行重命名。

步骤1:重新布线的时间

最初,UFM无法检测出320个网络交换机,更不用说识别网络中预期部署的所有主机。经过与数据中心合作伙伴的深入沟通,我们确认交换机均已开启且连接无误,但仍无法被系统检测。

在检查网络布线清单时,我们发现网络顶层设计有误:原本应是一个统一的网络结构,却分成了八个独立网络,彼此之间缺乏共同的路由路径。在重新布线连接后,我们实施了检查流程,确保所有物理连接与新设计保持一致。

步骤2:一万条温度预警

在解决了物理布线的困扰之后,统一网络结构管理器(UFM)顺利地与网络中所有InfiniBand交换机建立了链接。然而,几乎每一个交换机端口都开始报告过高的温度读数,即便在数据传输尚未启动的情况下,温度有时竟高达70摄氏度。

我们发现这一现象的根源在于同一网络机架内的各个交换机之间存在着空隙,导致热空气在机架内部循环流动,最终又回流至前端区域。为了解决这一问题,我们数据中心的合作伙伴迅速协助我们定诊断了故障原因,并研发出一套行之有效的应对方案。

步骤3:1800条预警

许多端口也表现出高错误率,或是在正常工作与故障之间波动,这种状况业内被称作“抖动(flapping)”。这些问题仅在端口活跃运作时才会显现,因此提前检测变得极具挑战性,因为整个网络由10,000个具有高度冗余的连接构成。数据中心的合作伙伴帮助我们清理并重新安装了警报端口,同时,在等待替换部件期间,我们停用了剩余的警报收发器。


尽管InfiniBand对硬件故障具有极高的容错率,但一旦大约10%的网络结构开始出现故障,自适应路由等特性就难以可靠地工作,从而无法有效地应对链路随机中断的情况。

在此期间,我们成功实施了包含100至200台机器的多节点训练任务。我们的流程主要基于现场发挥:我们有时会在随机选择的节点上启动训练,观察其性能表现,并尽可能让更多的节点保持运行状态。这种方法使我们能够识别出InfiniBand网络结构中可靠的子网络,但同时也存在一定的挑战,因为每次更改用于训练的节点集时,InfiniBand链路的默认配置也会随之改变。

步骤四:极限测试InfiniBand

为了更高效地诊断InfiniBand的问题,我们针对整个集群量身定制了一个专门工作负载,该工作负载同时在整个fabric的每个端口上尽可能多地传输数据。这种方法与在集群中运行单一的大型all-reduce工作负载截然不同,后者会通过NCCL在服务器PCIe模块(SXM)插槽,让GPU通过NVLink进行通信,从而优化单个节点内部的GPU间通信。

相反,我们选择了蛮力方法,并顺利取得了胜利。UFM开始发出警报,报告通过大多数端口的传输数据已超过理论容量的97%,并且一些交换机暂时出现故障。到一天运结束时,所有保持运行的端口都被认为足够稳健,可以继续使用,而其余的端口则被停用或进行后续的维修。

步骤5:GPUDirect RDMA

为了实现GPU之间能够通信且无需产生CPU计算开销,我们启用了名为GPUDirect RDMA的功能,该功能允许GPU直接与InfiniBand网络卡进行通信。这一过程涉及两个关键步骤:

  1. 启用一个额外的内核模块 (https://download.nvidia.com/XFree86/Linux-x86_64/535.183.01/README/nvidia-peermem.html)

  2. 确保禁止PCIe访问控制服务(ACS),防止出现立即挂起情况。(https://docs.nvidia.com/deeplearning/nccl/archives/nccl_2114/user-guide/docs/troubleshooting.html#pci-access-control-services-acs)

步骤 6:扩展“黄金”服务器集合

对于使用最新硬件的GPU集群,一条经验规则是:预计每周大约有3%的设备可能出现故障。

然而,一个常被忽视的关键细节是:并非每台机器都有相同的3%故障概率;实际上,只有少数几台“问题”机器会不断以各种形式出现故障,直到它们被彻底修复。这一点突显了在单一fabric上部署大量机器的优势。与其在我们的大规模训练运行中随机应对“打地鼠”式的故障,我们转而致力于构建一个由已知可靠机器组成的集合,这些机器被称为“黄金”机器。

步骤7:维护

InfiniBand的维护主要涉及响应UFM警报、更换故障的电缆和收发器,以及偶尔诊断更棘手的错误,例如故障的交换机。大规模性能退化通常由两个因素引起:

  1. 固件更新,尤其是当这种更新仅针对一般集群进行时,可能会破坏UFM的状态,并导致所有InfiniBand交换机上的UFM都需要重启。

  2. 同时进行的大规模GPU服务器重启,可能会向UFM状态中涌入大量更新数据,从而同样引发对UFM服务的重启需求。

确保机器完全健康

在整个过程中,我们发现了多种可能导致单个机器故障或减慢训练进程的方式。其中许多故障模式并不直观,因此我们编写了一系列健康检查程序,以确定哪些主机足够健康,可用于训练任务。我们已将相关代码在此处发布 (https://github.com/imbue-ai/cluster-health)。

请注意,这些运行状况检查大多针对我们特定的运行环境,并不一定关联基础硬件,或者修复和自动化起来极为简便。这是有意为之的设计:为了实现机器能够训练这一总体目标,我们希望有一个单一的入口点,它能给出“可以”或“不可以”的明确答复,并抽象掉所有那些“琐碎”的细节。

GPU健康检查

我们检查了GPU的数量是否准确无误,确认ECC(错误纠正码)检查功能已激活,并确保没有检测到任何ECC错误。此外,我们还对连接GPU之间的NVLink拓扑结构进行了检查,确保其稳定运行且无任何故障。

磁盘空间健康检查

我们检查了主机磁盘空间利用率不超过95%。

Docker健康检查

我们确保了Docker能够运行配备GPU的容器(即NVIDIA容器运行时工作正常),同时所有用于监控/分析的相关Docker容器均处于活跃状态,并且配置了正确的主机权限。

DMESG健康检查

我们检查了dmesg日志,确保其中没有出现硬件Xids或SXid错误(这是NVIDIA GPU或GPU间NVIDIA交换抛出的故障)。同时,我们还仔细审查了所有的dmesg日志条目,确保它们都能被归入我们列出的“常规/预期日志条目”类别中。

iDRAC健康检查

我们检查了机器上的iDRAC错误,并忽略了非致命错误信息。这一检查过程是针对戴尔服务器设计的,并不包含在我们将开源的健康检查内容。

磁盘健康检查

我们确认了zpool已妥善挂载,Docker已正确与其建立连接,并且在进行操作时不会引发CPU锁定现象。

InfiniBand 健康检查

我们监测了InfiniBand错误率的上升情况,以及是否安装了过时的驱动程序固件。

Nvlink健康检查

我们对机器上的NVLink错误进行了排查。经验表明,这些问题似乎并不会直接导致训练任务失败,但它们可能会延缓训练进程。

GDR健康检查

我们检查计算机上是否启用了GDR。

VBIOS健康检查

我们确保了GPU的VBIOS版本和H100底板的固件都是最新版本。

Flint健康检查

我们借助flint和hca_self_test工具,检查了对Mellanox OFED驱动程序的版本、卡片固件以及收发器固件,确保它们与NVIDIA驱动程序兼容,并已正确编译。

PSB健康检查

我们查询了PCIe设备,以确认GPU、PSB(PCIe交换总线)以及网卡之间的连接速度和宽度是否符合预期。此外,我们还检查了交换机固件是否更新至最新版本。这个脚本是由戴尔开发的,而非Imbue团队,因此我们目前无法分享它。

除了上述便捷的健康检查外,我们还实施了几项更为细致的健康检查,具体包括:

  • 利用PyTorch初始化矩阵计算,并评估NVLink的带宽以及GPU的计算速度和内存效率。我们设置了正确的GDR标志以测试InfiniBand和NVLink的性能。

  • 通过ib_write_bw并激活-use_cuda,我们实现了在InfiniBand卡上传输数据,并对PCIe及InfiniBand卡的带宽进行了精确测量。为了确保能够捕捉出现“抖动”的InfiniBand链路,我们进行了长达15分钟的持续测试。

  • 我们执行了多节点诊断运行,以检验NCCL的初始化功能以及是否存在随机停滞的情况。如果发生停滞,我们分支的NCCL代码会添加额外的日志记录。这个过程可能需要12到24小时,因此,我们通常只在添加新节点或怀疑存在问题时才会运行此测试。

  • 检查DCGM导出中是否存在任何GPU时钟节流事件(不包括预期的gpu_idle和power_cap)。进行多节点训练,激活所有GPU、InfiniBand卡、CPU和磁盘,是测试这些功耗事件的最佳方法。


诊断常见的训练问题

一旦硬件进入工作状态,便可以着手启动训练流程了。在本节中,我们将基于在集群上执行大规模语言模型训练的实战经验,分享由此得来的见解和具体调试步骤。

启动时崩溃

从某些方面来说,遇到这种错误是最好的,因为它(理论上)很容易重现并进行迭代。

首先,我们确认了代码是否在正确的版本、配置和环境变量下执行。虽然这一步骤看似基础,但它对于保证训练启动的可重现性和易于审查性至关重要,尤其是涉及到Docker镜像缓存或不可见机密配置等中间抽象时,这些因素可能会引起混淆。

另外一项基本检查,即确认所有机器均在线运行,并且生成的堆栈跟踪或日志能够轻松地进行汇总和检查。我们采用了Loki、Prometheus和Grafana的堆栈,但任何适合的日志聚合或追踪SaaS都可以胜任。鉴于这些运行的同步和分布式特性,往往第一个触发的错误会导致一系列无关错误级联发生。在此情况下,健康检查有助于迅速识别明显的故障,比如硬盘损坏、GPU缺失或配置无效等问题。

我们构建了一个系统,以便在发生故障时自动重新启动,这使得日志和报错的聚合变得更加重要,以防止将不同重启过程中的报错混淆。我们遇到的一些常见错误包括:

  1. 像 'Forward order differs across ranks: rank 0 is all-gathering 43 parameters while rank 1228 is all-gathering 1 parameters' 的错误 。我们发现,这属于PyTorch完全分片数据并行(FSDP)实现中的一个特性,可以通过重启来解决这个问题。

  2. GPU内存不足(OOM)错误,表现类似 'CUDA out of memory Tried to allocate … '。为了解决这个问题,我们双重检查了配置和代码,并回滚了所有可能因启动时PyTorch设备指定不当而导致的GPU#0额外资源占用的新近代码更改。

  3. CPU/RAM内存不足(OOM)错误,这类错误在错误日志中往往不易察觉,通常最佳检测手段是通过主机外部的dmesg日志来识别。我们主要在遇到子进程或网络对等方被OOM Killer回收时,遭遇以 'CalledProcessError' 或 'ConnectionError' 形式出现的错误。我们倾向于在检测到dmesg中的OOM Killer调用时,直接使健康检查失败并重启系统。我们还检查了代码路径中是否包含了足够的手动垃圾回收(参见下文关于如何禁用垃圾回收的章节),并确保没有意外尝试在CPU上进行计算或将张量搬运到CPU上。

训练途中故障

首要任务是自动化系统,以便重新执行所有诊断性健康检查(详见前述章节),并在排除不健康主机的情况下自动重启运行。我们遇到了一些随机的硬件故障,包括Xid和SXid错误,这些错误可能导致运行崩溃,不会生成有意义的Python堆栈跟踪。某些情况,例如行重映射(row remapping),可以通过简单的重启来恢复。然而,对于如不可纠正的ECC错误,通常需要硬件维护或更换零件。


此外,我们还观察到由异常格式化的训练数据引起的崩溃。例如,语料库中的一个超大型单一文档可能导致GPU或CPU出现内存不足(OOM)错误。为了规避这类问题,我们实施了一个完全确定性的数据加载器,这使得每次崩溃都能通过关联到特定的epoch或步骤编号来轻松地重现。我们发现,通过禁用数据加载或用假数据(例如全零数据)替换,可以有效确认数据是否真的是导致崩溃的根本原因。

最终,利用任何偏好的指标聚合方法来记录网络及节点整体健康统计信息同样至关重要。诸如以太网短暂断开连接或磁盘空间耗尽等问题可能不会直接以清晰的错误信息显现,但它们可以通过收集到的数据轻松进行关联。

无堆栈跟踪信息导致的挂起(可能随后出现超时)

这类错误在调试过程中极其棘手,一方面是因为缺乏有助于诊断的信息,另一方面则是因为它们往往难以稳定地重现。最令人记忆犹新的错误类型,其特征由以下类似错误信息标识

Watchdog caught collective operation timeout: WorkNCCL(SeqNum=408951, OpType=_ALLGATHER_BASE, … , Timeout(ms)=600000) ran for 600351 milliseconds before timing out

同时出现在训练运行中的所有GPU工作进程中。

这意味着,一个或多个主机未能完成NCCL操作,甚至可能从NCCL和InfiniBand连接中崩溃,导致所有其他主机在特定的张量操作上同步阻塞,直到达到 'NCCL_TIMEOUT'。遗憾的是,由于NCCL库的特性,要找出是哪个具体的主机导致了这一问题变得异常艰难。

我们对NCCL库进行了日志改进(详见我们的fork),以便更清晰地追踪崩溃发生时的活动消息或操作,进而识别出那些似乎阻碍了训练运行的主机或GPU。

请注意,为了识别表现异常的主机,我们通常需要确定哪些主机没有生成特定的日志消息。这些消息的缺失表明该主机上的工作进程已经落后或已崩溃。

在其他情况下,尽管没有出现有用的错误消息,但无响应的问题通常可以关联到硬件相关的问题,例如之前提到的Xid/SXid/ECC错误,这些错误会导致NVIDIA驱动程序或NVIDIA Docker通信驱动程序锁定。

为了区分NCCL挂起、驱动程序挂起以及Python代码中的争用条件或死锁,我们使用了Py-Spy和GNU项目调试器(GDB)等工具,在遇到停滞进程时进行现场调试。通过这种方法,我们能够捕捉到一个特定的问题,即由于Python线程设置配置不当,我们无法在某些主机上正确启动八个多线程的NCCL GPU进程,这些主机在预PyTorch初始化代码阶段遇到了竞争条件。

训练速度放缓(以模型浮点运算利用率(MFU)衡量)

缺乏监控工具可能会让这些问题比之前类别更加棘手。除了使用Py-Spy、堆栈跟踪检查和GDB之外,我们还启动了NVIDIA Nsight和性能分析工具来协助诊断,其中一些工具在高度分布式的环境中操作起来相当困难。

遗憾的是,训练速度的下降或低于以往所观测到的模型浮点运算次数(MFU)利用率,可能是由多种复杂因素共同作用的结果。

首先,对配置、代码和环境变量进行复查证明是非常有帮助的。我们曾遭遇过运行错误模型、不正确的batch大小、错误的UFM或NCCL配置、不恰当的CUDA_DEVICE_MAX_CONNECTIONS设置等问题,所有这些都会导致性能不佳。

此外,我们发现,相较于平滑或窗口平均值,直接测量每组batch的瞬时MFU更为有用,因为这种未经过平滑处理的MFU曲线形状通常能帮助我们快速诊断问题的类别。这些问题涵盖了:

  • 训练一开始就立即达到了极低的MFU(低于预期值的1/10),并且保持稳定

这种情况通常源于InfiniBand网络硬件的问题,比如T2或T3层级的交换故障。此外,也可能是GPU与网络接口卡(NIC)之间的硬件故障所致,这在dmesg日志中体现为PCIe x16 lanes limited by …

  • 训练一开始就达到了预期MFU的30%,并且保持稳定

这种情况可能是由于某个主机上的GDR(NVIDIA Peer Memory)设置不当,或者GDR环境变量配置错误导致。

  • 训练一开始就达到了预期MFU的约60-80%,并且保持稳定

这种情况通常是由退化或故障的InfiniBand链路引起的,尤其是如果某个特定的GPU关联的InfiniBand网络接口卡(NIC)出现故障,导致NCCL尝试通过本地NVLink路由流量,并使用同一主机上另一个GPU上的NIC。这也可能是由CPU节流(CPU throttling)引起的,这需要针对特定主机调整一些BIOS设置。

  • 单组batch频繁出现的剧烈下降(降低10倍)

这种情况几乎可以肯定是与检查点或评估操作相关——可以通过对比epoch或步骤计数来验证。遗憾的是,如果仅仅基于MFU异常来触发自动化警报,这会导致许多误报。

  • 随机且罕见地(大约每15分钟发生一次)出现单组batch突然急剧下降(下降10倍),随后立即完全恢复到良好的MFU

导致这种情况最常见的原因似乎是运行中的一台主机上安排了其他 CPU-heavy的工作负载。与其构建用于识别特定主机的分析工具,我们发现通过PID粗略监控CPU使用率更容易操作。这种波动也可能与偶尔的网络问题有关,比如数据加载时的瓶颈问题。我们使用了指标监控,并为数据加载、检查点以及任何非NCCL代码添加了Python代码计时日志,这种方法证明非常有效和可靠。

  • 在运行过程中,MFU图表呈现出逐渐下滑的趋势,但在每次重启后都能迅速回升至100%

从理论上讲,这种情况可能是由于交换机上的热量积累造成的,但我们并未观察到这一现象。相反,我们使用了Python和NVIDIA分析器来确定原因,这种性能下降似乎是由于自动垃圾回收导致的。

a405269a8031cc1725fa854cf87e5948.png

在调试这些性能下降时,我们注意到吞吐量出现周期性的下降,这种波动几乎呈现出一种确定性。随着训练的不断深入,这些波动对分布式操作的影响范围逐渐增大。这促使我们提出了一个假设,即这些波动可能与自动垃圾回收有关,我们通过性能分析和测试验证了这一假设。一旦我们禁用了自动垃圾回收,并在所有主机上安排垃圾回收在特定时间间隔内进行,这些吞吐量的“下降”现象就消失了。

我们使用了基于ZeRO-3的同步分布式训练算法 FSDP。在阻塞操作期间,运行垃圾收集的单个工作进程可能会减慢其他所有工作进程的速度。如果有数百个工作进程,这可能会导致速度显著下降。

  • 初期性能表现良好,随后突然下降(降至预期的70%),并且以高频率(每15秒一次)持续发生

我们观察到这种情况与NVIDIA GPU的“时钟节流原因(clock throttle reasons)”相关,我们通过应用合适的设置来使用NVIDIA DCGM收集到了这些信息。这通常是由于GPU过热(GPU温度过高或主机冷却风扇损坏/性能下降)或电源供应故障导致的。

此外,当我们同时将所有8个GPU的利用率、8倍的NIC InfiniBand利用率和CPU/RAM/磁盘的利用率全部推至最大时,一些具有特定电源供应硬件的主机会出现电压问题,但这种问题仅在所有组件同时被充分利用时出现——通常是在实际进行训练运行时。

  • 性能良好,但比平时更“嘈杂”(高频白噪声在预期MFU的90%到100%之间的方差较大)

这个问题同样与InfiniBand硬件有关,通常是由网络中较高层次的适配器性能轻微下降或不稳定(即“flapping”链接)引起的,而不是主机到T2层的冗余较低的连接部分造成的。

这也是InfiniBand硬件相关的问题,但通常是由于网络中较高层的中度降级或抖动链路(flapping link)引起的——而不是在冗余较低的主机到T2层。

遗憾的是,许多这些问题很难直接归因于某个特定的主机,并且由于InfiniBand交换技术具有拓扑感知特性,导致与InfiniBand相关问题尤其难以确定。InfiniBand似乎在InfiniBand fat-tree设计中倾向于相邻的主机,而UFM(用户功能管理器)可以以导致不对称链路速度的方式路由数据包。

以下是对调试吞吐量下降问题的快速总结、流程图和合理性检查清单:

  • 它奏效过吗?

  • 你最近有改变过什么吗 (例如, 合并了代码,更新了驱动程序)?

  • 你是在健康主机上运行的吗?所有依赖的服务是否都在运行,包括第三方SaaS服务,如Docker Hub、GitHub或其他你所用的堆栈依赖的服务?

  • 你确定你使用的是与上次完全相同的代码、环境、配置、版本、主机列表、排名顺序和随机种子吗(如果可能的话)?

  • 这个问题可以复现吗?

  • 它与其他事情有关联吗?其他进程?每日定时任务?主机或DCGM、UFM指标?

  • 你的度量工具是否正确?

  • 当运行缩减代码(较小的模型、伪造的数据、不保存或加载检查点)时,问题仍然存在吗?

3

改进基础设施工具

完成上述步骤后,可以确保在系统稳定且无重大故障的情况下,实现高效且稳定的模型训练性能。

在这一部分,我们将介绍我们开发的几种工具和系统,旨在确保训练过程能够顺利进行,尽可能地减少人工干预。鉴于我们团队规模较小,难以承担起持续的人工维护工作,因此我们致力于自动化更多流程。

几乎所有的训练运行问题都可以归因于故障的机器或网络组件。在大型集群中,这种故障发生频率较高,因此自动禁用故障机器和网络组件,并请求维修的过程至关重要。

故障机器

我们构建了一个系统,能够自动从最近的检查点重新启动出现故障的运行。首先,它会在所有可用的机器上执行一系列健康检查,并根据每台机器通过的检查项目对其健康状况进行分类;接着,系统会尝试在健康状况评估最高的机器上重新启动训练任务。

网络组件故障

我们所观察到的所有网络组件故障均被UFM检测到,并记录在UFM事件日志中,因此应对网络故障仅需分析UFM日志,针对每条事件采取相应的应对措施。

尽管UFM事件系统包含多种事件类型,但在实际应用中,我们发现只有少数几种事件才是问题所在,主要涉及链路故障或符号错误计数过高。通过识别这些事件,我们编写了脚本用于解析UFM事件日志,禁用近期事件中涉及的链路和端口,针对这些网络组件提交维护工单,并在完成维护后重新启用这些组件。

本地镜像文件系统

很早就发现,大规模分布式训练运行的瓶颈之一是进出集群的以太网速度。如果数百名工作人员试图同时下载数据集和模型检查点,带宽约为10Gbit/s的共享以太网连接很快就会饱和。

因此,我们决定在集群内部构建一个本地文件系统,以镜像云存储,并作为缓存来减少从S3获取文件的数量。为了应对集群中的机器频繁停机 (https://en.wikipedia.org/wiki/Consistent_hashing) 或因维护需要更换的问题,我们对每个文件采取了三重复制策略,并使用一致哈希算法均匀分配负载,从而在变动期间最小化文件移动。

由于集群磁盘空间有限,我们还必须开发各种工具来跟踪文件生命周期,并清除不再相关的文件。

本地分布式Docker注册表

我们还使用了Kraken (https://github.com/uber/kraken),这是一款出色的开源软件,可实现Docker 镜像的点对点传输。我们几乎没有遇到任何问题,考虑到任务和实施的复杂性,这有点令人惊讶。

各种性能监控工具

我们设置了默认的 Torch 分析器以及 NVIDIA 的 Nsight Systems。后者有助于准确了解前向/后向传递和 NCCL 通信需要多长时间,并确定我们是否因给定模型大小和worker数量而受到通信或计算的瓶颈。然而,Nsight Systems 有点难以使用,因为它需要在特权模式下运行 Docker,禁用与性能监控事件相关的安全检查,并且保存配置文件通常需要停止整个训练过程。

此外,我们发现编写工具来检测缓慢的训练batch并理解缓慢的潜在原因非常有益。其中最有效的工具是一种监控每个batch运行时间,并在batch运行异常缓慢时记录所有worker的堆栈跟踪工具,这使得识别存在细微硬件或软件问题的特定主机变得更加简便。

将机器分组细化以定位故障主机

在我们使用集群的最初几个月(当时我们的健康检查还没有像现在这样全面),我们经常遇到这样的情况:在特定一组机器上运行的训练失败了,但不清楚哪台机器出了问题。为了查明故障主机,我们开发了一些工具,以便轻松地将一组机器划分为若干子集,并在每个子集机器上启动一个较小的作业。

例如,如果 一组48 台机器上的作业出现故障,我们会在 6 组(每组 8 台)机器上启动小规模作业运行,然后在 8 组(每组 6 台)机器上启动小规模作业运行。通常情况下,这两个阶段中每个阶段,只会有一次运行失败,因此我们可以非常自信地得出结论,两个阶段中都参与了故障运行的机器是有问题的。

4

反思和学习

在构建和维护基础设施的过程中,我们在整个过程中获得了一些有用的经验:

  • 能够相互替换机器极为实用。对于任何特定的训练运行,我们发现拥有比实际需要多出10-20%的机器是有益的,这样在机器发生故障时,我们能够轻松地重新启动运行。通过设置集群网络使每台机器都与所有其他机器紧密相连,我们实际上可以利用任何处于工作状态的子集机器。

  • 对于每一次在训练过程中遇到的硬件或软件故障,编写相应的测试和自动化解决方案是非常值得的,因为这些已解决的问题可能会再次发生。同样地,对于每一个难以理解的错误信息,开发工具使其更加清晰易懂也是十分有价值的。

  • 重现性是优质科学研究的基础。我们迅速采纳的一个原则是“每次只变动一个因素”,即便是处理最简单的情况也不例外。

  • “信任,但需实践检验”。每当我们在流程中引入新的外部工具,或无论是内部还是外部新加入团队成员时,我们确保对其声明进行复核,特别是当后续步骤依赖于这些结果时。

5

总结

构建大规模语言模型所需的基础设施极其复杂。我们之所以选择深度参与基础设施的搭建,一方面是因为我们坚信理解所使用系统的重要性,另一方面是因为我们预见到这终将带来更高的效率。经过完整流程的实践,我们非常庆幸采取了这一策略——拥有对基础设施的完全控制权,并能在各个抽象层次上轻松地解决问题。虽然这个过程需要大量的监督和迭代,但它使我们能够深入理解底层流程,构建一系列工具确保主机健康,学习如何自动化系统以确保训练过程的持续平滑,最终创建了允许我们快速迭代训练尖端语言模型的基础架构。

这一基础设施建设过程体现了我们对研究和构建AI智能体坚实基础的方法:深入探究细节,持续优化现有流程,并构建实用的工具和系统,使我们这个充满活力的团队能够应对更大的挑战。

其他人都在看

3efedb034d24af5882c024dea2c9838b.png

让超级产品开发者实现“Token自由”

邀请好友体验SiliconCloud狂送2000万Token/人

邀请越多,Token奖励越多
siliconflow.cn/zh-cn/siliconcloud

扫码加入用户交流群

e828fd8acf5b0e975d6d7496f27b7a86.jpeg

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值