nemo_automodel.components.speculative.decode_eval
nemo_automodel.components.speculative.decode_eval
Periodic real-acceptance-length eval during draft training (decode eval).
Training-time draft metrics (loss, top-1 accuracy, the simulated tau_sim)
are proxies: the acceptance length that matters is produced by the draft and
the target interacting inside a real speculative-decoding engine. This module
closes that gap without touching the training step:
- On a cadence (
decode_eval.every_steps), rank 0 snapshots the current draft weights to disk and launches a detached worker subprocess pinned to a reserved GPU (decode_eval.cuda_visible_devices). Training continues immediately; at most one eval is in flight. - The worker (this module’s CLI) packages the snapshot through the existing
serve_vllmconversion, boots a vLLM OpenAI server as its child, drives a fixed prompt set through it with the existingbench_vllmworkload runner, and writes a JSON result (realaccept_lengthfrom the engine’s spec-decode counters) next to the snapshot. - The trainer’s logging block collects finished results and logs them as
train/tau_real(withtrain/tau_real_stepmarking the optimizer step the evaluated snapshot was taken at).
The trainer process never imports vllm; the worker inherits the training env minus the torchrun/elastic variables, so the engine initializes as a plain single-process job on the reserved GPU.
Module Contents
Classes
Functions
Data
API
Resolved settings for the periodic decode eval.
every_steps is the launch cadence in optimizer steps; launches are
checked at the recipe’s logging points, so it should be a multiple of
log_every_steps (a non-multiple fires at the first log point past the
boundary). cuda_visible_devices is the GPU (or comma list) reserved for
the eval engine; training must not place work there.
Rank-0 trainer-side orchestrator: launch at cadence, collect results.
At most one eval subprocess is alive at a time; a launch that lands while
the previous eval is still running is skipped (the next cadence boundary
picks it up). Results are one-line JSON files the worker writes on success;
collect() returns the not-yet-seen ones in step order.
Remove heavyweight weights after an eval no longer needs them.
Report a completed worker and release its Popen state.
Return valid step directories in numeric order, ignoring stray names.
Return finished, not-yet-reported eval results in snapshot-step order.
Whether a new eval should launch at this optimizer step.
Snapshot the draft and launch the worker if the cadence is due and no eval is running.
Terminate a still-running eval (its vLLM child dies with the session).
Assemble the bench_vllm._run_summary argument namespace.
Choose an unused loopback port, or fail if an explicit port is occupied.
Build the vLLM api_server argv for the snapshot via the serve_vllm library surface.
Poll the vLLM server’s /health until it responds or dies/times out.
Training env minus torchrun/elastic state, pinned to the reserved GPU.
Write the draft’s current weights as a serve-ready consolidated export.
Produces <out_dir>/model/consolidated/{model.safetensors, config.json},
the same layout the final checkpoint’s consolidated export uses, so
serve_vllm.resolve_draft_artifacts can consume it directly (the
d2t/t2d vocab-mapping buffers ride along in the state dict).
Assumes the draft is replicated on the calling rank (the EAGLE-3 draft trains under DDP, so rank 0 holds the full weights); a sharded draft would snapshot an incomplete state dict.
Parameters:
the (unwrapped) draft nn.Module; its parameter and
buffer tensors are copied to CPU, so shapes/layouts are whatever the
draft’s state_dict() reports.
snapshot root; created if missing.
Returns: str
The model/consolidated directory path containing the export.
Worker entry: boot the engine on the snapshot, bench it, write the result JSON.
Read the optional decode_eval: block from recipe_args.
Returns None when the block is absent or every_steps is unset/0
(feature disabled). Raises on a partially-configured block so a typo’d GPU
reservation fails at setup rather than silently evaluating on a training
GPU. Field defaults live on :class:DecodeEvalConfig only; the block just
overrides the fields it sets.