Skip to content

Commit d0d0bb9

Browse files
authored
feat(config): inline DeepSpeed config into main YAML (#36)
* feat(config): inline DeepSpeed config into main YAML, drop separate config file * fix(tests): update test_config.py for inline deepspeed config * fix(config): type deepspeed field as dict[str, Any] | None * fix(methods): normalize empty deepspeed dict to None * fix(slurm): gate DeepSpeed accelerate flags on the deepspeed config * fix(config): reject deprecated deepspeed.config_path style * docs: note how to switch DeepSpeed ZeRO stage 2 to 3
1 parent afa69d1 commit d0d0bb9

12 files changed

Lines changed: 234 additions & 43 deletions

File tree

README.md

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -302,7 +302,8 @@ You must specify exactly one determining factor for training duration in the `tr
302302

303303
### 4. Infrastructure & Compute
304304

305-
- **DeepSpeed**: configured via `deepspeed.config_path` (e.g., `configs/deepspeed/zero3.yaml`)
305+
- **DeepSpeed**: configured inline under the top-level `deepspeed:` key (see the reference config below); set `deepspeed: null` to disable DeepSpeed entirely.
306+
To switch from ZeRO stage 2 to stage 3, bump `zero_optimization.stage` to `3` and add the `stage3_*` tuning keys — see `configs/deepspeed/zero3.yaml` for a full example.
306307
- **Accelerate flags**: the `accelerate` section in the YAML mirrors the CLI flags required for multi-node setups (`mixed_precision`, `dynamo_backend`, `rdzv_backend`, etc.).
307308
These are used by the SLURM launcher to generate the correct job script.
308309
- **Self-healing**: the SLURM launcher (`src/post_training/slurm/`) supports auto-requeueing.
@@ -485,8 +486,28 @@ data:
485486
transform: null # null = already conversational
486487
487488
# -- DeepSpeed ---------------------------------------------------------------
489+
# Set to null to disable DeepSpeed entirely.
490+
# To switch from ZeRO stage 2 to stage 3, bump zero_optimization.stage to 3 and
491+
# add the stage3_* tuning keys — see configs/deepspeed/zero3.yaml for a full example.
488492
deepspeed:
489-
config_path: "configs/deepspeed/zero2.yaml"
493+
bf16:
494+
enabled: true
495+
zero_optimization:
496+
stage: 2
497+
overlap_comm: true
498+
contiguous_gradients: true
499+
reduce_scatter: true
500+
gradient_clipping: 1.0
501+
train_micro_batch_size_per_gpu: "auto"
502+
gradient_accumulation_steps: "auto"
503+
train_batch_size: "auto"
504+
optimizer:
505+
type: AdamW
506+
params:
507+
lr: "auto"
508+
betas: "auto"
509+
eps: "auto"
510+
weight_decay: "auto"
490511
491512
# -- Accelerate launch flags (explicit multi-node control) -------------------
492513
accelerate:

configs/trl/dpo.yaml

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,8 +73,28 @@ data:
7373
transform: null # null = already "chosen" / "rejected" columns
7474

7575
# -- DeepSpeed ---------------------------------------------------------------
76+
# Set to null to disable DeepSpeed entirely.
77+
# To switch from ZeRO stage 2 to stage 3, bump zero_optimization.stage to 3 and
78+
# add the stage3_* tuning keys — see configs/deepspeed/zero3.yaml for a full example.
7679
deepspeed:
77-
config_path: "configs/deepspeed/zero2.yaml"
80+
bf16:
81+
enabled: true
82+
zero_optimization:
83+
stage: 2
84+
overlap_comm: true
85+
contiguous_gradients: true
86+
reduce_scatter: true
87+
gradient_clipping: 1.0
88+
train_micro_batch_size_per_gpu: "auto"
89+
gradient_accumulation_steps: "auto"
90+
train_batch_size: "auto"
91+
optimizer:
92+
type: AdamW
93+
params:
94+
lr: "auto"
95+
betas: "auto"
96+
eps: "auto"
97+
weight_decay: "auto"
7898

7999
# -- Accelerate launch flags -------------------------------------------------
80100
accelerate:

configs/trl/sft.yaml

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,8 +80,28 @@ data:
8080
transform: null # null = already conversational
8181

8282
# -- DeepSpeed ---------------------------------------------------------------
83+
# Set to null to disable DeepSpeed entirely.
84+
# To switch from ZeRO stage 2 to stage 3, bump zero_optimization.stage to 3 and
85+
# add the stage3_* tuning keys — see configs/deepspeed/zero3.yaml for a full example.
8386
deepspeed:
84-
config_path: "configs/deepspeed/zero2.yaml"
87+
bf16:
88+
enabled: true
89+
zero_optimization:
90+
stage: 2
91+
overlap_comm: true
92+
contiguous_gradients: true
93+
reduce_scatter: true
94+
gradient_clipping: 1.0
95+
train_micro_batch_size_per_gpu: "auto"
96+
gradient_accumulation_steps: "auto"
97+
train_batch_size: "auto"
98+
optimizer:
99+
type: AdamW
100+
params:
101+
lr: "auto"
102+
betas: "auto"
103+
eps: "auto"
104+
weight_decay: "auto"
85105

86106
# -- Accelerate launch flags (explicit multi-node control) -------------------
87107
accelerate:

src/post_training/backend.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,13 @@ def validate(self, config: PostTrainingConfig) -> None:
8282
f"Supported methods: {', '.join(_SUPPORTED_METHODS)}"
8383
)
8484

85+
if isinstance(config.deepspeed, dict) and "config_path" in config.deepspeed:
86+
raise ValueError(
87+
"deepspeed.config_path is no longer supported. Inline the DeepSpeed "
88+
"JSON/YAML directly under the `deepspeed:` key (see configs/trl/sft.yaml "
89+
"for an example), or set `deepspeed: null` to disable DeepSpeed."
90+
)
91+
8592
# Container validation (only when container.image is set)
8693
if config.container is not None and config.container.image:
8794
if not config.container.bind_mounts:

src/post_training/config.py

Lines changed: 4 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@
1111
from pathlib import Path
1212
from typing import Any
1313

14-
import yaml
1514
from omegaconf import MISSING, DictConfig, OmegaConf
1615

1716
logger = logging.getLogger(__name__)
@@ -151,18 +150,13 @@ class DataConfig:
151150
datasets: list[DatasetEntry] = field(default_factory=list)
152151

153152

154-
@dataclass
155-
class DeepSpeedConfig:
156-
"""Pointer to the DeepSpeed YAML config file. Set to ``null`` to disable."""
157-
158-
config_path: str | None = "configs/deepspeed/zero2.yaml"
159-
160-
161153
@dataclass
162154
class AccelerateConfig:
163155
"""Flags forwarded to ``accelerate launch`` for explicit multi-node control."""
164156

165157
mixed_precision: str = "bf16"
158+
# Only takes effect when the top-level `deepspeed:` config is also set;
159+
# `deepspeed: null` disables DeepSpeed at launch regardless of this flag.
166160
use_deepspeed: bool = True
167161
deepspeed_multinode_launcher: str = "standard"
168162
same_network: bool = True
@@ -252,7 +246,8 @@ class PostTrainingConfig:
252246
dpo: DPOMethodConfig = field(default_factory=DPOMethodConfig)
253247

254248
# Infrastructure.
255-
deepspeed: DeepSpeedConfig = field(default_factory=DeepSpeedConfig)
249+
# Inline DeepSpeed config dict. Set to null to disable DeepSpeed entirely.
250+
deepspeed: dict[str, Any] | None = None
256251
accelerate: AccelerateConfig = field(default_factory=AccelerateConfig)
257252
logging: LoggingConfig = field(default_factory=LoggingConfig)
258253
slurm: SlurmConfig = field(default_factory=SlurmConfig)
@@ -354,12 +349,3 @@ def resolve_gradient_accumulation_steps(self, world_size: int) -> int:
354349
f"* world_size ({world_size}). Got gradient_accumulation_steps={gas}."
355350
)
356351
return int(gas)
357-
358-
def load_deepspeed_config(self) -> dict[str, Any]:
359-
"""Load the DeepSpeed YAML config and return it as a plain dict."""
360-
ds_path = Path(self.deepspeed.config_path)
361-
if not ds_path.is_absolute():
362-
# Resolve relative to the project root (cwd).
363-
ds_path = Path.cwd() / ds_path
364-
with open(ds_path) as f:
365-
return yaml.safe_load(f)

src/post_training/methods/common.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,9 @@ def build_common_training_kwargs(
7575
logger.info("world_size=%d, gradient_accumulation_steps=%d", world_size, grad_accum)
7676

7777
t = config.training
78-
ds_config = config.load_deepspeed_config() if config.deepspeed.config_path else None
78+
# Normalize a falsy config (e.g. `{}`) to None so it isn't forwarded to
79+
# TrainingArguments as if it were an enabled DeepSpeed config.
80+
ds_config = config.deepspeed or None
7981

8082
os.environ.setdefault("TENSORBOARD_LOGGING_DIR", str(run_dir / "logs"))
8183

src/post_training/slurm/job.sh.jinja

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,8 +68,10 @@ export LAUNCHER="accelerate launch \
6868
--main_process_port $MASTER_PORT \
6969
--mixed_precision {{ mixed_precision }} \
7070
--dynamo_backend {{ dynamo_backend }} \
71-
{% if use_deepspeed %} --use_deepspeed \{% endif %}
71+
{% if use_deepspeed -%}
72+
--use_deepspeed \
7273
--deepspeed_multinode_launcher {{ deepspeed_multinode_launcher }} \
74+
{% endif -%}
7375
{% if same_network %} --same_network \{% endif %}
7476
--rdzv_backend {{ rdzv_backend }} \
7577
--max_restarts 0 \

src/post_training/slurm/job_trl_container.sh.jinja

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -107,8 +107,10 @@ srun --export=ALL --wait=60 --kill-on-bad-exit=1 \
107107
--main_process_port $MASTER_PORT \
108108
--mixed_precision {{ mixed_precision }} \
109109
--dynamo_backend {{ dynamo_backend }} \
110-
{% if use_deepspeed %} --use_deepspeed \
111-
{% endif %} --deepspeed_multinode_launcher {{ deepspeed_multinode_launcher }} \
110+
{% if use_deepspeed -%}
111+
--use_deepspeed \
112+
--deepspeed_multinode_launcher {{ deepspeed_multinode_launcher }} \
113+
{% endif -%}
112114
{% if same_network %} --same_network \
113115
{% endif %} --rdzv_backend {{ rdzv_backend }} \
114116
--max_restarts 0 \

src/post_training/slurm/launcher.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ def render_trl_slurm_script(
7777
# Accelerate flags
7878
mixed_precision=config.accelerate.mixed_precision,
7979
dynamo_backend=config.accelerate.dynamo_backend,
80-
use_deepspeed=config.accelerate.use_deepspeed,
80+
use_deepspeed=config.accelerate.use_deepspeed and bool(config.deepspeed),
8181
deepspeed_multinode_launcher=config.accelerate.deepspeed_multinode_launcher,
8282
same_network=config.accelerate.same_network,
8383
rdzv_backend=config.accelerate.rdzv_backend,
@@ -134,7 +134,7 @@ def render_trl_container_slurm_script(
134134
# Accelerate flags
135135
mixed_precision=config.accelerate.mixed_precision,
136136
dynamo_backend=config.accelerate.dynamo_backend,
137-
use_deepspeed=config.accelerate.use_deepspeed,
137+
use_deepspeed=config.accelerate.use_deepspeed and bool(config.deepspeed),
138138
deepspeed_multinode_launcher=config.accelerate.deepspeed_multinode_launcher,
139139
same_network=config.accelerate.same_network,
140140
rdzv_backend=config.accelerate.rdzv_backend,

src/post_training/utils/guardrails.py

Lines changed: 5 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,6 @@
1414
from pathlib import Path
1515
from typing import TYPE_CHECKING
1616

17-
import yaml
18-
1917
if TYPE_CHECKING:
2018
from post_training.config import PostTrainingConfig
2119

@@ -70,19 +68,12 @@ def _row(label: str, value: str, warn: bool = False) -> None:
7068

7169

7270
def _deepspeed_summary(config: PostTrainingConfig) -> str:
73-
"""Return a short description of the DeepSpeed config, e.g. 'zero2.yaml (ZeRO stage 2)'."""
74-
ds_path = config.deepspeed.config_path
75-
if not ds_path:
71+
"""Return a short description of the inline DeepSpeed config, e.g. 'ZeRO stage 2'."""
72+
ds = config.deepspeed
73+
if not ds:
7674
return "disabled"
77-
path = Path(ds_path)
78-
try:
79-
resolved = path if path.is_absolute() else Path.cwd() / path
80-
with open(resolved) as fh:
81-
ds_cfg = yaml.safe_load(fh)
82-
stage = ds_cfg.get("zero_optimization", {}).get("stage", "?")
83-
return f"{path.name} (ZeRO stage {stage})"
84-
except Exception:
85-
return str(ds_path)
75+
stage = ds.get("zero_optimization", {}).get("stage", "?") if isinstance(ds, dict) else "?"
76+
return f"ZeRO stage {stage}"
8677

8778

8879
# ---------------------------------------------------------------------------

0 commit comments

Comments
 (0)