mirror of
https://github.com/radixark/miles.git
synced 2026-10-02 07:14:53 +08:00
223 lines
7.0 KiB
Python
223 lines
7.0 KiB
Python
"""NVIDIA Nemotron-3-Nano GRPO training script (single node, 8 GPUs).
|
|
|
|
=====================
|
|
|
|
One recipe covers both Nano variants: the dense 4B and the 128-expert 30B-A3B MoE. They
|
|
differ in the parallelism cell, the dynamic-batch token budget, activation recompute,
|
|
log-prob chunking, the MoE-only rollout routing replay, and the rollout shape (the 4B fits
|
|
8 samples x 4096 tokens per prompt, the 30B-A3B runs 4 x 1024). Everything else -- rollout
|
|
dataset, GRPO constants, optimizer schedule, SGLang engine layout -- is shared.
|
|
|
|
nemotron_h is a hybrid Mamba2 + Attention architecture loaded through NVIDIA
|
|
``megatron.bridge`` (``--megatron-to-hf-mode bridge``): ``--ref-load`` points at the HF
|
|
checkpoint itself, so no offline torch_dist conversion step is needed.
|
|
|
|
Both variants are 10-step smoke tests, hence no eval. The parallelism fields of `_Recipe`
|
|
are the knobs a sweep swaps per cell (PP=2, CP=2, TP=4, TP=2 x PP=2 for the dense 4B;
|
|
EP for the MoE).
|
|
|
|
=====================
|
|
|
|
Args:
|
|
--model-name: Model variant, NVIDIA-Nemotron-3-Nano-4B-BF16 or NVIDIA-Nemotron-3-Nano-30B-A3B-BF16.
|
|
--num-gpus-per-node: GPUs per node (default: 8).
|
|
--num-rollout: Rollout steps (default: 10, i.e. the smoke test).
|
|
--model-dir / --data-dir: Checkpoint / dataset directories.
|
|
|
|
=====================
|
|
|
|
python scripts/run_nemotron_3_nano.py --model-name NVIDIA-Nemotron-3-Nano-30B-A3B-BF16
|
|
"""
|
|
|
|
from dataclasses import dataclass
|
|
from typing import Literal
|
|
|
|
import typer
|
|
|
|
import miles.utils.external_utils.command_utils as U
|
|
|
|
_MODEL_NAMES = Literal["NVIDIA-Nemotron-3-Nano-4B-BF16", "NVIDIA-Nemotron-3-Nano-30B-A3B-BF16"]
|
|
|
|
|
|
@dataclass(frozen=True)
|
|
class _Recipe:
|
|
megatron_model_type: str
|
|
tp: int
|
|
pp: int
|
|
cp: int
|
|
ep: int
|
|
etp: int
|
|
max_tokens_per_gpu: int
|
|
recompute: bool
|
|
log_probs_chunk_size: int | None
|
|
use_rollout_routing_replay: bool
|
|
global_batch_size: int
|
|
n_samples_per_prompt: int
|
|
rollout_max_response_len: int
|
|
|
|
|
|
_RECIPES: dict[str, _Recipe] = {
|
|
"NVIDIA-Nemotron-3-Nano-4B-BF16": _Recipe(
|
|
megatron_model_type="nemotron-3-nano-4b",
|
|
tp=2,
|
|
pp=2,
|
|
cp=1,
|
|
# Dense architecture, so EP/ETP are N/A.
|
|
ep=1,
|
|
etp=1,
|
|
max_tokens_per_gpu=9216,
|
|
recompute=False,
|
|
log_probs_chunk_size=None,
|
|
use_rollout_routing_replay=False,
|
|
global_batch_size=256,
|
|
n_samples_per_prompt=8,
|
|
rollout_max_response_len=4096,
|
|
),
|
|
"NVIDIA-Nemotron-3-Nano-30B-A3B-BF16": _Recipe(
|
|
megatron_model_type="nemotron-3-nano-30b-a3b",
|
|
tp=2,
|
|
pp=2,
|
|
cp=1,
|
|
# 64 of the 128 experts per rank.
|
|
ep=2,
|
|
etp=1,
|
|
max_tokens_per_gpu=1024,
|
|
recompute=True,
|
|
log_probs_chunk_size=128,
|
|
use_rollout_routing_replay=True,
|
|
global_batch_size=128,
|
|
n_samples_per_prompt=4,
|
|
rollout_max_response_len=1024,
|
|
),
|
|
}
|
|
|
|
|
|
@dataclass
|
|
class ScriptArgs(U.ExecuteTrainConfig):
|
|
run_id: str = U.create_run_id()
|
|
model_name: _MODEL_NAMES = "NVIDIA-Nemotron-3-Nano-4B-BF16"
|
|
num_gpus_per_node: int = 8
|
|
num_rollout: int = 10
|
|
extra_args: str = ""
|
|
data_dir: str = "/root/datasets"
|
|
model_dir: str = "/root/models"
|
|
megatron_path: str = "/root/Megatron-LM"
|
|
|
|
@property
|
|
def recipe(self) -> _Recipe:
|
|
return _RECIPES[self.model_name]
|
|
|
|
|
|
def execute(args: ScriptArgs):
|
|
recipe = args.recipe
|
|
hf_checkpoint = f"{args.model_dir}/{args.model_name}"
|
|
|
|
ckpt_args = (
|
|
# --ref-load at an HF directory routes load_checkpoint through megatron.bridge,
|
|
# so the run needs no torch_dist copy of the weights.
|
|
f"--hf-checkpoint {hf_checkpoint} "
|
|
f"--ref-load {hf_checkpoint} "
|
|
f"--save {args.output_dir}/checkpoints "
|
|
"--save-interval 20 "
|
|
"--megatron-to-hf-mode bridge "
|
|
)
|
|
|
|
rollout_args = (
|
|
f"--prompt-data {args.data_dir}/dapo-math-17k/dapo-math-17k.jsonl "
|
|
"--input-key prompt "
|
|
"--label-key label "
|
|
"--apply-chat-template "
|
|
"--rollout-shuffle "
|
|
"--rm-type deepscaler "
|
|
f"--num-rollout {args.num_rollout} "
|
|
"--rollout-batch-size 32 "
|
|
f"--n-samples-per-prompt {recipe.n_samples_per_prompt} "
|
|
f"--rollout-max-response-len {recipe.rollout_max_response_len} "
|
|
"--rollout-temperature 1 "
|
|
f"--global-batch-size {recipe.global_batch_size} "
|
|
"--balance-data "
|
|
)
|
|
|
|
perf_args = (
|
|
f"--tensor-model-parallel-size {recipe.tp} "
|
|
"--sequence-parallel "
|
|
f"--pipeline-model-parallel-size {recipe.pp} "
|
|
f"--context-parallel-size {recipe.cp} "
|
|
f"--expert-model-parallel-size {recipe.ep} "
|
|
f"--expert-tensor-parallel-size {recipe.etp} "
|
|
)
|
|
if recipe.recompute:
|
|
perf_args += "--recompute-granularity full " "--recompute-method uniform " "--recompute-num-layers 1 "
|
|
perf_args += "--use-dynamic-batch-size " f"--max-tokens-per-gpu {recipe.max_tokens_per_gpu} "
|
|
if recipe.log_probs_chunk_size is not None:
|
|
perf_args += f"--log-probs-chunk-size {recipe.log_probs_chunk_size} "
|
|
|
|
grpo_args = (
|
|
"--advantage-estimator grpo "
|
|
"--use-kl-loss "
|
|
"--kl-loss-coef 0.00 "
|
|
"--kl-loss-type low_var_kl "
|
|
"--entropy-coef 0.00 "
|
|
"--eps-clip 0.2 "
|
|
"--eps-clip-high 0.28 "
|
|
)
|
|
|
|
optimizer_args = (
|
|
"--optimizer adam "
|
|
"--lr 1e-6 "
|
|
"--lr-decay-style constant "
|
|
"--weight-decay 0.1 "
|
|
"--adam-beta1 0.9 "
|
|
"--adam-beta2 0.98 "
|
|
)
|
|
|
|
sglang_args = "--rollout-num-gpus-per-engine 1 " "--sglang-mem-fraction-static 0.7 "
|
|
if recipe.use_rollout_routing_replay:
|
|
# Replay the exact rollout routing during the training forward so train logprobs
|
|
# match rollout logprobs (needed for MoE).
|
|
sglang_args += "--use-rollout-routing-replay "
|
|
|
|
misc_args = (
|
|
# default dropout in megatron is 0.1
|
|
"--attention-dropout 0.0 "
|
|
"--hidden-dropout 0.0 "
|
|
# should be good for model performance
|
|
"--accumulate-allreduce-grads-in-fp32 "
|
|
"--attention-softmax-in-fp32 "
|
|
"--attention-backend auto "
|
|
"--colocate "
|
|
f"--actor-num-nodes {args.num_nodes} "
|
|
f"--actor-num-gpus-per-node {args.num_gpus_per_node} "
|
|
f"--num-gpus-per-node {args.num_gpus_per_node} "
|
|
f"--rollout-num-gpus {args.num_nodes * args.num_gpus_per_node} "
|
|
)
|
|
|
|
train_args = (
|
|
f"{ckpt_args} "
|
|
f"{rollout_args} "
|
|
f"{optimizer_args} "
|
|
f"{grpo_args} "
|
|
f"{U.get_default_wandb_args(__file__, run_id=args.run_id)} "
|
|
f"{perf_args} "
|
|
f"{sglang_args} "
|
|
f"{misc_args} "
|
|
f"{args.extra_args} "
|
|
)
|
|
|
|
U.execute_train(
|
|
train_args=train_args,
|
|
config=args,
|
|
num_gpus_per_node=args.num_gpus_per_node,
|
|
megatron_model_type=recipe.megatron_model_type,
|
|
megatron_path=args.megatron_path,
|
|
)
|
|
|
|
|
|
@U.dataclass_cli
|
|
def main(args: ScriptArgs):
|
|
execute(args)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
typer.run(main)
|