微信扫码
与创始人交个朋友
我要投稿
地址:https://imbue.com/research/70b-infrastructure/
时间:June 25, 2024
In the span of a few months, with a small team of researchers and engineers, we trained a 70B parameter model from scratch on our own infrastructure that outperformed zero-shot GPT-4o on reasoning-related tasks. Today, we’re sharing an end-to-end guide for setting up the required infrastructure…… Along with our detailed process, we are releasing:
二 背景:这应该如何运作
我们计算的目的是实现大规模语言模型的快速实验。为此,我们需要大量能够高速相互通信的快速 GPU。
本文重点介绍一个集群,该集群拥有分布在 511 台计算机上的 4,088 个 H100 GPU,其中一台计算机有 8 个 GPU。有 511 台计算机配备 GPU,因为需要为管理 InfiniBand 网络的 Unified Fabric Manager 节点保留一些连接。在配备 GPU 的 511 主机上,每个 GPU 直接连接到 ConnectX-7 卡,该卡可以通过自己的 ConnectX-7 卡以 400 Gbps 的速度同时向 InfiniBand 网络上的任何其他 GPU 进行传输和接收。
我们的 InfiniBand 网络拓扑被称为“完全无阻塞(fully non-blocking)”,因为理论上每个 GPU 都可以以最大速率同时与另一个 GPU 通信。这是通过三层 InfiniBand 网络架构实现的:三层 InfiniBand 交换机在正确连接时,可以在整个网络上实现这种高水平的吞吐量。请参阅下文了解 InfiniBand 网络的概述:
还有一个单纯用于配置和管理的辅助以太网,支持访问基本输入/输出系统 (BIOS) 的控制器接口、电源和其他低级机器接口。如果没有这个管理网络,我们将不得不使用 USB 驱动器、键盘和显示器手动设置节点,这对于数百台机器来说并不是不可持续的方法。(这里所说就是BMC网络)
使用我们的集群进行高性能训练意味着每个组件(InfiniBand、以太网、GPU 和节点本身)都必须近乎完美地工作。如果 12,000 多个连接中哪怕只有一个连接有点不稳定,都可能会减慢整个训练运行的速度。
本文的其余部分详细介绍了实际达到一切完美状态并确保保持这种状态的过程。
第2/7步:一万次温度警报
解决物理布线问题后,UFM成功与结构中所有InfiniBand交换机建立了联系。然而,几乎每个交换机端口都开始报告过高的温度,有时甚至超过70摄氏度,尽管它们还没有传输数据。我们发现,问题源于同一网络机架中的交换机之间存在空隙,导致热空气重新循环回到前部。我们的数据中心合作伙伴帮助我们迅速诊断出问题并制定了合适的解决方案。
第3/7步:1800个警报
许多端口还表现出高错误率或在工作状态和故障状态之间波动,这被称为“抖动”。这些问题仅在端口被积极使用时才出现,因此由于整个结构由10000条具有高度冗余性的链路组成,所以预先检测证明具有挑战性。我们的数据中心合作伙伴帮助清理并重新安置了发出警报的端口,我们在等待更换的同时禁用了其余发出警报的收发器。
尽管InfiniBand对硬件故障具有高度恢复能力,但一旦结构中约有10%的部分开始出现问题,自适应路由等功能就无法可靠地运行,以应对随意断开的链路。
在此期间,我们设法使用100至200台机器进行了多节点训练运行。我们的过程基本上是即兴发挥的:我们有时会在随机的一组节点上启动,观察它们的性能,并尝试在尽可能多的节点上保持运行。这种方法使我们能够找到InfiniBand结构中可靠的一部分,但证明很棘手,因为每当我们更改用于训练的节点集时,默认的InfiniBand链路集也会更改。
第4/7步:燃烧吧,InfiniBand,地狱之舞
启用额外的内核模块
https://download.nvidia.com/XFree86/Linux-x86_64/535.183.01/README/nvidia-peermem.html
确保禁用PCIe访问控制服务(ACS)以防止立即挂起
https://docs.nvidia.com/deeplearning/nccl/archives/nccl_2114/user-guide/docs/troubleshooting.html#pci-access-control-services-acs
InfiniBand的维护工作主要包括响应UFM警报、更换损坏的电缆和收发器,以及偶尔诊断更复杂的错误,如交换机故障。大规模回归通常由两个因素引起:
(1)固件升级,特别是如果仅对集群的一半进行升级,可能会损坏UFM状态,并需要在所有InfiniBand交换机上重新启动UFM。
(2)MAAS会在同时大规模重启GPU boxes,这可能会导致UFM状态被大量更新淹没,同样需要重启UFM服务。
在这个过程中,我们发现了许多单个机器可能发生故障或减慢训练运行速度的方式。许多故障模式并不明显,因此我们编写了许多健康检查来确定哪些主机足够健康,可以用于训练。我们已发布这些代码:https://github.com/imbue-ai/cluster-health。
我们查询了PCIe设备,以检查GPU、PSB(PCIe交换总线)和网络卡之间的连接速度和宽度是否符合预期。我们还检查了交换机固件是否为当前版本。此脚本由Dell开发,而非Imbue,因此我们目前无法共享。
除了这些快速健康检查外,我们还进行了一些更为复杂的健康检查,包括:
通过PyTorch初始化矩阵计算,并测量NVLink带宽、GPU计算速度和内存。我们设置了适当的GDR标志来测试InfiniBand和NVLink。
使用带有“-use_cuda”参数的ib_write_bw通过IB卡发送数据,并测量PCIe和InfiniBand卡的带宽。我们运行了较长时间(约15分钟),以确保捕捉到不稳定的InfiniBand链路。
运行多节点诊断,以检查NCCL初始化能力及其是否会随机停滞。如果停滞,我们分叉的NCCL代码会添加额外的日志记录。这可能需要12到24小时才能检测到问题,因此我们通常只在新节点上或怀疑存在问题时运行此检查。
检查DCGM导出的任何GPU时钟节流事件(排除预期的gpu_idle和power_cap)。同时锻炼所有GPU、InfiniBand卡、CPU和磁盘的多节点训练是锻炼这些电源事件的最佳方式。
4 诊断常见训练问题
由于某些节点的GPU间通信无法进行NCCL初始化,因此错误消息可能会提到卡上的某些GPU超时。解决此问题的最常见方法是更换问题节点,并重启NCCL会话。
如果GDR硬件有问题,可能会出现NCCL hanging(暂停)或性能下降。
由于NCCL使用GPU内存进行通信,因此任何内存硬件问题都会导致NCCL错误或性能下降。
调试这些问题的方法之一是运行带有适当日志记录的NCCL测试工作负载,并查找错误或挂起点。
从某些方面来说,遇到这种错误反而是最好的,因为(理论上)它易于重现和迭代。
我们首先检查代码是否在正确的版本、配置和环境变量下运行。虽然这是基本操作,但我们发现确保训练启动的可重现性和易检查性至关重要,尤其是像Docker镜像缓存或不透明的密钥配置这样的中间抽象可能会混淆问题。
我们进行的另一项基本检查是确保所有机器都在线,并且发出的堆栈跟踪或日志可以轻松地聚合和检查。我们使用了Loki、Prometheus和Grafana技术栈,但任何合适的日志聚合或追踪SaaS都适用。由于这些运行的同步和分布式特性,通常第一个触发的错误会导致一系列不相关的错误。在这里,健康检查也有助于立即检测明显的问题,如硬盘损坏或丢失或无效的GPU。
我们建立了一个系统,在失败时自动重新启动,这使得日志和错误聚合变得更加重要,以避免混淆来自不同重启的错误。我们遇到的一些常见错误包括:
诸如“不同等级的转发顺序不同:等级0正在全收集43个参数,而等级1228正在全收集1个参数”之类的错误。我们发现这是PyTorch的Fully Sharded Data Parallel(FSDP)实现中的一个特殊问题,可以通过重新启动来解决。
GPU内存不足(OOM)错误,看起来像是CUDA内存不足。尝试分配……我们通过仔细检查我们的配置和代码,并撤销任何可能由于在启动时PyTorch设备规范不当而导致额外利用GPU#0的最近代码更改来解决这些问题。
CPU/RAM内存不足错误,这些错误在错误日志中较不易被发现,通常最好通过Docker容器外部主机的dmesg日志来检测。我们主要看到它们表现为CalledProcessError或ConnectionError,当一个派生进程或网络对等体被OOM Killer调用时终止。当从dmesg检测到OOM Killer调用时,我们更倾向于直接使健康检查失败并重启机器。我们还检查了我们的代码路径是否有足够的手动垃圾收集(见下文关于如何禁用它的部分),并且没有意外地尝试在CPU上进行计算或移动张量。
首要任务是自动化系统,以重新运行所有诊断性健康检查(见前几节),然后在没有不健康主机的情况下自动重新启动运行。我们遇到了一些随机的硬件故障,包括Xid和SXid错误,这些错误可能导致运行崩溃,而不会发出有意义的Python堆栈跟踪。某些情况,如行重映射,可以通过重新启动来恢复。其他情况,如不可纠正的ECC错误,通常需要硬件维护或更换零件。
此外,我们还观察到由特别畸形的训练数据导致的崩溃。例如,语料库中的一个非常大的单个文档可能会导致GPU或CPU出现OOM错误。为了防止这些问题,我们使用了一个完全确定性的数据加载器,这使得每个崩溃都可以通过与时期或步骤号的相关性轻松重现。我们发现禁用数据加载或用假数据(如全为零的数据)替换以确认数据是否确实是根本原因的做法很有帮助。
最后,通过任何首选的指标聚合方法来记录网络和一般节点健康统计信息也很有帮助。诸如以太网短暂中断或磁盘空间不足等问题可能不会显示为有用的错误消息,但可以很容易地与收集到的数据进行关联。
这类错误由于缺乏有用的信息并且通常难以可靠重现,调试起来非常令人沮丧。
最令人印象深刻的类型以如下错误消息为特征:
Watchdog caught collective operation timeout: WorkNCCL(SeqNum=408951, OpType=_ALLGATHER_BASE, ... , Timeout(ms)=600000) ran for 600351 milliseconds before timing out
这意味着在训练运行期间,一个或多个主机未能完成NCCL操作,甚至从NCCL和InfiniBand连接中崩溃,导致所有其他主机在达到NCCL_TIMEOUT之前,都在特定的张量操作上同步阻塞。不幸的是,NCCL库的性质使得很难找出是哪个特定的主机是罪魁祸首。
我们对NCCL库进行了一些日志记录更改(参见我们的分支),以更好地显示崩溃时哪些消息或操作正在进行中,从而确定是哪个主机或GPU似乎阻止了运行。
请注意,为了识别行为异常的主机,我们经常需要找出哪些主机没有生成某些日志消息。缺乏此类消息表明该主机上的工作进程是落后者或已崩溃。
其他没有有用错误消息的不响应实例通常可能与硬件相关问题有关,例如前面提到的Xid/SXid/ECC错误,这些错误可能导致NVIDIA驱动程序或NVIDIA Docker通信驱动程序锁定。为了区分NCCL挂起与驱动程序挂起以及Python代码中的竞态条件或死锁,我们使用了包括Py-Spy和GNU Project Debugger(GDB)在内的工具,对我们遇到的任何停滞进程进行实时调试。使用这种方法,我们能够捕获一个特定问题,即由于Python线程设置中的配置错误,我们无法在某些主机上正确启动八个多线程NCCL GPU进程,这些主机在PyTorch初始化之前的代码中遇到了竞态条件。
缺乏检测工具可能会使这类问题比前一类问题更加令人沮丧。除了使用Py-Spy、堆栈跟踪检查和GDB之外,我们还使用了NVIDIA Nsight和性能分析工具来帮助解决问题,但在高度分布式的设置中,其中一些工具很难使用。
遗憾的是,通用的性能下降或低于之前演示的模型浮点运算利用率(MFU)可能由多种原因造成。
首先,事实证明,再次检查配置、代码和环境变量是很有帮助的。我们经历过运行错误模型、错误的批次大小、错误的UFM或NCCL设置、错误的CUDA_DEVICE_MAX_CONNECTIONS等问题,这些都导致了性能不佳。
我们还发现,测量瞬时(即每个批次)的MFU而不是平滑或窗口化的平均值也很有用,因为MFU曲线的预平滑形状经常帮助我们诊断问题类别。问题包括:
训练立即以极低的MFU(低于预期的1/10)开始并保持稳定
这最常见的是InfiniBand网络的硬件问题,如T2或T3层的死交换机。这也可能是由GPU和NIC之间的硬件问题引起的,这些问题会在dmesg中显示为PCIe x16通道受限……
训练立即以预期MFU的30%开始并保持稳定
这可能是由于一个主机的GDR(NVIDIA Peer Memory)设置不当或GDR环境变量不正确造成的。
训练立即以预期MFU的60-80%开始并保持稳定。
最常见的原因是InfiniBand链路退化或故障,尤其是如果单个特定GPU关联的InfiniBand NIC出现故障,导致NCCL尝试通过本地NVLink路由流量并使用同一主机上另一个GPU的NIC。这也可能是由CPU节流引起的,这需要为特定主机调整一些BIOS设置。
单个批次突然出现大幅度的下降(降低10倍),且这种情况定期发生
这几乎肯定与检查点或评估有关,可以通过检查时期或步骤计数来验证。令人恼火的是,如果自动化警报仅设置为在MFU异常时触发,这会导致许多误报。
单个批次突然出现大幅度的下降(降低10倍),且这种情况随机发生且相当罕见(大约每15分钟发生一次),并且之后立即完全恢复到良好的MFU。
这似乎最常见的原因是在运行中的一个主机上安排了其他CPU密集型工作负载。我们发现,与其构建分析工具来识别特定主机,不如通过PID粗略地监控CPU使用情况来得更容易。这也可能归因于偶尔的网络状况不佳,如数据加载器瓶颈。我们使用了指标监控,并为数据加载、检查点和任何非NCCL代码添加了Python代码计时日志,这证明相当可靠。
MFU图在运行过程中逐渐下降,但在任何重启后都会恢复到100%。
理论上,这可能是由于交换机上的热量积累造成的,但我们从未见过这种情况。相反,我们使用Python和NVIDIA性能分析工具来确定,这种性能下降似乎是自动垃圾收集的结果。
在调试这些性能下降问题时,我们注意到吞吐量出现了几乎呈确定性的周期性下降模式。随着训练的进行,这种下降影响了越来越大的分布式操作比例。这导致我们提出了一个假设,即这种下降可能与自动垃圾收集有关,我们通过分析和测试验证了这个假设。一旦我们禁用了自动垃圾收集,并安排在所有主机上以特定间隔进行垃圾收集,这些吞吐量“下降”就消失了。
我们使用了基于ZeRO-3(https://www.microsoft.com/en-us/research/blog/zero-deepspeed-new-system-optimizations-enable-training-models-with-over-100-billion-parameters/)的同步分布式训练算法FSDP。在阻塞操作中,单个工作进程运行垃圾收集可能会减慢其他所有工作进程的速度。如果有数百个工作进程,这可能会导致显著的性能下降。
性能一开始很好,然后突然下降到预期值的70%,并且以高频率(每15秒)持续出现。
我们观察到这与NVIDIA GPU的“时钟节流原因”有关,我们通过将适当的设置应用于NVIDIA DCGM来收集这些信息。这是由散热问题(GPU温度过高或主机冷却风扇损坏/性能下降)或电源供应故障引起的。此外,当我们同时最大化所有8个GPU的利用率、8x NIC InfiniBand的利用率以及CPU/RAM/磁盘的利用率时,我们的一些具有特定电源供应硬件的主机会出现电压问题,但这种情况仅发生在所有资源都被使用时,通常只在实际的训练运行过程中出现。
性能良好,但比平时更“嘈杂”(预期MFU的90%至100%之间存在高频白噪声方差)。
这也与InfiniBand硬件有关,但通常是由于网络中较高层的链路适度退化或波动,而不是主机到T2层之间冗余较少的链路。
不幸的是,许多问题并不容易定位到特定的主机,而与InfiniBand相关的问题尤其难以确定,因为InfiniBand交换技术具有拓扑感知特性。InfiniBand似乎更倾向于在InfiniBand胖树设计中选择相邻的主机,而UFM可能会以导致链路速度不对称的方式路由数据包。
以下是调试吞吐量回归的快速总结/流程图/健全性检查清单:
它之前工作过吗?
你最近是否更改了什么(例如合并了代码、更新了驱动程序)?
你是否在健康的主机上运行?你的所有依赖服务是否都在运行,包括第三方SaaS,如Docker Hub、GitHub或你的堆栈所依赖的其他服务?
你确定你使用的是与上次完全相同的代码、环境、配置、版本、主机列表、排名顺序、随机种子吗(如果可能)?
这个问题是可复现的吗?
它与其他任何东西相关吗?其他进程?每日crontab?主机或DCGM或UFM指标?
你用来测量指标的工具是否正确?
在运行简化的代码(更小的模型、伪造的数据、不保存或加载检查点)时,问题是否仍然存在?
5 完善基础设施工具
完成上述步骤后,在训练模型时可以获得良好的性能……但直到某些东西不可避免地出现故障。
在本节中,我们将介绍我们制作的一些不同的工具和系统,以确保训练能够继续顺利进行,理想情况下只需最少的人工干预。由于我们的团队规模较小,我们没有足够的人手来进行不断的手动修复,因此我们尝试尽可能多地自动化这一过程。
我们几乎所有的训练运行问题都可以归结为机器或网络组件的故障。在一个大型集群中,这些故障经常发生,因此自动化禁用故障机器和网络组件并请求修复的过程至关重要。
(1)故障机器
我们开发了一个系统,用于从最近的检查点自动重新启动崩溃的运行。重新启动过程将首先对每个可用的机器运行健康检查,并根据其通过的健康检查对每台机器的健康状况进行分类;然后,它会尝试在最健康的机器上重新启动训练作业。
(2)故障网络组件
我们观察到的所有网络组件故障都被UFM检测到并记录在UFM事件日志中,因此应对网络组件故障只是解析UFM日志并针对每个事件采取适当行动的问题。
UFM事件系统相当复杂,包含数十种事件类型。然而,在实际应用中,我们发现只有少数几种事件是有问题的,主要与链路故障或高符号错误计数有关。在识别出这些事件后,我们能够编写脚本来解析UFM事件日志,禁用与最近事件相关的链路和端口,对这些网络组件提交维护工单,并在维护完成后重新启用这些组件。
(3)本地镜像文件系统
很明显,大型分布式训练运行的一个瓶颈将是集群内外的以太网速度。如果数百个工作进程同时尝试下载数据集和模型检查点,那么带宽约为10Gbit/s的共享以太网连接将很快达到饱和。
因此,我们决定在集群内构建一个本地文件系统来镜像云存储,并作为缓存来减少我们需要从S3获取的文件数量。为了处理集群的周转(机器经常因维护原因而被禁用或替换),我们对每个文件进行了三副本,并使用一致性哈希来均匀分配负载,以最大限度地减少周转期间的文件移动。集群上有限的磁盘空间意味着我们还必须开发各种工具来跟踪文件生命周期并清除不再相关的文件。
(4)本地分布式Docker注册表
我们还使用了Kraken,这是一个出色的开源软件,能够实现Docker镜像的P2P传输。考虑到任务和实现的复杂性,我们几乎没有遇到任何问题,这有点令人惊讶。
(5)各种性能监控工具
我们设置了默认的Torch分析器以及NVIDIA的Nsight Systems。后者有助于准确了解前向/后向传递和NCCL通信所需的时间,并确定在给定的模型大小和工作进程数下,我们是否受到通信或计算的瓶颈限制。然而,Nsight Systems的使用有些困难,因为它需要在特权模式下运行Docker,禁用与性能监控事件相关的安全检查,并且保存配置文件通常需要停止整个训练过程。
此外,我们发现编写工具来检测训练批次缓慢并了解其潜在原因很有帮助。其中最有用的是一个工具,它监控每个批次所需的时间,并在批次异常缓慢时转储每个工作进程的堆栈跟踪,这使得更容易识别具有细微硬件或软件问题的特定主机。
(6)细分机器组以定位故障主机
在使用集群的前几个月(当时我们的健康检查并不像现在这样彻底)中,我们经常遇到一种情况,即在特定的一组机器上进行训练运行失败,但不清楚是哪台机器出了问题。为了定位故障主机,我们开发了工具,以便轻松地将机器集划分为子集,并在每个机器子集上启动较小的作业。
例如,如果一组48台机器上的作业失败,我们将在六组八台机器上启动较小的运行,然后在八组六台机器上启动较小的运行。通常情况下,在这两个阶段中,只有一个运行会失败,从而使我们能够高度确信这两个阶段中都出现故障运行的机器是有问题的。
四 反思与收获
在搭建和维护我们的基础设施的过程中,我们对整个流程有了一些有用的收获:
能够互相替换机器是非常有用的。对于任何给定的训练运行,我们发现拥有比运行所需多出10-20%的机器是很有帮助的,这样我们可以在机器出现故障时轻松重新启动。将集群网络设置成每台机器都与其他每台机器紧密相连,意味着我们基本上可以使用任何工作的机器子集。
值得为遇到的每一种硬件或软件故障编写测试和自动化解决方案,因为训练过程中遇到的每个问题都会再次出现。同样,对于每一个模糊的错误信息,编写工具使其更易于解释也是值得的。
可重复性是良好科学的关键。我们迅速采纳的一条规则是“每次只改变一件事”,即使是最简单的事情也是如此。
信任,但要验证。每当我们在流程中引入外部工具或吸纳新成员(无论是外部还是内部),我们都会确保对他们的说法进行复核,特别是如果后续步骤依赖于这些结果的话。
五 结论
训练大型语言模型从一开始就需要复杂的基础设施。我们选择深入参与基础设施设置的细节,既因为我们相信完全理解我们工作的系统很重要,也因为我们怀疑这最终会证明更高效。现在,经历了整个过程,我们很高兴我们采取了这种方法——它最终对完全控制我们的基础设施以及能够在每个抽象层次上轻松调试问题至关重要。虽然这个过程需要广泛的监督和迭代,但它使我们能够深入理解底层程序,构建一系列工具来确保主机健康,学习如何自动化系统以确保持续平稳的训练,并最终创建基础设施,使我们能够快速迭代前沿语言模型的训练。
这个基础设施过程体现了我们研究和构建人工智能代理坚实基础的方法:探究细微末节,不断改进现有流程,并构建有用的工具和系统,使我们的精悍团队能够应对更大的挑战。
53AI,企业落地应用大模型首选服务商
产品:大模型应用平台+智能体定制开发+落地咨询服务
承诺:先做场景POC验证,看到效果再签署服务协议。零风险落地应用大模型,已交付160+中大型企业
2024-07-11
2024-07-11
2024-07-09
2024-09-18
2024-06-11
2024-07-23
2024-07-20
2024-07-12
2024-07-26
2024-07-23
2024-11-18
2024-11-16
2024-11-16
2024-10-31
2024-10-31
2024-10-27
2024-10-26
2024-10-25