想象你是一位连锁餐厅的老板,招牌菜是“红烧肉”,每家分店里都放着一本一模一样的菜谱。一天客人太多,单店厨师忙不过来,于是你开了 N 家分店:各家分店用同一本菜谱、接待不同的客人。打烊后,所有大厨开视频会议对表——“我今天糖色偏重”“我这儿火候小了”——大家把各自发现汇总成一份统一的改进建议,第二天所有分店照同一份新菜谱开工。 这套“多家分店 + 同一菜谱 + 收工对表”的运营法,正是分布式训练中的数据并行(Data Parallel)。它把同一个模型复制到 N 张显卡上,每张卡吃不同的数据批次、算出各自的梯度,再通过 AllReduce 把梯度汇总同步,让所有副本始终步调一致。 不过,分店越多,对表的电话费越贵;菜谱太厚,单家厨房放不下。于是又有了同步与异步的取舍、梯度累积、模型并行和 ZeRO 等补充方案。本文就沿着这家连锁餐厅的扩张故事,把数据并行的原理、机制与 JAX 实践一次讲透。

岗位关联

数据并行是分布式训练的第一课。读懂它,你才能真正看懂大模型训练脚本里“多卡同步、梯度汇聚”的标准流水线,也才能在面试和调优时准确判断瓶颈在算力还是通信。

目录
  1. 可视化演示
  2. 核心思想 — 多店同谱,一菜并行
  3. AllReduce机制 — 环形传话的对表大会
  4. 同步与异步 — 齐步走还是各自飞
  5. 梯度累积 — 小本本攒意见
  6. 显存瓶颈 — 菜谱太厚怎么办
  7. JAX实践 — 一键分派的总部系统
  8. 经典场景 — 从单店到千店连锁
  9. AI 应用场景
  10. Python 演示
  11. 常见误区
  12. 面试题

可视化演示

展示数据并行训练中数据分片、并行梯度计算与AllReduce同步的完整流程

本节配有交互动效(进入视口后自动加载,约 3KB)。

数据分片与分发 全局数据集均匀切分 每个Worker获取独立子集 D_i = D[N/i : N/(i+1)] 总结:数据并行第一步是数据分片

核心思想 — 多店同谱,一菜并行

老板决定开分店,需要同时办三件事:给每家分店发同一本菜谱(模型副本)、给每家分店分配不同的客人(数据批次)、规定每晚对表规则(梯度同步)。这三件事,恰好就是数据并行的全部骨架。

在深度学习里,“菜谱”是模型的参数 w,“客人”是训练数据。数据并行把一份完整模型复制到 N 个设备上(常见的是 GPU),再把每一批次训练数据均匀切成 N 份,每张卡只处理自己那份。由于各卡数据不同,前向计算和反向传播算出的损失梯度也各不相同——这些梯度就是各分店“今天发现的问题”。

数据并行最优雅的地方在于:虽然每张卡只看到 1/N 的数据,但只要把所有本地梯度求平均,再让所有副本都按这个平均梯度更新一次参数,效果就等价于用 N 倍数据训练了一步。这让它在实现上对模型结构零侵入——不需要改动任何网络层,只要把数据切分和梯度同步包在外面。

所以你看,数据并行没有发明任何新的网络结构,它只是把“训练”这件事从单店变成连锁:副本相同、数据分开、意见平均。模型本身完全不用动——这就是工程师们常说的“对模型无侵入”。

wt+1=wt−η⋅1N∑i=1N∇Li(wt)\displaystyle w_{t+1} = w_t - \eta \cdot \frac{1}{N} \sum_{i=1}^{N} \nabla L_i(w_t)
所有设备算出的梯度取平均,再按学习率迈一步;这就是“统一改菜谱”的数学写法。
  1. 损失梯度第 i 个设备在自己的数据分片上算出的梯度,相当于这家分店今天的改进意见。
    ∇Li(wt)\nabla L_i(w_t)
  2. 跨设备求和把 N 个设备的梯度逐元素相加,等价于把 N 份意见汇总到同一张纸上。
    ∑i=1N∇Li(wt)\sum_{i=1}^{N} \nabla L_i(w_t)
  3. 求平均除以设备数 N,把汇总梯度折中为一条共同意见,避免某一家分店的一家之言主导全体。
    1N∑i=1N∇Li(wt)\frac{1}{N}\sum_{i=1}^{N} \nabla L_i(w_t)
  4. 参数更新用学习率 η 控制修改幅度,让所有副本朝同一方向迈出完全一致的一步。
    wt+1=wt−η⋅gˉw_{t+1} = w_t - \eta \cdot \bar{g}

AllReduce机制 — 环形传话的对表大会

每晚的对表大会怎么开才高效?最笨的办法是每家分店把意见书寄给总部,总部汇总后再复印发给所有人——总部成了瓶颈,店越多,总部的收发件压力越大。

这个“汇总意见并广播给所有人”的操作,在分布式系统里叫 AllReduce(全归约)。All = 所有节点参与,Reduce = 求和/求平均等归约。数据并行每迭代一次,就要对全部梯度做一次 AllReduce。注意,梯度通常有几亿个浮点数,也就是那张“意见书”厚得惊人。

因此,AllReduce 的工程实现直接决定扩展效率。工业界主流是 Ring-AllReduce(环形全归约):设备们围成一个环,把梯度切成 N 块,先沿环传递并就地累加(reduce-scatter 阶段),再把完整结果沿环传一圈让每个人各拿走一块(all-gather 阶段)。每个节点只和左右邻居通信,没有谁是中心。

回到连锁餐厅:Ring-AllReduce 就像分店们围成一圈传话,每家只需把意见传给左右邻居,传两圈,所有人就同时拿到了全体的平均意见。没有总部,也没有人需要听 N 遍完整发言。

Vnode=2(N−1)×DN=2D(1−1N)\displaystyle V_{\text{node}} = 2(N-1) \times \frac{D}{N} = 2D\left(1 - \frac{1}{N}\right)
环形方案把通信量均匀摊到每个节点上,单点通信量接近 2D,和卡数几乎无关。
  1. 梯度总大小 D模型全部梯度参数的字节数,相当于“意见书”的厚度。
    DD
  2. 分块大小 D/N梯度被切成 N 块,每轮每个节点只传一块,工作量被摊薄。
    DN\frac{D}{N}
  3. 绕环轮数 2(N-1)reduce-scatter 与 all-gather 两个阶段各绕环 N-1 轮,共 2(N-1) 轮。
    2(N−1)2(N-1)
  4. 每节点通信量化简为 2D(1-1/N),当 N 很大时趋近 2D,只和模型大小有关——这就是 Ring-AllReduce 能支撑千卡训练的原因。
    Vnode=2D(1−1N)V_{\text{node}} = 2D\left(1 - \frac{1}{N}\right)

同步与异步 — 齐步走还是各自飞

对表大会有两种开法。第一种:全员必须到齐、确认完所有意见后,才允许任何一家修改菜谱——这是同步训练。第二种:谁先算完谁先改,其他人稍后跟上——这是异步训练。

同步训练(Synchronous Training)中,N 个设备各自前向反向后,必须等最慢的设备完成,AllReduce 结束才统一更新参数。它收敛稳定、行为可复现,但整轮速度被最慢节点拖住,这就是“木桶效应”。异步训练(Asynchronous Training)不需要等待,设备算完就把梯度发到参数服务器并立即拉取最新参数继续下一轮,吞吐更高,但拉取到的参数可能已经又更新了好几轮。

异步的问题在于陈旧梯度(stale gradient):梯度是在旧参数状态下算出的,用它更新新参数就像拿着一个月前的城市地图开车。梯度越旧,方向越不可靠,严重时会导致 loss 震荡甚至不收敛。

所以连锁企业通常的做法是:总部定下的核心配方(同步)稳定为主,允许部分门店小范围试新(异步)。工业界也类似——大规模预训练用同步保证质量,在线场景用异步换吞吐。

wt+1=wt−η⋅g(wt−τ)\displaystyle w_{t+1} = w_t - \eta \cdot g(w_{t-\tau})
异步更新用的是“过期意见”:等梯度算完时,参数早已不是当初的模样。
  1. 滞后步数 τ梯度计算完成时参数已落后当前版本多少步,即“意见的保鲜期”。
    τ\tau
  2. 旧状态梯度用旧参数算出的梯度,与当前参数状态存在偏差。
    g(wt−τ)g(w_{t-\tau})
  3. 异步更新直接用这份旧梯度更新当前参数,每张卡各更各的,互不等待。
    wt+1=wt−η⋅g(wt−τ)w_{t+1} = w_t - \eta \cdot g(w_{t-\tau})
  4. 与同步对照同步训练等价于 τ = 0,梯度永远新鲜,代价是大家都要等最慢者。
    τ=0\tau = 0

梯度累积 — 小本本攒意见

对表大会每天开一次,电话费吃不消。聪明的老板规定:每家分店把连续 K 桌客人的反馈先记在小本本上,攒够一页,再带着这页意见参加对表。

对应到训练,就是梯度累积(Gradient Accumulation):单卡显存不足以直接装下大批次时,把一个大批次拆成 K 个微批次(micro-batch)顺序前向反向,K 个微批次的梯度在本地累加并取平均,攒满 K 步才做一次参数更新和一次 AllReduce。

这样做有两个好处:一是峰值显存只按一个微批次计算,不必为完整大 batch 预留显存;二是 AllReduce 频率降为原来的 1/K,通信开销大减。数学上,在普通 SGD 下,K 个微批次梯度平均再更新,等价于一次性用 K 倍数据更新——只是等效 batch 变大后,学习率通常也要按比例放大以保持收敛速度。

小本本攒意见,攒够了才开会——梯度累积用“少开会、多记录”换来了通信频率的下降和对大 batch 的模拟,是显存与速度之间最常用的折中。

gacc,i=1K∑k=1K∇Li,k(w)\displaystyle g_{\text{acc},i} = \frac{1}{K} \sum_{k=1}^{K} \nabla L_{i,k}(w)
每张卡在本地攒 K 个小批次的梯度再求平均,相当于“小本本攒一页再开会”。
  1. 设备下标 i第 i 张卡在本地独立执行整个攒梯度流程。
    i∈{1,2,…,N}i \in \{1, 2, \dots, N\}
  2. 微批次梯度第 k 个微批次完整前向反向后得到的梯度。
    ∇Li,k(w)\nabla L_{i,k}(w)
  3. 累加求和把 K 个微批次的梯度逐元素相加,即小本本上记录的总意见。
    ∑k=1K∇Li,k(w)\sum_{k=1}^{K} \nabla L_{i,k}(w)
  4. 平均归一除以 K 使梯度量级与单批次一致,等效扩大训练 batch 但没扩大单步显存。
    gacc,i=1K∑k=1K∇Li,k(w)g_{\text{acc},i} = \frac{1}{K}\sum_{k=1}^{K}\nabla L_{i,k}(w)

显存瓶颈 — 菜谱太厚怎么办

有一天老板发现,菜谱已经厚到单家厨房的调料架放不下了。数据并行有个前提:每家分店必须带全套菜谱。所以分店开得再多,单店的“书架空间”也没有变小——显存瓶颈不会因为加卡而缓解。

这是数据并行的硬边界:每个副本都要保存模型参数、梯度以及优化器状态(如 Adam 的动量与方差),这三块加起来通常是参数量的 4~16 倍。当模型大到单卡装不下时,必须请出两位外援。

第一位是模型并行(Model Parallel):把模型按层切开,每家厨房只负责一道工序,数据依次流过所有设备,相当于流水线;第二位是 ZeRO:不切模型计算,而是把参数、梯度、优化器状态分片存到 N 张卡上,谁用到谁“临时调货”,用完就还。ZeRO 与数据并行正交,是目前大模型训练的标准组合。

所以聪明的连锁老板两手抓:数据并行负责“人海战术”,让每张卡忙起来;模型并行负责“流水线分工”,ZeRO 负责“把菜谱拆成 N 本分册放各家书库”。三者互补,缺一不可。

MZeRO-3≈Mθ+Mg+MoN+Ma\displaystyle M_{\text{ZeRO-3}} \approx \frac{M_{\theta} + M_{g} + M_{o}}{N} + M_{a}
ZeRO 把“参数 + 梯度 + 优化器状态”三大块分片存储,单卡负担降为 1/N。
  1. 参数占用模型参数本身,即“菜谱正文”。
    MθM_{\theta}
  2. 梯度和优化器状态反向梯度与 Adam 动量等状态,常是参数量的数倍。
    Mg+MoM_{g} + M_{o}
  3. 分片存储ZeRO-3 把三大块均分到 N 张卡上,单卡负担降为原来的 1/N。
    Mθ+Mg+MoN\frac{M_{\theta} + M_{g} + M_{o}}{N}
  4. 激活值不变前向计算的中间缓存只取决于本地微批次大小,不随 N 摊薄,仍占大头。
    MaM_{a}

JAX实践 — 一键分派的总部系统

作为老板,你当然希望“开分店、发名单、统一对表”这些杂事能一键自动化。JAX 的 pmap 就是这样的调度系统:你只需写一份单设备版本的函数,它帮你完成复制、分派、同步。

pmap(parallel map)接受一个函数 f,把它复制到所有设备(jax.devices() 返回的设备列表),并约定:带设备维度的输入按第一维切成 N 份分别送入,不带设备维度的输入则广播给所有副本。返回结果依然是带设备维度的 DeviceArray,代表“每个设备上各有一份结果”。

梯度同步需要显式声明:在 pmap 函数内部用 jax.lax.pmean(g, axis_name='devices') 把各设备梯度求平均并广播——pmean 会被 XLA 编译成真正的 AllReduce 指令。这是 JAX 数据并行的一个鲜明特点:把“同步”明明白白写进代码,而不是藏在框架默认行为里。

pmap 就像总店发出的“今日操作手册”:同一本手册(函数代码)发给所有分店,但附上各自不同的客人名单(数据切片)。打烊后要不要对表、怎么对表,由 pmean 一句指令说了算。

xi=x[i⋅b:(i+1)⋅b],b=BN,i=0,1,…,N−1\displaystyle x_i = x[i \cdot b : (i+1) \cdot b], \quad b = \frac{B}{N}, \quad i = 0, 1, \ldots, N-1
pmap 按照上述切片规则,把总批次 B 均分成 N 份,分发给 N 个设备副本。
  1. 总批次大小每个训练迭代的全部样本数,如 1024 张图片。
    BB
  2. 每卡份额平均分到每张卡的样本数。
    b=BNb = \frac{B}{N}
  3. 切片区间第 i 张卡取第 i·b 到 (i+1)·b 个样本,各卡互不重叠。
    xi=x[i⋅b:(i+1)⋅b]x_i = x[i \cdot b : (i+1) \cdot b]
  4. SPMD 执行同一份函数在所有设备上运行,只是各自处理不同的切片。
    fθ(xi),i=0,1,…,N−1f_{\theta}(x_i), \quad i = 0, 1, \ldots, N-1

经典场景 — 从单店到千店连锁

分店从 4 家开到 1024 家后,老板发现收益不再线性增长:每天光是对表电话费就占了运营成本的大头。这就是分布式训练里著名的“通信与计算比”问题。

设每轮训练中,可并行的计算部分(前向 + 反向)占比为 P,剩下 1-P 是无论如何也无法摊薄的串行部分(同步握手、启动延迟、通信调度)。Amdahl 定律给出了加速比上限:设备数 N 再大,加速比也只能逼近 1/(1-P)。

对现代大模型,P 常在 0.95~0.99,理论加速上限是 20~100 倍。实际训练还会遇到通信带宽瓶颈、负载不均等问题。常用的对冲手段包括:梯度累积加大单卡计算量、用 Ring-AllReduce 替代参数服务器、梯度压缩、以及用 NVLink/IB 等高速互联降低单次通信延迟。

开再多的分店,总部每天要做的“开业训话”与“对表例会”始终省不掉——这就是 1-P 那条串行尾巴。数据并行工程的本质,就是尽量让每家分店多干活、少开会。

S(N)=1(1−P)+PN\displaystyle S(N) = \frac{1}{(1-P) + \frac{P}{N}}
Amdahl 定律:只要存在不可并行的串行尾巴,加速比就有天花板。
  1. 并行比例 P可被多卡分摊的计算时间比例,如 95%~99%。
    PP
  2. 串行尾巴每轮不可避免的单点开销,不随卡数减少。
    1−P1 - P
  3. 并行部分摊薄可并行计算被 N 张卡均摊后的耗时。
    PN\frac{P}{N}
  4. 加速比与上限N 趋于无穷时,S 趋于 1/(1-P),卡再多收益也封顶。
    S(N)=1(1−P)+PNS(N) = \frac{1}{(1-P) + \frac{P}{N}}

AI 应用场景

大语言模型预训练

阶段:分布式训练与算力扩展

解决的问题:单卡训练 70B 参数模型需要数十年;数据并行让 N 张卡并行吃下不同数据,配合 ZeRO 把参数与优化器状态分片存储,将训练时间压缩到周级。

具体案例:某团队用 512 张 A100 训练 70B 大模型:ZeRO-3 负责“把菜谱拆成 512 本分册”,数据并行负责让每张卡处理不同的数据批次,每步等效 batch 达 2048,AllReduce 由 NCCL 在 NVLink 拓扑上完成。

对比学习与超大 batch 训练

阶段:模型训练与超参调优

解决的问题:SimCLR 等对比学习需要 4096 以上的超大 batch,单卡显存远不够;数据并行天然把 batch 切到各卡,再通过 AllReduce 保持梯度一致,就像“各家分店同时接待大量客人再统一对表”。

具体案例:用 128 张 GPU 训练 ResNet-50 对比学习模型,每卡 batch = 32,全局 batch = 4096,学习率按线性缩放规则从 0.06 提高到 1.92(η × 128),最终准确率与单卡小 batch 训练持平。

JAX 科学计算加速

阶段:模型推理/训练加速

解决的问题:蛋白质折叠等科学计算任务输入尺寸大、内存需求高,单机难以完成;JAX 的 pmap 让研究者在 TPU/GPU 集群上以 SPMD 方式并行,只写一份函数即可分散到多核。

具体案例:研究小组用 jax.pmap 包装折叠函数,将蛋白质序列按 batch 分片到 8 个 TPU 核,每个核计算一部分结构,反向时用 pmean 同步每条序列的梯度并统一更新参数。

Python 演示

python50 行
# -*- coding: utf-8 -*-"""数据并行演示: 用 JAX pmap 在多个设备上训练一个小型线性模型."""from functools import partialimport jaximport jax.numpy as jnpfrom jax import random  def build_data(key, n_samples=256, n_features=8):    # 请保证 n_samples 能被设备数整除    key_x, key_y = random.split(key)    x = random.normal(key_x, (n_samples, n_features))    true_w = jnp.linspace(-1.0, 1.0, n_features)    y = x @ true_w + 0.05 * random.normal(key_y, (n_samples,))    return x, y  def compute_loss(w, x, y):    pred = x @ w    return jnp.mean((pred - y) ** 2)  @partial(jax.pmap, axis_name='devices')def train_step(w, x, y):    # 每个设备副本在自己的数据分片上计算本地梯度    g = jax.grad(compute_loss)(w, x, y)    # pmean 触发跨设备 AllReduce: N 个本地梯度求平均并广播回所有副本    g = jax.lax.pmean(g, axis_name='devices')    return w - 0.05 * g  def main():    n_devices = jax.local_device_count()    print('可用设备数:', n_devices)    x, y = build_data(random.PRNGKey(0))    # 数据分片: 把批次按设备数均分, 首维变成设备维度    x = x.reshape(n_devices, -1, x.shape[1])    y = y.reshape(n_devices, -1)    w = jnp.zeros(x.shape[2])     for epoch in range(100):        w = train_step(w, x, y)        if epoch % 20 == 0:            # 所有副本完全同步, 取任一设备的参数即可            loss_val = compute_loss(w[0], x.reshape(-1, x.shape[2]), y.reshape(-1))            print('epoch {:3d}, loss = {:.4f}'.format(epoch, loss_val))  if __name__ == '__main__':    main()
目的:演示 JAX 数据并行的完整闭环——pmap 把一份训练函数复制到多设备,数据按首维分片,pmean 完成跨设备梯度 AllReduce。
  1. @partial(jax.pmap, axis_name='devices')把 train_step 复制到所有设备并行执行,axis_name 是 pmean 归约时使用的“暗号”。
  2. x.reshape(n_devices, -1, x.shape[1])数据分片:把总批次按设备数均分,首维变成设备维度,供 pmap 切分。
  3. g = jax.grad(compute_loss)(w, x, y)每张卡在自己的数据分片上算出本地梯度,对应“分店意见”。
  4. g = jax.lax.pmean(g, axis_name='devices')跨设备 AllReduce:把所有本地梯度求平均并广播回每个副本。
  5. w = train_step(w, x, y)每轮训练后拿到的是所有副本同步一致的新参数。
  6. loss_val = compute_loss(w[0], ...)副本完全同步,取任一设备的参数即可评估全局损失。

常见误区

误区数据并行就是把同一份训练代码多开几个进程跑,模型和算法都不用改,卡越多训练一定越快。

正解训练确实不用改模型结构,但“卡越多越快”只在算力远大于通信时成立。每轮迭代结束都要对全部梯度做一次 AllReduce,通信量和梯度大小成正比。当单卡 batch 很小、梯度很大时,通信时间会吃掉并行收益,加速比被 Amdahl 定律封顶。工程上要用梯度累积、梯度压缩、更快的互联来提升计算/通信比。

为何常见:只盯着“前向 + 反向”被 N 张卡均摊,下意识忽略了每轮结束后那条必须在所有卡之间流动的梯度洪流。

误区在 JAX 里只要用 jax.pmap 包一下训练函数,各设备的梯度就会被自动平均同步,不需要写任何额外代码。

正解pmap 只负责把函数复制到设备、把带首维的输入切分、把不带首维的输入广播。要对跨设备的梯度做归约,必须在函数体内显式写 jax.lax.pmean(g, axis_name='devices');否则每张卡都按自己的本地梯度更新广播参数,副本会立刻失步。对 pmap 后的函数整体求导时虽然会自动插入归约,但显式 pmean 才是清晰可控的写法。

为何常见:pmap 的抽象太“自动化”,包装了 SPMD 的复制与分片,让人误以为反向的跨卡同步也一并打包好了。

误区显存不够就用梯度累积,把 8 个微批次梯度加起来,效果和 8 倍大 batch 完全一样,可以放心使用。

正解对以线性层和 LayerNorm 为主的 Transformer 类模型,梯度累积与真实大 batch 在数值上基本等价;但对含 BatchNorm 的模型,BN 的均值/方差仍按每个微批次独立统计,累积 8 步会得到有偏的统计量,必须改用同步 BN。此外,等效 batch 变大后学习率也要相应放大,否则收敛速度变慢。

为何常见:从梯度求和公式看两者确实相等,容易忽略归一化层的统计量差异和超大 batch 下的学习率缩放需求。

误区模型太大单卡放不下?那就多加几张卡做数据并行,反正每张卡只存一份模型,卡多了自然就装下了。

正解数据并行要求每张卡都保存一份完整模型副本,单卡显存占用 = 参数 + 梯度 + 优化器状态 + 激活值,这个值不会随卡数下降。参数超显存时必须用模型并行把计算切到不同设备,或用 ZeRO 把三块状态分片存储。数据并行从来不是显存扩容方案,而是吞吐扩展方案。

为何常见:“多卡 = 更多显存”的印象根深蒂固,忽略了数据并行“每副本完整”这一前提。

误区异步训练不收敛、行为不可复现,早就被淘汰了,工业界现在只用同步训练。

正解同步训练的收敛保证来自“每步都用最新平均梯度”,但它受最慢设备拖累,且掉线一台就要整体阻塞。异步训练收敛可能震荡,但在通信带宽受限、设备异构、在线学习场景中吞吐优势明显。实际系统常以同步为主干,结合梯度累积降低同步频率;异步更新仍是推荐系统在线训练和部分联邦学习的标配。

为何常见:教材普遍论证同步收敛更稳,读者容易把“更稳”演绎成“唯一正确”。

面试题

Q1. 数据并行中“数据分片”指的是什么?

单选题 · easy

  • A.把模型参数按层拆给不同设备
  • B.把训练数据按批次拆给多个设备副本← 正确答案
  • C.把优化器状态拆分到多张卡上
  • D.把前向激活值缓存到本地硬盘

答案:B。数据并行的本质是“模型复制、数据切分”:每个设备持有完整模型,只是各自处理不同的数据批次。A 是模型并行,C 是 ZeRO 的做法,D 是激活值相关优化,都不属于数据分片。

Q2. 关于 AllReduce 操作,下列说法正确的是?

单选题 · easy

  • A.每个设备只得到其他设备的梯度之和
  • B.所有设备得到完全相同的归约结果← 正确答案
  • C.只有主设备拿到最终的归约结果
  • D.每个设备各自返回自己的局部梯度

答案:B。AllReduce = All + Reduce,先归约(求和/平均)再把结果广播给所有节点。若只有主设备拿到结果是 Reduce + Broadcast 而非 All;若各自返回局部梯度则没有归约。

Q3. Ring-AllReduce 相对集中式参数服务器的主要优势是?

单选题 · mid

  • A.完全不需要任何跨设备网络通信
  • B.通信量不随设备数膨胀且无单点瓶颈← 正确答案
  • C.只适用于单机多卡的小规模场景
  • D.能显著提高梯度计算的数值精度

答案:B。Ring 把通信均匀分摊到相邻节点,每个节点通信量约为 2D,不存在集中收发导致的主节点瓶颈,因此容易扩展到千卡。A 显然错误,C 错误(多机同样适用),D 与数值精度无关。

Q4. 在 JAX 中,pmap 训练函数内跨设备求平均梯度应使用?

单选题 · easy

  • A.jax.lax.pmean 求平均← 正确答案
  • B.jax.numpy.mean 直接求平均
  • C.jax.vmap 自动完成向量化
  • D.jax.random.split 拆分数据

答案:A。jax.lax.pmean 配合 pmap 的 axis_name 才会触发跨设备 AllReduce;jnp.mean 只在本设备内求均值;vmap 是向量化映射;random.split 是密钥拆分,均无跨卡通信。

Q5. 梯度累积的主要作用不包括?

单选题 · mid

  • A.减少跨设备 AllReduce 通信频率
  • B.在不增加峰值显存时扩大等效批次
  • C.让单卡能够容纳更大的模型参数← 正确答案
  • D.用多个微批次模拟真正的大批次

答案:C。梯度累积只改变训练批次的节奏,参数本身仍要在每张卡上存一份完整副本,不能减小参数显存。降低激活峰值显存、模拟大 batch、减少 AllReduce 频率都是它的真实收益。

Q6. 数据并行下,单卡显存占用随设备数 N 的变化是?

单选题 · hard

  • A.随设备数 N 增加线性下降为原来的 1/N
  • B.与 N 无关,每卡仍需完整的参数/梯度/优化器状态← 正确答案
  • C.随 N 线性增加,因为每增加一卡就多一份副本
  • D.仅由训练数据集的大小决定,与模型无关

答案:B。数据并行每张卡都要存一份完整模型副本及其梯度与优化器状态,N 只影响“总显存”翻倍,不减少单卡需求。要让单卡负担随 N 下降,需 ZeRO 分片或模型并行。

Q7. 设可并行比例 P = 0.9,根据 Amdahl 定律,加速比上限约为?

单选题 · hard

  • A.加速比上限约为 90 倍
  • B.加速比上限约为 10 倍← 正确答案
  • C.加速比上限约为 9 倍
  • D.加速比上限可无限接近 1000 倍

答案:B。Amdahl 定律 S = 1 / ((1-P) + P/N),N 趋于无穷时上限为 1/(1-P) = 1/0.1 = 10 倍。A 直接把 P 当倍数,C 未取倒数,D 忽略了串行尾巴的存在。

Q8. 模型参数超出单卡显存时,可行方案有?

多选题 · mid

  • A.模型并行:按层把模型切给多卡← 正确答案
  • B.ZeRO:分片存储参数与优化器状态← 正确答案
  • C.增加数据并行卡数降低单卡显存
  • D.梯度累积降低前向激活值显存

答案:AB。参数放不下是“存储”问题,模型并行和 ZeRO 直接改变参数的存放方式。C 是常见误区:数据并行每卡仍存完整副本;D 只降激活值缓存,不降参数显存。

Q9. 关于同步训练与异步训练,正确的有?

多选题 · mid

  • A.同步训练受最慢设备的拖累← 正确答案
  • B.异步训练会遇到陈旧梯度问题← 正确答案
  • C.同步训练一定收敛得比异步快
  • D.异步训练必须依赖参数服务器

答案:AB。A 即木桶效应;B 是异步的核心风险。C 过于绝对:同步每步质量高但整体速率可能被拖累,异步单位时间步数多,收敛快慢取决于问题;D 错误,异步也可用 AllReduce 类通信,不由结构强制。

Q10. 用梯度累积等效模拟大 batch 训练时,合理做法包括?

多选题 · extreme

  • A.学习率按累积倍数线性放大← 正确答案
  • B.BatchNorm 模型改用同步 BN← 正确答案
  • C.与真实大批次完全无条件等价
  • D.本地累积 K 步后做一次 AllReduce← 正确答案

答案:ABD。等效大 batch 后学习率通常按线性缩放规则放大(A);BN 统计量需跨设备同步才与大 batch 等价(B);C 过于绝对,归一化与学习率都需调整;D 正是梯度累积的节奏定义。