Files
Model-Optimizer/tools/launcher/launch.py
T
Chenhan D. YuandClaude Opus 4.6 3131195241 add: DFlash block diffusion speculative decoding (#1211)
DFlash (Block Diffusion for Flash Speculative Decoding) predicts an
entire block of tokens in a single forward pass using masked parallel
prediction with KV injection from the target model's hidden states.

Key features:
- Feature fusion (multi-layer hidden states -> FC + RMSNorm)
- KV injection (fused features as K/V in every draft layer with QK-norm)
- Random anchor sampling with bidirectional intra-block attention
- Logit distillation with exponential loss decay (gamma weighting)
- Multi-node DDP training with checkpoint resume
- Export to z-lab compatible HF format
- Online validation (context-dependent ground truth)

Training recipe:
modelopt_recipes/general/speculative_decoding/dflash.yaml
Results: examples/speculative_decoding/doc/dflash_results.md

### ModelOpt Eval (online validation, osl=512)

| Dataset | z-lab | ModelOpt (306K) | Diff |
|---------|-------|-----------------|------|
| gsm8k | 4.10 | **5.19** | **+1.09** |
| MT-Bench | 3.58 | **4.36** | **+0.78** |

### z-lab Official Eval (dflash.benchmark, osl=512)

| Dataset | z-lab | ModelOpt (306K) | Diff |
|---------|-------|-----------------|------|
| gsm8k | **5.00** | 4.08 | -0.92 |
| MT-Bench | **3.28** | 2.99 | -0.29 |

> z-lab model trained with block_size=16. ModelOpt trained with
block_size=8.

## Evaluation Method Impact (gsm8k)

| Eval Method | z-lab checkpoint | ModelOpt (306K) |
|-------------|-----------------|-----------------|
| Fixed GT (ModelOpt eval) | 2.95 | 4.23 |
| Online GT (ModelOpt eval) | 4.10 | **5.19** |
| z-lab official eval | **5.00** | 4.08 |

### What does this PR do?

Type of change: ? <!-- Use one of the following: Bug fix, new feature,
new example, new tests, documentation. -->

<!-- Details about the change. -->

### Usage

```python
# Add a code snippet demonstrating how to use this
```

### Testing
<!-- Mention how have you tested your change if applicable. -->

### Before your PR is "*Ready for review*"

Make sure you read and follow [Contributor
guidelines](https://github.com/NVIDIA/Model-Optimizer/blob/main/CONTRIBUTING.md)
and your commits are signed (`git commit -s -S`).

Make sure you read and follow the [Security Best
Practices](https://github.com/NVIDIA/Model-Optimizer/blob/main/SECURITY.md#security-coding-practices-for-contributors)
(e.g. avoiding hardcoded `trust_remote_code=True`, `torch.load(...,
weights_only=False)`, `pickle`, etc.).

- Is this change backward compatible?: ✅ / ❌ / N/A <!--- If ❌, explain
why. -->
- If you copied code from any other sources or added a new PIP
dependency, did you follow guidance in `CONTRIBUTING.md`: ✅ / ❌ / N/A
<!--- Mandatory -->
- Did you write any new necessary tests?: ✅ / ❌ / N/A <!--- Mandatory
for new features or examples. -->
- Did you update
[Changelog](https://github.com/NVIDIA/Model-Optimizer/blob/main/CHANGELOG.rst)?:
✅ / ❌ / N/A <!--- Only for new features, API changes, critical bug fixes
or backward incompatible changes. -->

### Additional Information
<!-- E.g. related issue. -->


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **New Features**
* Added DFlash speculative decoding mode with parallel block prediction
support.
* Included training launchers and MT-Bench evaluation scripts for DFlash
models.
* Added online acceptance rate validation for improved inference
verification.

* **Documentation**
* DFlash quick start guide with configuration parameters and training
examples.
  * Performance results and benchmarks for DFlash-trained models.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Signed-off-by: Chenhan Yu <chenhany@nvidia.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-14 15:58:39 -07:00

130 lines
4.7 KiB
Python

# SPDX-FileCopyrightText: Copyright (c) 2024 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
# SPDX-License-Identifier: Apache-2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""ModelOpt Launcher — submit quantization, training, and evaluation jobs to Slurm clusters.
Usage:
uv run launch.py --yaml examples/Qwen/Qwen3-8B/megatron_lm_ptq.yaml --yes
uv run launch.py --yaml examples/Qwen/Qwen3-8B/megatron_lm_ptq.yaml hf_local=/mnt/hf-local --yes
Environment variables:
SLURM_HOST Slurm login node hostname (required for remote jobs)
SLURM_ACCOUNT Slurm account/partition billing (default: from YAML)
SLURM_JOB_DIR Remote directory for job artifacts
SLURM_HF_LOCAL Path to HuggingFace model cache on the cluster
HF_TOKEN HuggingFace API token
NEMORUN_HOME NeMo Run home directory (default: current working directory)
"""
import getpass
import os
import subprocess # nosec B404
import warnings
import nemo_run as run
from core import SandboxPipeline, get_default_env, register_factory, run_jobs, set_slurm_config_type
from slurm_config import SlurmConfig, slurm_factory
set_slurm_config_type(SlurmConfig)
register_factory("slurm_factory", slurm_factory)
# ---------------------------------------------------------------------------
# Launcher-specific configuration
# ---------------------------------------------------------------------------
LAUNCHER_DIR = os.path.dirname(os.path.abspath(__file__))
MODELOPT_ROOT = os.path.dirname(os.path.dirname(LAUNCHER_DIR))
# Ensure modules/Model-Optimizer symlink exists (points to parent Model-Optimizer root)
_mo_symlink = os.path.join(LAUNCHER_DIR, "modules", "Model-Optimizer")
if not os.path.exists(_mo_symlink):
os.makedirs(os.path.join(LAUNCHER_DIR, "modules"), exist_ok=True)
os.symlink(os.path.relpath(MODELOPT_ROOT, os.path.join(LAUNCHER_DIR, "modules")), _mo_symlink)
EXPERIMENT_TITLE = "cicd"
DEFAULT_SLURM_ENV, DEFAULT_LOCAL_ENV = get_default_env(EXPERIMENT_TITLE)
packager = run.PatternPackager(
include_pattern=[
"modules/Megatron-LM/megatron/*",
"modules/Megatron-LM/examples/*",
"modules/Megatron-LM/*.py",
"modules/Model-Optimizer/modelopt/*",
"modules/Model-Optimizer/modelopt_recipes/*",
"modules/Model-Optimizer/examples/*",
"examples/*",
"common/*",
],
relative_path=[LAUNCHER_DIR] * 8,
)
MODELOPT_SRC_PATH = os.path.join(LAUNCHER_DIR, "modules/Model-Optimizer/modelopt")
# ---------------------------------------------------------------------------
# Entrypoint
# ---------------------------------------------------------------------------
@run.cli.entrypoint
def launch(
job_name: str = "01_job",
job_dir: str = os.environ.get("SLURM_JOB_DIR", os.path.expanduser("~/experiments")),
pipeline: SandboxPipeline = None,
hf_local: str = None, # noqa: RUF013
user: str = getpass.getuser(),
identity: str = None, # noqa: RUF013
detach: bool = False,
clean: bool = False,
) -> None:
"""Launch ModelOpt jobs on Slurm or locally with Docker."""
if clean:
examples_dir = os.path.join(_mo_symlink, "examples")
print(f"Cleaning {examples_dir} with git clean -xdf ...")
subprocess.run(["git", "clean", "-xdf", "."], cwd=examples_dir, check=True) # nosec B603 B607
if "NEMORUN_HOME" not in os.environ:
warnings.warn("NEMORUN_HOME is not set. Defaulting to current working directory.")
run.config.set_nemorun_home(os.environ.get("NEMORUN_HOME", os.getcwd()))
if hf_local is not None:
job_dir = os.path.join(os.getcwd(), "local_experiments")
job_table = {}
if pipeline is not None:
job_table[job_name] = pipeline
else:
print("No pipeline provided. Use pipeline=@<yaml> or --yaml <yaml>.")
return
run_jobs(
job_table=job_table,
hf_local=hf_local,
user=user,
identity=identity,
job_dir=job_dir,
packager=packager,
default_slurm_env=DEFAULT_SLURM_ENV,
default_local_env=DEFAULT_LOCAL_ENV,
experiment_title=EXPERIMENT_TITLE,
detach=detach,
modelopt_src_path=MODELOPT_SRC_PATH,
base_dir=LAUNCHER_DIR,
)
if __name__ == "__main__":
run.cli.main(launch)