执行摘要
- 一句话:FSDP 后端迁出 experimental,升级为正式后端模块
- 推荐动作:值得快速浏览:这是大型仓库中“大范围纯移动”重构的工程样板,重点观察三点:一是相对导入转绝对导入使包位置无关的做法;二是源码移动与 docker 补丁、CODEOWNERS 等基础设施的联动检查清单;三是用全量 fast 测试证明“零行为变化”的验证方式。无需精读逻辑实现。
功能与动机
PR body 明确说明:FSDP 后端已从 experimental 毕业,应成为与 megatron_utils、sglang_utils、training_utils 平级的正式后端模块;且 experimental/ 下已无其他内容,可以整体移除。迁移后包减少一层嵌套,配合把跨包相对导入改为绝对导入,使包位置无关,并与仓库现有代码风格一致。
实现拆解
- 目录迁移:将
miles/backends/experimental/fsdp_utils/ 整体 git mv 为 miles/backends/fsdp_utils/,删除已空的 experimental/ 目录;共涉及 50 个文件,涵盖源码、测试、构建脚本与基础设施配置。
- 导入改写:包内 7 处跨包相对导入改为绝对导入(如
from ...training_utils.parallel 改为 from miles.backends.training_utils.parallel),包内相对导入(.、..)保持不变;代表文件为 actor.py、parallel.py、models/qwen3_moe.py。这样包位置独立,未来再次移动时无需调整点号数量。
- 构建与基础设施联动:
docker/Dockerfile.rocm 中创建并拷贝 sglang_attn_bridge 的目标路径、docker/amd_patch/latest/miles.patch 与 docker/npu_patch/miles.patch 的 patch 目标路径均从 experimental/fsdp_utils 更新为 fsdp_utils,避免 ROCm/NPU 构建失效;.github/CODEOWNERS 原地更新条目路径,保持其在 /miles/backends/ 之后的位置,使更具体规则仍生效。
- 外部引用点同步:
miles/utils/arguments.py 中 load_fsdp_args 与 validate_hybrid_shard_args 的动态导入、miles/ray/train/actor_factory.py 中的 FSDP actor 引用均切换到新路径。setup.py 使用 find_packages(include=["miles*"]),CI 工作流未按旧路径过滤,均无需改动。
- 验证与未纳入:
pre-commit run --all-files 通过;tests/fast/backends/ 447 项通过;tests/fast/ 4547 项通过,唯一失败 test_tracking_backend_end_to_end_local_ray 在未修改的 main 上同样失败,与本 PR 无关。docs/developer/experimental-features.md 与 docs/developer/architecture.md 仍描述旧位置,刻意留作 code-only 后续跟进。
关键文件:
miles/backends/fsdp_utils/actor.py(模块 FSDP 后端;类别 source;类型 rename-or-move;符号 FSDPTrainRayActor): FSDP 后端核心 actor,搬迁后集中展示了跨包相对导入转绝对导入的改造方式,是验证纯移动的主样本。
miles/backends/fsdp_utils/parallel.py(模块 FSDP 后端;类别 source;类型 rename-or-move;符号 build_fsdp_meshes): 展示 FSDP 并行状态构建逻辑搬迁后函数体零变化、仅 import 路径变化的典型样例。
miles/utils/arguments.py(模块 参数解析;类别 source;类型 dependency-wiring;符号 load_fsdp_args, validate_hybrid_shard_args): FSDP 后端参数入口的外部引用点,搬迁后动态导入路径同步更新,是外部依赖检查的关键样本。
tests/fast/backends/test_fsdp_hf_compat.py(模块 单元测试;类别 test;类型 test-coverage): 测试文件中所有 experimental 路径 import 与 logger 名同步更新,是测试配套随源码迁移的完整样例。
docker/Dockerfile.rocm(模块 构建脚本;类别 infra;类型 configuration): ROCm 构建脚本中创建并拷贝 sglang_attn_bridge 的路径依赖旧目录,须同步更新,否则 ROCm 镜像构建失败。
关键符号:FSDPTrainRayActor, build_fsdp_meshes, load_fsdp_args, validate_hybrid_shard_args
关键源码片段
miles/backends/fsdp_utils/actor.py
FSDP 后端核心 actor,搬迁后集中展示了跨包相对导入转绝对导入的改造方式,是验证纯移动的主样本。
# miles/backends/fsdp_utils/actor.py
# 搬迁自 miles/backends/experimental/fsdp_utils/actor.py。
# 关键变化:包层级从 experimental/fsdp_utils 变为 fsdp_utils,
# 原本指向 training_utils 的跨包相对导入(...training_utils)改写成绝对导入,
# 使包位置无关、与仓库代码风格一致;包内相对导入(. / ..)保持不变。
import logging
import os
import random
from argparse import Namespace
from contextlib import ExitStack
from typing import TYPE_CHECKING
import ray
import torch
import torch.distributed as dist
from tqdm import tqdm
# 原 : from miles.backends.experimental.fsdp_utils.adaptations import routing_replay
from miles.backends.fsdp_utils.adaptations import routing_replay
# 以下原先通过 ...training_utils 相对导入,现改为绝对导入
from miles.backends.training_utils.ci_utils import check_grad_norm
from miles.backends.training_utils.data import DataIterator, get_batch, get_data_iterator, get_rollout_data
from miles.backends.training_utils.log_utils import (
aggregate_forward_results,
aggregate_train_losses,
log_rollout_data,
log_train_step,
)
from miles.backends.training_utils.loss import compute_advantages_and_returns, get_log_probs_and_entropy, loss_function
from miles.backends.training_utils.parallel import get_parallel_state, set_parallel_state
from miles.ray.train_actor import TrainRayActor
from miles.utils import train_dump_utils, train_metric_utils
# 原 : from ....utils.profile_utils import TrainProfiler
from miles.utils.profile_utils import TrainProfiler
from miles.utils.timer import Timer, inverse_timer, timer
from miles.utils.tracking_utils.tracking import init_tracking
# 包内相对导入保持不变,因为模块间相对位置没有变
from . import checkpoint
from .adaptations.class_patches import apply_class_patches, apply_model_instance_patches
from .adaptations.packing import apply_packing
from .adaptations.post_load_fixups import apply_post_load_fixups
from .adaptations.precision import apply_fp32_master, precision_forward_context, resolve_precision_policy
from .lr_scheduler import get_lr_scheduler
from .parallel import create_fsdp_parallel_state
from .update_weight_utils import UpdateWeightFromDistributed, UpdateWeightFromTensor
logger = logging.getLogger(__name__)
class FSDPTrainRayActor(TrainRayActor):
"""纯 HF+FSDP 训练的简化 TrainRayActor。
rank 0 初始化原生 HF 模型(其余 rank 在 meta 上),外包 FSDP2,并提供 train / save /
update_weights 钩子;权重同步采用 rank 0 汇总完整 state_dict 后逐张量广播。
本次重构仅改变模块路径,类行为没有变化。
"""
miles/backends/fsdp_utils/parallel.py
展示 FSDP 并行状态构建逻辑搬迁后函数体零变化、仅 import 路径变化的典型样例。
# miles/backends/fsdp_utils/parallel.py
# 搬迁后唯一的代码变化是 import:
# 原 : from ...training_utils.parallel import ParallelState
# 现 : from miles.backends.training_utils.parallel import ParallelState
# 函数体与搬迁前完全一致,证明这是纯移动。
import logging
from argparse import Namespace
import torch.distributed as dist
from torch.distributed.device_mesh import DeviceMesh, init_device_mesh
# 跨包相对导入改为绝对导入
from miles.backends.training_utils.parallel import ParallelState
from miles.utils.distributed_utils import get_gloo_group
from miles.utils.ft_utils.process_group_utils import GroupInfo
logger = logging.getLogger(__name__)
def build_fsdp_meshes(
device_type: str,
world_size: int,
context_parallel_size: int,
dp_replicate_size: int,
) -> dict[str, DeviceMesh]:
"""构建数据并行/上下文并行视图与 FSDP2 分片 mesh。"""
data_parallel_size = world_size // context_parallel_size
# 先建立 (dp, cp) 二维 mesh,再从 dp 维派生 FSDP 分片 mesh
dp_cp_mesh = init_device_mesh(
device_type,
mesh_shape=(data_parallel_size, context_parallel_size),
mesh_dim_names=("dp", "cp"),
)
dp_mesh = dp_cp_mesh["dp"]
fsdp_mesh = dp_mesh
if dp_replicate_size > 1:
# 需要副本时,把 dp 维拆成 (dp_replicate, dp_shard) 两个维度
fsdp_mesh = dp_mesh._unflatten(
0,
(dp_replicate_size, data_parallel_size // dp_replicate_size),
("dp_replicate", "dp_shard"),
)
return {
"dp_cp": dp_cp_mesh,
"dp": dp_mesh,
"cp": dp_cp_mesh["cp"],
"fsdp": fsdp_mesh,
}
评论区精华
Review 讨论极少,仅有一条。fzyzcjy 给出 APPROVED,并附言 “LGTM if it is pure move”,即认可的前提是确认这是纯移动而非行为变更。PR 的实际改动(67 增 / 77 删、仅 import 与路径引用变化、全量 fast 测试通过)证实了这一点,无未解决的疑虑。
- 纯移动的确认 (design): PR 的验证结果(67 增 / 77 删、只改 import 与路径引用、tests/fast 4547 项通过)证实了纯移动,review 通过并已合入。
风险与影响
- 风险:
- 回归风险(低):纯目录移动不改变逻辑,但 50 个文件的 7 处跨包导入与外部引用点(
arguments.py、actor_factory.py)若有遗漏会直接引发 ImportError;已通过 tests/fast 4547 项覆盖,但 fast 测试不含真实 GPU 训练路径。
- 构建风险(中):
Dockerfile.rocm、amd_patch、npu_patch 均为路径硬编码,若不同步会破坏 ROCm/NPU 镜像构建或 patch 应用;本 PR 已同步,但当前 CI 中 MI300X runner 已被禁用(见 PR#2230),短期无法自动验证。
- 兼容性风险(低):任何仍引用
miles.backends.experimental 的外部脚本或插件会失效;experimental 路径本身不承诺稳定,且仓库内引用已全部更新。
- 文档风险(低):两处 developer 文档仍指向旧路径,可能误导后续阅读者,已明确留作 follow-up。
- 影响:
- 用户/开发者:运行时行为无变化;内部开发者的 FSDP 后端 import 路径统一为
miles.backends.fsdp_utils,后续基于该后端的 PR 需使用新路径。
- 系统结构:
experimental/ 目录消失,后端模块布局与 megatron_utils 等对齐,降低包嵌套深度和 import 复杂度。
- 团队协作:FSDP 后端正式化后,相关改动(如 R3 路由重放、GDN packing)将落在稳定路径上;文档同步工作被显式留出,便于后续单独 review。
- 风险标记:纯移动重构(回归风险低), docker/补丁路径引用同步, 外部 imports 需全量核对, docs 仍指向旧路径
关联脉络
- PR #2235 feat(fsdp): support rollout routing replay (R3) for fsdp backend: 直接改动 miles/backends/experimental/fsdp_utils/ 下 routing_replay、replay_routers、actor 等文件,本 PR 将这些文件迁至新路径,后续 R3 演进需基于 miles/backends/fsdp_utils。
- PR #2213 fix(fsdp): apply the GDN packing patch to dense qwen3_5 and fix patch: 同样改动 experimental/fsdp_utils 下的 qwen3_5 模型与适配文件,迁移后旧路径失效,属于同一条 FSDP 后端成熟化演进线。
- PR #2230 fix(ci): disable MI300X runner jobs: 其中包含 tests/e2e/fsdp/test_qwen3_4B_fsdp_hybrid_shard_r2s2.py 的禁用,反映 FSDP 后端 CI 覆盖正在调整,与本 PR 同属 FSDP 后端稳定化进程。
参与讨论