假设你住的小区要办一场三百人的百家宴。每家厨房都不大,谁也没本事把三十道菜的食材一次全塞进自家冰箱。于是居委会立下新规矩:每家只负责寄存一小部分食材——你管葱姜蒜,我管鸡鸭鱼,他管瓜果蔬菜。等到开席,大家把各自那部分端到长桌上,拼出完整的一道菜;这道菜用完,剩下的料立刻拆回各家冰箱,腾出桌面给下一道菜。这个“分着存、按需凑、用完散”的组织方式,就是今天的主角——FSDP(Fully Sharded Data Parallel,全分片数据并行)。它是 PyTorch 官方对 ZeRO-3 的实现,专门解决“模型太大、一块显卡装不下”的难题。本篇文章将带你认识参数如何被切开、前向反向如何自动聚合、混合精度如何配合,以及它与 DeepSpeed ZeRO 的异同,最后给出工程实践中的取舍建议。

岗位关联

掌握 FSDP,意味着你能在相同的 GPU 预算下训练更大的模型——这是大模型时代 AI 工程师最值钱的能力之一。

目录
  1. 可视化演示
  2. 核心思想
  3. 分片机制
  4. 前反流程
  5. 混合精度
  6. ZeRO对比
  7. 工程实践
  8. 适用场景
  9. AI 应用场景
  10. Python 演示
  11. 常见误区
  12. 面试题

可视化演示

动态演示FSDP全分片数据并行的核心机制,通过采集/分析/决策/反馈四种模式展示关键概念与交互流程。

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

FSDP全分片数据并行:FSDP FSDP的基本概念与ZeRO- 参数分片与前向反向自动聚合 核心机制可视化 FSDP全分片数据并行整体架构与关键组件

核心思想

想象小区要办一场三百人的百家宴,每家的厨房都只有巴掌大。如果沿用老办法——每家都独自准备一整桌菜,那么食材总量就得乘以户数,绝大多数人家连原料都堆不下。DDP(数据并行)正是这个老办法:每张 GPU 都复制一份完整模型。一个 7B 参数的模型,全精度参数就要 28GB,加上梯度和 Adam 优化器状态,总开销超过 100GB,任何单卡都装不下,这就是典型的“DDP 显存瓶颈”。

于是居委会想出新规矩:把整只鸡、整条鱼都拆开,每家只认领一小份食材存在自家冰箱里。N 户人家的冰箱加起来,正好装下全部三十道菜的原料。FSDP 的做法如出一辙:把模型参数均匀切成 N 份,每张卡只保存其中的 1/N,完成“参数全局分片”。

这样一来,每张卡需要常驻的参数显存就从 4Φ 降到了 4Φ/N。其中 Φ 是模型参数量(单位是“个”),4 是 FP32 格式每个参数占用的字节数,N 是参与训练的 GPU 数量。7B 模型用八张卡一摊,每卡只需背 3.5GB 的参数,显存压力瞬间卸掉大半。

食材是分出去了,可做菜时终归需要完整的鸡和鱼。怎样在开席的刹那把分散在各家的原料凑齐?这正是 FSDP 的第二招——分片机制。

Mparam=4ΦN\displaystyle M_{param} = \frac{4\Phi}{N}
Φ 为模型参数量,N 为 GPU 数,4 为每个 FP32 参数占用的字节数。N 张卡均摊后,每卡只负担原先的 1/N。
  1. 参数量 Φ模型里所有权重和偏置的总个数。7B 模型就是 70 亿个参数。
    Φ=7×109\Phi = 7 \times 10^9
  2. 字节数 4FP32 格式下每个参数占 4 字节内存。
    4 Bytes4\ \text{Bytes}
  3. 均分 N 份把总字节数除以 GPU 数量,得到每卡需保存的部分。
    4ΦN\frac{4\Phi}{N}

分片机制

好厨师切菜讲究“顺着纹理下刀”,把一条鱼片成薄片要沿着鱼肉的肌理走,不能乱剁。FSDP 切参数也一样,不是把内存地址拦腰砍一刀,而是“按层切”:最小的分片单元通常是一个 Transformer Block(多头注意力层加前馈层),每一层内部的参数张量沿第一个维度均匀切成 N 片,各自存在对应卡上。

这样切有两个好处。第一,前向和反向都是逐层推进的,计算到某一层时只需临时凑齐“当前层”的参数,不必把整个模型的数据一次性搬到某张卡上;第二,未计算到的层继续保持分片状态,大大降低了峰值显存。但这套方案也有代价——每次凑齐一层都要做一次 all-gather,也就是让所有卡把自己的那一片广播给其他人。

就像百家宴上各家把鱼片端到长桌,拼成一条完整的鱼,桌面只占“一条鱼”的地方。分片粒度直接决定了这桌菜怎么摆、邻居之间要跑多少趟,这就是分片粒度与通信开销的权衡。

Cgather=Φlayer×N−1N≈Φlayer\displaystyle C_{gather} = \Phi_{layer} \times \frac{N-1}{N} \approx \Phi_{layer}
问遍 N-1 个邻居凑齐一层参数,通信量接近该层参数量本身。这就是“分片省显存、聚合花带宽”的代价。
  1. 单层参数量 Φ_layer一个 Transformer Block 里的权重总个数,是分片和通信的基本单位。
    Φlayer\Phi_{layer}
  2. 缺的 N-1 份本卡只留了 1/N,需要从其余 N-1 张卡各取一份拼齐。
    N−1N\frac{N-1}{N}
  3. 近似相等GPU 数 N 越大,(N-1)/N 越接近 1,通信量就越接近整层参数大小。
    N−1N≈1\frac{N-1}{N} \approx 1

前反流程

百家宴的完整节奏是:开席前各家端菜上桌,吃完立刻撤桌腾地方,第二波菜再上来,散席后把剩菜打包分回各家。FSDP 的一次训练迭代也严格踩着这四拍:前向聚合参数 → 算完即释放 → 反向再聚参数 → 梯度归约回分。

前向计算第 i 层时,先执行 all-gather 把第 i 层的完整参数凑齐,随后计算这一层;该层前向一结束,完整参数副本立刻被丢弃,只保留本卡那 1/N 的分片。反向传播时同样需要再次聚合第 i 层参数——因为上一次的副本早已释放——求出该层梯度后执行 reduce-scatter:先把所有卡上的梯度求和归约,再按各家分片切回本卡存储,用于后续的优化器更新。

桌面永远只摆一道菜,其他食材都安心躺在各家冰箱——峰值显存被死死压在“单层参数加单层梯度”的水平,而不是整个模型。这套前反流程与内存生命周期,正是 FSDP 能在大模型训练中站稳脚跟的根本原因。

Cstep=Cfwd+Cbwd+Cgrad≈3Φ\displaystyle C_{step} = C_{fwd} + C_{bwd} + C_{grad} \approx 3\Phi
每步训练约产生 3 倍模型规模的通信量:前向一次聚合、反向一次聚合、梯度一次回分。这就是“全分片”在带宽上的真实账单。
  1. 前向聚合 C_fwd每个 Transformer 层在前向计算前都要 all-gather 一次该层参数,总量约等于一个模型体积。
    Cfwd≈ΦC_{fwd} \approx \Phi
  2. 反向聚合 C_bwd反向传播时重新聚合同一层参数,因为前向后已被释放,又是一次约 Φ 的通信。
    Cbwd≈ΦC_{bwd} \approx \Phi
  3. 梯度回分 C_grad反向算完梯度后做 reduce-scatter,把梯度归约并按分片存回各卡,同样约 Φ 的通信量。
    Cgrad≈ΦC_{grad} \approx \Phi

混合精度

好厨师不会把什么都塞进冰箱,也不会把什么都放在常温架上:贵的新鲜食材要冷藏,常用的大白菜放架子就好。FSDP 的后厨里也有两种食材:BF16/FP16 低精度副本负责前向和反向计算,省显存、跑得快;FP32 主权重负责优化器更新,保证数值精度不漂移。

主权重和 Adam 优化器状态(动量与方差)都以分片形式长期存储;低精度副本只在计算某一层时才临时凑齐,算完即弃。长期占用的是“分片后的主权重与优化器状态”,临时占用的是“单层低精度工作副本”。值得一提的是,BF16 的指数位宽与 FP32 相同,训练稳定性优于 FP16,这是它的精度收益所在。

冰箱放得下、架子也腾得开,加上分片存储让主权重和优化器状态不再奢侈,这便是百家宴后厨的从容。混合精度不是偷工减料,而是把“贵食材”和“常用菜”分门别类地安置。

MFSDP=4ΦN+8ΦN+2Φlayer\displaystyle M_{FSDP} = \frac{4\Phi}{N} + \frac{8\Phi}{N} + 2\Phi_{layer}
4Φ/N 是 FP32 主权重分片,8Φ/N 是 Adam 动量与方差分片,2Φ_layer 是当前层 BF16 工作副本。对比之下,DDP 单卡需要扛下完整的 16Φ。
  1. 主权重分片FP32 主权重每卡只存 1/N,长期驻留显存。
    4ΦN\frac{4\Phi}{N}
  2. 优化器状态分片Adam 的动量与方差各占 4Φ,同样全部均分到 N 张卡。
    8ΦN\frac{8\Phi}{N}
  3. 当前层工作副本BF16 每个参数占 2 字节,且只保留当前计算层,用完立即释放。
    2Φlayer2\Phi_{layer}

ZeRO对比

同一个百家宴想法,不同社区各有一本执行手册。DeepSpeed 的 ZeRO 把“分着存”分成三个阶段:ZeRO-1 只分片优化器状态,ZeRO-2 再分片梯度,ZeRO-3 连参数也全部按层分片。FSDP 本质上就是 PyTorch 官方实现的 ZeRO-3,两者算法高度一致。

两者的最大差异在“手感”。FSDP 使用 PyTorch 原生 API(torch.distributed.fsdp.FSDP),能直接与 torch 生态的 DCP 分布式检查点、torch.compile 等工具无缝衔接;DeepSpeed ZeRO 则提供了更细的调参旋钮,并附带推理引擎等周边能力。选型建议很直接:团队已在 PyTorch 生态就选 FSDP,深度绑定 DeepSpeed 基建就继续用 ZeRO。

菜谱可能不同,但“每家只做一部分菜”的精神是相通的。理解 ZeRO 三阶段的演进,就能明白 FSDP 在显存优化坐标系里的准确位置。

MDDP=4Φ+4Φ+8Φ=16Φ,MZeRO-3=16ΦN\displaystyle M_{DDP} = 4\Phi + 4\Phi + 8\Phi = 16\Phi,\qquad M_{ZeRO\text{-}3} = \frac{16\Phi}{N}
DDP 单卡要扛 16Φ 字节(参数 4Φ + 梯度 4Φ + Adam 状态 8Φ);ZeRO-3 把它均摊到 N 张卡,每卡只剩 16Φ/N。
  1. 参数与梯度DDP 每卡各存 4Φ,分别是全精度参数副本和梯度缓冲。
    4Φ+4Φ4\Phi + 4\Phi
  2. Adam 状态动量与方差两个状态合计 8Φ。
    8Φ8\Phi
  3. 总数均摊DDP 单卡总计 16Φ;FSDP 把这三项全部除以 N。
    16ΦN\frac{16\Phi}{N}

工程实践

菜谱再完美,掌勺的手艺决定成败。FSDP 最基础的用法是给模型套一层 FSDP(model),但更专业的做法是用 auto_wrap_policy 让框架自动扫描模型结构,把每个 Transformer Block 识别为分片边界,这就是“自动包装策略”。

分片粒度是个典型的工程权衡:wrap 单元越大(比如整个模型只包一层),通信次数越少,但峰值显存越高;wrap 单元越小(每个子层都单独 wrap),显存越省,但通信往返越频繁。工程上默认按 Transformer Block 分片。为了隐藏通信延迟,还可以开启 forward_prefetch(前向预取下一层参数)和 backward_prefetch(反向预取),让聚合与计算重叠起来;保存模型时则使用 FSDP 自带的分布式检查点(DCP),避免保存出 N 份零散文件。

大火快炒还是小火慢炖,勺子的翻动节奏决定了这道“大模型硬菜”能否顺利出锅。掌握了自动包装、分片单元和预取这三件套,你就能在显存与带宽之间找到自己的平衡点。

Mpeak=4Φlayer+12ΦN\displaystyle M_{peak} = 4\Phi_{layer} + \frac{12\Phi}{N}
峰值显存 = 当前层的 BF16 参数与梯度(4Φ_layer)+ 分片存储的主权重与优化器状态(12Φ/N)。分片单元越小,Φ_layer 越小,峰值越低,但通信也越密集。
  1. 当前层瞬态 4Φ_layerBF16 参数副本 2Φ_layer 加梯度缓冲 2Φ_layer,只活在计算当下。
    2Φlayer+2Φlayer2\Phi_{layer} + 2\Phi_{layer}
  2. 持久分片 12Φ/NFP32 主权重 4Φ/N 加 Adam 状态 8Φ/N,长期以分片形式驻留。
    4Φ+8ΦN\frac{4\Phi + 8\Phi}{N}

适用场景

不是所有宴席都适合百家宴。三五好友小聚,自家厨房足够,何必大费周章。那么什么样的训练任务需要 FSDP?答案很直接:当模型总开销 16Φ 字节超过单卡显存时,就该考虑分片扩展了。

FSDP 的主场是“大模型预训练”与“全参数微调”:7B 乃至上百 B 的模型,单卡放不下,多卡摊薄后每卡负担可控。它同样适用于显存受限环境——比如实验室只有几张 24GB 的 RTX 4090,想把 7B 模型微调起来;参数分片配合激活检查点,能把峰值压进预算。在多机扩展方面,FSDP 跨节点的通信模式与单机一致,吞吐随卡数近似线性增长,是大规模训练的标准底座。

百人宴席办百家宴,十人小灶自己掌勺。先算清楚这道菜多大,再决定用哪套方案——这正是 FSDP 最朴素也最重要的应用准则。

Nmin⁡=⌈16ΦMGPU⌉\displaystyle N_{\min} = \left\lceil \frac{16\Phi}{M_{GPU}} \right\rceil
所需 GPU 数下限 = 16Φ 字节总负担 ÷ 单卡显存(字节),向上取整。例如 7B 模型在 80GB 卡上:112GB ÷ 80GB = 1.4,向上取整为 2 张;实际还要留出激活值空间,工程上通常按 3~4 倍余量规划。
  1. 总负担 16Φ参数、梯度、优化器状态的完整显存开销。
    16Φ16\Phi
  2. 单卡显存 M_GPU一张 GPU 的可用显存字节数。
    MGPUM_{GPU}
  3. 向上取整GPU 数量是整数,算出来有余数必须进位。
    ⌈⋅⌉\left\lceil \cdot \right\rceil

AI 应用场景

大模型预训练

阶段:模型训练 / 分布式训练显存优化

解决的问题:7B 模型按 DDP 需要 112GB 以上显存(参数 + 梯度 + Adam 状态),单张 A100-80G 直接 OOM。传统做法是换更大显存或牺牲 batch size,成本高且效率低。FSDP 将这 112GB 摊到 8 张卡上,每卡仅 14GB,为激活值、通信缓冲留出充足空间。

具体案例:某团队在 8×A100-80G 上用 PyTorch 2.x + transformers 从零预训练 7B LLM。核心代码只需一行 model = FSDP(model, auto_wrap_policy=transformer_auto_wrap_policy),配合 BF16 混合精度,吞吐从 DDP 的 0(OOM)提升到每卡约 1800 tokens/s。这就像百家宴把 8 户人家的小冰箱合起来,装下了原本需要超大厨房才能容纳的食材。

大模型全参微调

阶段:模型微调 / 迁移学习

解决的问题:微调 Qwen-14B 时单卡放不下全量参数与梯度,很多团队被迫降级到 LoRA,牺牲了全参微调的精度上限。FSDP 让 14B 模型在 4×A100-80G 上完成全参微调,参数、梯度、优化器状态全部按层分片,各卡只背 1/4。

具体案例:某算法团队在 4×A100-80G 上对 14B 模型做领域微调,开启 FSDP 后配合 activation checkpointing,batch size 从 1 提升到 8,收敛轮次减少近一半。这相当于百家宴上各家分工备菜,开席时再拼桌,既保证了“菜品”完整,又让每个灶台都忙得过来。

显存受限的科研环境

阶段:模型训练 / 资源受限场景

解决的问题:实验室只有 4×RTX 4090(24GB 显存),却要微调 7B 对话模型。DDP 会直接 OOM;FSDP 把 112GB 总开销分到 4 卡,每卡 28GB,再配合激活检查点把峰值压进 24GB,让科研实验得以在小机器上跑起来。

具体案例:一名研究生用 4×RTX 4090 微调 7B 模型做指令跟随实验,采用 FSDP + BF16 + 激活检查点,训练吞吐约 500 tokens/s。虽然不快,但足够支撑小规模消融实验的迭代。这就像小厨房里的百家宴——每家分一点,拼一拼,照样能做出一桌像样的菜。

Python 演示

python45 行
"""MiniFSDP: 用 NumPy 模拟 FSDP 的分片、聚参、回分三步曲""" import numpy as np  class MiniFSDP:    """百家宴式参数管理: 每张卡只放参数的 1/world_size"""     def __init__(self, world_size, layer_sizes):        self.world_size = world_size        self.layer_sizes = layer_sizes        chunk_sizes = [s // world_size for s in layer_sizes]        # 每家一个小冰箱, 存放各层参数的 1/world_size        self.fridges = [[np.random.randn(c) * 0.01 for c in chunk_sizes]                        for _ in range(world_size)]     def gather_layer(self, idx):        """开席凑菜: 从所有卡凑齐第 idx 层完整参数"""        return np.concatenate([self.fridges[g][idx]                               for g in range(self.world_size)])     def scatter_grad(self, idx, grad):        """撤席分菜: 梯度按分片切回各卡, 并做一步 SGD"""        pieces = np.array_split(grad, self.world_size)        for g in range(self.world_size):            self.fridges[g][idx] -= 0.01 * pieces[g]     def peak_params(self):        """单卡峰值: 本卡分片 + 当前层完整副本"""        biggest = max(self.layer_sizes)        return biggest // self.world_size + biggest  def main():    layer_sizes = [4096, 4096, 4096, 11008, 4096]    for w in (1, 2, 4, 8):        model = MiniFSDP(world_size=w, layer_sizes=layer_sizes)        full = model.gather_layer(0)      # 前向: 凑齐第一层        grad = np.random.randn(full.size)  # 反向: 算出梯度        model.scatter_grad(0, grad)       # 回分: 梯度切回各家        print("world_size=%2d  peak_params=%6d" % (w, model.peak_params()))  if __name__ == "__main__":    main()
目的:用纯 NumPy 模拟 FSDP 的三板斧——参数分片存储、前向按需聚合、梯度回分更新,并对比不同卡数下的单卡峰值参数量。
  1. chunk_sizes = [s // world_size for s in layer_sizes]把每一层参数均分成 world_size 块,对应“每家分到的食材份额”。
  2. self.fridges = [[np.random.randn(c) * 0.01 for c in chunk_sizes] ...]每张卡一个“小冰箱”,只存全部参数的 1/N。
  3. np.concatenate([self.fridges[g][idx] for g in range(self.world_size)])模拟 all-gather:从所有邻居冰箱拼出这一层的完整参数。
  4. pieces = np.array_split(grad, self.world_size)把梯度按分片切成 N 份,准备“回分”给各家。
  5. self.fridges[g][idx] -= 0.01 * pieces[g]每家只更新自己负责的那部分参数,完成一步 SGD。
  6. return biggest // self.world_size + biggest单卡峰值 = 本卡分片 + 当前整层副本;卡越多,分片部分越小。
  7. for w in (1, 2, 4, 8):对比不同卡数下的峰值参数量,直观验证“分片越多人均负担越小”。

常见误区

误区我以为 FSDP 就是 DDP 加了个省显存的开关,名字都叫 Data Parallel,训练出的指标也一样,只是内部把权重压缩了一下而已。

正解两者机制完全不同。DDP 每卡持有完整模型副本,每步用 all-reduce 同步完整梯度;FSDP 把参数、梯度、优化器状态全部按层分片,前向反向时临时 all-gather 聚合、算完释放、梯度再 reduce-scatter 回分,通信模式和内存生命周期都不一样。这就像“每家做一整桌菜”和“每家只存一把菜、开席再拼桌”,流程和盘子数量完全不同。

为何常见:两者都叫数据并行,损失曲线和精度指标常常完全一致,只看训练日志很难发现底层差异。

误区只要把模型包一层 FSDP(……),显存立刻省一大截,训练速度也不会受影响,纯赚不亏。

正解显存收益是真的,但通信开销也是真的:每步多出约 3Φ 的 all-gather / reduce-scatter 流量。要想把这笔账抹平,需要开启前向/反向预取、选择合适的 auto_wrap_policy,必要时配合激活检查点。省显存是用更多通信换来的,工程上必须主动“还债”。

为何常见:显存数字立竿见影,通信开销却藏在训练日志背后,被吞吐量稀释,初学者很容易只看见收益、看不见成本。

误区既然分片能省显存,那分得越细越好,最好每个参数都单独分片,一次省个痛快。

正解分片粒度是典型的权衡:单元越小,同一时刻需要聚合的参数越少,峰值显存越低;但每多一个分片单元就多一次聚合与释放的往返,通信次数随单元数线性上涨,颗粒过细会导致训练大幅变慢。工程共识是按 Transformer Block 分片,在显存与带宽之间取平衡。

为何常见:教科书只说“分片省显存”,没强调“聚合有成本”,直觉上的“少存一点 = 省一点”误导了初学者。

误区前向反向都用 BF16 计算,那 FP32 主权重纯属浪费,干脆也存成 2 字节,还能再省一半显存。

正解BF16 指数位与 FP32 相同、训练比 FP16 更稳,但它在很多中间结果上的精度仍不足。优化器更新时若直接用 BF16 数值累加,误差会不断累积甚至不收敛,因此必须保留 FP32 主权重用于更新。FSDP 的聪明之处在于:主权重和优化器状态本身也是分片的,单卡只需负担 12Φ/N,附带成本很小。

为何常见:看到前向计算用低精度,就误以为全程都是低精度,把“计算精度”和“存储精度”两件事混为一谈。

面试题

Q1. FSDP 的核心思想是什么?

单选题 · easy

  • A.每张卡只存部分参数按需聚合← 正确答案
  • B.每张卡保存一份完整模型副本
  • C.用 CPU 内存缓存所有模型参数
  • D.将模型量化到低精度来训练

答案:A。FSDP 的立身之本就是“分着存”:每个参数张量都切出 1/N 存在各卡,计算时再按需聚合。B 是 DDP 的做法,C 是 CPU offload,D 是量化压缩,都不是 FSDP 的核心机制。一句话记忆:全分片。

Q2. FSDP 前向计算某一层时需要什么操作?

单选题 · easy

  • A.直接使用本卡全部模型参数
  • B.先做 all-gather 再计算该层← 正确答案
  • C.从 CPU 把整层参数拷回显存
  • D.将梯度平均后复制到各张卡

答案:B。前向走到某一层时,本卡只握有该层参数的 1/N,必须先 all-gather 把 N 份拼成完整参数才能计算。C 是 CPU offload 的场景,D 说的是梯度同步,A 在 FSDP 中不存在——完整模型参数任何时刻都不常驻单卡。

Q3. FSDP 某层前向计算完成后,完整参数副本会怎样?

单选题 · mid

  • A.永久保存便于下次使用
  • B.立即释放以节省显存空间← 正确答案
  • C.转变为梯度继续参与传播
  • D.复制到其他卡上做备份

答案:B。FSDP 奉行“用完就撤桌”:该层前向算完,完整参数副本立即丢弃,只为下一步反向腾地方。B 正是这个设计,A 是 DDP 的保留方式,C、D 与内存管理无关。

Q4. FSDP 反向传播中梯度是如何同步的?

单选题 · mid

  • A.全量梯度广播到所有 GPU 卡
  • B.等价于 all-reduce 后再平均
  • C.reduce-scatter 归约后按分片存← 正确答案
  • D.每个 GPU 独立计算不通信

答案:C。反向的梯度同步走 reduce-scatter:先跨卡求和归约,再按原先分片切回各卡,一步完成“汇总 + 分片”。B 近似 DDP 的 all-reduce,A 是广播不是归约,D 完全错误——梯度必须同步。

Q5. 关于 FSDP 的说法正确的是?

多选题 · mid

  • A.是 PyTorch 官方对 ZeRO-3 的实现← 正确答案
  • B.每张卡只持有参数的 N 分之一← 正确答案
  • C.不能与原生优化器混合使用
  • D.完全不需要任何通信开销

答案:AB。FSDP 正是 PyTorch 官方对 ZeRO-3 的落地,每卡只持参数的 1/N(A、B 对)。它完全可以配合 torch 原生优化器与 DCP 使用(C 错);分片后仍有聚合与归约通信(D 错)。

Q6. FSDP 中 auto_wrap_policy 的作用是什么?

单选题 · mid

  • A.自动修复损坏的模型梯度数据
  • B.自动确定参数的保存与加载路径
  • C.自动识别 Transformer 层作为分片单元← 正确答案
  • D.自动调节学习率防止训练发散

答案:C。auto_wrap_policy 让框架自动扫描模型结构,把每个 Transformer Block 包成一个分片单元,从而在“省显存”和“少通信”之间取得平衡。它不是修梯度、存 checkpoint 或调学习率的工具。

Q7. 混合精度训练下 FSDP 中分片存储的内容包括?

多选题 · hard

  • A.FP32 全精度主权重参数副本← 正确答案
  • B.Adam 优化器的动量与方差← 正确答案
  • C.当前层的完整参数量副本
  • D.模型训练过程中的全部历史梯度

答案:AB。混合精度下长期分片保存的是 FP32 主权重与 Adam 的动量、方差(A、B 对);当前层完整参数只是临时聚合、用完即弃(C 错);历史梯度不会留存(D 错)。

Q8. 同一模型、同一批数据下,相比 DDP,FSDP 的通信量大致是多少?

单选题 · hard

  • A.约一点五倍于 DDP 的通信量← 正确答案
  • B.大约为 DDP 通信量的三倍
  • C.恰好等于 DDP 的通信总量
  • D.低于 DDP 因为梯度更小

答案:A。DDP 每步只有一次梯度 all-reduce,通信量约 2Φ;FSDP 要“前向聚一次、反向聚一次、梯度回分一次”,约 3Φ,故约为 DDP 的 1.5 倍。3 倍、相等、可忽略都不符合实际。

Q9. 以下哪种场景最适合用 FSDP?

单选题 · easy

  • A.模型太大单卡显存放不下← 正确答案
  • B.模型极小且数据量也很少
  • C.单卡推理延迟要求不高
  • D.只需单机且不使用 GPU

答案:A。FSDP 的价值在于用多卡分担单卡放不下的大模型。A 是它的主场;B、C、D 的场景小模型或无需多卡训练,杀鸡不用牛刀。

Q10. FSDP 相比 DeepSpeed ZeRO-3 的优势体现在哪些方面?

多选题 · hard

  • A.贴合 PyTorch 原生编程习惯← 正确答案
  • B.与 torch 生态工具天然兼容← 正确答案
  • C.完全不需要理解分片的概念
  • D.显存一定比 ZeRO-3 更省

答案:AB。FSDP 更贴合 PyTorch 原生习惯,与 torch 生态无缝衔接(A、B 对);使用任何分片方案都要理解分片概念(C 错);显存上它与 ZeRO-3 理论等价,不会天然更省(D 错)。