想象一家叫「梯度坊」的餐厅,主厨老李手握独家菜谱,每晚要出几百道菜。生意越来越火爆,一个人实在忙不过来。老板于是多请了几位厨师,人手一份菜谱复印件,每人负责一批不同的顾客订单。每位厨师照着菜谱做完菜,都会默默记下心得——这道菜盐多了,那道火候大了。打烊后,大家围坐一桌,把各自的心得摊开、取平均值,形成一份「集体改进方案」;第二天所有人都按这份统一方案微调菜谱。就这样,靠「分订单、记心得、汇总平均、同步改进」四步循环,餐厅的产出直接翻了几倍。这正是多卡训练里数据并行策略的缩影:每张 GPU 卡都保存一份完整模型,各自吃下一批数据算出改进建议(梯度),再通过梯度同步把所有人的建议平均,最后一起更新模型参数。JAX 把从「分数据」到「同步梯度」的整套流程封装成了简洁的并行原语,让普通工程师也能轻松指挥一支「后厨团队」。这篇文章,我们就跟着梯度坊的厨师们,把每一步拆开看明白。

岗位关联

数据并行是分布式训练里最常用、收益最直接的加速策略,掌握它,你就能在大模型和长周期训练场景中把硬件算力真正变成训练吞吐。

目录
  1. 可视化演示
  2. 数据并行原理
  3. pmap 分片映射
  4. 梯度 all-reduce
  5. 批大小扩展
  6. 性能优化
  7. 实战要点
  8. AI 应用场景
  9. Python 演示
  10. 常见误区
  11. 面试题

可视化演示

动态演示JAX数据并行的核心机制,通过全局/局部/动态/静态四种模式展示关键概念与交互流程。

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

JAX数据并行:数据并行 数据并行的核心思想与工作原理 梯度同步机制:AllReduc 核心机制可视化 JAX数据并行整体架构与关键组件

数据并行原理

单卡训练像是主厨老李一个人扛下所有订单:模型参数是那本菜谱,训练数据是一张张订单,一次「前向传播 + 反向传播」就相当于按菜谱做一道菜、再根据成品与期望的差距写下心得。订单爆炸之后,老李就算三头六臂也忙不过来——算力不够,显存也装不下那么多菜。梯度坊的解法很朴素:多请几位厨师,人手一份菜谱复印件。

既然人手一本菜谱,最自然的协作方式就是把「数据」这个维度拆开。整批训练数据按厨师人数切成 N 份,第 i 份订单交给第 i 位厨师;每位厨师从完全相同的菜谱出发,分别处理自己那份订单,互不干扰、也无需交流。这就是数据并行最核心的直觉——模型的「容器」只有一个,但副本有 N 份。

落到深度学习语言里:每台设备上都保存一份完整的模型参数副本(菜谱复印件),训练数据被切成 N 个分片,每台设备在自己的分片上完成前向与反向传播,得到局部梯度。由于所有设备起始参数一致,N 个局部梯度的平均,在数学上等价于在一个 N 倍大的合并批次上算出的梯度。于是所有设备用同一个平均梯度去更新各自的参数副本,N 份模型便始终步调一致。

所以,数据并行的灵魂从来不是「把模型复制几份」这么简单,而是「从同一份菜谱出发、按集体心得同步改进」这套纪律。任何一台设备擅自改动菜谱,N 份菜谱就会立刻分道扬镳——这份对「同步」的执念,会贯穿后面每一个环节。

θt+1=θt−η×1N∑i=1Ngi(θt,Bi)\displaystyle \theta_{t+1} = \theta_t - \eta \times \frac{1}{N} \sum_{i=1}^{N} g_i(\theta_t, \mathcal{B}_i)
整张菜谱被 N 位厨师分别试做,大家把心得取平均后,所有人都按同一份集体改进方案调整菜谱。
  1. 当前参数 θ_t第 t 步训练时所有设备共享的模型参数,相当于此刻菜谱的完整内容。
    θt\theta_t
  2. 数据分片 B_i第 i 台设备分到的训练批次,即每位厨师手里的订单。
    Bi\mathcal{B}_i
  3. 局部梯度 g_i第 i 台设备在自己分片上反向传播得到的梯度,即厨师个人的改进建议。
    gi(θt,Bi)g_i(\theta_t, \mathcal{B}_i)
  4. 梯度平均把所有设备的局部梯度相加再除以设备数 N,得到集体改进方案。
    1N∑i=1Ngi(θt,Bi)\frac{1}{N}\sum_{i=1}^{N} g_i(\theta_t, \mathcal{B}_i)
  5. 同步更新所有设备按同一个平均梯度和学习率 η 更新参数,保证副本永远一致。
    θt+1=θt−η×gˉ\theta_{t+1} = \theta_t - \eta \times \bar{g}

pmap 分片映射

分工方案定了,下一个问题很实际:怎么把订单高效地送到每位厨师手上,而不是由老李亲自挨个交代?JAX 里的 pmap 就是那块「派单板」。它把输入数据沿第一个维度自动切成若干份,并把第 i 份送到第 i 台设备的工位上——全程不需要你手动搬运任何数组。

pmap 的全称是 parallel map,意为「并行映射」。普通 map 是 Python 里把一个函数作用到列表的每个元素,pmap 则把一个函数同时作用到多台设备上:被 pmap 包装的函数会在每台设备上各执行一份,输入张量的第一个维度自动变成「设备轴」,像一张大订单被均匀地分派到每个工位。

具体怎么写?pmap(f)(x) 中,x 的形状是 (N, B, ……),JAX 会把 x[i] 送到第 i 台设备去执行 f,返回的结果同样带一个长度为 N 的设备轴。若函数内部还想跨设备通信——比如后面要做的梯度平均——就得给这个轴起个名字:pmap(f, axis_name='devices')。axis_name 相当于给这条「后厨交流通道」贴上标签,之后 pmean 等集体算子都靠它来定位要找哪些设备开会。

有了派单板,厨师们只管专注做菜,不用操心订单如何送达。但请注意:派单只是把活分下去了,「打烊后怎么交流心得」还没有着落——这就要引出数据并行里最能定成败的角色:梯度归约。

yi=f(xi),i=0,1,…,N−1\displaystyle y_i = f(x_i), \quad i = 0, 1, \ldots, N-1
派单板把一份大订单切成 N 份,第 i 份送到第 i 位厨师,每位厨师用同样的工序做出第 i 份成品。
  1. 并行函数 f被 pmap 包装的训练函数,在每个设备上各执行一份。
    ff
  2. 输入分片 x_i输入张量沿设备轴切出的第 i 份数据。
    xix_i
  3. 设备编号 ii 从 0 到 N-1,对应第 i 台设备。
    i=0,1,…,N−1i = 0, 1, \ldots, N-1
  4. 并行输出 y_i第 i 台设备对 x_i 的计算结果,输出张量同样带设备轴。
    yiy_i

梯度 all-reduce

打烊后,四位厨师各自翻开心得本:编号 1 觉得盐放多了,编号 2 觉得火候偏小,编号 3 对甜度满意,编号 4 想缩短炖煮时间。每个人的建议都只来自自己做的那批订单,如果各改各的菜谱,明天这间餐厅就会出现四本互相矛盾的菜谱。唯一的出路是围坐一堂,把心得当场汇总成一份「集体改进方案」。

这个「围坐汇总」的机制,在分布式系统里叫做 all-reduce(全局归约)。所有设备把各自的局部梯度送入通信网络,先逐元素相加,再除以设备数 N 得到全局平均梯度,最后把这个平均梯度广播回每一台设备。巧合的是,因为每台设备起始参数相同,这个平均梯度恰好等价于在合并的大批次上计算出的梯度——数据并行的有效性正是建立在这条数学性质上。

为什么是「平均」而不是「相加」?如果只加不除,每次参数更新都相当于把步长放大了 N 倍,模型一步就会「飞」出正常区域。在 JAX 里,这一切只需一行:jax.lax.pmean(grads, axis_name='devices'),放在 pmap 函数内部,pmean 就会沿着名为 devices 的轴执行「求和再平均」并广播结果。

交流会带来收获,也带来成本:心得在厨师之间传递需要时间,设备越多、梯度越大,开会越久。数据并行的加速,本质上就是一场「做菜速度」与「开会时间」的赛跑——这为后面的性能优化埋下了伏笔。

gˉ=1N∑i=1Ngi\displaystyle \bar{g} = \frac{1}{N} \sum_{i=1}^{N} g_i
N 位厨师的个人心得加起来,除以人数,得到每个人都能接受的「平均心得」,再人手一份。
  1. 局部梯度 g_i每台设备在自己数据分片上反向传播算出的梯度。
    gig_i
  2. 跨设备求和把 N 台设备的梯度逐元素相加,这一步需要设备间通信。
    ∑i=1Ngi\sum_{i=1}^{N} g_i
  3. 求平均求和后除以设备数 N,得到全局平均梯度。
    1N∑i=1Ngi\frac{1}{N}\sum_{i=1}^{N} g_i
  4. 广播回各设备所有设备拿到同一个平均梯度,才能保证参数一致。
    gˉ\bar{g}

批大小扩展

协作最直接的红利是「吞吐量」:原来一位厨师一次只能处理 B 份订单,现在 N 位厨师同时开工,一轮就能处理 N×B 份。训练里给这个数值起了个名字——有效批大小:它衡量「所有设备合起来,一步到底看了多少样本」,是单卡视角永远看不到的全局指标。

但订单量变大,改菜谱的节奏也得变。想想看:四位厨师的心得来自 N 倍多的样本,比单人的心得更可靠、噪声更小——既然如此,改动菜谱的步子理应迈得更大一些,否则就会白白浪费这么可靠的信息,收敛变慢。

在深度学习里,这条经验被总结为「线性缩放规则」:保持单设备批大小不变、把设备数扩到 N 倍时,学习率也近似乘以 N。大 batch 的梯度方差更小、方向更稳,可以承受更大的更新步长。当然,线性缩放只是经验起点,真正落地时往往还要配学习率 warmup,并警惕大 batch 对 BatchNorm 等组件统计行为的影响。

天下没有免费的午餐:有效批大小翻倍意味着显存占用也近似翻倍。模型本身就很大时,单设备 batch 被迫调小,通信占比反而上升——批大小扩展,本质是一场在「算力、显存、通信」之间的三方谈判。

Beff=N×Bdevice,ηeff=ηbase×N\displaystyle B_{\text{eff}} = N \times B_{\text{device}}, \qquad \eta_{\text{eff}} = \eta_{\text{base}} \times N
N 位厨师同时开工,每步能试做的菜变成 N 倍;样本多了、心得更可靠,就可以更大胆地调整菜谱。
  1. 单设备批大小 B_device每台设备一次训练步处理的数据条数。
    BdeviceB_{\text{device}}
  2. 设备数 N参与数据并行的设备总数。
    NN
  3. 有效批大小 B_eff所有设备合计一步处理的样本数。
    Beff=N×BdeviceB_{\text{eff}} = N \times B_{\text{device}}
  4. 学习率线性缩放有效批大小扩 N 倍时,学习率近似乘以 N。
    ηeff=ηbase×N\eta_{\text{eff}} = \eta_{\text{base}} \times N

性能优化

会议的成本有多高?假设做一道菜 10 分钟、开会 10 分钟,那么两位厨师合作并不会更快——总时间还是 20 分钟。数据并行正是如此:设备越多,单步计算时间越短,但 all-reduce 的通信与同步等待时间随之变长。一步训练的总耗时,可以用一个朴素的公式概括:计算时间 + 通信时间。

聪明的后厨会想各种办法压缩会议成本。第一招,攒着心得再开会:本地累积多步梯度后再做一次全局同步,这叫「梯度累积」,能大幅减少通信次数,代价是参数更新有延迟。第二招,发言只说重点:把梯度量化压缩后再上网络,这叫「通信压缩」,用一点点精度损失换数倍的传输量下降。

第三招最妙:边做菜边开会。串行流程是「全部做完→再开会」,而通信与计算重叠技术把反向传播和 all-reduce 在时间上交错开——计算第 i 层的梯度时,第 i-1 层的梯度已经在网络上传输了。JAX 中可以通过 pmean 的异步调度机制或手动分块通信实现这种流水线,让通信时间「藏」进计算时间里。

优化的本质,是把「串行的协作」改造成「并行的流水线」。值得一提的是,通信与计算的相对占比高度依赖模型大小和 batch 大小:小模型配小 batch 时,通信往往成为主角——很多「8 卡还不如 1 卡快」的诡异事故,幕后黑手正是它。

Ttotal=Tcompute+Tcomm\displaystyle T_{\text{total}} = T_{\text{compute}} + T_{\text{comm}}
做菜和开会的时间相加才是真正花掉的时间;如果能边做边开,开会的时间就被「隐藏」了。
  1. 计算时间 T_compute前向与反向传播消耗的纯算力时间。
    TcomputeT_{\text{compute}}
  2. 通信时间 T_commall-reduce 同步与等待占用的时间。
    TcommT_{\text{comm}}
  3. 总时间 T_total一步训练的真实总耗时。
    Ttotal=Tcompute+TcommT_{\text{total}} = T_{\text{compute}} + T_{\text{comm}}
  4. 重叠优化目标让通信藏进计算里,使通信时间不再额外计时。
    Ttotal≈TcomputeT_{\text{total}} \approx T_{\text{compute}}

实战要点

理论知识讲完,回到现实:梯度坊的老板要落地这套方案,第一步是资源盘点。确认手上有几张卡、每张卡的显存能容纳多大的 batch,由此算出全局批大小,再按线性缩放规则设定初始学习率。设备数和 batch 的匹配,直接决定了通信占比和加速比的天花板。

第二步是验证同步。数据并行最怕「假同步」——表面上一堆卡跑得欢,实际上参数早就分叉了。一个有效的体检方法:每训练几步,把各设备的参数或 loss 取回来对比。在 JAX 里,pmap 返回的结果带设备轴,用 jax.device_get 取回后逐个设备检查,一套简单的断言就能防住大问题。

第三步是监控与调优。用 JAX profiler 或 nvidia-smi 观察各卡的利用率和通信占比;通信偏高时,梯度累积、增大单设备 batch、换用 NVLink/InfiniBand 等高带宽互联都是可选手段。生产环境还要为设备故障留后路:pmap 要求所有设备同时就绪,一台掉线全网卡死,因此检查点与容错机制必不可少。

最后记住一条心法:数据并行不是一个「开了 pmap 就万事大吉」的开关,而是一套围绕「数据怎么分、心得怎么聚、节奏怎么调」的工程体系。把每个环节都想到位,多卡训练才能跑出接近线性的加速——梯度坊的厨师们,也才能真的让餐厅从「翻几倍」走向「翻几十倍」。

TN=T1N+Tcomm\displaystyle T_N = \frac{T_1}{N} + T_{\text{comm}}
加速比的天花板由「做菜的时长除以人数 + 开会的时长」决定,人越多,开会越可能成为主角。
  1. 单卡步耗时 T_1仅用一台设备训练一步的时间。
    T1T_1
  2. 理想并行耗时N 卡并行后每步计算的理想时间。
    T1N\frac{T_1}{N}
  3. 通信开销 T_comm同步梯度引入的额外时间。
    TcommT_{\text{comm}}
  4. 实际步耗时 T_NN 卡数据并行一步的真实时间。
    TN=T1N+TcommT_N = \frac{T_1}{N} + T_{\text{comm}}

AI 应用场景

多卡图像分类训练

阶段:模型训练

解决的问题:单卡训练 ImageNet 级别数据集需要数周,算力和显存双双触顶。传统做法是一张卡反复洗数据,训练周期长、GPU 利用率低;数据并行把数据按卡切分,让每张卡同时开工,训练吞吐近似随卡数线性增长。

具体案例:某自动驾驶团队用 JAX pmap 在 8 张 A100 上训练 ResNet-50,每卡 batch 设为 128,全局 batch 达到 1024。按线性缩放规则把学习率从 0.1 提到 0.8,并配合 5 个 epoch 的 warmup,最终精度不掉,训练时间从三周压缩到四天。

推荐系统 CTR 模型迭代

阶段:模型训练与迭代

解决的问题:推荐系统训练样本每天数十亿,模型需要频繁全量更新。单卡训练一轮要十几个小时,等模型上线时用户偏好早已变化;数据并行让模型每一步都能看到更大规模的样本,缩短迭代周期。

具体案例:某电商团队用 JAX 在 16 卡上对 CTR 预估模型做数据并行,单卡 batch 4096,全局 batch 65536,梯度经 pmean 全局平均后同步更新,训练吞吐提升约 14 倍,每日模型刷新频率从 1 次提升到 4 次。

大模型微调提速

阶段:模型微调

解决的问题:微调阶段 batch 太小会导致 loss 剧烈抖动、收敛不稳。数据并行放大有效 batch 后,梯度估计更平滑,微调过程更稳定;同时多卡并行也直接缩短了微调等待时间。

具体案例:某 NLP 团队用 pmap 对 1.8B 参数的对话模型做指令微调,4 卡数据并行配合梯度累积,把有效 batch 从 8 扩到 64,loss 曲线明显更平稳,整体微调耗时缩短 3 倍。

Python 演示

python63 行
import jaximport jax.numpy as jnpfrom jax import random, pmap  def init_params(key):    """初始化一个简单的两层 MLP 参数"""    k1, k2 = random.split(key)    w1 = random.normal(k1, (784, 128)) * 0.01    b1 = jnp.zeros(128)    w2 = random.normal(k2, (128, 10)) * 0.01    b2 = jnp.zeros(10)    return (w1, b1, w2, b2)  def forward(params, x):    """前向传播: 两层 MLP + ReLU"""    w1, b1, w2, b2 = params    h = jnp.maximum(x @ w1 + b1, 0)    return h @ w2 + b2  def loss_fn(params, x, y):    """交叉熵损失"""    logits = forward(params, x)    log_probs = jax.nn.log_softmax(logits)    return -jnp.mean(jnp.sum(log_probs * y, axis=1))  @pmap(axis_name='devices')def train_step(params, x_batch, y_batch):    """单步数据并行: 算梯度 -> pmean 全局平均 -> 更新"""    grads = jax.grad(loss_fn)(params, x_batch, y_batch)    grads = jax.lax.pmean(grads, axis_name='devices')    return jax.tree_util.tree_map(        lambda p, g: p - 0.01 * g, params, grads    )  def main():    key = random.PRNGKey(0)    params = init_params(key)    n_devices = jax.device_count()    per_device_batch = 32    global_batch = n_devices * per_device_batch    print(f'设备数: {n_devices}, 全局批大小: {global_batch}')     x = random.normal(key, (global_batch, 784))    y = jax.nn.one_hot(random.randint(key, (global_batch,), 0, 10), 10)    x = x.reshape(n_devices, per_device_batch, 784)    y = y.reshape(n_devices, per_device_batch, 10)     for step_i in range(3):        params = train_step(params, x, y)        p0 = jax.tree_util.tree_map(            lambda p: jax.device_get(p)[0], params        )        loss = loss_fn(p0, jax.device_get(x)[0], jax.device_get(y)[0])        print(f'step {step_i}: loss = {loss:.4f}')  if __name__ == '__main__':    main()
目的:用 JAX pmap 在全部可用设备上跑一个完整的数据并行训练步——每卡算局部梯度、pmean 做 all-reduce 平均、再同步更新参数。
  1. @pmap(axis_name='devices')把 train_step 同时映射到所有设备,并给设备轴命名,以便 pmean 跨设备通信。
  2. grads = jax.grad(loss_fn)(params, x_batch, y_batch)每台设备在自己的数据分片上计算局部梯度,这就是「每位厨师记下的心得」。
  3. grads = jax.lax.pmean(grads, axis_name='devices')所有设备的梯度先相加再除以设备数,完成 all-reduce 平均并广播回各设备。
  4. jax.tree_util.tree_map(lambda p, g: p - 0.01 * g, params, grads)用平均梯度同步更新每一份参数副本,保证所有设备参数一致。
  5. x.reshape(n_devices, per_device_batch, 784)把全局批次按设备轴切分,pmap 会把第 i 块自动送到第 i 台设备。
  6. jax.device_get(p)[0]从带设备轴的结果中取回第 0 台设备的参数,用于验证 loss。

常见误区

误区数据并行不就是把模型在每张卡上复制一份,然后各跑各的吗?开头写几行 pmap,训练能跑起来就算成功。

正解复制模型只是「准备工作」,数据并行的灵魂是梯度同步。如果不做 all-reduce,每张卡各算各的梯度、各改各的参数,几轮之后 N 份模型就完全分叉,等于同时训练了 N 个互不相干的模型。就像四本菜谱各自改各自的,餐厅第二天直接乱套。

为何常见:单卡代码里根本没有「梯度同步」这一步,初学者很容易忽略它才是数据并行和单卡训练的本质区别。

误区反正加了卡就快,8 卡不够上 16 卡,钱能解决的事都不是事。

正解加速是有上限的。每加一台设备,计算时间变短,但 all-reduce 的通信与同步等待时间变长。当 batch 很小、通信占比很高时,甚至会出现「8 卡比 1 卡还慢」的反直觉现象。厨师多了是好,但开会时间也会淹没做菜时间。

为何常见:直觉上「人多好办事」,但忽略了协作需要额外成本,而这个成本在分布式系统里是网络传输和同步等待。

误区数据并行只是把数据拆开算,梯度平均之后就跟用大 batch 训练完全一样,学习率保持原样就行。

正解梯度平均在数学上确实等价于大 batch 梯度,但大 batch 改变了优化轨迹——梯度更平滑、噪声更小。按线性缩放规则,有效 batch 扩 N 倍时学习率也要近似乘 N,否则步子太小、收敛变慢;直接乘 N 又可能发散,所以还要配 warmup。

为何常见:大家只记住了「平均等价于合并」的结论,却忽略了 batch 大小对梯度统计特性的影响,把「等价」误当成「完全一样」。

误区all-reduce 嘛,就是把所有卡上的梯度汇聚到一起,求个总和发给每张卡。

正解梯度同步用的是 all-reduce 的 mean 语义,即求和后再除以设备数 N。如果只加不除,每次参数更新的步长都被放大 N 倍,模型一两步就会发散。想想看:四份心得相加后不除以 4,等于假设四个人有四倍的改动权,菜谱早就改飞了。

为何常见:all-reduce 名字里的「reduce」让人只想到求和,而「除以设备数求平均」这一步太容易被忽略。

误区只要写了 `@pmap`,数据怎么切、梯度怎么同步、结果怎么取回,JAX 全都包办了。

正解pmap 只负责「把函数映射到设备、按设备轴切分输入」,梯度是否做 pmean、batch 如何组织、评估时如何取回结果,都需要你自己设计和编写。pmap 是派单板,不是后厨总管。

为何常见:pmap 名字里带「自动映射」的味道,且常见的教程示例都只展示一行代码,容易让人误以为整条流水线都被封装好了。

面试题

Q1. 数据并行的核心思想是什么?

单选题 · easy

  • A.把模型切分到多台设备分头执行
  • B.完整模型各算数据分片梯度再聚合← 正确答案
  • C.多台设备轮流训练并接力传递
  • D.各设备独立训练最后投票择优

答案:B。数据并行不切模型而是切数据,每台设备保存完整模型副本,各自计算局部梯度后同步聚合。A 是模型并行思路,C 是流水线并行,D 是集成学习思路,都不属于数据并行。

Q2. JAX 中 pmap 的作用是什么?

单选题 · easy

  • A.将函数映射到多台设备并行执行← 正确答案
  • B.将全部数据压缩后送入单台设备
  • C.自动优化模型显存的占用分配
  • D.把模型代码编译为底层语言运行

答案:A。pmap 的核心作用是把函数 f 映射到所有设备上同时执行,输入按设备轴自动切分。B 说的是数据压缩,C 是显存优化,D 是编译,都与 pmap 的职责无关。

Q3. all-reduce 在梯度同步中完成的数学操作是?

单选题 · mid

  • A.所有梯度求平均后广播到各设备← 正确答案
  • B.所有梯度求最大值后广播到各设备
  • C.仅把梯度总和发送给主控设备
  • D.将所有梯度相乘后广播到各设备

答案:A。梯度同步使用 all-reduce 的 mean 语义:先求和再除以设备数,然后把平均梯度广播给所有设备。B 是 max 归约,C 是 reduce 到主设备,D 是乘积归约,都不对。

Q4. 8 台设备数据并行,每台设备 batch size = 64,全局有效 batch size 是?

单选题 · mid

  • A.全局批次大小仍保持为 64 条
  • B.全局批次大小扩大为 128 条
  • C.全局批次大小扩大为 256 条
  • D.全局批次大小扩大为 512 条← 正确答案

答案:D。有效全局 batch = 单卡 batch × 设备数 = 64 × 8 = 512。数据并行把数据维度拆分后,所有设备每一步合计看到的样本数就是各自 batch 之和。

Q5. 线性缩放规则建议:有效批大小扩大为原来的 N 倍时,学习率应如何调整?

单选题 · mid

  • A.学习率建议保持原有数值不变
  • B.学习率建议缩小为原来的 N 分之一
  • C.学习率近似扩大为原来的 N 倍← 正确答案
  • D.学习率扩大为原来的 N 的平方倍

答案:C。线性缩放规则指出,batch 扩 N 倍时学习率近似乘 N。保持不动会收敛变慢,除以 N 或乘 N² 都会破坏梯度方向与步长的匹配,导致欠调或发散。

Q6. 关于 pmap 中的 axis_name 参数,下列说法正确的是?

单选题 · hard

  • A.指定数据切分时沿哪一条轴进行
  • B.为并行轴命名供集体算子引用← 正确答案
  • C.设置设备间通信所用网卡名称
  • D.指定每台设备的 CUDA 核心数量

答案:B。axis_name 为 pmap 映射出的设备轴命名,pmean 等集体算子通过它定位通信范围。A 混淆了设备轴与数据张量轴,C、D 是与并行轴无关的干扰项。

Q7. 在 JAX 中,pmean 与 pmap 配合的正确用法是?

单选题 · hard

  • A.在 pmap 函数内用 pmean 求全局平均← 正确答案
  • B.在 pmap 函数外调用即可跨设备通信
  • C.用 pmean 替代 pmap 完成数据并行映射
  • D.pmean 专门合并模型参数而不是梯度

答案:A。pmean 必须在 pmap 函数内部调用,并指定与 pmap 一致的 axis_name,才能跨设备归约。函数外的调用无法触发设备间通信,pmean 也不能替代 pmap 或只用于合并参数。

Q8. 下列哪些做法可以降低数据并行中的通信开销?(多选)

多选题 · mid

  • A.压缩梯度后传输减少通信数据量← 正确答案
  • B.将通信与计算完全串行执行流程
  • C.让梯度通信与反向计算重叠运行← 正确答案
  • D.各设备独立更新参数并不同步

答案:AC。梯度压缩减小传输量、通信与计算重叠隐藏延迟,都是经典优化手段。串行执行会让通信时间原封不动地累加,独立更新参数则直接违背数据并行的同步语义。

Q9. 关于数据并行中的同步更新,以下说法正确的有?(多选)

多选题 · extreme

  • A.各设备在梯度归约完成后统一更新← 正确答案
  • B.同步更新允许各设备参数逐渐分叉
  • C.各设备使用不同初始参数也能收敛
  • D.同步更新等价于训练更大的有效批次← 正确答案

答案:AD。同步更新要求所有设备在 all-reduce 完成后统一更新参数,数学上等价于在 N 倍大的合并批次上训练。B 是异步更新才有的现象,C 中不同初始参数无法保证一致收敛,均错误。

Q10. 下列哪种场景最适合使用数据并行策略?

单选题 · easy

  • A.模型太大无法放入单张卡的显存
  • B.数据量巨大但模型可放入单张卡← 正确答案
  • C.网络带宽受限且通信延迟十分高
  • D.需要模型逐层串行推理的场景

答案:B。数据并行适合「数据大、模型放得下」的场景,通过切分数据扩展吞吐。模型太大放不下属于模型并行或分片范畴,网络受限时通信开销会抵消收益,逐层推理则更适合流水线并行。