Skip to content

Commit 5c7866f

Browse files
committed
feat(distillation): add experimental Nano3 TransferQueue QAOPD
Signed-off-by: Meng Xin <mxin@nvidia.com>
1 parent 86af478 commit 5c7866f

32 files changed

Lines changed: 4232 additions & 59 deletions

examples/configs/distillation_math.yaml

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,28 @@ loss_fn:
1717
kl_type: "mixed" # forward, reverse, mixed
1818
mixed_kl_weight: 0.5 # when kl_type is "mixed", this is the weight of the forward KL
1919
zero_outside_topk: false # zero out the teacher logits outside the top k when calculate forward KL loss
20+
sft_weight: 0.0
21+
entropy_diagnostics:
22+
enabled: false
23+
teacher_entropy_low: 0.3
24+
teacher_entropy_high: 0.7
25+
log_position_buckets: true
26+
entropy_aware:
27+
enabled: false
28+
teacher_entropy_threshold: 0.8
29+
weighting_mode: "threshold" # threshold, linear_ramp
30+
ramp_start: 0.3
31+
ramp_width: 0.6
32+
forward_kl_weight: 1.0
33+
high_entropy_reverse_kl_weight: 1.0
34+
high_entropy_reverse_kl_threshold: 0.7
35+
low_entropy_sharpening_enabled: false
36+
low_entropy_sharpening_weight: 0.0
37+
low_entropy_sharpening_temperature: 0.7
38+
low_entropy_sharpening_support_ratio_threshold: 1.0
39+
low_entropy_sharpening_requires_support_ratio_above_threshold: true
40+
require_sft_weight_zero: true
41+
require_zero_outside_topk_false: true
2042

2143
checkpointing:
2244
enabled: true
Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
# Nano3 QAOPD with TransferQueue
2+
3+
This experimental example trains an NVFP4 Nemotron 3 Nano student against the
4+
matching BF16 teacher. Student generation produces NeMo-Gym trajectories, the
5+
teacher writes top-k logits directly to TransferQueue, and the Megatron policy
6+
worker consumes the queued fields during training.
7+
8+
The supplied recipe preserves the development topology:
9+
10+
- 8 nodes with 4 GPUs per node
11+
- Megatron TP=4, CP=4, EP=8
12+
- vLLM TP=4 and EP=4
13+
- 128 prompts with 4 generations per prompt
14+
- global training batch size 512
15+
- teacher top-k 300, BF16 logits, and int32 indices
16+
- `mooncake_cpu` storage with microbatch-streamed top-k writeback
17+
18+
Treat these as starting values. Memory capacity and parallelism must be checked
19+
again when the GPU type, sequence length, or batch shape changes.
20+
21+
## Build an Enroot image
22+
23+
Enroot imports OCI/Docker images; it does not build a Dockerfile itself. Build
24+
the NeMo RL release image first, then convert it to a squashfs image.
25+
26+
Initialize the repository and build on the same CPU architecture as the target
27+
GPU cluster whenever possible:
28+
29+
```bash
30+
git submodule update --init --recursive
31+
32+
docker buildx build \
33+
--platform linux/arm64 \
34+
--target release \
35+
--build-context nemo-rl=. \
36+
--file docker/Dockerfile \
37+
--tag nemo-rl:nano3-tq \
38+
--load \
39+
.
40+
41+
enroot import \
42+
--output nemo-rl-nano3-tq.sqsh \
43+
dockerd://nemo-rl:nano3-tq
44+
```
45+
46+
Use `linux/amd64` for an x86_64 target. Cross-architecture builds require a
47+
Buildx builder with binfmt/QEMU support and are slower and less reliable for
48+
CUDA extension builds. A native build on the target architecture is preferred.
49+
50+
If Docker and Enroot are on different machines, push to a registry and import
51+
the image there. The `#` separates the registry from the image path in Enroot's
52+
Docker URI syntax:
53+
54+
```bash
55+
docker buildx build \
56+
--platform linux/arm64 \
57+
--target release \
58+
--build-context nemo-rl=. \
59+
--file docker/Dockerfile \
60+
--tag registry.example.com/team/nemo-rl:nano3-tq \
61+
--push \
62+
.
63+
64+
enroot import \
65+
--arch arm64 \
66+
--output nemo-rl-nano3-tq.sqsh \
67+
docker://registry.example.com#team/nemo-rl:nano3-tq
68+
```
69+
70+
The resulting `.sqsh` file can be passed directly to Pyxis through
71+
`--container-image` or the repository's `ray.sub` launcher.
72+
73+
Do not put registry credentials, Hugging Face tokens, W&B keys, or other
74+
secrets in the image, scripts, Git configuration, or recipe. Authenticate with
75+
the registry before import and inject runtime credentials through your
76+
cluster's secret-management mechanism.
77+
78+
## Prepare data
79+
80+
The launcher expects two files on storage shared by every Ray node:
81+
82+
1. NeMo-Gym JSONL training data accepted by `NemoGymDataset`.
83+
2. JSONL calibration data used to initialize the NVFP4 quantizers.
84+
85+
The model may be a shared local directory or the public Hugging Face model ID
86+
used by the recipe. For gated models, configure Hugging Face authentication in
87+
the runtime environment without adding credentials to this repository.
88+
89+
## Run on an existing Ray cluster
90+
91+
From the repository root inside the container:
92+
93+
```bash
94+
bash examples/modelopt/nano3_qaopd_tq/run_training.sh \
95+
/shared/data/train.jsonl \
96+
/shared/data/quant-calibration.jsonl \
97+
/shared/results/nano3-qaopd-tq \
98+
nvidia/NVIDIA-Nemotron-3-Nano-30B-A3B-BF16
99+
```
100+
101+
W&B is disabled in the example recipe. Checkpoints, JSON logs, and TensorBoard
102+
files are written below the supplied output directory.
103+
104+
## Submit through Slurm, Pyxis, and Ray
105+
106+
The submission wrapper takes all cluster-specific values as arguments. No
107+
account, partition, filesystem root, or credential is stored in the script.
108+
109+
```bash
110+
bash examples/modelopt/nano3_qaopd_tq/submit_slurm.sh \
111+
/shared/containers/nemo-rl-nano3-tq.sqsh \
112+
SLURM_ACCOUNT \
113+
SLURM_PARTITION \
114+
/shared \
115+
/shared/models/NVIDIA-Nemotron-3-Nano-30B-A3B-BF16 \
116+
/shared/data/train.jsonl \
117+
/shared/data/quant-calibration.jsonl \
118+
/shared/results/nano3-qaopd-tq
119+
```
120+
121+
The wrapper mounts only the specified shared root and the source checkout. The
122+
underlying `ray.sub` invocation uses exclusive nodes and disables automatic
123+
home-directory mounting inside the container.
124+
125+
For a smoke test, copy the recipe, reduce only the number of steps and prompt
126+
count, and keep sequence length, model parallelism, quantization, top-k, and
127+
TQ dtype settings unchanged when validating memory behavior.
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
#!/bin/bash
2+
# Copyright (c) 2026, NVIDIA CORPORATION. All rights reserved.
3+
#
4+
# Licensed under the Apache License, Version 2.0 (the "License");
5+
# you may not use this file except in compliance with the License.
6+
# You may obtain a copy of the License at
7+
#
8+
# http://www.apache.org/licenses/LICENSE-2.0
9+
#
10+
# Unless required by applicable law or agreed to in writing, software
11+
# distributed under the License is distributed on an "AS IS" BASIS,
12+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
# See the License for the specific language governing permissions and
14+
# limitations under the License.
15+
16+
set -euo pipefail
17+
18+
usage() {
19+
cat <<'EOF'
20+
Usage:
21+
run_training.sh TRAIN_DATA QUANT_CALIB_DATA OUTPUT_DIR [MODEL]
22+
23+
Arguments:
24+
TRAIN_DATA NeMo-Gym JSONL training data on shared storage.
25+
QUANT_CALIB_DATA JSONL calibration data on shared storage.
26+
OUTPUT_DIR Shared directory for checkpoints and logs.
27+
MODEL Local model path or Hugging Face model ID.
28+
29+
Optional environment:
30+
NUM_NODES Ray worker nodes (default: 8).
31+
GPUS_PER_NODE GPUs per node (default: 4).
32+
MAX_NUM_STEPS Training steps (default: 3000).
33+
EOF
34+
}
35+
36+
if (( $# < 3 || $# > 4 )); then
37+
usage >&2
38+
exit 2
39+
fi
40+
41+
TRAIN_DATA=$1
42+
QUANT_CALIB_DATA=$2
43+
OUTPUT_DIR=$3
44+
MODEL=${4:-nvidia/NVIDIA-Nemotron-3-Nano-30B-A3B-BF16}
45+
NUM_NODES=${NUM_NODES:-8}
46+
GPUS_PER_NODE=${GPUS_PER_NODE:-4}
47+
MAX_NUM_STEPS=${MAX_NUM_STEPS:-3000}
48+
49+
if [[ ! -f "$TRAIN_DATA" ]]; then
50+
echo "Training data does not exist: $TRAIN_DATA" >&2
51+
exit 1
52+
fi
53+
if [[ ! -f "$QUANT_CALIB_DATA" ]]; then
54+
echo "Calibration data does not exist: $QUANT_CALIB_DATA" >&2
55+
exit 1
56+
fi
57+
58+
mkdir -p "$OUTPUT_DIR"
59+
60+
export DISABLE_MODELOPT_LAYER_SPEC=1
61+
export ENABLE_BRIDGE_QUANT_MAPPING=1
62+
export NRL_TQ_MOONCAKE_BATCH_SIZE_LIMIT=1
63+
export NRL_TQ_MOONCAKE_MAX_WORKER_THREADS=1
64+
export MC_TCP_ENABLE_CONNECTION_POOL=1
65+
66+
uv run --locked --extra modelopt --extra mcore --extra nemo-gym \
67+
python examples/nemo_gym/run_distillation_nemo_gym.py \
68+
--config examples/modelopt/qa_distillation_nano3_megatron_tq.yaml \
69+
"policy.model_name=$MODEL" \
70+
"policy.tokenizer.name=$MODEL" \
71+
"teacher.model_name=$MODEL" \
72+
"teacher.tokenizer.name=$MODEL" \
73+
"policy.quant_calib_data=$QUANT_CALIB_DATA" \
74+
"data.train.data_path=$TRAIN_DATA" \
75+
"distillation.max_num_steps=$MAX_NUM_STEPS" \
76+
"cluster.num_nodes=$NUM_NODES" \
77+
"cluster.gpus_per_node=$GPUS_PER_NODE" \
78+
"checkpointing.checkpoint_dir=$OUTPUT_DIR/checkpoints" \
79+
"logger.log_dir=$OUTPUT_DIR/logs" \
80+
"logger.tensorboard.log_dir=$OUTPUT_DIR/tensorboard"
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
#!/bin/bash
2+
# Copyright (c) 2026, NVIDIA CORPORATION. All rights reserved.
3+
#
4+
# Licensed under the Apache License, Version 2.0 (the "License");
5+
# you may not use this file except in compliance with the License.
6+
# You may obtain a copy of the License at
7+
#
8+
# http://www.apache.org/licenses/LICENSE-2.0
9+
#
10+
# Unless required by applicable law or agreed to in writing, software
11+
# distributed under the License is distributed on an "AS IS" BASIS,
12+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
# See the License for the specific language governing permissions and
14+
# limitations under the License.
15+
16+
set -euo pipefail
17+
18+
usage() {
19+
cat <<'EOF'
20+
Usage:
21+
submit_slurm.sh CONTAINER ACCOUNT PARTITION SHARED_ROOT MODEL \
22+
TRAIN_DATA QUANT_CALIB_DATA OUTPUT_DIR [NUM_NODES] [GPUS_PER_NODE]
23+
24+
CONTAINER may be an Enroot .sqsh file or a registry image accepted by Pyxis.
25+
All local paths must be visible from every allocated node.
26+
EOF
27+
}
28+
29+
if (( $# < 8 || $# > 10 )); then
30+
usage >&2
31+
exit 2
32+
fi
33+
34+
CONTAINER_IMAGE=$1
35+
SLURM_ACCOUNT=$2
36+
SLURM_PARTITION=$3
37+
SHARED_ROOT=$4
38+
MODEL=$5
39+
TRAIN_DATA=$6
40+
QUANT_CALIB_DATA=$7
41+
OUTPUT_DIR=$8
42+
NUM_NODES=${9:-8}
43+
GPUS_PER_NODE=${10:-4}
44+
45+
REPO_ROOT=$(git rev-parse --show-toplevel)
46+
mkdir -p "$OUTPUT_DIR"
47+
48+
printf -v TRAIN_COMMAND '%q ' \
49+
bash examples/modelopt/nano3_qaopd_tq/run_training.sh \
50+
"$TRAIN_DATA" "$QUANT_CALIB_DATA" "$OUTPUT_DIR" "$MODEL"
51+
COMMAND="cd /opt/nemo-rl && $TRAIN_COMMAND"
52+
MOUNTS="$SHARED_ROOT:$SHARED_ROOT,$REPO_ROOT:/opt/nemo-rl"
53+
54+
CONTAINER="$CONTAINER_IMAGE" \
55+
MOUNTS="$MOUNTS" \
56+
COMMAND="$COMMAND" \
57+
BASE_LOG_DIR="$OUTPUT_DIR/ray" \
58+
GPUS_PER_NODE="$GPUS_PER_NODE" \
59+
sbatch \
60+
--nodes="$NUM_NODES" \
61+
--account="$SLURM_ACCOUNT" \
62+
--partition="$SLURM_PARTITION" \
63+
--time=04:00:00 \
64+
--exclusive \
65+
--dependency=singleton \
66+
--job-name=nano3-qaopd-tq \
67+
ray.sub

0 commit comments

Comments
 (0)