跳到主要内容

数据截至 (上游 commit fd01e35c83d8)

Accelerate — 架构与原理

30 秒导读: Accelerate 是 HuggingFace 的「分布式训练与大模型推理适配层」。它不发明任何训练算法,只做两件事:第一,让你用同一份纯 PyTorch 训练脚本跑在单卡、多卡 DDP、FSDP、DeepSpeed、TPU 上——accelerator.prepare(model, optimizer, dataloader, scheduler) 一行把四样对象换成带分布式和混合精度能力的包装版;第二,让你把塞不进单卡的大模型按层切到多张 GPU、CPU 内存甚至磁盘上跑推理(device_map="auto")。整个库的主干不到二十个文件,核心技巧是「包装 + 钩子」:不改你的类,只在对象外面套一层。


1. 这是什么(零基础也能懂)

一句话定义

Accelerate 是一个 PyTorch 训练/推理代码的「运行环境适配层」:你写单卡的 PyTorch 代码,它负责把这份代码在 N 个进程、M 种硬件、K 种分布式后端下跑起来,行为保持一致。

它要解决谁的什么问题

假设你写好了一个单卡训练循环,现在要上 8 卡:

  • 得自己起 8 个进程、配 init_process_group、给每个进程绑卡;
  • 得把模型包成 DistributedDataParallel,把 DataLoader 换成 DistributedSampler 版本,还要处理「数据集不能整除进程数」的尾巴;
  • 上混合精度要包 autocast + GradScaler,梯度累积要处理 DDP 的 no_sync、scaler 溢出时跳过 step;
  • 想换 FSDP 或 DeepSpeed?以上大半要重写。

Accelerate 把这些「环境差异」全部吸收到 prepare() 和三个状态单例里。你的训练循环只调 accelerator.backward(loss)accelerator.gather(x) 这类中性 API。

另一条独立的产品线是大模型推理:70B 模型放不进一张 24GB 的卡,Accelerate 提供 init_empty_weights() + device_map="auto" + dispatch_model(),把层切成「GPU 常驻 + CPU/磁盘暂存、用到才搬上来」的结构。HuggingFace transformersdevice_map 参数底层就是这套机制。

三个边界声明

  • 不是训练框架:没有 Trainer、没有训练循环抽象,循环永远是你自己的(transformers 的 Trainer 才是训练框架,其内部正是调用 Accelerate)。
  • 不是分布式引擎:真正的 all-reduce、分片、ZeRO 都由 PyTorch 分布式、FSDP、DeepSpeed、Megatron-LM 完成,Accelerate 只负责「按配置把对象交给正确的引擎」。
  • device_map 是推理机制:用它加载的模型不能直接分布式训练(prepare 会显式拒绝,见第 1、5 章)。

2. 顶层全景

2.1 一张图看全貌

accelerate launch ──▶ PartialState / AcceleratorState ──▶ Accelerator.prepare(*args)
(torchrun 起 N 进程 (Borg 单例:读环境变量、 │
写 LOCAL_RANK 等) init_process_group、定 device) │ 按类型两趟分发
┌──────────────┬─────────────────────┼──────────────────┐
▼ ▼ ▼ ▼
prepare_model prepare_optimizer prepare_data_loader prepare_scheduler
(AMP 改写 forward (Accelerated- (BatchSamplerShard / (Accelerated-
+ DDP/FSDP 包壳) Optimizer) DataLoaderDispatcher) Scheduler)

推理支线(与训练解耦的另一套 API):

init_empty_weights ──▶ infer_auto_device_map ──▶ load_checkpoint_in_model ──▶ dispatch_model ──▶ 每次 forward 经 AlignDevicesHook
(参数全在 meta 设备, (按显存预算装箱, (逐 shard 加载,按 map (给每个 block 挂 pre/ (pre: 物化权重到执行设备
零内存占用) 决定每层去哪台设备) 直接落到目标设备) post forward hook) post: 卸回 meta)

2.2 部件表

部件位置职责
Acceleratorsrc/accelerate/accelerator.py用户主入口:prepare / backward / gather / accumulate / save_state
PartialStatesrc/accelerate/state.py:123Borg 单例:进程组、rank、world size、device、分布式类型
AcceleratorStatesrc/accelerate/state.py:868在 PartialState 之上加混合精度与后端插件状态
GradientStatesrc/accelerate/state.py:1231梯度累积节拍与 dataloader 尾部状态的单例
AcceleratedOptimizersrc/accelerate/optimizer.py:38包装 optimizer:GradScaler step、累积期跳过、XLA 梯度同步
AcceleratedSchedulersrc/accelerate/scheduler.py:25包装 scheduler:只在 optimizer 真正 step 时步进
BatchSamplerShard / DataLoaderShard / DataLoaderDispatchersrc/accelerate/data_loader.py:110 / :510 / :723数据分片的两种策略与迭代器
multi_gpu_launcher / notebook_launcher / PrepareForLaunchsrc/accelerate/commands/launch.py:998src/accelerate/launchers.py:40src/accelerate/utils/launch.py:783三条进程启动路径
init_empty_weights / dispatch_model / load_checkpoint_and_dispatchsrc/accelerate/big_modeling.py:62 / :315 / :520大模型推理三件套
AlignDevicesHook / add_hook_to_modulesrc/accelerate/hooks.py:242 / :147forward 前后的权重物化/卸载钩子及其注入机制
infer_auto_device_map / get_balanced_memorysrc/accelerate/utils/modeling.py:1304 / :931device_map 的自动装箱算法与显存预算
gather / reduce / broadcast / send_to_devicesrc/accelerate/utils/operations.py跨后端集合通信与嵌套结构张量搬运原语
插件 dataclass 群src/accelerate/utils/dataclasses.pyDDP/FSDP/DeepSpeed/Megatron/梯度累积/FP8 等配置对象

2.3 主线:一次多卡训练是怎么跑起来的

  1. 启动accelerate launch --multi_gpu --num_processes 8 train.py 拼好环境变量后转交 torch.distributed.run(torchrun),拉起 8 个进程(src/accelerate/commands/launch.py:998)。
  2. 感知:每个进程执行到 Accelerator() 时,PartialStateLOCAL_RANK 等环境变量,探测后端(nccl/gloo/xla/…),调用 init_process_group,算出本进程的 device(src/accelerate/state.py:177)。
  3. 包装accelerator.prepare(model, optimizer, dataloader, scheduler) 按类型分两趟分发:model 被改写 forward(混合精度)并按需包 DDP/FSDP;optimizer/scheduler 各套一层包装;dataloader 换成分片版(src/accelerate/accelerator.py:1414)。
  4. 循环:训练循环不变,只是 loss.backward() 换成 accelerator.backward(loss)(内部处理 loss 缩放与 scaler);评估用 accelerator.gather() 聚合各进程结果。
  5. 收尾accelerator.wait_for_everyone() 对齐进程,unwrap_model() 剥掉包装后 save_state() 存档。

推理支线则是:空壳初始化 → 自动装箱出 device_map → 逐 shard 加载到目标设备 → 给每个 block 挂钩子,forward 时逐层「物化→计算→卸载」。


3. 阅读地图

章节读它回答什么问题前置
01 · prepare 包装机制prepare() 一行到底干了什么?包装后的对象和原来差在哪?无,建议第一个读
02 · 状态单例与进程启动进程怎么知道「我是谁、用哪张卡」?accelerate launch 做了什么?01
03 · DataLoader 分片每个进程的数据怎么分?评估指标的尾巴怎么修?02
04 · 混合精度与梯度累积两个开关背后,forward/backward/step 各发生了什么?01
05 · 大模型 offload 与 device_map24GB 的卡怎么跑 70B 模型?hook 怎么搬运权重?无(相对独立)

4. 巧妙之处

  1. Borg 单例代替全局变量PartialState.__init__self.__dict__ = self._shared_statesrc/accelerate/state.py:179),任何角落 PartialState() 都拿到同一份状态;测试又能 _reset_state() 清空。库内所有包装类借此「隔空」读到分布式配置,用户完全无感。
  2. prepare() 的两趟分发 + 幂等标记:先处理 model/optimizer/dataloader,再处理 scheduler(后者要引用已包装的 optimizer);每个被包装对象打上 _is_accelerate_prepared,二次 prepare 原样返回(src/accelerate/accelerator.py:1397src/accelerate/accelerator.py:1799-1803)。
  3. AMP 靠改写 forward,不动用户的模型类prepare_modelmodel.forward 换成 convert_outputs_to_fp32(autocast(forward))src/accelerate/accelerator.py:1818),混合精度对用户代码零侵入,且可通过 __wrapped__ 链完整还原。
  4. __class__ 属性伪装DataLoaderAdapter__class__ 定义成 property 返回被包装对象的类(src/accelerate/data_loader.py:458),下游 isinstance(dl, DataLoader) 检查照常通过——「透明包装」的教科书做法。
  5. 预取一批再判结束DataLoaderShard.__iter__ 永远比 yield 提前 next() 一个 batch(src/accelerate/data_loader.py:577),既能把当前 batch 提前送上设备,又能准确识别 StopIteration 并标记 end_of_dataloader——这个标记正是 gather_for_metrics 修尾的依据。
  6. scheduler 按 num_processes 补步:不分片模式下全局 batch 被放大 num_processes 倍,AcceleratedScheduler.step 每次连走 num_processes 步(src/accelerate/scheduler.py:76),用户按「原始 batch 大小」写的 scheduler 配置无需修改。
  7. meta 设备当「欠条」:offload 的层参数被搬到 meta 设备(不占内存的占位符),pre_forward 才从 weights_map 物化真权重,post_forward 立刻扔回 meta(src/accelerate/hooks.py:359src/accelerate/hooks.py:402)。磁盘 offload 时 weights_map 是个懒加载映射,连 CPU 内存都省。
  8. tied weights 用 data_ptr 记账:共享权重(如输入/输出 embedding)按 data_ptr() 登记,同一设备上只物化一份,避免重复占显存(src/accelerate/big_modeling.py:412src/accelerate/hooks.py:380-385)。
  9. 装箱时给「最大层」留座位infer_auto_device_map 在主设备上预留「剩余层中最大那层」的空间(src/accelerate/utils/modeling.py:1419-1423),保证 CPU 上的层永远能临时搬回 GPU 执行——这是 offload 可行性的关键不变量。

5. 边界与局限

  1. 抽象是「漏」的,且故意如此:Accelerate 不屏蔽后端语义。FSDP 的 auto_wrap_policy、DeepSpeed 的 ZeRO 行为、Megatron 的流水线切分,全部要你按后端文档配置插件;Accelerate 只负责翻译和转交。
  2. device_map 模型不能分布式训练prepare 对带 hf_device_map 的模型直接 raise(src/accelerate/accelerator.py:1469-1480);8bit/4bit 量化模型跨设备同样拒绝训练(src/accelerate/accelerator.py:1835-1841)。offload 推理也是逐层串行搬运,没有预取流水线,速度换显存。
  3. even_batches 的静默复制:默认 even_batches=True 会在数据集尾部复制样本对齐进程数,评估指标必须用 gather_for_metrics 修尾,否则结果 silently 偏差(见第 3 章)。
  4. dispatch 模式要求等长 batchsplit_batches=False 的 dispatch 要把 num_processes 个 batch 做 concatenate,尺寸不齐直接报错(src/accelerate/data_loader.py:845)。
  5. 单例污染:三个状态单例在进程内全局唯一,同进程想跑两套不同配置(如两个 Accelerator 用不同 DeepSpeed 插件)会被显式拒绝;测试必须 _reset_state()
  6. 对象改写带来的副作用:forward 被换、model.to() 被加警告、__class__ 被伪装——与 torch.compile、GraphModule、pickle 等机制相交处都有特例代码(如 src/accelerate/hooks.py:196),深度定制时会踩到。
  7. 单机思维的多机默认值:多机时端口占用只在 rank 0 检查、CPU 分布式要手填 MASTER_ADDR 等,多机体验明显让位于单机多卡主场景(src/accelerate/utils/launch.py:236)。

6. 横向对比

维度AccelerateDeepSpeedMegatron-LMtransformers Trainertrl
定位环境适配薄层分布式训练引擎(ZeRO/管道/3D 并行)训练引擎 + 模型实现(张量/流水线并行)完整训练框架(循环、日志、回调)RLHF 训练算法库
与 Accelerate 的关系本体被 Accelerate 当作一种后端(DeepSpeedPlugin被 Accelerate 当作一种后端(MegatronLMPlugin内部用 Accelerate 做分布式适配内部用 Accelerate
训练循环用户自己写用户写或 engine 托管engine 托管Trainer 托管各 RL Trainer 托管
大模型推理 offload核心能力(device_map/hooks)有 ZeRO-Inference 路线不涉及依赖 Accelerate 的 device_map不涉及
改动用户代码的程度只加 prepare/backward 等调用需要 engine 初始化与 config需要按 Megatron 的模型/数据规范换成 Trainer 范式换成其 Trainer 范式

一句话:当你要「保住自己的 PyTorch 循环,只解决跑在哪」时选 Accelerate;当你要引擎替你管显存与并行时,选 DeepSpeed/Megatron——Accelerate 恰好是把你的代码接到这两类引擎上的官方转接头。

7. 代码地图

克隆根:aiRef/repos/accelerate(sourceCommit fd01e35c83d8cc43b88cf0896007716fc5986558)。以下路径均相对克隆根。

src/accelerate/
├── accelerator.py # Accelerator 主类:prepare 家族、backward、gather、save/load_state(约 4.4k 行)
├── state.py # PartialState / AcceleratorState / GradientState 三个 Borg 单例
├── launchers.py # notebook_launcher / debug_launcher(进程内起多进程)
├── optimizer.py # AcceleratedOptimizer 包装
├── scheduler.py # AcceleratedScheduler 包装
├── data_loader.py # BatchSamplerShard / DataLoaderShard / DataLoaderDispatcher / prepare_data_loader
├── big_modeling.py # init_empty_weights / dispatch_model / load_checkpoint_and_dispatch / cpu_offload
├── hooks.py # ModelHook 协议、AlignDevicesHook、hook 注入/移除
├── checkpointing.py # save/load_accelerator_state 的落盘实现
├── local_sgd.py # LocalSGD 辅助
├── parallelism_config.py # TP/CP 等并行度配置(新版并行动词)
├── tracking.py # 实验跟踪(wandb/tensorboard 等)
├── commands/ # accelerate CLI:launch/config/env/test/to_fsdp2 等子命令
└── utils/
├── operations.py # gather/reduce/broadcast/pad/slice/send_to_device 等跨后端原语
├── modeling.py # device_map 装箱、set_module_tensor_to_device、AMP context、checkpoint 加载
├── dataclasses.py # 全部插件配置(DDP/FSDP/DeepSpeed/Megatron/梯度累积/FP8/Dynamo…)
├── launch.py # 环境变量拼装、PrepareForLaunch、get_launch_prefix
├── offload.py # OffloadedWeightsLoader / offload_state_dict(磁盘卸载)
├── other.py # extract_model_from_parallel 等杂项
└── ... # deepspeed.py / fsdp_utils.py / megatron_lm.py / bnb.py 等后端适配

各文件内部的精确行号锚点见五章正文的「真实实现」节。