API reference - thekaveh/NNx GitHub Wiki
Generated from runtime signatures and source docstrings as portable Markdown so the same reference is readable in the repository, on the documentation site, and in the wiki. Sections are ordered from foundational APIs to specialized modules.
module nnxnnx โ lightweight PyTorch training / eval / visualization toolkit.
Details
The core lives under `nnx.nn` (model, params, datasets, enums, nets,
callbacks); feature subpackages and modules such as `nnx.paradigms`,
`nnx.peft`, `nnx.calibration` and `nnx.distributed` sit beside it (see
`__all__`). The curated re-exports below give a flat surface for the most
common imports without forbidding the deep paths existing notebook code
relies on.
nnx.__version__ = '0.4.1' # x-release-please-versionExported value.
nnx.set_seed(seed: 'int', strict: 'bool' = False) -> 'None'Pin every RNG that affects training and toggle cuDNN deterministic.
Details
Args:
seed: integer seed shared across Python `random`, NumPy, and PyTorch
(CPU + CUDA). Also written to `os.environ["PYTHONHASHSEED"]`
so DataLoader workers started via the `spawn` method (default
on Windows + macOS/Py3.8+) inherit a deterministic hash seed.
Note: the current Python interpreter's hash state was fixed at
startup โ this assignment only affects spawned subprocesses.
For full hash determinism in the current process, set
`PYTHONHASHSEED=<N>` in the shell BEFORE invoking Python.
strict: when True also calls torch.use_deterministic_algorithms(True)
and sets CUBLAS_WORKSPACE_CONFIG. Slower and may raise on ops
that lack a deterministic CUDA implementation; opt in only when
full bit-for-bit reproducibility matters.
nnx.dataloader_worker_init_fn(worker_id: 'int') -> 'None'DataLoader worker_init_fn that pins each worker's numpy/python seed deterministically from the worker_id + the parent torch seed.
Details
Pass as: `DataLoader(..., worker_init_fn=dataloader_worker_init_fn)`.
nnx.env_snapshot(force_refresh: 'bool' = False) -> 'dict'Capture a snapshot of the runtime environment for reproducibility.
Details
Returned dict is JSON-serializable. Includes Python / torch / numpy
versions, GPU info if any, OS, and the git commit hash if running
inside a git repo. Safe to call from anywhere โ failures degrade to
`None` per field rather than raising.
Result is memoized within the process (versions/hardware don't
change between calls). Caveat: the ``git_commit`` / ``git_dirty``
fields are frozen at first call too, so a long session that commits
mid-run records the session-start git state in later runs'
metadata.yaml. Pass ``force_refresh=True`` to re-compute โ useful
in tests that mutate the environment, or to re-stamp git state.
class nnx.LRFinderResult(*, lrs: 'list[float]', losses: 'list[float]', suggested_lr: 'float', figure: 'go.Figure') -> 'None'Result of an lr_finder sweep.
Details
Attributes:
lrs: list of learning rates actually exercised. Length matches
``losses``. May be shorter than ``num_iter`` if the sweep
early-exited due to loss divergence.
losses: list of loss values, one per LR.
suggested_lr: the recommended ``max_lr`` for a subsequent
real training run โ the LR at the steepest-descent
point of the smoothed loss curve.
figure: Plotly ``Figure`` plotting loss vs log(LR) with the
suggested LR marked.
nnx.lr_finder(model: 'nn.Module', train_loader: 'DataLoader', *, loss_fn: 'Callable[[torch.Tensor, torch.Tensor], torch.Tensor]', optimizer_cls: 'type[torch.optim.Optimizer]' = torch.optim.adam.Adam, start_lr: 'float' = 1e-07, end_lr: 'float' = 10.0, num_iter: 'int' = 100, diverge_threshold: 'float' = 4.0, device: 'Optional[torch.device]' = None, ema_alpha: 'float' = 0.5) -> 'LRFinderResult'Sweep LRs exponentially from start_lr to end_lr and suggest a one-cycle max_lr.
Details
Args:
model: the network to sweep against. ``model.train()`` is
called internally; the original training-mode state, the
weights, AND the RNG state are all restored on exit.
train_loader: a DataLoader yielding ``(X, Y)`` batches the
model can forward and ``loss_fn`` can score against. The
loader is iterated, and if the sweep exceeds one epoch
the loader is re-iterated from the start.
loss_fn: callable ``(y_hat, Y) -> scalar Tensor`` for the
per-batch loss. Same shape contract as torch loss
functions. It receives the model's raw output: unlike
``NNModel.train`` / ``evaluate``, no log-softmax is inserted
for ``torch.nn.NLLLoss``, so pass
``lambda y_hat, Y: F.nll_loss(F.log_softmax(y_hat, dim=1), Y)``
(or simply ``F.cross_entropy``) when sweeping an NLL model.
optimizer_cls: optimizer class. Adam by default; SGD also
works for the sweep.
start_lr: low end of the sweep range. Must be > 0.
end_lr: high end of the sweep range. Must be > start_lr.
num_iter: number of training iterations to run. Must be >= 2.
diverge_threshold: stop the sweep early when the EMA-smoothed
loss rises more than ``(diverge_threshold - 1) * |smoothed_min|``
above ``smoothed_min`` (the minimum EMA-smoothed loss observed so
far; for a positive loss, past ``diverge_threshold * smoothed_min``). Default 4. The
smoothed check matches fastai's lr_find heuristic โ using
the raw ``min(losses)`` would let a single anomalous low
first-batch loss pull the threshold too tight and abort
the sweep prematurely.
device: device to move batches to. If None, inferred from
the first model parameter.
ema_alpha: smoothing coefficient for the loss curve before
the steepest-descent search. Default 0.5.
Returns:
:class:`LRFinderResult` with the raw sweep data, the suggested
max_lr, and a Plotly figure of loss vs log(LR).
Raises:
ImportError: Plotly (the ``plots`` extra, ``pip install
"thekaveh-nnx[plots]"``) is not installed โ raised before any
data, model or RNG state is touched.
ValueError: on invalid arguments (``num_iter`` not an integer >= 2, a non-finite
``start_lr`` / ``end_lr`` / ``diverge_threshold``,
``start_lr <= 0``, ``end_lr <= start_lr``,
``diverge_threshold <= 1``, ``ema_alpha`` outside ``(0, 1)``), a
``train_loader`` with a live persistent-worker iterator, or a
loader that yields no batch.
class nnx.nn.nn_model.NNModel(net_params: 'Optional[NNParams]' = None, params: 'Optional[NNModelParams]' = None, *, module: 'Optional[torch.nn.Module]' = None, batch_adapter: 'Optional[BatchAdapter]' = None)Top-level training/eval/predict wrapper around an nn.Module.
Details
Inherits from :class:`huggingface_hub.PyTorchModelHubMixin` (when the
``thekaveh-nnx[hub]`` extra is installed) to gain ``save_pretrained`` /
``push_to_hub`` / ``from_pretrained``. Without the extra installed,
those three methods raise a clear ImportError pointing at the extra;
no other NNModel functionality is affected.
property nnx.nn.nn_model.NNModel.task_adapterThe adapter for params.task, or None for a legacy classification model. Custom steps can call task_adapter.record(output, target, loss=...) to write the same task record the default step writes.
property nnx.nn.nn_model.NNModel.resolved_precisionThe precision this model runs in on its current device: params.precision resolved against device โ re- resolved, never read from saved metadata, whenever the device or the policy changes (a loaded model resolves on its destination device).
nnx.nn.nn_model.NNModel.to_onnx(self, path: 'str', example_input: 'Union[torch.Tensor, tuple, np.ndarray]', input_names: 'Optional[list[str]]' = None, output_names: 'Optional[list[str]]' = None, dynamic_batch: 'bool' = True, opset_version: 'int' = 17, dynamo: 'bool' = False) -> 'str'Export the underlying network to ONNX format.
Details
Args:
path: output filename (e.g., "model.onnx").
example_input: a tensor (or tuple of tensors for multi-input
nets) with realistic shape/dtype used to trace the network.
input_names: optional list of human-readable input port names.
output_names: optional list of human-readable output port names.
dynamic_batch: when True (default), marks dim 0 as dynamic so
the exported model accepts any batch size at inference.
opset_version: ONNX opset to target. 17 is broadly supported
by current runtimes.
dynamo: when False (default), uses the legacy TorchScript-based
`torch.onnx.export` path โ plain `pip install onnx` is
enough. When True, dispatches to PyTorch's new
`torch.export`-based exporter (default in torch>=2.9,
supports >2 GB models via external data, faster). The
dynamo path requires `onnxscript`; install via
`pip install thekaveh-nnx[onnx-dynamo]`.
Returns the path written. Network is put in eval mode for tracing.
A network that declares ``onnx_export_unsupported`` (a graph
classifier) is refused before anything is written, and so is a
mapping example: the export passes positional inputs only, so a
keyword-input module (``KeywordInputs``) needs a positional wrapper.
nnx.nn.nn_model.NNModel.from_checkpoint(checkpoint: 'NNCheckpoint', device: 'Optional[Devices]' = None, *, module: 'Optional[torch.nn.Module]' = None, batch_adapter: 'Optional[BatchAdapter]' = None, precision: 'Optional[PrecisionPolicy]' = None, exclude_submodules: 'Sequence[str]' = (), **model_kwargs: 'Any') -> 'Self'Rebuild a model, replay topology transforms, and load its weights.
Details
``exclude_submodules`` names top-level submodules the training run
attached to the net that are not part of the rebuilt architecture โ
a JEPA predictor registered as ``model.net._jepa_predictor``, say โ
whose weights are left out; every other weight still loads strictly.
A name the checkpoint does not hold raises ``ValueError``.
Ordinary and legacy FP32 checkpoints have no transforms. Converted
QAT checkpoints replay their persisted torchao recipe before state
loading; unsupported recipes fail explicitly rather than constructing
a model with the wrong topology.
A registered :class:`~nnx.models.ModelSpec` is rebuilt
through its factory โ an unregistered one raises
:class:`~nnx.models.MissingModelFactoryError`, and a rebuilt topology
that differs from the saved weights raises ``ValueError``, both
before any weight is loaded. A runtime-only module
(``reconstructible=False``) needs ``module=`` โ a module of the same
topology, into which the weights are loaded.
The precision policy is re-resolved on the destination
device (the constructor resolves it) โ never read from the
checkpoint's recorded precision โ so a policy that device cannot
run fails here; ``precision=`` replaces the saved policy (and the
legacy ``mixed_precision`` flag) for this model.
nnx.nn.nn_model.NNModel.save_pretrained(self, save_directory, *args: 'Any', **kwargs: 'Any') -> 'Any'Hub save (huggingface_hub.PyTorchModelHubMixin.save_pretrained).
Details
A runtime-only module has no factory to rebuild it from,
so it is rejected with :class:`~nnx.models.MissingModelFactoryError`
before any file or directory is written.
nnx.nn.nn_model.NNModel.freeze(self, *patterns: 'str') -> 'int'Freeze parameters under self.net matching any of patterns (fnmatch globs against the dotted parameter name). Returns the number of parameters newly frozen.
Details
Convenience wrapper around :func:`nnx.finetune.freezing.freeze`
โ use the standalone function when freezing a module that isn't
``self.net`` (e.g., a custom decoder hanging off this model).
nnx.nn.nn_model.NNModel.unfreeze(self, *patterns: 'str') -> 'int'Mirror of freeze โ set requires_grad=True on matching parameters.
nnx.nn.nn_model.NNModel.export_state_dict(self, path: 'str') -> 'str'Save just self.net.state_dict() to path.
Details
The file is a plain ``torch.save`` of a state-dict โ loadable by
any torch consumer without nnx installed, and by
:func:`nnx.finetune.load_pretrained` for the fine-tuning round-trip.
Companion to the NNCheckpoint format, which carries the params +
idp wrapper alongside the weights; ``export_state_dict`` strips
all of that and leaves just the weights. It records no
transformation recipe (``nnx.transforms``): it cannot
rebuild a recipe's topology alone โ materialize the recipe on a
fresh model before loading it, or keep a checkpoint or
``save_pretrained`` artifact, which carry the recipe.
Returns ``path`` so calls can be chained.
nnx.nn.nn_model.NNModel.train(self, params: 'NNTrainParams', callbacks: 'Optional[list[CallbackLike]]' = None, train_step_fn: 'Optional[TrainStepFn]' = None, eval_step_fn: 'Optional[EvalStepFn]' = None, salt: 'Optional[str]' = None, components: 'Optional[list[Any]]' = None, objective: 'Optional[Callable[[Any], Any]]' = None, provenance: 'Optional[ExperimentManifest]' = None, history: 'Optional[HistoryJournal]' = None, compile: 'Optional[CompileSpec]' = None, distributed: 'Optional[Any]' = None) -> 'NNRun'Train the model and return its persisted run history.
Details
Args:
params: Required loaders, optimizer/scheduler configuration,
epoch count, persistence controls, and optional resume source.
callbacks: Lifecycle callbacks invoked around training and epochs.
train_step_fn: Optional per-batch override; the default performs
supervised forward, loss, backward, and optimizer stepping.
eval_step_fn: Optional once-per-epoch validation override that
receives the complete validation loader.
salt: Optional string folded into the run.id hash so identical
(model, net, train) configs run as distinct experiments
without altering modeled params. ``None`` (the default)
preserves existing run.id hashes exactly.
components: Extra checkpointable components
(:class:`~nnx.StatefulComponent`) whose state is saved with
every checkpoint and restored on a stateful warm resume.
Callbacks and step functions that implement the protocol
(``EarlyStopping``, the JEPA step) are registered
automatically; names must be unique.
objective: Optional objective (``nnx.objectives``) โ
a callable returning loss terms with explicit denominators.
NNx's shared update engine then owns backward,
accumulation (``accumulate_grad_batches``, exact for uneven
microbatches and masks), mixed precision, clipping and the
optimizer step, firing ``Callback.on_optimizer_update`` once
per committed update. Mutually exclusive with
``train_step_fn``.
provenance: Optional :class:`~nnx.provenance.ExperimentManifest`
โ the declared intent. The fit records it
(``runs/<id>/provenance.json``, with its fingerprint) and a
fresh attempt (``attempt.json``: parent attempt and
checkpoint generation on resume; final status and last
committed checkpoint). Never part of the run id.
history: Optional :class:`~nnx.history.HistoryJournal`:
keep only its ``retention`` most recent records
in memory (``ctx.idps``, the returned ``NNRun.idps``) and
append every record once to ``runs/<id>/history/`` instead
of rewriting ``idps.csv`` each epoch. ``None`` (the default)
keeps the eager in-memory list and CSV. Never part of the
run id.
compile: Optional :class:`~nnx.compilation.CompileSpec`:
run the built-in step's FP32 forward (and the
fit's validation) through ``torch.compile``. The wrapper is
built per call beside ``model.net``, never assigned to it, so
checkpoints, export and optimizers see the eager module.
Refused, before any run is reserved, with a custom
``train_step_fn`` or ``objective``, a graph or custom net, a
reduced precision or a topology-changing callback (QAT).
``NNRun.compile`` records the request and what took effect.
``None`` (the default) trains eagerly. Never part of the run
id.
distributed: Optional :class:`nnx.distributed.DDP`:
train data-parallel over the process group ``torchrun``
started (``nnx.distributed.init_process_group()``), with
``nnx.distributed.train_loader`` / ``validation_loader``
partitions. Each update equals a single process training on
the ranks' union batch; records are global and identical on
every rank; only the writer rank holds the lease and writes
artifacts, and every rank returns the same run. FP32, the
default steps and one node only โ refused, on every rank
together, before any run is reserved otherwise. Never part
of the run id.
Returns:
The completed :class:`NNRun`, persisted with run metadata,
iteration history, and configured checkpoints under
``<cwd>/runs/<run.id>/``. The printed completion line names
``runs/<id>`` relative to the working directory: a display path
with no absolute prefix, so captured notebook output stays
portable; it is not artifact provenance.
Raises:
ValueError: If required training inputs are missing or invalid,
the model is fully frozen, or resume state is incompatible.
FileExistsError: If the content-addressed run already exists and
``overwrite_existing`` is false.
FloatingPointError: If the default step encounters non-finite loss.
The run lease prevents another process using ``overwrite_existing``
from deleting or interleaving artifacts until final persistence ends.
nnx.nn.nn_model.NNModel.evaluate(self, loader: 'Iterable[Any]', extra_metrics=None, metrics: 'Sequence[MetricSpec]' = ()) -> 'NNEvaluationDataPoint'Aggregate predictions across all batches in loader and compute a single NNEvaluationDataPoint. Aggregating (rather than averaging per-batch metrics) gives correct sample-weighted f1/precision/recall when the final batch is short.
Details
`extra_metrics` ({name -> callable(y_true, y_pred) -> float}) are
called once on the aggregate truth / decoded predictions.
`metrics` are declared :class:`~nnx.MetricSpec`s,
accumulated over every valid sample of the loader from the input
each declares (decoded labels, probabilities or continuous outputs)
and reported in the record's ``metrics`` under each spec's name. A
metric whose input this model cannot provide raises before any
batch is read.
Raises ValueError if the loader yields zero batches (there would be
nothing to average).
nnx.nn.nn_model.NNModel.predict(self, X) -> 'PredictResult'Run the network in eval mode and return raw logits and decoded predictions (argmax classes; continuous values for a regression task; 0/1 indicators for a bernoulli / BCE output).
Details
Accepts any of:
- ``np.ndarray`` (single input tensor) โ historical API.
- ``tuple[np.ndarray, ...]`` โ for multi-input networks.
- ``torch.Tensor`` / ``tuple[torch.Tensor, ...]`` โ skips the numpy
conversion when callers already have tensors.
- ``DataLoader`` โ iterates the loader, runs predictions per batch,
concatenates and returns the full result. Y labels in the batch
(if present) are ignored.
- a list or tuple of graph batches (``NNGraphDataset(sampler="full")``'s
loaders) โ read like a loader, graph seed rows only.
Returns a ``PredictResult`` (a ``NamedTuple`` of (logits, classes))
that unpacks like the original 2-tuple. For probabilities, decoded
labels and per-row sample ids, use :meth:`predict_proba`.
Non-destructive: ``self.net.training`` is snapshotted before
switching to ``eval()`` and restored on exit (matches
``NNModel.evaluate``, ``nnx.viz.activation_map``, and
``nnx.lr_finder``). Without this, a caller doing the common
train โ predict โ train-more pattern silently leaves the net
in ``.eval()`` mode.
nnx.nn.nn_model.NNModel.iter_predict(self, X: 'Iterable[Any]', spec: 'Optional[ProbabilitySpec]' = None, *, rich: 'bool' = False) -> 'PredictionStream'Stream predictions one loader batch at a time.
Details
Returns a :class:`~nnx.streaming.PredictionStream` โ use it as a
context manager โ over ``X``, a ``DataLoader`` or another iterable of
batches (in-memory arrays and tensors go to :meth:`predict`). Each
item is a :class:`~nnx.streaming.PredictionBatch` of ``logits``,
``classes`` and ``sample_ids``, or, with a ``spec`` (or ``rich=True``
for a model with a task), a :class:`~nnx.prediction.PredictionResult`
as :meth:`predict_proba` builds it. The batches follow loader order,
and concatenated they are exactly the eager result for the same
``DataLoader`` (the eager calls read other iterables, except a list of
graph batches, as one in-memory input): ``predict(X)``'s logits and classes and ``predict_proba(X)``'s
sample ids, graph seed-row slicing included.
Each batch runs in eval mode under ``no_grad``, and every submodule's
training mode is restored before the batch is yielded or its error
raised. The stream holds only the batch in flight; closing it drops
its references to the loader's iterator and the model and ends the
iteration, and a closed or consumed stream cannot be iterated again.
An empty loader yields no batches (the eager calls raise instead).
Over a shuffling ``DataLoader``, the first batch whose sample ids are
iteration positions warns (graph seed rows carry global node indices,
graph-level rows their own graph ids).
nnx.nn.nn_model.NNModel.predict_proba(self, X, spec: 'Optional[ProbabilitySpec]' = None) -> 'PredictionResult'Probability-aware prediction declared by an explicit spec.
Details
``spec`` may be omitted for a model with a task: the task
supplies it โ softmax over axis 1 for ``categorical``, sigmoid
decoded at the task's ``threshold`` for ``multilabel`` โ and a
``regression`` task returns its continuous values with
``probabilities=None`` and ``spec=None`` (``decoded`` holds the
values). An explicit ``spec`` always wins.
Accepts the same inputs as :meth:`predict` (arrays, tensors, tuples
and ``DataLoader``s, including graph loaders whose rows are sliced
to seed nodes) with the same non-destructive eval-mode contract,
and returns a :class:`~nnx.prediction.PredictionResult`: the same
raw logits ``predict()`` returns, probabilities (softmax over
``spec.class_axis`` for ``"categorical"``, element-wise sigmoid for
``"bernoulli"``), decoded values and ``sample_ids``: the row index
for arrays, tensors and tuples; the position in iteration order for
an ordinary ``DataLoader`` (the dataset index only when the loader
does not shuffle โ a shuffling loader warns); and the global node
index for graph seed rows. A spec that does not fit the logits is
rejected on the first loader batch.
The task kind is never inferred from the loss or the output shape.
For the default decoding rules, a categorical spec's ``decoded``
equals ``predict().classes`` when the class axes agree, and a
bernoulli spec's equals the ``BCEWithLogitsLoss`` threshold.
Raises :class:`~nnx.prediction.PredictionValidationError` for a
class axis or label count that does not fit the logits, or for
non-finite logits. Parameters and gradients are never touched.
class nnx.nn.nn_model.PredictResult(logits: 'np.ndarray', classes: 'np.ndarray')Structured result of NNModel.predict().
Details
Unpacks positionally as ``(logits, classes)`` so callers doing
``log, hat = model.predict(X)`` keep working after the upgrade from
the original 2-tuple. Field access (``result.logits``, ``result.classes``)
is preferred for new code.
class nnx.nn.nn_model.TrainStepContext(model: 'NNModel', batch: 'Any', optimizer: 'torch.optim.Optimizer', scaler: 'Optional[torch.amp.GradScaler]', grad_clip_norm: 'Optional[float]', extra_metrics: 'Optional[Mapping[str, Callable]]', accumulate_grad_batches: 'int', batch_idx: 'int', epoch_idx: 'int', is_last_batch: 'bool' = False, accumulation_state: 'Optional[GradientAccumulationState]' = None, epoch_summary: 'Optional[_TrainEpochSummary]' = None, precision: 'Optional[ResolvedPrecision]' = None, report_update: 'Callable[[], None]' = <no update listener>) -> 'None'Frozen bundle of state passed into a training-step function.
Details
The default `default_train_step` runs the standard supervised
forward/backward/step. Users can pass their own
`train_step_fn: Callable[[TrainStepContext], NNEvaluationDataPoint]`
to NNModel.train() for non-supervised paradigms (autoencoder, VAE,
link prediction, recommendation, diffusion, etc.). The custom step
is fully responsible for forward, backward, optimizer.step,
gradient accumulation, AMP scale/unscale, grad clipping, and the
NaN/Inf guard โ the context tells it what knobs are set; honoring
them is on the caller.
type alias nnx.nn.nn_model.TrainStepFnPublic type alias.
nnx.nn.nn_model.default_train_step(ctx: 'TrainStepContext') -> 'NNEvaluationDataPoint'Standard supervised training step: forward โ loss โ backward โ step.
Details
This is the body that `NNModel.train()` runs when no custom
`train_step_fn` is supplied. It honors:
- gradient accumulation (zero_grad at cycle start, step at cycle
end). A trailing partial cycle is stepped at the epoch boundary;
gradients use each loss's effective normalization weight.
- the run's precision: autocast around the forward only,
the backward outside it; fp16 unscales before grad clip and steps
through the scaler, scaler-free bf16 checks its gradients are
finite before clipping
- grad clipping by L2 norm
- the NaN/Inf guard (raises FloatingPointError on divergent loss)
- extra_metrics injection on the returned NNEvaluationDataPoint
Custom training-step functions can call this directly to layer on
behavior (e.g., extra logging) without reimplementing the standard
forward/backward dance.
class nnx.nn.nn_model.EvalStepContext(model: 'NNModel', val_loader: 'Iterable[Any]', extra_metrics: 'Optional[Mapping[str, Callable]]', epoch_idx: 'int', metrics: 'tuple[MetricSpec, ...]' = ()) -> 'None'Frozen bundle of state passed into a validation-step function.
Details
Mirrors :class:`TrainStepContext` for the per-epoch VALIDATION pass: users
can pass ``eval_step_fn: Callable[[EvalStepContext], NNEvaluationDataPoint]``
to ``NNModel.train()`` to replace the built-in classification ``evaluate()``
for non-classification paradigms (next-token LM perplexity, DPO margins,
regression MAE, ...). The step runs under ``torch.no_grad()`` with the
net in eval mode (every submodule's mode is restored after it), and its
returned EDP becomes ``val_edp`` โ recorded on the epoch's last idp and
persisted through the incremental run save like any built-in val metric.
type alias nnx.nn.nn_model.EvalStepFnPublic type alias.
class nnx.nn.generative_nn_model.GenerativeNNModel(net_params: 'NNParams', params: 'NNModelParams', tokenizer: 'Optional[NNTokenizerParams]' = None)Language model with an autoregressive generate() method.
Details
``tokenizer`` is held as a regular instance attribute (not a
constructor-arg of NNModel) so existing NNModel callers don't
have to know about it. It's required for ``generate()`` but
optional at construction โ train-time you can build the model
first and attach the tokenizer later.
nnx.nn.generative_nn_model.GenerativeNNModel.generate(self, prompt: 'str', *, max_new_tokens: 'int' = 64, temperature: 'float' = 1.0, top_k: 'Optional[int]' = None, top_p: 'Optional[float]' = None, repetition_penalty: 'float' = 1.0, stop: 'Optional[list[str]]' = None, seed: 'Optional[int]' = None, use_cache: 'bool' = True, logits_chain: 'Optional[LogitsChain]' = None, on_token: 'Optional[Callable[[int], None]]' = None, logits_pipeline: 'Optional[OrderedLogitsPipeline]' = None) -> 'str'Autoregressive decode from prompt.
Details
Args:
prompt: input text. Encoded via ``self.tokenizer``.
max_new_tokens: hard cap on new tokens emitted. Generation
also stops when a ``stop`` string is decoded. It does not
stop on an end-of-sequence token, and a prompt longer than
the context window (max_seq_len) slides the window rather
than stopping.
temperature: 0 means greedy (argmax). Higher values produce
more diverse output. Routes through TemperatureScaling.
top_k: keep only the top-k logits. None disables.
top_p: nucleus (top-p) cutoff. None disables.
repetition_penalty: divide previously-seen tokens' positive
logits by this. 1.0 is no-op (default).
stop: list of stop strings โ generation halts once any of
them appears in the decoded CONTINUATION (the prompt
itself is not searched, so a prompt containing a stop
string doesn't halt generation immediately; a stop
string straddling the prompt/continuation boundary is
likewise not detected โ matching the generated-text-only
convention HF uses). The continuation is decoded without
special tokens, so a special token such as ``"<eos>"``
never matches a stop string.
seed: when set, sampling is reproducible โ two calls with
the same seed + prompt + model produce identical output.
use_cache: when True (default), uses an incremental KV
cache โ each new token only re-runs attention on the
last position, not the whole prefix. When False, falls
back to the full-recompute path (kept for regression
testing). Both paths produce the same tokens for greedy
decoding (sampling paths agree given the same seed).
logits_chain: optional pre-built ``LogitsChain`` (see
``nnx.LogitsChain.builder()``). When provided, the
inline chain construction from ``temperature`` /
``top_k`` / ``top_p`` / ``repetition_penalty`` kwargs
is skipped โ the supplied chain is used as-is.
Power-user path for custom logit processors (e.g.,
logit-bias for forbidden tokens). When ``None`` (the
default), behavior is unchanged.
logits_pipeline: optional :class:`~nnx.generation.OrderedLogitsPipeline`:
its stages run exactly in declared order,
compiled once per call into fresh processors; like
``logits_chain`` it replaces the inline kwargs. Passing
both ``logits_chain`` and ``logits_pipeline`` raises
``ValueError`` before the model is touched.
on_token: optional callback invoked with each newly
generated token id immediately after it is appended.
Lets callers stream partial output or drive progress
reporting without re-running decode over the public forward /
apply_chain / sample_next_token primitives. ``None``
(default) is a no-op so existing callers are
unaffected. Fires only for newly generated tokens
(not prompt tokens) and on both the cached and
no-cache decode paths. Fires before any ``stop``
string check so the callback observes every emitted
token including the one that triggers a stop.
Returns:
The full decoded string (prompt + generated continuation).
Non-destructive: ``self.net.training`` is snapshotted before
switching to ``eval()`` and restored on exit (including the
exception path via ``try/finally``). Matches the convention
used by ``NNModel.predict`` / ``NNModel.evaluate``,
``nnx.diffusion.sample``, ``nnx.embeddings.embed_texts``,
``nnx.viz.activation_map``, and ``nnx.lr_finder``.
class nnx.trainer.trainer.Trainer(model: 'NNModel')Multi-optimizer training orchestrator.
Details
Constructed around a single NNModel. At train() time, builds one
torch.optim.Optimizer per entry in NNTrainerParams.optims (each
scoped to its sub-net via NNOptimParams.param_groups) and invokes
the user-supplied trainer_step_fn for each batch.
Same NNRun + per-tag NNCheckpoint cadence as NNModel.train(),
with the extra `trainer` block on NNRun preserving the multi-optim
configuration on disk.
nnx.trainer.trainer.Trainer.train(self, params: 'NNTrainerParams', trainer_step_fn: 'Optional[TrainerStepFn]' = None, callbacks: 'Optional[list[CallbackLike]]' = None, salt: 'Optional[str]' = None, components: 'Optional[list[Any]]' = None, objective: 'Optional[Callable[[Any], Any]]' = None, provenance: 'Optional[ExperimentManifest]' = None, history: 'Optional[HistoryJournal]' = None) -> 'NNRun'Run the multi-optimizer training loop and return the resulting NNRun.
Details
Args:
params: NNTrainerParams โ train_loader + n_epochs + optims dict +
(optional) schedulers dict + (optional) val_loader, seed,
save_phase_checkpoints, extra_metrics. Schedulers step once
per epoch by default (an ``optimizer_update``-clock scheduler
after each reported update); set auto_step_schedulers=False when the
custom step function owns scheduler timing.
trainer_step_fn: `Callable[[TrainerStepContext],
NNEvaluationDataPoint]`. The function owns the entire per-batch
update โ including which optimizers to step, in what order, and
with what loss(es). There is no supervised fallback: pass it or
``objective``.
objective: an objective (``nnx.objectives``) in place of
``trainer_step_fn``: the shared update engine accumulates its
loss terms over each window (the optimizers'
``accumulate_grad_batches``, which must agree), clips each
optimizer's parameters with its own ``grad_clip_norm`` and
steps every named optimizer that holds gradients once per
committed update (an optimizer without gradients is not
stepped but still counts), announcing each to
``Callback.on_optimizer_update``.
provenance: an optional ``nnx.provenance.ExperimentManifest``:
the declared intent, recorded with a fresh
attempt exactly as ``NNModel.train`` records it.
history: an optional ``nnx.history.HistoryJournal``:
a bounded in-memory window and an append journal instead of
the eager list and ``idps.csv``, exactly as in
``NNModel.train``.
callbacks: optional list of Callback instances. The callback
context exposes `ctx.optimizer` (primary, sorted-first), plus
a `ctx.optimizers` dict and `ctx.trainer` reference for
trainer-aware callbacks.
salt: mirrors ``NNModel.train()``'s ``salt`` parameter โ an
optional string folded into the run.id hash so identical
(model, net, train) configs run as distinct experiments
without altering modeled params. ``None`` (the default)
preserves existing run.id hashes exactly.
components: extra checkpointable components;
callbacks and a step function that implement
:class:`~nnx.StatefulComponent` register automatically.
Returns:
NNRun with per-iteration idps, persisted under runs/<run.id>/
alongside the standard FIRST/Q1/Q2/Q3/LAST/BEST checkpoints.
The printed completion line uses the same cwd-relative
``runs/<id>`` display path as ``NNModel.train``.
Raises:
ValueError: when params is None, params.train_loader is None,
neither or both of trainer_step_fn and objective are given,
any optim's NNOptimParams.is_valid() returns False, a
trainer_step_fn run sets grad_clip_norm or
accumulate_grad_batches, an objective run's optimizers share
a parameter, an objective run sets
auto_step_schedulers=False, or a callback registers update
listeners or sets ctx.stop_at_update.
RuntimeError: in a process group of more than one rank.
class nnx.trainer.trainer.TrainerStepContext(model: 'NNModel', batch: 'Any', optimizers: 'Mapping[str, torch.optim.Optimizer]', schedulers: 'Mapping[str, Any]', extra_metrics: 'Optional[Mapping[str, Callable]]', batch_idx: 'int', epoch_idx: 'int', report_update: 'Callable[[str], None]' = <no update listener>) -> 'None'Per-batch state passed into a trainer_step_fn.
Details
Mirrors TrainStepContext from NNModel.train() but with `optimizer`
(singular) replaced by `optimizers` (name-keyed dict) and `schedulers`
threaded through alongside for inspection. Step functions should only
call schedulers directly when ``auto_step_schedulers=False``.
`model` is the single NNModel the Trainer was constructed with;
`model.net` carries the actual nn.Module (which may itself be a
composite, e.g., a GAN-style wrapper exposing G and D as submodules).
type alias nnx.trainer.trainer.TrainerStepFnPublic type alias.
class nnx.trainer.params.NNTrainerParams(*, n_epochs: 'int', optims: 'Mapping[str, Union[NNOptimParams, NNOptimFactoryParams]]', schedulers: 'Mapping[str, NNSchedulerParams]' = <factory>, seed: 'Optional[int]' = None, data_id: 'Optional[str]' = None, save_phase_checkpoints: 'bool' = True, auto_step_schedulers: 'bool' = True, overwrite_existing: 'bool' = False, train_loader: 'Optional[DataLoader]' = None, val_loader: 'Optional[DataLoader]' = None, extra_metrics: 'Optional[Mapping[str, Callable]]' = None, resume_from_run_id: 'Optional[str]' = None, resume_from_checkpoint: 'str' = 'last', parent_run_id: 'Optional[str]' = None, resume_mode: 'str' = 'auto', metrics: 'tuple[MetricSpec, ...]' = (), monitor: 'Optional[MonitorSpec]' = None) -> 'None'Configuration for Trainer.train() โ the multi-optimizer parallel to NNModel.train() / NNTrainParams.
Details
`optims` is a name-keyed mapping of NNOptimParams; each entry
produces a distinct torch Optimizer. Use `NNOptimParams.param_groups`
on each entry (the fine-tuning hook from :mod:`nnx.finetune`) to scope an optimizer
to a subset of the model's parameters โ e.g., one optim for the
generator sub-net (`name_pattern="G.*"`), one for the discriminator
(`name_pattern="D.*"`) inside a single combined NNModel.
`schedulers` is similarly keyed and indexes the same names. Missing
entries default to ReduceLROnPlateau with the same defaults
NNTrainParams uses, so callers only have to populate schedulers for
the optims they want to customize.
`seed`, `save_phase_checkpoints`, `extra_metrics`, `train_loader`,
`val_loader` mirror NNTrainParams (`extra_metrics` are
``callable(y_true, y_pred)``; Trainer's validation calls them on the
aggregate). The custom `trainer_step_fn` owns every optimizer update;
by default Trainer steps every scheduler once after each epoch (an
``optimizer_update``-clock scheduler after each reported update) โ set
`auto_step_schedulers=False` when the custom step function owns
scheduler timing too.
`optims`, `schedulers` and `extra_metrics` are captured as read-only,
insertion-ordered snapshots at construction (also via `from_state`,
`dataclasses.replace` and the `with_*_loader` helpers): mutating the
mapping you passed in โ or reusing the builder that produced this
configuration โ never changes it, and item assignment / deletion on
the exposed mappings raises `TypeError`. Values are shared by
identity (immutable params dataclasses; runtime-only metric callables
and loaders are never copied).
nnx.trainer.params.NNTrainerParams.with_train_loader(self, value: 'DataLoader') -> 'NNTrainerParams'No public description is currently available.
nnx.trainer.params.NNTrainerParams.with_val_loader(self, value: 'DataLoader') -> 'NNTrainerParams'No public description is currently available.
nnx.trainer.params.NNTrainerParams.state(self)No public description is currently available.
nnx.trainer.params.NNTrainerParams.from_state(state: 'dict') -> 'NNTrainerParams'No public description is currently available.
nnx.trainer.params.NNTrainerParams.builder() -> 'NNTrainerParamsBuilder'Return a composite multi-optim builder. See NNTrainerParamsBuilder. Composes NNOptimParams.builder() + NNSchedulerParams.builder().
class nnx.trainer.params_builder.NNTrainerParamsBuilder() -> 'None'Composite builder for NNTrainerParams.
Details
Reach via `NNTrainerParams.builder()`. The required setter is
`.n_epochs(N)`; at least one `.optimizer(name, params)` call is
also required (`NNTrainerParams.__post_init__` rejects empty
optims). Schedulers, seed, loaders, etc. are all chained optionals.
Ownership (see `NNTrainerParams`): the custom ``trainer_step_fn``
owns every optimizer update; Trainer steps registered schedulers once
per epoch (an ``optimizer_update``-clock scheduler after each reported
update instead) unless ``.auto_step_schedulers(False)``.
`copy()` branches a (possibly partial) builder and `from_params()`
rebuilds one from an existing `NNTrainerParams`, so a shared base
(epochs, loaders, a common optimizer) is written once and each branch
adds its own optimizers, schedulers or `data_id`.
nnx.trainer.params_builder.NNTrainerParamsBuilder.copy(self) -> 'NNTrainerParamsBuilder'Return an independent branch of this builder, complete or partial.
Details
The `optims` / `schedulers` maps and the `extra_metrics` mapping
are copied, so registering, replacing or dropping an entry on one
builder never shows up on the other (nor in values already
built). Their values are shared by identity: optimizer and
scheduler params are immutable, and `DataLoader` s and metric
callables are runtime objects that are never copied or iterated.
Nothing is built or validated here โ `build()` validates each
branch, including the `schedulers โ optims` rule.
nnx.trainer.params_builder.NNTrainerParamsBuilder.from_params(params: 'NNTrainerParams') -> 'NNTrainerParamsBuilder'Return a builder pre-loaded with every field of params.
Details
`from_params(params).build()` reproduces `params` field for field,
with the same `state()` (sorted keys, omitted defaults) and the
same runtime insertion order of `optims`. Built-in and
registered-factory optimizer params, loaders and metric callables
keep their identity; the builder's own maps are fresh copies, so
extending it never alters `params`.
Raises:
TypeError: if `params` is not exactly an `NNTrainerParams`
(the single-optimizer `NNTrainParams` has no builder).
ValueError: if `params` carries a field this builder cannot
reproduce.
nnx.trainer.params_builder.NNTrainerParamsBuilder.n_epochs(self, n: 'int') -> 'NNTrainerParamsBuilder'Number of training epochs. Required.
nnx.trainer.params_builder.NNTrainerParamsBuilder.optimizer(self, name: 'str', params: 'Union[NNOptimParams, NNOptimFactoryParams]') -> 'NNTrainerParamsBuilder'Register one optimizer under name. Each name gets its own torch.optim.Optimizer at Trainer.train() time. Use NNOptimParams.builder() to construct a built-in params, or nnx.NNOptimFactoryParams for a registered factory.
nnx.trainer.params_builder.NNTrainerParamsBuilder.scheduler(self, name: 'str', params: 'NNSchedulerParams') -> 'NNTrainerParamsBuilder'Register one scheduler under name. The name must match a previously-registered .optimizer(name, ...) call โ .build() enforces the subset invariant.
nnx.trainer.params_builder.NNTrainerParamsBuilder.seed(self, value: 'int') -> 'NNTrainerParamsBuilder'Seed for reproducibility. None at default (no seeding via params; the caller's set_seed() is the only path).
nnx.trainer.params_builder.NNTrainerParamsBuilder.data_id(self, value: 'str') -> 'NNTrainerParamsBuilder'Name the dataset or split this configuration trains on.
Details
Part of `state()` and therefore of the run id, so two otherwise
identical configurations (e.g. sibling builder branches) over
different data get distinct run directories. None at default.
nnx.trainer.params_builder.NNTrainerParamsBuilder.metrics(self, *specs: 'MetricSpec') -> 'NNTrainerParamsBuilder'Declare named, registered metrics โ computed over the whole validation set by evaluate() and reported under each spec's name. Replaces any earlier declaration.
nnx.trainer.params_builder.NNTrainerParamsBuilder.monitor(self, spec: 'MonitorSpec') -> 'NNTrainerParamsBuilder'What BEST selection and plateau schedulers track.
nnx.trainer.params_builder.NNTrainerParamsBuilder.resume_from(self, run_id: 'str', checkpoint: 'str' = 'last', mode: 'str' = 'auto') -> 'NNTrainerParamsBuilder'Warm-resume from run_id's checkpoint: the model, every named optimizer and scheduler, the RNG and the registered components (callbacks such as EarlyStopping) continue where that run stopped. mode is "auto" (stateful when the checkpoint has training state, else weights-only), "stateful" (fail unless it has) or "weights_only". Serialized only as parent lineage, so the resumed run gets its own id.
nnx.trainer.params_builder.NNTrainerParamsBuilder.overwrite_existing(self, value: 'bool') -> 'NNTrainerParamsBuilder'Allow a run to replace an existing run directory with the same id. Default False; not part of state() or the run id.
nnx.trainer.params_builder.NNTrainerParamsBuilder.save_phase_checkpoints(self, value: 'bool') -> 'NNTrainerParamsBuilder'Whether to write phase checkpoints (FIRST / Q1 / Q2 / Q3 / LAST / BEST). Default True. The fluent contract is "last call wins" โ a prior .save_phase_checkpoints(False) followed by .save_phase_checkpoints(True) leaves the dataclass at the default (which state() then omits).
nnx.trainer.params_builder.NNTrainerParamsBuilder.auto_step_schedulers(self, value: 'bool') -> 'NNTrainerParamsBuilder'Choose whether Trainer steps every scheduler after each epoch (an optimizer_update-clock scheduler after each reported update).
Details
Disable this when the custom step function owns scheduler timing.
nnx.trainer.params_builder.NNTrainerParamsBuilder.train_loader(self, loader: 'DataLoader') -> 'NNTrainerParamsBuilder'Training DataLoader. Optional at Builder time (can be wired later via NNTrainerParams.with_train_loader).
nnx.trainer.params_builder.NNTrainerParamsBuilder.val_loader(self, loader: 'DataLoader') -> 'NNTrainerParamsBuilder'Validation DataLoader. Optional at Builder time (can be wired later via NNTrainerParams.with_val_loader).
nnx.trainer.params_builder.NNTrainerParamsBuilder.extra_metrics(self, metrics: 'Mapping[str, Callable]') -> 'NNTrainerParamsBuilder'Extra metrics, name-keyed callable(y_true, y_pred) -> float (truth first, decoded predictions second). Trainer's built-in validation calls each once on the aggregate predictions (NNModel.evaluate); the custom trainer_step_fn decides whether and how to call them on training batches.
nnx.trainer.params_builder.NNTrainerParamsBuilder.build(self) -> 'NNTrainerParams'Validate the key-subset invariant, then construct the dataclass.
Details
`schedulers.keys() โ optims.keys()` is the contract
`NNTrainerParams.__post_init__` enforces. We check here so the
user sees the violation at the Builder boundary โ e.g., they
called `.scheduler("d", ...)` without first calling
`.optimizer("d", ...)` โ rather than at the dataclass ctor.
`n_epochs` has no meaningful default โ call `.n_epochs(N)` before
`.build()`. Caught here too, for the same Builder-boundary reason.
Raises:
ValueError: if `.n_epochs(N)` was not called before
`.build()`, OR if a `.scheduler(name, ...)` was
attached for a name that has no corresponding
`.optimizer(name, ...)`. Both messages name the
Builder methods to call so the user can fix the chain
without consulting the dataclass schema.
class nnx.prediction.ProbabilitySpec(kind: 'str', class_axis: 'int' = -1, labels: 'Optional[tuple[str, ...]]' = None) -> 'None'Explicit declaration of how raw logits become probabilities.
Details
Args:
kind: ``"categorical"`` (softmax over ``class_axis``, argmax
decoding) or ``"bernoulli"`` (independent sigmoid per output,
``logit >= 0`` decoding).
class_axis: axis of the logits that indexes classes / outputs.
Negative values count from the end; ``-1`` (default) fits
``(N, C)`` classifier logits and class-last ``(B, T, V)``
language-model logits, while class-first outputs such as
``(N, C, H, W)`` need ``class_axis=1``. Axis 0 is always the
sample axis and cannot be the class axis.
labels: optional ordered class / output names, one per entry
along ``class_axis``; unique, non-empty strings.
Serializes with :meth:`state` / :meth:`from_state` (plain YAML-safe
types, label order preserved).
nnx.prediction.ProbabilitySpec.resolve_class_axis(self, ndim: 'int') -> 'int'Return class_axis as a non-negative axis of an ndim-D logits array, or raise if it is out of range or the sample axis.
nnx.prediction.ProbabilitySpec.state(self) -> 'dict[str, Any]'No public description is currently available.
nnx.prediction.ProbabilitySpec.from_state(state: 'Mapping[str, Any]') -> 'ProbabilitySpec'No public description is currently available.
class nnx.prediction.PredictionResult(logits: 'np.ndarray', probabilities: 'Optional[np.ndarray]', decoded: 'np.ndarray', sample_ids: 'np.ndarray', spec: 'Optional[ProbabilitySpec]') -> 'None'Probability-aware prediction for N samples.
Details
Attributes:
logits: raw network output, exactly what ``predict().logits``
returns for the same input.
probabilities: same shape as ``logits``; softmax over the class
axis (categorical, rows sum to 1) or element-wise sigmoid
(bernoulli, not normalized). ``None`` for a continuous
(regression-task) result, which has no probabilities.
decoded: categorical โ argmax class indices (``logits`` without
the class axis); bernoulli โ 0/1 indicators shaped like
``logits`` (``logit >= 0``, or the task's threshold);
continuous โ the predicted values themselves.
sample_ids: ``int64[N]`` identity of each row: the input row index
for arrays, tensors and ordinary loaders, and the global node
index (``input_id``) for seed rows of a graph loader.
spec: the :class:`ProbabilitySpec` that produced the result, or
``None`` for a continuous result (``NNModel.predict_proba``
on a regression task).
property nnx.prediction.PredictionResult.kind"categorical", "bernoulli" or "continuous" (no spec).
property nnx.prediction.PredictionResult.class_axisThe class axis of logits / probabilities, non-negative.
property nnx.prediction.PredictionResult.labelsNo public description is currently available.
property nnx.prediction.PredictionResult.class_indicesDecoded class indices โ categorical results only.
Details
Bernoulli outputs are independent per-output indicators, not
mutually exclusive classes, so they are refused here (and must
never be fed to class-index consumers such as
``VisUtils.confusion_matrix``).
nnx.prediction.PredictionResult.decoded_labels(self) -> 'np.ndarray'Decoded class names (categorical results with spec.labels).
nnx.prediction.prediction_from_logits(logits: 'Any', spec: 'ProbabilitySpec', *, sample_ids: 'Any' = None) -> 'PredictionResult'Build a PredictionResult from raw logits.
Details
``logits`` is an array or tensor whose axis 0 indexes samples and whose
``spec.class_axis`` indexes classes / outputs. Raises
:class:`PredictionValidationError` when the class axis is invalid, the
label count differs from the class-axis size, any logit is NaN or
ยฑinf (masked logits must use a large finite negative value), or
``sample_ids`` is not one id per sample. Probabilities are computed
stably (max-shifted softmax, ``exp(-|x|)`` sigmoid) in the logits'
float precision and returned in their dtype โ float16 is computed in
float32 and returned as float16, bfloat16 tensors are upcast to
float32, integer logits give float64.
class nnx.prediction.PredictionValidationErrorAn invalid ProbabilitySpec, or logits / sample ids that do not satisfy one (wrong label count, invalid class axis, non-finite logits, misaligned sample ids).
class nnx.prediction.NonFiniteLogitsErrorThe logits hold NaN or +-inf: the model's output, not the request, is at fault (a decision provider reports it as a provider failure).
nnx.optimizers.build_optimizer(net: 'nn.Module', params: 'AnyOptimParams', *, strict_param_groups: 'bool' = False) -> 'torch.optim.Optimizer'Build the optimizer params describes for net.
Details
The one construction hook behind ``NNModel.train`` (non-strict: an
unmatched trainable parameter joins a default group) and
``Trainer.train`` (``strict_param_groups=True``: each optimizer owns
only what its ``param_groups`` specs select, so disjoint optimizers
never co-own a parameter).
Built-in :class:`NNOptimParams` dispatch through
:class:`~nnx.nn.enum.optims.Optims` exactly as before. For
:class:`NNOptimFactoryParams` the factory is resolved in the registry,
called once with the groups a built-in would receive (the shared
:func:`~nnx.nn.enum.optims.resolve_param_groups` rule, each group with
explicit ``lr`` / ``weight_decay``) and the read-only config, and its
result is checked: it must be a ``torch.optim.Optimizer`` holding
exactly those parameters.
type alias nnx.optimizers.OptimizerFactoryPublic type alias.
class nnx.optimizers.OptimizerFactorySpec(id: 'str', version: 'int', config: 'Optional[Mapping[str, Any]]' = None) -> 'None'Stable, serializable reference to a registered optimizer factory.
Details
``id`` and ``version`` name the factory in the registry; ``config`` is
handed to it read-only and must be JSON-like โ ``None``, ``bool``,
``int``, finite ``float``, ``str``, lists (exposed as tuples) and
string-keyed mappings. The spec never holds the callable, so it
round-trips through ``run.yaml`` and reloads without the factory being
registered or imported. Immutable; equal specs have equal ``state()``.
nnx.optimizers.OptimizerFactorySpec.state(self) -> 'dict[str, Any]'No public description is currently available.
nnx.optimizers.OptimizerFactorySpec.from_state(state: 'Mapping[str, Any]') -> 'OptimizerFactorySpec'No public description is currently available.
nnx.optimizers.register_optimizer_factory(id: 'str', version: 'int', factory: 'OptimizerFactory', *, replace: 'bool' = False) -> 'None'Register factory under (id, version) for this process.
Details
``factory(param_groups, config)`` receives the resolved parameter
groups and the spec's read-only config and must return a
``torch.optim.Optimizer`` over exactly those parameters. Registering an
existing ``(id, version)`` raises unless ``replace=True``; bump
``version`` instead when the factory's behaviour changes, so resumed
runs can tell the difference.
nnx.optimizers.unregister_optimizer_factory(id: 'str', version: 'int') -> 'bool'Remove (id, version) from the registry; returns whether it was registered. Run metadata that references it still loads โ only training with it needs the registration.
nnx.optimizers.registered_optimizer_factories() -> 'tuple[tuple[str, int], ...]'Sorted (id, version) pairs currently registered.
nnx.optimizers.resolve_optimizer_factory(spec: 'OptimizerFactorySpec') -> 'OptimizerFactory'Look spec up in the registry. Never imports anything: an unregistered id or version raises ValueError naming what is registered.
nnx.optimizers.optim_params_from_state(state: 'Mapping[str, Any]') -> 'AnyOptimParams'Decode a serialized optimizer config into the right variant.
Details
A ``factory`` entry is the discriminator for
:class:`NNOptimFactoryParams`; anything else is a built-in
:class:`NNOptimParams` (every config without a ``factory``
entry). Decoding never resolves or runs a factory.
module nnx.tasksTask adapters: declare what a supervised model's outputs and targets mean.
Details
By default ``NNModel`` assumes categorical classification: the default
training step, :meth:`NNModel.evaluate` and :meth:`NNModel.predict` decode
by argmax (or the ``BCEWithLogitsLoss`` threshold) and every evaluation
record carries accuracy / f1 / recall / precision. A :class:`TaskSpec` on
``NNModelParams(task=...)`` opts a model into an explicit task instead::
params = NNModelParams(net=Nets.FEED_FWD, loss=Losses.MEAN_SQUARED_ERROR, task=TaskSpec.regression(2))
The task's adapter then owns, for the default step, ``evaluate()``,
``predict()`` and ``predict_proba()``:
* **validation** โ each batch's outputs and targets are checked for the
declared shape, dtype and value range *before* any backward pass or
optimizer update (:class:`TaskValidationError`);
* **masking** โ categorical targets equal to ``ignore_index``, and NaN
multilabel / regression targets, are excluded from the loss and every
metric alike;
* **loss units** โ the loss is averaged over the valid targets, with the
same denominators the default step already uses for gradient
accumulation, so uneven batches and accumulated windows reduce exactly
like one full batch;
* **decoding and metrics** โ argmax and accuracy / macro f1 / recall /
precision (``categorical``), independent sigmoid probabilities with a
declared threshold plus subset and element accuracy (``multilabel``),
continuous values with MSE / MAE and no classification fields
(``regression``).
Three kinds exist; the kind is always declared, never inferred:
* ``"categorical"`` โ mutually exclusive classes along axis 1 of the
output (``(N, C)`` or ``(N, C, ...)``); integer targets shaped like the
output without the class axis. Needs ``Losses.CROSS_ENTROPY`` or
``Losses.NEGATIVE_LOG_LIKELIHOOD``.
* ``"multilabel"`` โ independent binary labels: ``(N, L)`` logits and
0/1 targets of the same shape (NaN = masked). Needs
``Losses.BINARY_CROSS_ENTROPY`` (``BCEWithLogitsLoss``).
* ``"regression"`` โ continuous targets shaped like the output (a
``(N,)`` target is accepted for an ``(N, 1)`` output; NaN = masked).
Needs a regression loss (``Losses.MEAN_SQUARED_ERROR``, or any
non-classification loss module assigned to ``model.loss_fn``).
Language-model, ranking, link-prediction and structured-prediction tasks
are out of scope; ``Nets.TRANSFORMER`` and ``Nets.VIT`` models are rejected. Models without
a task keep the legacy classification behaviour and serialization.
class nnx.tasks.TaskSpec(kind: 'str', num_outputs: 'Optional[int]' = None, ignore_index: 'Optional[int]' = None, threshold: 'float' = 0.5, labels: 'Optional[tuple[str, ...]]' = None) -> 'None'Declaration of a supervised task (see the module docstring).
Details
Prefer the constructors :meth:`categorical`, :meth:`multilabel` and
:meth:`regression`.
Args:
kind: ``"categorical"``, ``"multilabel"`` or ``"regression"``.
num_outputs: classes / labels / targets along output axis 1, or
``None`` to accept the net's width. When set, a model whose
``output_dim`` differs is rejected at construction.
ignore_index: categorical only โ the target value excluded from
the loss and every metric (e.g. ``-100`` for padding).
threshold: multilabel only โ probability at or above which a label
is decoded as positive (default ``0.5``, i.e. ``logit >= 0``).
labels: optional ordered names, one per output.
Serializes with :meth:`state` / :meth:`from_state` as a versioned
mapping (``version`` = :data:`TASK_SPEC_VERSION`).
nnx.tasks.TaskSpec.categorical(num_classes: 'Optional[int]' = None, *, ignore_index: 'Optional[int]' = None, labels: 'Optional[Sequence[str]]' = None) -> 'TaskSpec'Mutually exclusive classes along output axis 1.
nnx.tasks.TaskSpec.multilabel(num_labels: 'Optional[int]' = None, *, threshold: 'float' = 0.5, labels: 'Optional[Sequence[str]]' = None) -> 'TaskSpec'Independent binary labels, decoded at probability >= threshold.
nnx.tasks.TaskSpec.regression(num_targets: 'Optional[int]' = None, *, labels: 'Optional[Sequence[str]]' = None) -> 'TaskSpec'Continuous targets shaped like the output.
nnx.tasks.TaskSpec.state(self) -> 'dict[str, Any]'Versioned, YAML-safe representation (defaults omitted).
nnx.tasks.TaskSpec.from_state(state: 'Mapping[str, Any]') -> 'TaskSpec'Rebuild a spec from state, rejecting an unknown version or key.
nnx.tasks.TaskSpec.probability_spec(self) -> 'Optional[ProbabilitySpec]'The ProbabilitySpec this task implies: categorical softmax or multilabel (bernoulli) sigmoid over axis 1, or None for regression (no probabilities).
class nnx.tasks.TaskAdapter(spec: 'TaskSpec') -> 'None'Validation, masking, loss units, decoding and metrics for one TaskSpec kind. Obtain one with task_adapter.
nnx.tasks.TaskAdapter.check_model(self, *, net: 'Any', loss: 'Losses', output_dim: 'Optional[int]') -> 'None'Reject a model configuration this task cannot drive โ before any net is built or loader is iterated. net is a Nets member or a registered / runtime descriptor, whose output width is unknown until the first batch (output_dim=None).
nnx.tasks.TaskAdapter.check_loss_fn(self, loss_fn: 'torch.nn.Module') -> 'None'Reject a runtime model.loss_fn that cannot score this task.
nnx.tasks.TaskAdapter.prepare(self, output: 'torch.Tensor', target: 'torch.Tensor') -> 'tuple[torch.Tensor, torch.Tensor, torch.Tensor]'Validate one batch and return (output, target, valid): tensors shaped for the loss plus a boolean mask of the targets that count. Raises TaskValidationError before any backward pass for a shape, dtype or value the task rejects.
nnx.tasks.TaskAdapter.loss_terms(self, loss_fn: 'torch.nn.Module', output: 'torch.Tensor', target: 'torch.Tensor', valid: 'torch.Tensor') -> 'tuple[torch.Tensor, torch.Tensor, Optional[float]]'(display loss, additive numerator, normalization weight) over the valid targets only โ the same contract as the default step's loss terms, so accumulation windows normalize once by their valid count. An all-masked batch contributes a differentiable zero with weight 0 and a NaN display loss.
nnx.tasks.TaskAdapter.decode(self, output: 'torch.Tensor') -> 'torch.Tensor'Decoded predictions for a prepared output.
nnx.tasks.TaskAdapter.decode_array(self, logits: 'np.ndarray') -> 'np.ndarray'Decoded predictions for raw predict() logits (numpy).
nnx.tasks.TaskAdapter.accumulator(self, *, keep_arrays: 'bool' = False, bounded: 'bool' = False) -> 'TaskMetricAccumulator'A fresh TaskMetricAccumulator; pass keep_arrays=True when extra_metrics will be computed from it. bounded=True keeps only counts and sums, so memory does not grow with the number of samples; it cannot keep arrays.
nnx.tasks.TaskAdapter.record(self, output: 'torch.Tensor', target: 'torch.Tensor', *, loss: 'Optional[float]', extra_metrics: 'Optional[Mapping[str, Callable]]' = None) -> 'NNEvaluationDataPoint'Validate one batch and return its task record โ for custom training steps that want the same record the default step writes.
nnx.tasks.TaskAdapter.check_logits(self, logits: 'np.ndarray') -> 'None'Shape check for raw prediction logits (a loader's first batch), so a mismatched net fails before the rest of inference runs.
nnx.tasks.TaskAdapter.prediction(self, logits: 'np.ndarray', sample_ids: 'np.ndarray') -> 'PredictionResult'The rich prediction this task implies (see predict_proba).
class nnx.tasks.TaskMetricAccumulator(adapter: 'TaskAdapter', *, keep_arrays: 'bool' = False, bounded: 'bool' = False) -> 'None'Mergeable per-task statistics over any number of batches.
Details
``update`` takes one prepared batch; ``result`` builds the
:class:`~nnx.NNEvaluationDataPoint` for everything seen so far.
Metrics are computed over the whole accumulation (not averaged per
batch), so uneven batches give the same numbers as one full batch.
``keep_arrays`` retains the valid targets and predictions for
user ``extra_metrics``; it is off unless those are requested.
nnx.tasks.TaskMetricAccumulator.update(self, output: 'torch.Tensor', target: 'torch.Tensor', valid: 'torch.Tensor') -> 'None'No public description is currently available.
nnx.tasks.TaskMetricAccumulator.result(self, *, loss: 'Optional[float]', extra_metrics: 'Optional[Mapping[str, Callable]]' = None) -> 'NNEvaluationDataPoint'No public description is currently available.
class nnx.tasks.TaskValidationErrorAn invalid TaskSpec, a model the task cannot drive, or a batch whose outputs / targets do not satisfy the declared task.
nnx.tasks.task_adapter(spec: 'TaskSpec') -> 'TaskAdapter'Return the adapter that drives spec.
class nnx.components.ComponentSpec(name: 'str', version: 'int' = 1, required: 'bool' = True, numbered: 'bool' = False) -> 'None'Identity and schema version of a checkpointable component.
Details
Args:
name: unique within a training run โ a filename-safe slug
(letters, digits, ``.``, ``_``, ``:``, ``-``).
version: schema version of ``component_state()`` (a positive
integer). A checkpoint written by a *newer* schema than the
component supports is rejected before anything is restored.
required: when ``True`` (default) a resume from a stateful
checkpoint that lacks this component's state fails; an optional
component simply keeps its fresh state.
numbered: ``name`` is a default the registry may number when several
components share it โ ``name``, ``name.2``, ``name.3`` โฆ in
registration order โ instead of rejecting the duplicate
(built-in callbacks such as ``EarlyStopping`` use it, so two of
them in one run still work). A resume then maps state by that
order. Explicit names leave it ``False`` and must be unique.
class nnx.components.StatefulComponent(*args, **kwargs)The contract a checkpointable component implements.
Details
A component may also define ``check_component_state(state, *, version)
-> Iterable[str]``: the registry calls it while validating a resume,
before anything is mutated, and reports every string it returns (for
example a configuration that no longer matches the saved state) in the
same :class:`ComponentRestoreError` as the other problems.
nnx.components.StatefulComponent.component_spec(self) -> 'ComponentSpec'No public description is currently available.
nnx.components.StatefulComponent.component_state(self) -> 'dict[str, Any]'No public description is currently available.
nnx.components.StatefulComponent.load_component_state(self, state: 'Mapping[str, Any]', *, version: 'int') -> 'None'No public description is currently available.
class nnx.components.ComponentRegistry(components: 'Iterable[Any]' = ()) -> 'None'Ordered, uniquely named set of the components of one training run.
nnx.components.ComponentRegistry.discover(*sources: 'Any', explicit: 'Iterable[Any]' = ()) -> 'ComponentRegistry'Register every source that implements StatefulComponent (callbacks, step functions), in order โ None and lists are flattened, anything else is ignored โ then every explicit component, which must implement the protocol (TypeError otherwise). An object that appears more than once (say, a callback also passed through components=[...]) is registered once.
nnx.components.ComponentRegistry.register(self, component: 'Any') -> 'str'Register component and return the name its state is saved under. Registering the same object again is a no-op; a different object with a taken name raises ValueError unless its spec is numbered.
property nnx.components.ComponentRegistry.namesNo public description is currently available.
nnx.components.ComponentRegistry.collect(self) -> 'dict[str, dict[str, Any]]'Every component's versioned state, for the training-state sidecar.
nnx.components.ComponentRegistry.fresh_plan(self) -> '_Plan'A plan that restores nothing: every component keeps its fresh state (weights-only resumes, checkpoints without component state).
nnx.components.ComponentRegistry.plan(self, saved: 'Optional[Mapping[str, Any]]') -> '_Plan'Validate saved component metadata against the registered components without mutating anything; raise one ComponentRestoreError listing every problem.
nnx.components.ComponentRegistry.restore(self, plan: '_Plan') -> 'tuple[str, ...]'Load a validated plan transactionally: on any failure every component attempted โ the failing one included โ gets a deep copy of its pre-call state back, then the original error propagates. A component whose own rollback fails does not stop the others from being rolled back, nor replace the original error; it is reported in a RuntimeWarning.
class nnx.components.ComponentRestoreError(problems: 'Iterable[str]') -> 'None'Saved component state that cannot be restored: every problem found (missing, unknown or incompatible components) is listed in problems, and nothing has been mutated when it is raised by validation.
class nnx.components.ResumeStatus(mode: 'str' = 'fresh', source_run_id: 'Optional[str]' = None, source_checkpoint: 'Optional[str]' = None, restored_components: 'tuple[str, ...]' = (), fresh_components: 'tuple[str, ...]' = (), source_epoch: 'Optional[int]' = None, source_generation: 'Optional[int]' = None, source_checkpoint_id: 'Optional[str]' = None) -> 'None'How a training session started.
Details
Attributes:
mode: ``"fresh"`` (no resume), ``"stateful"`` (model plus the
complete training-state bundle, components included) or
``"weights_only"`` (model weights only โ the checkpoint had no
training state, or ``resume_mode="weights_only"`` was asked).
source_run_id / source_checkpoint: where the resume came from.
source_epoch: the epoch of that checkpoint when the session resumed
from it (the history journal's lineage reads it); ``None`` for a
fresh run and for status recorded without it.
restored_components: names of the components whose state was
restored, in registration order.
fresh_components: registered components that kept their fresh
state (optional components absent from the checkpoint, or every
component on a weights-only resume).
source_generation / source_checkpoint_id: the resume point's
generation ordinal and checkpoint id from its manifest;
``None`` for a checkpoint without a manifest.
nnx.components.ResumeStatus.state(self) -> 'dict[str, Any]'No public description is currently available.
nnx.components.ResumeStatus.from_state(state: 'Mapping[str, Any]') -> 'ResumeStatus'No public description is currently available.
class nnx.monitors.MetricSpec(id: 'str', version: 'int' = 1, config: 'Optional[Mapping[str, Any]]' = None, name: 'Optional[str]' = None) -> 'None'Serializable declaration of a registered metric.
Details
Args:
id / version: the registration to use.
config: JSON-like options handed to the metric read-only
(``None``, ``bool``, ``int``, finite ``float``, ``str``, lists
and string-keyed mappings).
name: the key the value is reported under (default: ``id``); a slug,
unique among a run's metrics, and never ``"loss"`` / ``"error"``.
A name equal to a task's own metric (``mse``, ``subset_accuracy``)
replaces that value in the record's ``metrics``; loggers report
one equal to a record field (``accuracy``, ``f1``) as
``metrics/<name>``.
The spec never holds code, so it round-trips through ``run.yaml`` and
reloads without the metric being registered. It is resolved against
the registry when training starts: an unknown ``(id, version)`` then
fails before anything is trained, and no metric code has run.
property nnx.monitors.MetricSpec.labelThe key the metric is reported under.
property nnx.monitors.MetricSpec.definitionThe registration this spec names (ValueError when unknown).
property nnx.monitors.MetricSpec.inputNo public description is currently available.
property nnx.monitors.MetricSpec.modeNo public description is currently available.
nnx.monitors.MetricSpec.check(self) -> 'None'Resolve the registration and validate the config, without computing anything.
nnx.monitors.MetricSpec.accumulator(self) -> 'MetricAccumulator'No public description is currently available.
nnx.monitors.MetricSpec.state(self) -> 'dict[str, Any]'No public description is currently available.
nnx.monitors.MetricSpec.from_state(state: 'Mapping[str, Any]') -> 'MetricSpec'No public description is currently available.
class nnx.monitors.MetricDefinition(id: 'str', version: 'int', input: 'str', mode: 'str', factory: 'Callable[[Mapping[str, Any]], MetricAccumulator]', check_config: 'Optional[Callable[[Mapping[str, Any]], None]]' = None) -> 'None'A registered metric: what it receives and how it is computed.
class nnx.monitors.MonitorSpec(metric: 'str' = 'loss', split: 'str' = 'val', mode: 'Optional[str]' = None, min_delta: 'float' = 0.0, on_missing: 'str' = 'skip', on_nonfinite: 'str' = 'skip') -> 'None'What BEST selection, early stopping and plateau scheduling track.
Details
Args:
metric: ``"loss"``, ``"error"`` or the name of a declared
:class:`MetricSpec` (``NNTrainParams.metrics``).
split: ``"val"`` (default) โ the whole-validation-set record โ or
``"train"`` โ the whole-epoch training summary (full-epoch
denominators, never the last batch).
mode: ``"min"`` / ``"max"``; ``None`` (default) takes the metric's
natural direction (``"min"`` for loss and error).
min_delta: an improvement must beat the best by more than this
(finite, ``>= 0``); ties never improve.
on_missing: ``"skip"`` (default) โ an epoch without a value makes no
decision (not counted toward patience, no plateau step, no
BEST) โ or ``"error"``: raise :class:`MonitorUnavailableError`.
on_nonfinite: ``"skip"`` (default) โ a NaN / ยฑinf value is an epoch
without improvement โ or ``"error"``.
property nnx.monitors.MonitorSpec.key"<split>.<metric>", as shown by displays and loggers.
nnx.monitors.MonitorSpec.check_names(self, metrics: 'Sequence[MetricSpec]', owner: 'str' = 'monitor') -> 'None'Fail when the metric is neither a record field nor declared.
nnx.monitors.MonitorSpec.resolve(self, metrics: 'Sequence[MetricSpec]', owner: 'str' = 'monitor') -> 'MonitorSpec'Check the name against the declared metrics and fill in the metric's natural direction when mode is None.
nnx.monitors.MonitorSpec.improved(self, current: 'float', best: 'Optional[float]') -> 'bool'The shared rule: the first finite value improves; later values must beat best by more than min_delta (ties never improve); a non-finite value never improves.
nnx.monitors.MonitorSpec.value(self, *, train: 'Optional[NNEvaluationDataPoint]', val: 'Optional[NNEvaluationDataPoint]') -> 'Optional[float]'The monitored value from an epoch's training summary / validation record (None when absent).
nnx.monitors.MonitorSpec.state(self) -> 'dict[str, Any]'No public description is currently available.
nnx.monitors.MonitorSpec.from_state(state: 'Mapping[str, Any]') -> 'MonitorSpec'No public description is currently available.
class nnx.monitors.MonitorRecord(monitor: 'MonitorSpec', value: 'Optional[float]', status: 'str', improved: 'bool') -> 'None'One epoch's monitor decision, persisted with the epoch's record.
Details
``status`` is ``"ok"``, ``"missing"`` (no value; no decision) or
``"nonfinite"`` (an epoch without improvement); ``improved`` says
whether this epoch became the best.
nnx.monitors.MonitorRecord.state(self) -> 'dict[str, Any]'No public description is currently available.
nnx.monitors.MonitorRecord.from_state(state: 'Mapping[str, Any]') -> 'MonitorRecord'No public description is currently available.
class nnx.monitors.MonitorTracker(spec: 'MonitorSpec', *, warn_missing: 'bool' = False) -> 'None'Applies one MonitorSpec epoch by epoch: returns each epoch's MonitorRecord and remembers the best value.
Details
The run's tracker is also an optional checkpointable component
(``nnx.monitor``): a stateful warm resume continues from the
source run's best, so BEST keeps agreeing with a restored
``EarlyStopping`` and plateau scheduler after the split โ the resumed
run writes BEST only on an epoch that beats it, and none when no
resumed epoch does.
nnx.monitors.MonitorTracker.observe(self, value: 'Optional[float]', *, epoch: 'int') -> 'MonitorRecord'No public description is currently available.
nnx.monitors.MonitorTracker.component_spec(self) -> 'Any'No public description is currently available.
nnx.monitors.MonitorTracker.component_state(self) -> 'dict[str, Any]'No public description is currently available.
nnx.monitors.MonitorTracker.check_component_state(self, state: 'Mapping[str, Any]', *, version: 'int') -> 'list[str]'No public description is currently available.
nnx.monitors.MonitorTracker.load_component_state(self, state: 'Mapping[str, Any]', *, version: 'int') -> 'None'No public description is currently available.
class nnx.monitors.MonitorUnavailableErrorA monitor with on_missing="error" / on_nonfinite="error" found no finite value to decide on.
class nnx.monitors.MetricAccumulator(*args, **kwargs)Accumulates one metric over an epoch's full sample.
Details
``update`` receives one batch's valid targets and the declared
prediction input as NumPy arrays (sample axis first); ``result``
returns the metric over everything seen, or ``None`` if nothing was,
without changing the accumulation.
Optional, for bounded streaming (``nnx.streaming``):
``merge(other)`` adds another accumulator's state in place, keeping
nothing of ``other`` that a later update could change, and returns
``None``; ``stores_scores = True`` marks an accumulator that keeps every
score (such as a rank metric), which bounded streaming refuses. The
built-in metrics implement ``merge``.
nnx.monitors.MetricAccumulator.update(self, target: 'np.ndarray', prediction: 'np.ndarray') -> 'None'No public description is currently available.
nnx.monitors.MetricAccumulator.result(self) -> 'Optional[float]'No public description is currently available.
nnx.monitors.register_metric(id: 'str', version: 'int', factory: 'Callable[[Mapping[str, Any]], MetricAccumulator]', *, input: 'str', mode: 'str', check_config: 'Optional[Callable[[Mapping[str, Any]], None]]' = None, replace: 'bool' = False) -> 'None'Register a metric under (id, version) for this process.
Details
``factory(config)`` returns a fresh :class:`MetricAccumulator` for one
split of one epoch (``config`` is the spec's read-only config);
``input`` is one of :data:`METRIC_INPUTS`; ``mode`` is the natural
direction (``"min"``: lower is better). ``check_config(config)``, when
given, validates a spec's config as the run starts, before any batch.
Registering an existing ``(id, version)`` raises unless
``replace=True``; bump the version when the metric's meaning changes.
nnx.monitors.unregister_metric(id: 'str', version: 'int') -> 'bool'Remove a registration; True when one existed.
nnx.monitors.registered_metrics() -> 'tuple[tuple[str, int], ...]'Every registered (id, version), sorted.
class nnx.objectives.LossTerm(name: 'str', numerator: 'torch.Tensor', denominator: 'Optional[Union[int, float]]' = None, reduction: 'str' = 'mean', weight: 'float' = 1.0) -> 'None'One named part of an objective's loss for one microbatch.
Details
Args:
name: identifies the term across the microbatches of a window.
numerator: differentiable scalar โ the **sum** of the term over its
samples (or, for ``reduction="sum"``, the term's total).
denominator: for ``reduction="mean"``, the number (or total weight)
of samples ``numerator`` sums over โ ``0`` when every sample is
masked; must be ``None`` for ``reduction="sum"``.
reduction: ``"mean"`` (divided by the window-total denominator) or
``"sum"`` (the window total). A term keeps its reduction for
the whole window; mixing them fails.
weight: finite factor applied to the term's window value.
property nnx.objectives.LossTerm.valueThis microbatch's own value (None when fully masked).
class nnx.objectives.ObjectiveContext(model: 'NNModel', batch: 'Any', epoch_idx: 'int', batch_idx: 'int', extra_metrics: 'Optional[Mapping[str, Callable]]' = None, precision: 'Optional[ResolvedPrecision]' = None) -> 'None'What an objective sees for one microbatch.
Details
``precision`` is the run's resolved precision (``None`` when
called outside a run). The objective runs under its autocast, so a
reduced run's forward outputs are float16 / bfloat16:
:meth:`full_precision` returns them as float32 for the loss's
bookkeeping and the records (bf16 has no NumPy dtype).
nnx.objectives.ObjectiveContext.full_precision(self, tensor: 'torch.Tensor') -> 'torch.Tensor'tensor in full precision: a reduced run's float16 / bfloat16 output as float32 (differentiable), anything else unchanged.
class nnx.objectives.ObjectiveResult(terms: 'Sequence[LossTerm]', record: 'Optional[NNEvaluationDataPoint]' = None) -> 'None'An objective's output: the loss terms and, optionally, the microbatch's metric record (its loss is filled from the terms when absent).
nnx.objectives.ObjectiveResult.loss(self) -> 'Optional[float]'The microbatch's weighted loss over its unmasked terms.
class nnx.objectives.Objective(*, nonfinite: 'str' = 'fail') -> 'None'Base class for objectives: override __call__.
Details
``nonfinite`` (``"fail"`` default, or ``"skip"``) is the engine's policy
for a non-finite loss term or gradient. Any callable
``(ObjectiveContext) -> ObjectiveResult`` works as an objective; a plain
function uses ``"fail"``.
nnx.objectives.Objective.check_run(self, model: 'NNModel', *, optimizers: 'Mapping[str, torch.optim.Optimizer]', callbacks: 'Sequence[Any]') -> 'None'Refuse a run this objective cannot train โ called by NNModel.train and Trainer.train with the built optimizers and the callbacks, before any run is reserved or parameter changes. Raise to refuse; the default accepts every run.
nnx.objectives.Objective.after_update(self, events: 'tuple[UpdateEvent, ...]') -> 'None'Called once per committed update โ after the window's step (an optimizer without gradients is not stepped), never for a microbatch or a skipped window โ with its events (one per optimizer), before callbacks see them. The default does nothing.
class nnx.objectives.SupervisedObjective(*, nonfinite: 'str' = 'fail') -> 'None'The standard supervised loss as an objective: the model's loss_fn on its outputs, normalized by the loss's own denominator (non-ignored targets, class weights) โ or summed for a reduction="sum" loss โ and, with a TaskSpec, the task's masked loss over its valid targets.
class nnx.objectives.KDObjective(teacher: 'NNModel', *, alpha: 'float' = 0.5, temperature: 'float' = 4.0, nonfinite: 'str' = 'fail')Hinton knowledge distillation as an objective.
Details
Two terms: ``"distillation"`` โ the temperature-softened
``KL(teacher โ student)`` summed over the rows and normalized by their
count, scaled by ``Tยฒ`` (weight ``alpha``) โ and ``"supervised"`` โ
the student's loss on the labels with its own denominator (weight
``1 โ alpha``). Over a window this is exactly
``alpha ยท KL_batchmean + (1 โ alpha) ยท loss`` of the full batch, however
the rows are split into microbatches or targets are ignored. The teacher
is frozen and put in eval mode once, as ``kd_train_step_factory`` does,
and never updated. At ``alpha=0`` only the ``"supervised"`` term is
returned.
nnx.objectives.supervised_objective(*, nonfinite: 'str' = 'fail') -> 'SupervisedObjective'The standard supervised loss as an objective (see SupervisedObjective).
nnx.objectives.kd_objective(teacher: 'NNModel', *, alpha: 'float' = 0.5, temperature: 'float' = 4.0, nonfinite: 'str' = 'fail') -> 'KDObjective'Knowledge distillation as an objective (see KDObjective) โ the objective counterpart of kd_train_step_factory, which stays available (and unchanged) as the imperative step.
class nnx.objectives.UpdateEvent(optimizer: 'str', update_idx: 'int', epoch_idx: 'int', batch_idx: 'int', microbatches: 'int', losses: 'Mapping[str, float]', loss: 'float') -> 'None'One committed optimizer update (detached values only).
Details
Attributes:
optimizer: the optimizer that stepped (``"default"`` for
``NNModel.train``; the name for ``Trainer``).
update_idx: this optimizer's committed updates so far (1-based).
epoch_idx / batch_idx: where the window closed.
microbatches: microbatches the update accumulated.
losses: each term's window value (normalized terms divided by their
window-total denominator).
loss: the weighted total.
class nnx.models.ModelSpec(id: 'str', version: 'int' = 1, config: 'Optional[Mapping[str, Any]]' = None, *, seed: 'int' = 0)Stable, serializable reference to a registered model factory.
Details
``id`` and ``version`` name the factory; ``config`` is handed to it
read-only and must be JSON-like (``None``, ``bool``, ``int``, finite
``float``, ``str``, lists and string-keyed mappings); ``seed`` is the
``torch.manual_seed`` construction runs under. The spec never holds the
callable, so it round-trips through ``run.yaml``, checkpoints and Hub
configs. Immutable; equal specs have equal ``state()``.
nnx.models.ModelSpec.state(self) -> 'dict[str, Any]'No public description is currently available.
nnx.models.ModelSpec.from_state(state: 'Mapping[str, Any]') -> 'ModelSpec'No public description is currently available.
class nnx.models.RuntimeModule(module: 'str', topology: 'str') -> 'None'Descriptor of a caller-owned module that no factory describes.
Details
``module`` is the class's qualified name and ``topology`` a fingerprint
of its parameter and buffer names, shapes and dtypes and of its
``repr`` (layer hyperparameters such as dropout rates) โ never of
weight values. Like a built-in net, the run id therefore reflects the
architecture, not the weights: two differently-initialized instances of
one architecture share a run id, so give them distinct ``data_id`` /
``salt`` values. ``reconstructible`` is always ``False``: the weights
can only be loaded back into a module the caller supplies again.
nnx.models.RuntimeModule.of(module: 'nn.Module') -> 'RuntimeModule'The descriptor of module (its class and topology).
nnx.models.RuntimeModule.state(self) -> 'dict[str, Any]'No public description is currently available.
nnx.models.RuntimeModule.from_state(state: 'Mapping[str, Any]') -> 'RuntimeModule'No public description is currently available.
type alias nnx.models.ModelFactoryPublic type alias.
nnx.models.register_model_factory(id: 'str', version: 'int', factory: 'ModelFactory', *, replace: 'bool' = False) -> 'None'Register factory under (id, version) for this process.
Details
``factory(config)`` receives a :class:`ModelSpec`'s read-only config and
must return a ``torch.nn.Module``. Registering an existing
``(id, version)`` raises unless ``replace=True``; bump ``version`` when
the factory's architecture changes, so saved weights are never loaded
into a different topology.
nnx.models.unregister_model_factory(id: 'str', version: 'int') -> 'bool'Remove (id, version); returns whether it was registered. Runs and checkpoints that reference it still load their metadata โ only rebuilding the module needs the registration.
nnx.models.registered_model_factories() -> 'tuple[tuple[str, int], ...]'Sorted (id, version) pairs currently registered.
nnx.models.resolve_model_factory(spec: 'ModelSpec') -> 'ModelFactory'Look spec up in the registry. Never imports anything: an unregistered id or version raises MissingModelFactoryError naming what is registered.
nnx.models.build_module(spec: 'ModelSpec') -> 'nn.Module'Build spec's module: resolve the factory (failing before anything runs when it is unknown), then call it with Python's random, NumPy and torch all seeded from spec.seed and the ambient RNG states restored afterwards.
class nnx.models.MissingModelFactoryErrorA module cannot be rebuilt: its factory is not registered, or it is a runtime-only module (reconstructible=False) that no factory describes.
class nnx.models.BatchAdapter()How a non-built-in module sees a batch.
Details
:meth:`split` returns ``(args, kwargs, target)``: the module is called as
``module(*args, **kwargs)`` and ``target`` (``None`` when the batch has
none, e.g. for prediction) is what the loss scores. :meth:`output`
turns the module's raw return value into the output tensor; the default
requires a tensor. Subclass either to support other layouts.
nnx.models.BatchAdapter.split(self, batch: 'Any') -> 'tuple[tuple[Any, ...], dict[str, Any], Any]'No public description is currently available.
nnx.models.BatchAdapter.output(self, raw: 'Any') -> 'torch.Tensor'No public description is currently available.
class nnx.models.PositionalInputs(n_inputs: 'int' = 1) -> 'None'Positional inputs: a batch (x1, ..., xn, y) calls module(x1, ..., xn) and scores y; (x1, ..., xn) or a bare tensor has no target.
nnx.models.PositionalInputs.split(self, batch: 'Any') -> 'tuple[tuple[Any, ...], dict[str, Any], Any]'No public description is currently available.
class nnx.models.KeywordInputs(inputs: 'Sequence[str]', target: 'Optional[str]' = 'labels') -> 'None'Keyword inputs: a mapping batch calls module(**{name: batch[name] for name in inputs}) and scores batch[target] (None when the key is absent or target=None).
nnx.models.KeywordInputs.split(self, batch: 'Any') -> 'tuple[tuple[Any, ...], dict[str, Any], Any]'No public description is currently available.
nnx.models.module_topology(module: 'nn.Module') -> 'str'Fingerprint of module's state-dict names, shapes and dtypes plus its repr (layer hyperparameters). Uninitialized lazy parameters (nn.LazyLinear before its first forward) count by name only.
class nnx.decisions.Question()Base of the decision primitives. kind is the discriminator; digest identifies the exact question (kind, prompt, and every option's id and description in order) so a result can never be matched to a reordered or reworded question.
nnx.decisions.Question.state(self) -> 'dict[str, Any]'No public description is currently available.
nnx.decisions.Question.digest(self) -> 'str'SHA-256 of the canonical question state (computed once; questions are immutable).
nnx.decisions.Question.model_view(self) -> 'dict[str, Any]'What a model may see: the prompt and the option texts in order โ never the bookkeeping ids.
property nnx.decisions.Question.option_idsNo public description is currently available.
class nnx.decisions.Option(id: 'str', description: 'str') -> 'None'One answer of a Choice or one level of a Score.
Details
``id`` is bookkeeping โ how results are keyed, never shown to a model;
``description`` is the model-facing text.
class nnx.decisions.Choice(prompt: 'str', options: 'tuple[Option, ...]') -> 'None'Pick one of 2+ options; answered by a distribution over the options.
property nnx.decisions.Choice.option_idsNo public description is currently available.
nnx.decisions.Choice.state(self) -> 'dict[str, Any]'No public description is currently available.
nnx.decisions.Choice.model_view(self) -> 'dict[str, Any]'What a model may see: the prompt and the option texts in order โ never the bookkeeping ids.
class nnx.decisions.Boolean(prompt: 'str') -> 'None'A yes / no question; answered by one probability p_true.
nnx.decisions.Boolean.state(self) -> 'dict[str, Any]'No public description is currently available.
nnx.decisions.Boolean.model_view(self) -> 'dict[str, Any]'What a model may see: the prompt and the option texts in order โ never the bookkeeping ids.
class nnx.decisions.Score(prompt: 'str', levels: 'tuple[Option, ...]') -> 'None'Place the input on 2+ ordered levels (lowest first); answered by a distribution over the levels. The levels are ordinal: their order is meaningful, their spacing is not.
property nnx.decisions.Score.option_idsNo public description is currently available.
nnx.decisions.Score.state(self) -> 'dict[str, Any]'No public description is currently available.
nnx.decisions.Score.model_view(self) -> 'dict[str, Any]'What a model may see: the prompt and the option texts in order โ never the bookkeeping ids.
nnx.decisions.question_from_state(state: 'Mapping[str, Any]') -> 'Union[Choice, Boolean, Score]'Rebuild a question from Question.state, by its kind; malformed state raises InvalidDecisionRequest.
type alias nnx.decisions.DecisionResultPublic type alias.
class nnx.decisions.ChoiceResult(question_digest: 'str', distribution: 'tuple[tuple[str, float], ...]', provider: 'Optional[str]' = None, raw: 'Any' = None) -> 'None'A validated answer to a Choice: distribution in the question's option order. raw is the provider's own output, kept apart from the normalized fields (and from equality).
nnx.decisions.ChoiceResult.probabilities(self) -> 'dict[str, float]'No public description is currently available.
property nnx.decisions.ChoiceResult.topThe most probable option id (the first one on ties).
class nnx.decisions.BooleanResult(question_digest: 'str', p_true: 'float', provider: 'Optional[str]' = None, raw: 'Any' = None) -> 'None'A validated answer to a Boolean: one p_true in [0, 1].
property nnx.decisions.BooleanResult.p_falseNo public description is currently available.
class nnx.decisions.ScoreResult(question_digest: 'str', distribution: 'tuple[tuple[str, float], ...]', provider: 'Optional[str]' = None, vendor_score: 'Optional[float]' = None, raw: 'Any' = None) -> 'None'A validated answer to a Score: distribution over the levels in the question's order (lowest first).
Details
:attr:`expected_index` is ``sum(i * p_i)`` over zero-based levels โ an
**ordinal** summary (a position between levels), not an interval-scale
score: the levels' spacing is undefined. A provider's own numeric
score, if it reports one, stays in ``vendor_score`` and is never mixed
into the distribution.
nnx.decisions.ScoreResult.probabilities(self) -> 'dict[str, float]'No public description is currently available.
property nnx.decisions.ScoreResult.expected_indexNo public description is currently available.
nnx.decisions.validate_response(question: 'Union[Choice, Boolean, Score]', response: 'Any', *, provider: 'Optional[str]' = None, raw: 'Any' = None, vendor_score: 'Optional[float]' = None) -> 'DecisionResult'Validate a provider's keyed output against question and return the normalized result.
Details
For a :class:`Choice` or :class:`Score`, ``response`` maps option ids to
probabilities (a mapping, or ``(id, p)`` pairs in any order). The result
is reordered into the question's order; a missing, duplicate, unknown or
unlabeled (empty / non-string) id, a probability outside ``[0, 1]``
(one too large for a float included, a ``vendor_score`` too) and a
distribution not summing to 1 within :data:`PROBABILITY_TOLERANCE`
raise :class:`InvalidDecisionResponse` โ a malformed distribution is
never renormalized. For a :class:`Boolean`, ``response`` is ``p_true``
or ``{"true": p, "false": 1 - p}``. ``raw`` (the provider's own output)
and ``vendor_score`` (a :class:`Score` provider's own number) are kept
apart from the normalized fields; a ``vendor_score`` for another kind
of question raises :class:`InvalidDecisionResponse`.
class nnx.decisions.Capabilities(primitives: 'frozenset[str]', modalities: 'frozenset[str]', dynamic_labels: 'bool' = False, labels: 'Optional[tuple[str, ...]]' = None, max_batch: 'Optional[int]' = None, inference: 'bool' = True, training: 'bool' = False, export: 'bool' = False) -> 'None'What a provider declares it can do. check rejects anything else with UnsupportedCapability before any model call or network I/O.
Details
Attributes:
primitives: the decision kinds it answers (``"choice"``,
``"boolean"``, ``"score"``).
modalities: the input kinds it reads (e.g. ``"tensor"``,
``"text"``).
dynamic_labels: whether a request may bring any option set; ``False``
means the options must map exactly onto ``labels``.
labels: the fixed label space when ``dynamic_labels`` is ``False``;
``None`` declares none, so option sets are not checked.
max_batch: the most inputs per call (``None``: unbounded).
inference / training / export: whether the backend can answer, be
trained and be exported. A hosted provider declares inference
only.
nnx.decisions.Capabilities.check(self, question: 'Question', *, modality: 'str', batch_size: 'int', label_ids: 'Optional[Sequence[str]]' = None) -> 'None'Raise UnsupportedCapability unless this provider serves question for a batch of batch_size modality inputs.
Details
Without dynamic labels, a Choice's or Score's ids โ or ``label_ids``,
the provider's own mapping of them โ must be exactly the declared
``labels``, in any order.
class nnx.decisions.DecisionProvider(*args, **kwargs)Anything that answers typed decision questions: it declares Capabilities and answers one question for a batch of inputs with validated results, one per input.
nnx.decisions.DecisionProvider.capabilities(self) -> 'Capabilities'No public description is currently available.
nnx.decisions.DecisionProvider.decide(self, question: 'Question', inputs: 'Any') -> 'list[DecisionResult]'No public description is currently available.
class nnx.decisions.FixedHeadProvider(model: 'NNModel', labels: 'Optional[Sequence[str]]' = None, option_map: 'Optional[Mapping[str, str]]' = None, ordinal: 'bool' = False, max_batch: 'Optional[int]' = None, name: 'str' = 'nnx.fixed_head', question: 'Optional[Boolean]' = None) -> 'None'A trained NNx classifier as a decision provider.
Details
The head fixes the label space and the primitives it justifies: a
categorical head (softmax over its classes) answers :class:`Choice` โ
and :class:`Score` when constructed with ``ordinal=True``, whose levels
must then follow the head's class order โ and a single-logit Bernoulli
head (``BCEWithLogitsLoss``, or a one-output multilabel task) answers
one :class:`Boolean`: ``question``, the one it was trained for. Its
single probability is bound to no prompt, so any other Boolean (another
digest) raises :class:`UnsupportedCapability` before :attr:`model_calls`
advances. Probabilities come from the model's ``predict_proba``,
which evaluates in eval mode and restores every submodule's
training mode; they are computed in float64 from the returned logits,
so half-precision models meet the ``1e-6`` tolerance.
A request's option ids must be exactly the head's ``labels`` (in any
order), or ``option_map`` must map them one-to-one onto the labels; an
unseen or missing label raises :class:`UnsupportedCapability` before
:attr:`model_calls` advances. A label count that does not fit the head
is rejected at construction when the head's width is known (a task's
count or a built-in net's ``output_dim``), otherwise with
:class:`InvalidDecisionRequest` on the first call. Results come back in
the request's option order with each row's logits (a copy) as ``raw``;
an empty batch returns ``[]`` without calling the model.
Args:
model: the trained :class:`~nnx.NNModel`.
labels: one name per output class, in column order; defaults to the
model's ``TaskSpec`` labels, and must equal them when both exist.
option_map: request option id โ head label (a bijection);
categorical heads only.
ordinal: whether the class order is meaningful (enables Score);
categorical heads only.
max_batch: the most inputs per call.
name: the provider name recorded on results.
question: the :class:`Boolean` a Bernoulli head was trained to
answer โ required for one, refused for a categorical head
(which answers by its labels).
nnx.decisions.FixedHeadProvider.capabilities(self) -> 'Capabilities'No public description is currently available.
nnx.decisions.FixedHeadProvider.check(self, question: 'Question', inputs: 'Any') -> 'None'Every check decide makes before calling the model โ the question, the inputs' modality and batch size, and the label space (through option_map) or a Boolean's identity (its digest) โ so a caller (a decision job) can validate a request without calling the model.
nnx.decisions.FixedHeadProvider.decide(self, question: 'Question', inputs: 'Any') -> 'list[DecisionResult]'Answer question for every row of inputs (a tensor, an array or a tuple of them).
module nnx.decisions.nliA local label-conditioned baseline: decisions from a caller-supplied natural-language-inference (NLI) model.
Details
A fixed-head classifier knows only the labels it was trained on. An NLI
cross-encoder scores whether a *premise* (the input text) entails a
*hypothesis* (a sentence built from a candidate's description), so the
candidates can be supplied at inference time โ none of them needs to exist
when the provider is built::
from nnx.decisions import Choice, NLIProvider
provider = NLIProvider(model, tokenizer, entailment_id="entailment", contradiction_id="contradiction",
hypothesis_template="This text is about {}.", revision="abc123")
provider.decide(Choice("Topic?", (("t-sport", "sports"), ("t-econ", "the economy"))), texts)
**Scoring (explicit, never calibrated).**
- A :class:`Choice` scores every (input, candidate) pair and normalizes the
**entailment logits across the candidates** with a softmax
(``choice_scoring="entailment_softmax"``): logits ``log(3)`` and ``0``
give ``(0.75, 0.25)``. Results are keyed by the request's option ids, in
its order, whatever order the candidates arrive in. Only the candidates'
descriptions are scored: the Choice's prompt is not part of any
hypothesis (write it into ``hypothesis_template`` when it matters).
- A :class:`Boolean` scores one pair per input โ the prompt rendered by
``boolean_template`` โ and takes the softmax over that pair's
**contradiction and entailment** logits alone
(``boolean_scoring="entailment_vs_contradiction"``): contradiction ``0``,
entailment ``log(4)`` give ``p_true = 0.8``. Inputs are never normalized
together.
- :class:`Score` is unsupported (an NLI model has no ordinal semantics) and
is rejected before any model call.
These are zero-shot scores from a model trained for another task: they are
**not calibrated** probabilities, and how well they transfer to a decision
task stays an empirical question โ measure it on labelled records (see
``examples/decision_nli.py``).
**The provider contract.** NNx imports no NLI library and downloads
nothing: the caller passes a tokenizer and a ``torch.nn.Module`` already
loaded (from a local path, a pinned ``revision`` or a test stub). The
tokenizer is called HuggingFace-style โ ``tokenizer(premises, hypotheses,
truncation=..., max_length=..., padding=True, return_tensors="pt")``
returning a mapping of tensors (and, to measure lengths first, with
``truncation=False, padding=False`` and no ``return_tensors``, returning
per-pair ``input_ids`` โ or an ``attention_mask``, whose sums are used) โ
and the model as ``model(**encoded)``,
returning the logits ``(pairs, classes)`` or an object with ``.logits``.
Pairs are sent in chunks of ``pair_batch_size`` (the last chunk may be
shorter), moved to the model's device; the model runs in eval mode under
``no_grad`` and every submodule's training flag is restored afterwards, on
success and on failure.
**Truncation (explicit, reported).** Every pair is measured untruncated
first. ``truncation="only_first"`` cuts the premise to fit ``max_length``
and reports which pairs were cut in each result's ``raw["truncated"]``; a
hypothesis that leaves no room for the premise (measured with an empty
premise) rejects the request before any model call. ``truncation="error"`` rejects a request with an
over-long pair before any model call.
Each result's ``raw`` records the pair logits, the truncation report and
the provider's :meth:`NLIProvider.record` โ the NLI template, label ids,
scoring methods, truncation policy and model ``revision`` โ with
``"calibrated": False``, so a replayed or benchmarked result keeps how it
was scored.
class nnx.decisions.NLIProvider(model: 'Any', tokenizer: 'Any', entailment_id: 'Union[int, str]', contradiction_id: 'Union[int, str]', hypothesis_template: 'str' = 'This example is {}.', boolean_template: 'str' = '{}', choice_scoring: 'str' = 'entailment_softmax', boolean_scoring: 'str' = 'entailment_vs_contradiction', max_length: 'int' = 512, truncation: 'str' = 'only_first', pair_batch_size: 'int' = 16, max_batch: 'Optional[int]' = None, revision: 'Optional[str]' = None, name: 'str' = 'nnx.nli') -> 'None'A caller-supplied NLI model as a label-conditioned decision provider โ see the module docstring for the scoring, the provider contract and the truncation policy.
Details
Args:
model: the NLI cross-encoder (a ``torch.nn.Module``; ``model(**encoded)``
returns logits ``(pairs, classes)`` or an object with ``.logits``).
Its device is its parameters' device.
tokenizer: the matching tokenizer, called HuggingFace-style.
entailment_id / contradiction_id: the model's class ids for
entailment and contradiction โ ints, or label names resolved
through ``model.config.label2id``. Distinct, and within the
model's class count when its config declares one.
hypothesis_template: renders a Choice candidate's description
(exactly one ``{}``), e.g. ``"This example is {}."``.
boolean_template: renders a Boolean's prompt (exactly one ``{}``).
choice_scoring / boolean_scoring: the normalization methods (see the
module docstring); explicit so a record says how it scored.
max_length: the most tokens per pair.
truncation: ``"only_first"`` (cut the premise, report it) or
``"error"`` (reject over-long pairs before any model call).
pair_batch_size: pairs per model call.
max_batch: the most inputs per request (``None``: unbounded).
revision: the model revision the caller loaded (recorded, never
fetched).
name: the provider name recorded on results.
nnx.decisions.NLIProvider.capabilities(self) -> 'Capabilities'No public description is currently available.
nnx.decisions.NLIProvider.record(self) -> 'dict[str, Any]'How this provider scores: what a replay record or a benchmark keeps beside each result. The scores are never calibrated.
nnx.decisions.NLIProvider.hypotheses(self, question: 'Question') -> 'list[str]'The hypothesis per candidate (a Choice, in its option order) or the one hypothesis of a Boolean.
nnx.decisions.NLIProvider.check(self, question: 'Question', inputs: 'Any') -> 'None'Every check decide makes before calling the model โ the question, the texts (non-empty strings), the capabilities and the tokenizer-only length check of each pair โ so a caller (a decision job, the benchmark) can refuse a request without spending a call.
nnx.decisions.NLIProvider.decide(self, question: 'Question', inputs: 'Any') -> 'list[DecisionResult]'Answer question for every text in inputs.
module nnx.decisions.jevTyped decisions on Jev models through the TypeSafe Python SDK.
Details
:class:`JevProvider` answers :class:`Choice`, :class:`Boolean` and
:class:`Score` questions about text with one ``system_one`` call per input,
through the ``typesafe-sdk`` client โ NNx adds no second transport stack::
from nnx.decisions import Boolean, Choice, JevProvider
with JevProvider(model="jev-1.13.0") as provider: # TYPESAFE_API_KEY from the environment
topic, spam = provider.decide_many(
[Choice("Topic?", (("t-sport", "sports"), ("t-econ", "the economy"))), Boolean("Is this spam?")],
texts,
)
Install the extra first: ``pip install "thekaveh-nnx[jev]"`` (tested against
``typesafe-sdk>=0.7,<0.8``). Importing :mod:`nnx` or :mod:`nnx.decisions`
never imports the SDK; building a provider without an injected client does,
and raises an :class:`ImportError` naming the extra when it is missing.
**Translation.** A :class:`Choice` becomes a Jev ``choice`` keyed by its
options' descriptions โ the model-facing text, never the bookkeeping ids โ
a :class:`Score` a ``score`` whose criteria are its levels' descriptions in
order (lowest first), a :class:`Boolean` a ``noul``; the prompt is the
``instructions``. Every question of one :meth:`JevProvider.decide_many` goes
into the same call, one call per input. Answers are realigned to each
question's own option order and validated
(:func:`~nnx.decisions.validate_response`); a Score's expected score stays
in ``vendor_score``.
**Confidence.** A Choice's or Score's ``confidence`` is Jev's own certainty
in its selection, recorded as ``raw["provider_confidence"]`` โ it is not a
probability that the answer is correct. A Boolean has none.
**Metadata.** Each result's ``raw`` is plain JSON: ``provider``, the
``model`` the service resolved (even for an alias request), the
``requested_model`` when one was set, the ``request_id`` and the reported
``usage`` token counts. A field the service did not report is absent, and
clients, headers and credentials are never recorded.
**Retries and failures.** The SDK's :class:`~typesafe_sdk.RetryPolicy`
owns retries (pass ``retry=`` when the provider builds its client); the
adapter makes each request once. What still fails becomes a typed
:class:`JevError` โ :class:`JevTimeout`, :class:`JevAuthenticationError`,
:class:`JevRateLimited` or :class:`JevMalformedResponse` โ keeping the SDK
error as ``__cause__`` and, when the service answered, its ``request_id``
(a timeout or a lost connection has none).
**Cost.** Every text is its own ``system_one`` request, so one provider
call over ``n`` texts sends ``n`` requests: a benchmark ``Budget.max_calls``
or a job's ``Limits.max_requests`` counts provider calls, not requests โ set
``max_batch`` to bound requests per call. A failure on one text stops the
call; answers already received for earlier texts are not returned.
**Lifecycle.** An injected ``client`` / ``async_client`` stays the
caller's: the provider never closes it, and never builds a second client
beside it โ with only a sync client injected the provider is sync-only
(``adecide`` / ``adecide_many`` are ``None``, so a decision job's ``arun``
runs it in its own worker thread, one call at a time); with only an async
one, the sync methods are refused. With no client injected the provider builds its own (lazily, from
``api_key`` / ``base_url`` / ``timeout`` / ``retry`` or the SDK's
environment variables): :meth:`JevProvider.close` (``with``) closes a built
sync client, :meth:`JevProvider.aclose` (``async with``) both โ after an
error or a cancellation too. A built async client belongs to one event
loop; a call (or :meth:`JevProvider.aclose`) from another loop drops it
with a ``ResourceWarning`` โ close it with ``async with`` inside each
``asyncio.run``.
class nnx.decisions.JevProvider(client: 'Any' = None, *, async_client: 'Any' = None, model: 'Optional[str]' = None, api_key: 'Optional[str]' = None, base_url: 'Optional[str]' = None, timeout: 'Optional[float]' = None, retry: 'Any' = None, max_batch: 'Optional[int]' = None, name: 'str' = 'jev') -> 'None'A DecisionProvider for Jev models through the TypeSafe SDK. See the module docstring for translation, confidence, metadata, retry and lifecycle semantics.
Details
Args:
client: a ``typesafe_sdk.TypeSafeClient`` (or anything with its
``system_one``) the caller owns; never closed by the provider.
async_client: an ``AsyncTypeSafeClient`` the caller owns, for
:meth:`adecide` / :meth:`adecide_many`. Injecting either means
the provider builds no client at all.
model: the model to request (pin a version such as
``"jev-1.13.0"``; ``None`` uses the client's default).
api_key / base_url / timeout / retry: settings for the clients the
provider builds itself (``api_key`` defaults to the
``TYPESAFE_API_KEY`` environment variable). Refused with an
injected client, whose settings are the caller's.
max_batch: the most inputs per call (``None``: unbounded) โ each
input is one request.
name: the provider name recorded on results.
Raises:
ImportError: no client is injected and the ``jev`` extra is missing.
JevError: a client the provider builds cannot be configured (for
example no API key), at the first call.
nnx.decisions.JevProvider.capabilities(self) -> 'Capabilities'No public description is currently available.
nnx.decisions.JevProvider.record(self) -> 'dict[str, Any]'The provider's identity for benchmarks and replay: no client, header or credential.
nnx.decisions.JevProvider.check(self, question: 'Question', inputs: 'Any') -> 'None'Every check made before a request: the question, the inputs and the provider's capabilities โ no network I/O.
nnx.decisions.JevProvider.close(self) -> 'None'Close the sync client the provider built (never an injected one). A built async client needs aclose (async with).
nnx.decisions.JevProvider.aclose(self) -> 'None'Close every client the provider built, async one included.
nnx.decisions.JevProvider.decide(self, question: 'Question', inputs: 'Any') -> 'list[DecisionResult]'Answer question for every text in inputs.
nnx.decisions.JevProvider.decide_many(self, questions: 'Sequence[Question]', inputs: 'Any') -> 'list[list[DecisionResult]]'Answer every question for every text: one system_one call per text holding all the questions. One result list per question, one result per text.
nnx.decisions.JevProvider.adecide(self, question: 'Question', inputs: 'Any') -> 'list[DecisionResult]'decide through the async client.
nnx.decisions.JevProvider.adecide_many(self, questions: 'Sequence[Question]', inputs: 'Any') -> 'list[list[DecisionResult]]'decide_many through the async client (absent โ None โ when only a sync client was injected).
class nnx.decisions.DecisionErrorBase class of every decision-API error.
class nnx.decisions.UnsupportedCapabilityA request the provider cannot serve โ a primitive, modality, batch size or label space it does not declare. Raised before any model call or network I/O.
class nnx.decisions.InvalidDecisionRequestA malformed question: too few options, duplicate or empty ids, empty text.
class nnx.decisions.InvalidDecisionResponseA provider response that does not fit its question: missing, duplicate, unknown or unlabeled ids, or a probability outside [0, 1] or a distribution that does not sum to 1 (never renormalized).
class nnx.decisions.ProviderFailureThe provider's backend failed while answering a supported, valid request (the original error is the __cause__).
class nnx.decisions.JevError(message: 'str', *, request_id: 'Optional[str]' = None) -> 'None'A Jev request failed (the SDK error is the __cause__). request_id is the service's request id when it sent one.
class nnx.decisions.JevTimeout(message: 'str', *, request_id: 'Optional[str]' = None) -> 'None'The request exceeded its timeout (after the SDK's retries).
class nnx.decisions.JevAuthenticationError(message: 'str', *, request_id: 'Optional[str]' = None) -> 'None'The API key was rejected or lacks access (HTTP 401 / 403).
class nnx.decisions.JevRateLimited(message: 'str', *, request_id: 'Optional[str]' = None, retry_after_ms: 'Optional[float]' = None)The service refused the request for its rate limit (HTTP 429, after the SDK's retries). retry_after_ms is the wait it asked for, if any.
class nnx.decisions.JevMalformedResponse(message: 'str', *, request_id: 'Optional[str]' = None) -> 'None'The service answered with data that does not fit the request: a body the SDK cannot parse, a missing answer, or a distribution that does not fit the question.
module nnx.decisions.jobsDeferred decision jobs: batch independent questions, chain dependent ones.
Details
A :class:`DecisionJob` is an **immutable description** of decision work โ
building one calls no provider, runs no callback and draws no RNG::
from nnx.decisions import DecisionJob as Job
triage = Job.collect({
"topic": Job.ask(topic_question, id="topic"),
"urgent": Job.ask(urgent_question, id="urgent"),
}).map(lambda answers: answers["topic"])
routed = Job.ask(topic_question, id="topic").then(
lambda answer: Follow(Job.ask(team_question, id="team"), state=route_state(answer))
)
result = triage.run(provider, state=texts, limits=Limits(max_questions=2))
- :meth:`DecisionJob.ask` โ one question over the run's (or a
continuation's) **state**: the inputs the provider reads. Its value is the
provider's results, one per input row (or, with an abstention ``policy``,
the selective decisions).
- :meth:`DecisionJob.collect` โ independent jobs, keyed; the value is a
mapping in the given key order.
- :meth:`DecisionJob.map` โ a pure function of a job's value.
- :meth:`DecisionJob.then` โ a **dependent** job: once its prerequisite
succeeds, ``fn(value)`` returns a :class:`Follow` โ the next job and the
state it reads, mapped explicitly from the answer. It runs once, never
feeds a sibling question, and is bounded by ``Limits.max_depth``.
:meth:`DecisionJob.run` and :meth:`DecisionJob.arun` are the only effect
boundaries. They **borrow** the provider (never close it): independent
questions over the same state are batched into the fewest calls โ
``ceil(count / cap)`` per state, in a stable order โ where ``cap`` is the
provider's ``max_questions`` (a provider answering one question per call,
such as :class:`~nnx.decisions.FixedHeadProvider`, has cap 1) bounded by
``Limits.max_questions``. Duplicate question ids, questions the provider
cannot serve and a token cap the provider cannot enforce are rejected
before any call.
**Answers are checked.** Each call's answers must hold one result list per
question, one result per input row, each a result of that question (its
digest); anything else is an ``InvalidDecisionResponse`` failure.
**Fail-fast.** A provider error stops scheduling: :class:`JobFailed` carries
every outcome known so far (``outcomes``; the answered ones are
``completed``), the failing questions and the skipped ones; nothing
completed is re-run (the job never retries โ retries belong to the
provider). Request and depth limits raise :class:`JobLimitExceeded` (in
``arun``, after the calls already in flight finish), the ``timeout``
:class:`JobTimeout`, each with the outcomes so far; questions never sent
are ``skipped``.
**Async.** :meth:`DecisionJob.arun` runs up to ``Limits.max_concurrency``
provider calls at once through a provider's ``adecide_many`` / ``adecide``.
A provider with only synchronous methods is called in a worker thread,
never alongside another call (its methods need not be thread-safe), and the
run waits for a running thread before it returns โ a cancellation that
arrives meanwhile is delivered once the thread is done. Setting the
``cancel`` event (an ``asyncio.Event``; set at any point, even if cleared
again) stops scheduling โ no continuation runs after it โ and returns a
``"cancelled"`` :class:`JobResult`; cancelling the task cleans up the job's own tasks and
re-raises. A request whose call began is reported as ``sent`` โ never as
rolled back: whatever the provider did with it stays done.
**What "independent" means.** Computational, not statistical: questions
batched together share a call, nothing more. Their answers are separate
marginals; the job never multiplies them into a joint probability.
class nnx.decisions.DecisionJob(node: '_Node') -> 'None'An immutable, deferred description of decision work โ see the module docstring. Build with ask and collect, transform with map, chain with then; run with run / arun.
nnx.decisions.DecisionJob.ask(question: 'Question', *, id: 'str', policy: 'Any' = None, model_id: 'Optional[str]' = None) -> 'DecisionJob'One question over the state. id keys its outcome (unique in a run); an abstention policy (with the model_id it was tuned for) turns each row's result into a selective decision.
nnx.decisions.DecisionJob.collect(jobs: 'Mapping[str, DecisionJob]') -> 'DecisionJob'Independent jobs; the value maps each key to its job's value, in jobs' order.
nnx.decisions.DecisionJob.map(self, fn: 'Callable[[Any], Any]') -> 'DecisionJob'This job with fn applied to its value (pure: no provider calls).
nnx.decisions.DecisionJob.then(self, fn: 'Callable[[Any], Follow]') -> 'DecisionJob'A dependent job: after this one succeeds, fn(value) returns a Follow (the next job and its state), run once.
nnx.decisions.DecisionJob.question_ids(self) -> 'tuple[str, ...]'The ids of the questions known before running (not those a continuation will create), in scheduling order.
nnx.decisions.DecisionJob.state(self) -> 'dict[str, Any]'Plain data for a job of ask / collect only. A job holding a function (map, then) is a runtime value: refused.
nnx.decisions.DecisionJob.run(self, provider: 'Any', *, state: 'Any', limits: 'Optional[Limits]' = None) -> 'JobResult'Run the job synchronously with provider (borrowed, never closed) over state.
nnx.decisions.DecisionJob.arun(self, provider: 'Any', *, state: 'Any', limits: 'Optional[Limits]' = None, cancel: 'Optional[asyncio.Event]' = None) -> 'JobResult'Run the job on the event loop: up to limits.max_concurrency calls at once; setting cancel stops scheduling and returns a cancelled result (see the module docstring).
class nnx.decisions.Follow(job: 'DecisionJob', state: 'Any') -> 'None'What a continuation returns: the next job and the state it reads (the answer mapped explicitly into follow-up inputs).
class nnx.decisions.Limits(max_questions: 'Optional[int]' = None, max_tokens: 'Optional[int]' = None, max_depth: 'int' = 8, max_requests: 'Optional[int]' = None, timeout: 'Optional[float]' = None, max_concurrency: 'int' = 1) -> 'None'What a run may do.
Details
Attributes:
max_questions: the most questions per provider call (``None``: the
provider's own cap, or any number when it declares none; a
provider without ``decide_many`` answers one per call).
max_tokens: the most tokens per provider call โ enforceable only by
a provider with ``count_tokens(questions, state)``; any other is
refused before any call.
max_depth: the most nested continuations (``then``).
max_requests: the most provider calls in the run (``None``:
unbounded).
timeout: seconds for the whole run (``None``: none). ``run`` checks
it between calls and provider hooks; ``arun`` also cancels
in-flight asynchronous calls (a synchronous provider's call in
its worker thread is waited for).
max_concurrency: ``arun``'s concurrent provider calls.
class nnx.decisions.JobResult(value: 'Any', outcomes: 'Mapping[str, QuestionOutcome]', calls: 'int', status: 'str' = 'completed') -> 'None'A finished run: the job's value, every question's outcome in the order it was scheduled, the provider calls made and the status ("completed" or "cancelled"; a cancelled run has no value).
class nnx.decisions.QuestionOutcome(id: 'str', kind: 'str', rows: 'tuple[RowOutcome, ...]' = (), error: 'Optional[BaseException]' = None, sent: 'bool' = False) -> 'None'What happened to one question.
Details
``kind`` is ``"answered"`` (the provider answered; rows may still
abstain), ``"failed"`` (its provider call raised โ ``error``),
``"skipped"`` (never sent: a failure, a limit or the timeout stopped
scheduling) or ``"cancelled"`` (the run was cancelled, a sibling call
failed first, or the timeout elapsed while its call was in flight in
``arun``; ``sent`` says whether the request had already gone out โ
a sent request is not rolled back).
property nnx.decisions.QuestionOutcome.abstainedNo public description is currently available.
class nnx.decisions.RowOutcome(kind: 'str', result: 'DecisionResult', decision: 'Any' = None) -> 'None'One input row's answer: "answered", or "abstained" when the question's abstention policy declined it (the result is kept either way).
class nnx.decisions.JobError(message: 'str', *, outcomes: 'Optional[Mapping[str, QuestionOutcome]]' = None) -> 'None'Base of the decision-job errors. outcomes maps question ids to every QuestionOutcome known when the error was raised (answered, failed, skipped or cancelled โ a request already sent says so), in scheduling order; completed is the answered subset. Both are empty when the job was refused before any call.
Details
The error pickles (a process-pool worker's error reaches its parent): a
provider error that would not survive the round trip โ on its own or as
a ``__cause__`` โ is replaced by a :class:`ProviderFailure` naming it,
and an answer's provider ``raw`` output that would not is dropped.
class nnx.decisions.InvalidJob(message: 'str', *, outcomes: 'Optional[Mapping[str, QuestionOutcome]]' = None) -> 'None'A job that cannot run as described: duplicate question ids, an unsupported question, a token cap the provider cannot enforce, a continuation that returns something other than a Follow.
class nnx.decisions.JobFailed(message: 'str', *, outcomes: 'Mapping[str, QuestionOutcome]', failed: 'Sequence[str]', skipped: 'Sequence[str]') -> 'None'A provider call failed (fail-fast): failed names the questions of that call, skipped the known questions that were never sent (none when the job was refused before any call); the provider's error is the __cause__.
class nnx.decisions.JobLimitExceeded(message: 'str', *, outcomes: 'Optional[Mapping[str, QuestionOutcome]]' = None) -> 'None'A request or depth limit stopped the job before the next call.
class nnx.decisions.JobTimeout(message: 'str', *, outcomes: 'Optional[Mapping[str, QuestionOutcome]]' = None) -> 'None'The run's timeout elapsed; in-flight requests were sent and are not rolled back.
class nnx.decisions.benchmark.Sample(id: 'str', question: 'Question', input: 'Any', label: 'Union[str, bool]', family: 'str' = 'default', heldout: 'bool' = False, group: 'Optional[str]' = None, perturbation: 'Optional[str]' = None) -> 'None'One benchmark item.
Details
Attributes:
id: the sample id โ how records join (never row position).
question: the typed question; its digest is the schema identity.
input: what the provider reads for this sample (a text, a tensor
row, an array row).
label: the true option id (Choice / Score) or ``True`` / ``False``
(Boolean).
family: the task family.
heldout: whether the family is held out (reported apart from
in-family rows).
group: the grouping unit intervals resample (default: the id).
perturbation: what produced this sample from another (``None``:
an original).
property nnx.decisions.benchmark.Sample.digestNo public description is currently available.
property nnx.decisions.benchmark.Sample.unitThe grouping unit (group, else the id).
property nnx.decisions.benchmark.Sample.true_labelNo public description is currently available.
class nnx.decisions.benchmark.Record(sample_id: 'str', question_digest: 'str', provider: 'str', status: 'str', distribution: 'Optional[tuple[tuple[str, float], ...]]' = None, p_true: 'Optional[float]' = None, reason: 'Optional[str]' = None, revision: 'Optional[str]' = None, prompt_identity: 'Optional[str]' = None, execution: 'Mapping[str, Any]' = <factory>) -> 'None'One provider output for one sample.
Details
``status`` is ``"answered"`` (with ``distribution`` for a Choice or
Score, ``p_true`` for a Boolean), ``"unsupported"`` (the provider
declared it cannot serve the request) or ``"failed"`` (the call raised);
the latter two carry a ``reason``. ``revision`` is the model revision,
``prompt_identity`` how the provider rendered the question (a digest of
its settings), ``execution`` free JSON metadata (batch, timing, whether
the budget cut the batch short).
nnx.decisions.benchmark.Record.state(self) -> 'dict[str, Any]'No public description is currently available.
nnx.decisions.benchmark.Record.from_state(state: 'Mapping[str, Any]') -> 'Record'No public description is currently available.
nnx.decisions.benchmark.write_records(path: 'Union[str, os.PathLike[str]]', records: 'Iterable[Record]') -> 'None'One JSON object per line (strict JSON, sorted keys).
nnx.decisions.benchmark.read_records(path: 'Union[str, os.PathLike[str]]') -> 'list[Record]'The records of a JSONL file written by write_records.
class nnx.decisions.benchmark.Budget(max_calls: 'int', max_samples: 'Optional[int]' = None) -> 'None'What a live collection may spend: max_calls provider calls and, optionally, max_samples samples. A batch the sample budget cuts short is sent shortened and recorded as partial_batch.
nnx.decisions.benchmark.collect(provider: 'Any', samples: 'Sequence[Sample]', *, provider_id: 'str', budget: 'Budget', batch_size: 'int' = 16, revision: 'Optional[str]' = None, prompt_identity: 'Optional[str]' = None) -> 'Collection'Ask provider every sample's question, in batches of samples that share one question (at most batch_size inputs per call).
Details
The provider and its id and the :class:`Budget` are explicit. Each batch
is attempted once: a provider that raises gives ``"failed"`` records
(retries are the provider's own), a request the provider declares it
cannot serve (its ``check(question, inputs)``, or an
``UnsupportedCapability`` from ``decide``) gives ``"unsupported"``
records. Collection stops when the budget is spent; samples never
attempted have no record (``missing`` when evaluated).
``prompt_identity`` defaults to a digest of the provider's ``record()``.
class nnx.decisions.benchmark.Collection(records: 'tuple[Record, ...]', calls: 'int', complete: 'bool', stopped: 'Optional[str]' = None) -> 'None'A live collection: the records, the provider calls made, whether every sample was attempted (complete) and, if not, why.
nnx.decisions.benchmark.evaluate(samples: 'Sequence[Sample]', records: 'Sequence[Record]', *, split: 'str', provider: 'Optional[str]' = None, epsilon: 'Optional[float]' = None, n_bins: 'int' = 10, policy: 'Any' = None, model_id: 'Optional[str]' = None, resources: 'Optional[Resources]' = None) -> 'BenchmarkReport'Replay: join records to samples by sample id and question digest and report every slice. Calls no provider and fits nothing.
Details
Args:
split: the benchmark split's identity (reports of different splits
are never compared).
provider: which provider's records to score (``None``: all records,
which must then come from one provider).
epsilon: the NLL floor; ``None`` (default) is exact โ ``+inf`` when a
true label has probability 0.
n_bins: reliability bins.
policy / model_id: an ``nnx.abstention.AbstentionPolicy`` applied as
given (never tuned here) for selective coverage and risk.
resources: how cost was measured, if at all.
class nnx.decisions.benchmark.BenchmarkReport(split: 'str', provider: 'Optional[str]', metric_identity: 'Mapping[str, Any]', slices: 'Mapping[str, SliceReport]', resources: 'Resources' = <factory>, extra: 'int' = 0, sample_set: 'Optional[str]' = None) -> 'None'A replayed benchmark: the split and metric identity, every slice's coverage, metrics and bins, and the declared Resources.
nnx.decisions.benchmark.BenchmarkReport.state(self) -> 'dict[str, Any]'No public description is currently available.
nnx.decisions.benchmark.BenchmarkReport.to_json(self) -> 'str'No public description is currently available.
nnx.decisions.benchmark.BenchmarkReport.csv_rows(self) -> 'list[dict[str, Any]]'One row per slice and metric: value, unit, denominator, the unavailable reason, an available value's note, every coverage count of the slice (samples, eligible, failed, unsupported, missing, duplicate, mismatched, invalid โ they add up to the slice's samples) and the report's extra records. A non-finite value is written as in the JSON.
nnx.decisions.benchmark.BenchmarkReport.to_csv(self) -> 'str'No public description is currently available.
nnx.decisions.benchmark.BenchmarkReport.text(self) -> 'str'No public description is currently available.
nnx.decisions.benchmark.BenchmarkReport.save(self, path: 'Union[str, os.PathLike[str]]') -> 'None'Write the JSON report atomically (serialized first: a report that cannot be written never replaces an existing file).
nnx.decisions.benchmark.BenchmarkReport.load_state(path: 'Union[str, os.PathLike[str]]') -> 'dict[str, Any]'A saved report's JSON state (for compare_reports).
class nnx.decisions.benchmark.SliceReport(name: 'str', coverage: 'Coverage', metrics: 'Mapping[str, MetricValue]', bins: 'tuple[Any, ...]') -> 'None'One slice: its coverage, every metric and the reliability bins.
nnx.decisions.benchmark.SliceReport.state(self) -> 'dict[str, Any]'No public description is currently available.
class nnx.decisions.benchmark.Coverage(samples: 'int', eligible: 'int', missing: 'int', duplicate: 'int', mismatched: 'int', invalid: 'int', unsupported: 'int', failed: 'int', reasons: 'Mapping[str, int]' = <factory>) -> 'None'How the samples of a slice joined their records: eligible (one answered record with the sample's digest), missing (no record), duplicate (several), mismatched (a record for another question digest), invalid (an answered record that does not fit its question: options in another order or set, a Boolean answer for a Choice), unsupported and failed (with their reasons). Records for no sample are the report's extra count.
nnx.decisions.benchmark.Coverage.state(self) -> 'dict[str, Any]'No public description is currently available.
class nnx.decisions.benchmark.MetricValue(name: 'str', value: 'Optional[float]', denominator: 'int', reason: 'Optional[str]' = None, note: 'Optional[str]' = None) -> 'None'A metric's value over denominator rows, or None with the reason it is unavailable. note qualifies an available value (rows it does not count, say).
property nnx.decisions.benchmark.MetricValue.unitNo public description is currently available.
nnx.decisions.benchmark.MetricValue.state(self) -> 'dict[str, Any]'No public description is currently available.
class nnx.decisions.benchmark.Resources(warmup: 'Optional[int]' = None, hardware: 'Optional[str]' = None, timing_boundary: 'Optional[str]' = None, concurrency: 'Optional[int]' = None, batch_count: 'Optional[int]' = None, seconds: 'Optional[float]' = None, source: 'Optional[str]' = None) -> 'None'How cost was obtained โ every field None unless declared.
Details
``source`` is ``"measured"`` (timed here; ``hardware`` is then required)
or ``"supplied"`` (reported by someone else); ``timing_boundary`` says
what the time covers (for example ``"provider.decide calls only"``).
nnx.decisions.benchmark.Resources.state(self) -> 'dict[str, Any]'No public description is currently available.
nnx.decisions.benchmark.bootstrap_interval(samples: 'Sequence[Sample]', records: 'Sequence[Record]', *, metric: 'str', seed: 'int', slice: 'str' = 'in_family', resamples: 'int' = 1000, level: 'float' = 0.95, epsilon: 'Optional[float]' = None, provider: 'Optional[str]' = None) -> 'Interval'Bootstrap metric (accuracy, macro-F1, NLL or Brier) over the eligible rows of slice (of provider's records, as evaluate selects them), resampling grouping units โ every row of a drawn unit comes along โ with numpy.random.default_rng(seed). The bounds are resampled values (no interpolation); a non-finite bound (an exact NLL of +inf) flags the interval degenerate.
class nnx.decisions.benchmark.Interval(metric: 'str', estimate: 'Optional[float]', low: 'Optional[float]', high: 'Optional[float]', level: 'float', seed: 'int', resamples: 'int', units: 'int', degenerate: 'bool', unit: 'str' = 'group', method: 'str' = 'percentile bootstrap, grouping units resampled with replacement', reason: 'Optional[str]' = None) -> 'None'A percentile bootstrap interval of one metric, resampling the declared grouping units (unit="group") with seed. degenerate flags an interval that carries no spread information: fewer than two units, every resample giving the same value, bounds that coincide (low == high) while the resamples still vary, or a non-finite bound.
nnx.decisions.benchmark.Interval.state(self) -> 'dict[str, Any]'Strict JSON: a non-finite bound is written as MetricValue writes it.
nnx.decisions.benchmark.compare_reports(a: 'Union[BenchmarkReport, Mapping[str, Any]]', b: 'Union[BenchmarkReport, Mapping[str, Any]]') -> 'dict[str, dict[str, Optional[float]]]'b - a per slice and metric, for two reports of the same split and metric identity and โ when both record it โ the same sample set (a BenchmarkReport or a saved report's state); anything else raises BenchmarkError. A metric unavailable on either side has no delta.
nnx.decisions.benchmark.permute_options(sample: 'Sample', order: 'Sequence[str]', *, id: 'Optional[str]' = None) -> 'Sample'The same options in another order (order: every option id once). A Score's levels are ordered, so it is refused.
nnx.decisions.benchmark.redescribe(sample: 'Sample', descriptions: 'Mapping[str, str]', *, id: 'Optional[str]' = None) -> 'Sample'New descriptions for some options (id -> description): the label space is the same, the text the provider sees is not.
nnx.decisions.benchmark.add_distractors(sample: 'Sample', distractors: 'Sequence[Union[Option, tuple[str, str]]]', *, id: 'Optional[str]' = None) -> 'Sample'Extra wrong options appended to a Choice.
nnx.decisions.benchmark.add_none_of_the_above(sample: 'Sample', option: 'Union[Option, tuple[str, str]]' = ('none', 'None of the above'), *, remove_label: 'bool' = True, id: 'Optional[str]' = None) -> 'Sample'A none-of-the-above option; with remove_label the true option is removed, so none-of-the-above becomes the answer.
nnx.decisions.benchmark.add_context(sample: 'Sample', context: 'str', *, kind: 'str' = 'long_context', id: 'Optional[str]' = None) -> 'Sample'Irrelevant context appended to a text input.
nnx.decisions.benchmark.rewrite_input(sample: 'Sample', text: 'str', *, kind: 'str', id: 'Optional[str]' = None) -> 'Sample'The same question over a rewritten text input โ a translation (kind="multilingual") or an adversarial rewrite (kind="adversarial").
class nnx.decisions.benchmark.BenchmarkErrorA malformed sample, record, budget or report, or reports that cannot be compared.
Opt-in; see Concepts ยง27.
class nnx.result.Ok(value: 'T') -> 'None'A success holding value (any value: 0, None and False included).
property nnx.result.Ok.is_okNo public description is currently available.
property nnx.result.Ok.is_errNo public description is currently available.
nnx.result.Ok.map(self, fn: 'Callable[[T], U]') -> 'Ok[U]'Ok(fn(value)).
nnx.result.Ok.bind(self, fn: 'Callable[[T], Result[U, E]]') -> 'Result[U, E]'fn(value), which must be an Ok or an Err.
nnx.result.Ok.map_error(self, fn: 'Callable[[Any], Any]') -> 'Ok[T]'Unchanged: there is no error to map.
nnx.result.Ok.recover(self, fn: 'Callable[[Any], Any]') -> 'Ok[T]'Unchanged: there is nothing to recover from.
nnx.result.Ok.unwrap(self) -> 'T'No public description is currently available.
class nnx.result.Err(error: 'E') -> 'None'A failure holding a typed error.
property nnx.result.Err.is_okNo public description is currently available.
property nnx.result.Err.is_errNo public description is currently available.
nnx.result.Err.map(self, fn: 'Callable[[Any], Any]') -> 'Err[E]'Unchanged: there is no value to map.
nnx.result.Err.bind(self, fn: 'Callable[[Any], Any]') -> 'Err[E]'Unchanged: there is no value to bind.
nnx.result.Err.map_error(self, fn: 'Callable[[E], F]') -> 'Err[F]'Err(fn(error)).
nnx.result.Err.recover(self, fn: 'Callable[[E], Result[T, F]]') -> 'Result[T, F]'fn(error), which must be an Ok or an Err.
nnx.result.Err.unwrap(self) -> 'NoReturn'Raise UnwrapError carrying the error (and chaining it when it is an exception).
type alias nnx.result.ResultPublic type alias.
class nnx.result.UnwrapError(error: 'Any') -> 'None'Err.unwrap was called; error is the typed error (and the __cause__ when it is an exception).
class nnx.result.BoundaryError(code: 'str', where: 'str', context: 'Mapping[str, Any]' = <factory>, cause: 'Optional[BaseException]' = None) -> 'None'A typed failure at a boundary: a stable code, where it happened (a field name or a path), a read-only context mapping and the original exception as cause.
Details
Equality compares ``code``, ``where`` and ``context`` (not ``cause``:
exceptions compare by identity); the hash uses ``code`` and ``where``.
It pickles and deep-copies (``context`` as a plain mapping); being
read-only, ``context`` is not supported by ``dataclasses.asdict``.
nnx.result.validate_decision_request_result(kind: 'str', prompt: 'str', options: 'Optional[Sequence[Any]]' = None) -> 'Result[Any, BoundaryError]'Build a typed decision question from plain data.
Details
``kind`` is ``"choice"``, ``"boolean"`` or ``"score"``; ``options`` are
``(id, description)`` pairs (a Choice's options, a Score's levels, none
for a Boolean). The question objects are built inside the boundary, so a
malformed request โ duplicate or empty ids, too few options, an empty
prompt, an unknown kind โ is ``Err(BoundaryError(code=
"invalid_decision_request"))`` and nothing is ever sent to a provider.
Only :class:`~nnx.decisions.InvalidDecisionRequest` is converted.
nnx.result.decide_result(provider: 'Any', question: 'Any', inputs: 'Any', *, policy: 'Any' = None, model_id: 'Optional[str]' = None) -> 'Result[tuple[Any, ...], BoundaryError]'Ask provider question for inputs.
Details
``Ok`` holds the results, one per input (an answer of another count,
question, kind or option order is an ``"invalid_decision_response"``) โ or, with an abstention
``policy`` (and the ``model_id`` it was tuned for), each row's
``SelectiveDecision``: an abstention is a success whose probabilities
and reason are kept. Only the provider's own failures are errors:
``InvalidDecisionResponse`` (an answer that does not fit the question)
becomes ``"invalid_decision_response"``, any other ``ProviderFailure``
(an outage, a timeout, a refused credential) ``"provider_failure"``, and
``UnsupportedCapability`` ``"unsupported"``; the first two keep the
error's ``request_id`` in ``context``. The codes follow the exception
type, not whether a retry would help. Anything else propagates. A
``policy`` that does not fit the question (a ``Boolean``, other labels,
another ``model_id``) raises before the provider is called.
nnx.result.inspect_bundle_result(path: 'Union[str, os.PathLike[str]]') -> 'Result[Any, BoundaryError]'Summarize the run bundle at path with nnx.bundles.inspect_bundle โ manifest and JSON records only: it never unpickles a checkpoint, loads a tensor, calls a factory or a registry.
Details
``Ok`` holds the ``BundleInfo``. A path that does not exist is
``Err(BoundaryError(code="artifact_missing"))``; any other
``BundleError`` (not a bundle, a malformed or tampered manifest) is
``"bundle_invalid"``, its cause kept. Other exceptions propagate โ
a ``PermissionError`` on the path itself, or a ``TypeError`` for a
``path`` that is not a str or ``os.PathLike`` (``where`` is always a
str).
train(compile=...) compiles the built-in FP32 forward; see Concepts ยง28.
class nnx.compilation.CompileSpec(*, backend: 'str' = 'inductor', mode: 'Optional[str]' = None, fullgraph: 'bool' = False, dynamic: 'Optional[bool]' = None, on_failure: 'str' = 'error') -> 'None'What to compile with: a torch.compile backend (default "inductor"), its mode (one of COMPILE_MODES, or None), fullgraph (reject graph breaks), dynamic (None lets torch decide) and on_failure ("error" or "eager").
nnx.compilation.CompileSpec.options(self) -> 'dict[str, Any]'The keyword options passed to torch.compile.
nnx.compilation.CompileSpec.state(self) -> 'dict[str, Any]'Fields that differ from their defaults ({} for the defaults).
nnx.compilation.CompileSpec.from_state(state: 'Mapping[str, Any]') -> 'CompileSpec'No public description is currently available.
class nnx.compilation.CompileRecord(*, backend: 'str', options: 'Mapping[str, Any]', on_failure: 'str', torch_version: 'str', effective: 'str' = 'pending', graph_breaks: 'Optional[int]' = None, restart: 'Optional[Mapping[str, Any]]' = None) -> 'None'What a run asked to compile and what took effect.
Details
``backend`` / ``options`` / ``on_failure`` are the request and
``torch_version`` the framework that served it. ``effective`` is
``pending`` (no compiled forward has run yet), ``compiled``, ``eager``
(restarted eager under ``on_failure="eager"``) or ``failed``.
``graph_breaks`` counts the breaks of the first compiled forward
(``capture`` is ``"full"`` or ``"partial"``); ``restart`` holds where
and why an eager restart (or a failure) happened.
property nnx.compilation.CompileRecord.requestedAlways True: a record exists only when compilation was requested.
property nnx.compilation.CompileRecord.capture"full" / "partial" once a compiled forward ran, else None.
nnx.compilation.CompileRecord.record(self) -> 'dict[str, Any]'The plain mapping stored in metadata, checkpoints and attempts.
nnx.compilation.CompileRecord.from_record(record: 'Mapping[str, Any]') -> 'CompileRecord'No public description is currently available.
class nnx.compilation.CompileFailed(message: 'str', record: 'CompileRecord') -> 'None'A compiled forward failed to compile under on_failure="error"; __cause__ is torch's error and record the run's CompileRecord (effective state failed).
Bounded forward benchmarks and profiling.
nnx.benchmarking.benchmark_forward(net: 'torch.nn.Module', inputs: 'Inputs', *, compile: 'Optional[CompileSpec]' = None, warmup: 'int' = 3, repeats: 'int' = 10, device: 'Optional[Union[str, torch.device]]' = None, label: 'Optional[str]' = None) -> 'BenchmarkReport'Time net's forward on inputs (eager, or compiled with compile): the first call, then repeats calls after warmup untimed ones, on a deep copy in eval mode under torch.no_grad. device defaults to the module's.
nnx.benchmarking.compare_compile(net: 'torch.nn.Module', inputs: 'Inputs', *, compile: 'Optional[CompileSpec]' = None, warmup: 'int' = 3, repeats: 'int' = 10, device: 'Optional[Union[str, torch.device]]' = None) -> 'CompileComparison'Benchmark net eager and compiled (compile, default CompileSpec()) from identical weights.
nnx.benchmarking.profile_forward(net: 'torch.nn.Module', inputs: 'Inputs', *, output_dir: 'Union[str, os.PathLike[str]]', wait: 'int' = 1, warmup: 'int' = 1, active: 'int' = 3, repeat: 'int' = 1, compile: 'Optional[CompileSpec]' = None, device: 'Optional[Union[str, torch.device]]' = None, row_limit: 'int' = 10) -> 'ProfileReport'Profile net's forward with torch.profiler for exactly (wait + warmup + active) * repeat steps, writing Chrome traces to output_dir (created if needed). Every count is finite (active and repeat at least 1). The profiler is stopped and its traces flushed whether the steps finish, raise or are cancelled. With compile and wait=warmup=0 the first active step includes the compilation itself.
class nnx.benchmarking.BenchmarkReport(*, label: 'str', backend: 'Optional[str]', device: 'str', shapes: 'tuple[tuple[int, ...], ...]', dtypes: 'tuple[str, ...]', batch_size: 'int', warmup: 'int', repeats: 'int', first_call_seconds: 'float', latencies: 'tuple[float, ...]', peak_memory_bytes: 'Optional[int]', weights_digest: 'str', torch_version: 'str') -> 'None'One benchmarked configuration. Times are seconds; latencies are the warmed per-call times (repeats of them).
property nnx.benchmarking.BenchmarkReport.mean_secondsNo public description is currently available.
property nnx.benchmarking.BenchmarkReport.median_secondsNo public description is currently available.
property nnx.benchmarking.BenchmarkReport.stdev_secondsSample standard deviation (0.0 for a single repeat).
property nnx.benchmarking.BenchmarkReport.min_secondsNo public description is currently available.
property nnx.benchmarking.BenchmarkReport.max_secondsNo public description is currently available.
property nnx.benchmarking.BenchmarkReport.throughputWarmed samples per second (batch_size / mean_seconds), where batch_size is the first input's leading dimension (batch-first inputs).
nnx.benchmarking.BenchmarkReport.state(self) -> 'dict[str, Any]'A plain, JSON-ready summary.
class nnx.benchmarking.CompileComparison(*, eager: 'BenchmarkReport', compiled: 'BenchmarkReport', max_abs_diff: 'float') -> 'None'An eager and a compiled benchmark of identical weights, and the largest absolute difference between their outputs.
property nnx.benchmarking.CompileComparison.speedupWarmed eager mean over compiled mean (> 1 means faster compiled; nothing is guaranteed).
nnx.benchmarking.CompileComparison.state(self) -> 'dict[str, Any]'No public description is currently available.
class nnx.benchmarking.ProfileReport(*, output_dir: 'str', trace_files: 'tuple[str, ...]', schedule: 'Mapping[str, int]', steps: 'int', table: 'str') -> 'None'A finished profile: where its traces were written, the schedule and step count, and the top operators by self time.
Under torchrun; see Architecture ยง3.
nnx.distributed.init_process_group(backend: 'Optional[str]' = None, *, timeout_seconds: 'float' = 120.0) -> 'tuple[int, int]'Join the process group torchrun described in the environment (env://) and return (rank, world_size). backend defaults to nccl when CUDA is available, else gloo; timeout_seconds bounds every collective, so a rank that dies cannot hang the others forever. With CUDA, each process takes cuda:LOCAL_RANK.
nnx.distributed.shutdown(*, timeout_seconds: 'float' = 60.0) -> 'None'Leave the process group together, then destroy it: the counterpart of init_process_group, called once training is over.
Details
Every rank meets at a teardown barrier before the group is destroyed, so
no rank tears its connections down while a peer still uses them (with
Gloo, a peer can otherwise abort: "terminate called without an active
exception"). With Gloo the wait is bounded by ``timeout_seconds``: when a
rank does not arrive in time the others raise ``RuntimeError`` instead of
blocking in the call, and leave the group to process exit (destroying it
then could block on the missing rank). The timed-out barrier stays queued,
so the process itself may still wait at exit until the missing rank
arrives (its barrier then completes against the queued one) or the
group's own timeout (``init_process_group(timeout_seconds=...)``) expires.
With NCCL, older torch releases ignore ``timeout_seconds`` unless
``TORCH_NCCL_BLOCKING_WAIT=1`` is set, so the wait is bounded by the
group's timeout instead. Without a process group, or after a successful
call, it does nothing; after a failed call the group is still
initialized (``init_process_group()`` returns it as is), so exit.
nnx.distributed.train_loader(dataset: 'Any', batch_size: 'int', *, policy: 'str' = 'pad', seed: 'int' = 0, shuffle: 'bool' = True, collate_fn: 'Optional[Callable[[list[Any]], Any]]' = None) -> 'ShardedLoader'This rank's training loader: a per-epoch seeded partition of dataset evened out by policy ("pad" or "drop"), so every rank takes the same number of steps.
nnx.distributed.validation_loader(dataset: 'Any', batch_size: 'int', *, collate_fn: 'Optional[Callable[[list[Any]], Any]]' = None) -> 'ShardedLoader'This rank's validation shard: rows rank::world_size, unpadded โ every row is scored exactly once across the ranks.
class nnx.distributed.DDP(*, writer_rank: 'int' = 0, find_unused_parameters: 'bool' = False) -> 'None'model.train(params, distributed=DDP()): data-parallel training over the initialized process group. writer_rank owns every artifact; find_unused_parameters is passed to DistributedDataParallel.
nnx.distributed.writer_only(factory: 'Callable[[], Any]') -> 'Any'A callback NNx builds on the writer rank only โ for callbacks that write when constructed (TensorBoardCallback, WandbCallback): callbacks=[writer_only(lambda: TensorBoardCallback("tb"))]. Without distributed=, the factory is simply called.
class nnx.distributed.RankPartition(n: 'int', *, rank: 'int', world_size: 'int', kind: 'str' = 'train', policy: 'str' = 'pad', shuffle: 'bool' = True, seed: 'int' = 0) -> 'None'This rank's dataset indices, per epoch.
Details
``kind="train"``: a seeded permutation of ``range(n)`` (``seed + epoch``;
identity order with ``shuffle=False``), evened out by ``policy`` โ
``"pad"`` repeats leading indices, ``"drop"`` drops the tail โ and
dealt ``rank::world_size``, so every rank serves the same count.
``kind="validation"``: rows ``rank::world_size`` in order, unpadded
(counts may differ; a rank may serve none).
nnx.distributed.RankPartition.set_epoch(self, epoch: 'int') -> 'None'No public description is currently available.
nnx.distributed.RankPartition.global_order(self) -> 'list[int]'Every rank's indices for this epoch, before dealing (train: after padding or dropping).
nnx.distributed.RankPartition.indices(self) -> 'list[int]'No public description is currently available.
nnx.distributed.RankPartition.descriptor(self) -> 'dict[str, Any]'What a resume must match: the dataset size, world, kind, policy, shuffle and seed (never the epoch or rank).
class nnx.distributed.ShardedLoader(dataset: 'Any', batch_size: 'int', partition: 'RankPartition', *, collate_fn: 'Optional[Callable[[list[Any]], Any]]' = None) -> 'None'A re-iterable loader over one rank's RankPartition of a map-style dataset (DataLoader underneath, in-process, ordered). set_epoch reseeds a train partition; ids() names the dataset rows this rank serves this epoch.
nnx.distributed.ShardedLoader.set_epoch(self, epoch: 'int') -> 'None'No public description is currently available.
nnx.distributed.ShardedLoader.ids(self) -> 'list[int]'No public description is currently available.
nnx.distributed.ShardedLoader.descriptor(self) -> 'dict[str, Any]'No public description is currently available.
class nnx.distributed.DistributedFailureRaised on every rank when another rank failed (preflight, a non-finite loss, a step-count mismatch, a writer-side commit): the failing rank raises its own error, the others this one naming it.
Through Optuna (extra optuna); see Concepts ยง29.
module nnx.searchBudgeted experiment search through an optional Optuna adapter.
Details
``search(plan, space, apply=..., monitor=..., budget=..., study_name=...)``
asks an Optuna study for parameter sets one at a time; ``apply(plan,
params)`` turns each into a **fresh** :class:`~nnx.plans.ExperimentPlan`
(the base plan is never changed), which is fitted โ a new model, optimizer,
loaders and callbacks per trial, its own attempt id and run directory โ and
scored by one declared **validation** monitor. Requires the ``optuna``
extra (``pip install "thekaveh-nnx[optuna]"``); importing ``nnx`` never
imports Optuna or creates a study.
- **Validated first.** The space (bounds, steps, log scales, choices), the
budget, the monitor (a validation monitor with a direction, equal to the
plan's own ``NNTrainParams.monitor`` so the BEST checkpoint is chosen the
same way) and the plan (validation data, data and callbacks as factories)
are checked before any trial; reopening a stored study whose space,
monitor or plan identity differs is refused before any trial too.
- **The budget counts every trial.** Completed, failed, pruned and orphaned
(left running by a crashed process) trials all count against
``SearchBudget.trials``: a study holding two trials, searched with a
budget of 3, asks once.
- **Per-trial caps.** ``updates_per_trial`` stops a trial right after that
many committed optimizer updates (a partial accumulation window is not an
update). ``deadline_seconds`` (measured on ``clock``, from the search's
start) starts no new trial once expired and stops a running one at its
next update or epoch boundary, recording the overrun. Neither is
preemptive: a long batch finishes first. A trial stopped mid-epoch keeps
only its committed epochs โ LAST, its tensors and the history are those
of the last completed epoch (no LAST before a first completed one).
- **Pruning** (Optuna's ``pruner``) sees only the monitor's validation
value of each completed epoch, reported at step = the epoch index
(strictly increasing). A missing or non-finite value fails the trial; it
is never a candidate. A trial's value is the run's own monitor decision:
the value of the epoch its BEST checkpoint holds (``min_delta`` included).
- **Errors.** A fit that raises fails its trial (``TrialOutcome.error``
keeps the message) and the search continues; a configuration error โ
``apply`` raising or returning a plan the search cannot score โ stops the
search. An exhausted sampler ends it (``stop_reason="exhausted"``).
- **Outcomes.** Every trial's :class:`TrialOutcome` keeps its parameters,
state and terminal reason, resource counters (epochs, committed updates,
seconds), best monitor value, attempt id, run id and BEST checkpoint path.
:attr:`SearchResult.best` is the completed trial with the best finite
monitor value under the monitor's direction.
- **Cancellation** (``KeyboardInterrupt``) runs the fit's callback cleanup,
releases its run lease, records the trial as failed and propagates.
Trials run sequentially in this process, which is the study's only
writer: inside a ``torch.distributed`` process group of more than one rank
(a ``torchrun`` launch) ``search`` raises ``RuntimeError`` before loading or
creating a study, rather than run one study per rank. NNx artifacts live
under ``runs/`` and Optuna's in ``storage`` (an in-memory study when
``None``). Samplers keep their own limits (a seeded
``TPESampler`` is reproducible only sequentially).
nnx.search.search(plan: 'Any', space: 'SearchSpace', *, apply: 'Callable[[Any, Mapping[str, Any]], Any]', monitor: 'Any', budget: 'SearchBudget', study_name: 'str', storage: 'Optional[str]' = None, sampler: 'Any' = None, pruner: 'Any' = None, clock: 'Callable[[], float]' = <built-in function monotonic>) -> 'SearchResult'Run the budgeted search; see the module documentation.
class nnx.search.SearchSpace(*params: 'SearchParam') -> 'None'The parameters a trial draws, by unique name.
nnx.search.SearchSpace.state(self) -> 'list[dict[str, Any]]'No public description is currently available.
nnx.search.SearchSpace.digest(self) -> 'str'No public description is currently available.
class nnx.search.FloatParam(*, name: 'str', low: 'float', high: 'float', log: 'bool' = False, step: 'Optional[float]' = None) -> 'None'A float in [low, high]: log samples on a log scale (low > 0), step discretizes (not with log).
nnx.search.FloatParam.state(self) -> 'dict[str, Any]'Fields that differ from their defaults; bounds as floats (so low=0 and low=0.0 are one space).
nnx.search.FloatParam.suggest(self, trial: 'Any') -> 'float'No public description is currently available.
class nnx.search.IntParam(*, name: 'str', low: 'int', high: 'int', log: 'bool' = False, step: 'int' = 1) -> 'None'An int in [low, high] (inclusive): log (low >= 1) or a step.
nnx.search.IntParam.state(self) -> 'dict[str, Any]'Fields that differ from their defaults.
nnx.search.IntParam.suggest(self, trial: 'Any') -> 'int'No public description is currently available.
class nnx.search.CategoricalParam(*, name: 'str', choices: 'tuple[Any, ...]') -> 'None'One of choices (distinct None / bool / int / float / str).
nnx.search.CategoricalParam.state(self) -> 'dict[str, Any]'No public description is currently available.
nnx.search.CategoricalParam.suggest(self, trial: 'Any') -> 'Any'No public description is currently available.
class nnx.search.SearchBudget(*, trials: 'int', updates_per_trial: 'Optional[int]' = None, deadline_seconds: 'Optional[float]' = None) -> 'None'trials โ the study's total (every state counts); updates_per_trial โ committed optimizer updates per trial; deadline_seconds โ wall time from the search's start.
class nnx.search.TrialOutcome(*, number: 'int', params: 'Mapping[str, Any]', state: 'str', reason: 'str', monitor_value: 'Optional[float]' = None, epochs: 'int' = 0, updates: 'int' = 0, seconds: 'float' = 0.0, overrun_seconds: 'Optional[float]' = None, observations: 'tuple[tuple[int, float], ...]' = (), attempt_id: 'Optional[str]' = None, run_id: 'Optional[str]' = None, checkpoint: 'Optional[str]' = None, error: 'Optional[str]' = None) -> 'None'One trial: its Optuna number, params, state ("completed", "pruned", "failed" or "running" for an orphan) and terminal reason; the resources it used (epochs, committed updates, seconds, overrun_seconds past the deadline); its best finite monitor_value, the validation observations (epoch, value) pruning saw; and its attempt_id, run_id and BEST checkpoint path.
class nnx.search.SearchResult(*, study_name: 'str', outcomes: 'tuple[TrialOutcome, ...]', best: 'Optional[TrialOutcome]', asked: 'int', stop_reason: 'str', direction: 'str', monitor: 'str', deadline_overrun_seconds: 'Optional[float]' = None, trial_runs: 'Mapping[int, Any]' = <factory>) -> 'None'Every trial of the study (priors included), the best completed one, how many this call asked, and why it stopped ("budget", "deadline" or "exhausted" โ a sampler ran out of points).
class nnx.provenance.ExperimentManifest(task: 'Optional[Mapping[str, Any]]' = None, labels: 'Optional[Sequence[str]]' = None, model: 'Optional[Mapping[str, Any]]' = None, data: 'Mapping[str, Any]' = <factory>, splits: 'Mapping[str, Any]' = <factory>, objective: 'Optional[Mapping[str, Any]]' = None, config: 'Mapping[str, Any]' = <factory>) -> 'None'The declared intent of an experiment. Everything is JSON-like and immutable; fingerprint is the SHA-256 of its canonical bytes (with FORMAT), so changing the task, the label order, a split's digest or the objective's version changes it.
Details
Values are validated and copied into immutable form at construction, so
later changes to the dicts you passed never change the manifest;
equality and hashing follow the canonical bytes (``1`` and ``1.0``
differ, as they do in the fingerprint).
Args:
task: the task declaration (e.g. ``TaskSpec.state()``).
labels: the ordered output labels.
model: the model descriptor (e.g. ``NNModelParams.state()``).
data: named data identities (:class:`IdentityRef`, a declared id
string, or ``None`` for unknown).
splits: named split identities, likewise; a
:class:`nnx.data_splits.SplitManifest` is recorded as its digest
(:meth:`~nnx.data_splits.SplitManifest.identity`).
objective: the objective's identity, e.g. ``{"id": "kd", "version": 1}``.
config: any other JSON-like configuration.
nnx.provenance.ExperimentManifest.state(self) -> 'dict[str, Any]'No public description is currently available.
nnx.provenance.ExperimentManifest.from_state(state: 'Mapping[str, Any]') -> 'ExperimentManifest'No public description is currently available.
nnx.provenance.ExperimentManifest.document(self) -> 'dict[str, Any]'The hashed document: the format version plus the manifest.
nnx.provenance.ExperimentManifest.canonical_bytes(self) -> 'bytes'No public description is currently available.
nnx.provenance.ExperimentManifest.fingerprint(self) -> 'str'sha256:<hex> of the canonical bytes.
nnx.provenance.ExperimentManifest.for_model(model: 'Any', *, train: 'Any' = None, data: 'Optional[Mapping[str, Any]]' = None, splits: 'Optional[Mapping[str, Any]]' = None, objective: 'Optional[Mapping[str, Any]]' = None, config: 'Optional[Mapping[str, Any]]' = None, preprocessing: 'Any' = None) -> 'ExperimentManifest'A manifest from an existing model's declarations: its task and label order, its model descriptor and built-in net params, its recorded transformation recipe when it has one, and โ when given โ the training configuration (NNTrainParams.state() without n_epochs, resume_epochs and the resume lineage, which describe an attempt; loaders are never read) and a fitted nnx.preprocessing.Standardizer, recorded as config["preprocessing"]: its schema, frozen statistics and fit-membership identity, never recomputed. Builds no model and iterates no loader.
class nnx.provenance.IdentityRef(kind: 'str', value: 'Optional[str]' = None) -> 'None'How a data or split identity is known.
Details
- ``declared`` โ a name you supplied (a dataset id, a split name): it is
recorded and compared, but **never reported as verified**;
- ``digest`` โ a content digest NNx computed (:func:`hash_file`,
:func:`hash_bytes`): equal digests are verified equality;
- ``unknown`` โ not known.
nnx.provenance.IdentityRef.declared(value: 'str') -> 'IdentityRef'No public description is currently available.
nnx.provenance.IdentityRef.unknown() -> 'IdentityRef'No public description is currently available.
property nnx.provenance.IdentityRef.verifiedWhether this identity is a computed content digest.
nnx.provenance.IdentityRef.state(self) -> 'dict[str, Any]'No public description is currently available.
nnx.provenance.IdentityRef.from_state(state: 'Mapping[str, Any]') -> 'IdentityRef'No public description is currently available.
nnx.provenance.hash_file(path: 'Union[str, os.PathLike[str]]', *, chunk_size: 'int' = 1048576) -> 'IdentityRef'A verified sha256:<hex> identity of a file's bytes โ the one call here that reads data, and only when you make it.
nnx.provenance.hash_bytes(data: 'bytes') -> 'IdentityRef'A verified sha256:<hex> identity of data.
nnx.provenance.canonical_bytes(value: 'Any') -> 'bytes'Canonical UTF-8 JSON of a JSON-like value: sorted keys, arrays in order, compact separators, Unicode kept as UTF-8. Raises TypeError / ValueError (naming the path) for anything else.
class nnx.provenance.Attempt(attempt_id: 'str', fingerprint: 'str', run_id: 'str', status: 'str', started_at: 'str', finished_at: 'Optional[str]' = None, parent: 'Optional[Mapping[str, Any]]' = None, last_committed: 'Optional[Mapping[str, Any]]' = None, error: 'Optional[Mapping[str, Any]]' = None, compile: 'Optional[Mapping[str, Any]]' = None) -> 'None'One execution of a plan: a fresh attempt_id per train() call, the plan's fingerprint and run_id, its status (running / completed / failed / cancelled), the parent it resumed from (run, attempt, checkpoint tag and generation), the last_committed checkpoint (tag, epoch, generation) and, when it did not complete, the error (type and message). compile is the fit's compile record โ requested and effective โ when train(compile=...) was used; it is absent from state() otherwise.
nnx.provenance.Attempt.state(self) -> 'dict[str, Any]'No public description is currently available.
nnx.provenance.Attempt.from_state(state: 'Mapping[str, Any]') -> 'Attempt'No public description is currently available.
class nnx.provenance.ProvenanceRecord(manifest: 'ExperimentManifest', fingerprint: 'str', attempt: 'Optional[Attempt]' = None) -> 'None'A run's provenance: the declared manifest, its fingerprint and the attempt that produced the run (NNRun.provenance).
nnx.provenance.load_provenance(run_id: 'str', root: 'Optional[str]' = None) -> 'Optional[ProvenanceRecord]'Read a run's provenance files; None when it has none (absent). Raises ValueError for an unsupported format, a fingerprint that does not match its manifest, or JSON nested too deeply to read; a malformed entry (a missing key, a non-object entry) raises KeyError or TypeError.
nnx.provenance.compare(left: 'Any', right: 'Any', *, root: 'Optional[str]' = None) -> 'ProvenanceComparison'Compare two plans field by field โ manifests, provenance records, NNRuns or run ids (their provenance files are read; no model is loaded, no loader iterated, no file hashed).
class nnx.provenance.ProvenanceComparison(fields: 'tuple[FieldComparison, ...]') -> 'None'Every compared field. verified_equal is true only when both sides have provenance and every field is equal or verified โ declared or unknown identities and absent provenance never count as verified equality.
property nnx.provenance.ProvenanceComparison.differencesNo public description is currently available.
property nnx.provenance.ProvenanceComparison.verified_equalNo public description is currently available.
nnx.provenance.ProvenanceComparison.by_path(self) -> 'dict[str, FieldComparison]'No public description is currently available.
class nnx.provenance.FieldComparison(path: 'str', status: 'str', left: 'Any' = None, right: 'Any' = None) -> 'None'One field of a comparison: a stable dotted path and a status โ equal, different, missing (on one side only), unknown (an unknown identity), declared (equal declared identities: unverified), verified (equal digests) or absent (no provenance at all).
nnx.data_splits.plan_split(ids: 'Optional[Iterable[Any]]' = None, *, strategy: 'str', groups: 'Optional[Iterable[Any]]' = None, times: 'Optional[Iterable[Any]]' = None, labels: 'Optional[Iterable[Any]]' = None, proportions: 'Optional[Iterable[float]]' = None, cutoffs: 'Optional[Iterable[Any]]' = None, gap: 'Any' = None, seed: 'Optional[int]' = None, insufficient: 'Optional[str]' = None, source: 'Union[IdentityRef, str, None]' = None) -> 'SplitManifest'Plan a group, chronological or stratified split.
Details
Args:
ids: stable sample ids (str or int), one per row. ``None`` plans on
row positions, which needs ``source`` (row positions alone cannot
detect a reordered source) and replays only against it.
strategy: ``"group"``, ``"chronological"`` or ``"stratified"``.
groups: per-row group keys (``"group"``).
times: per-row numbers or dates / datetimes (``"chronological"``).
labels: per-row class labels (``"stratified"``).
proportions: ``(train, validation, test)`` shares summing to 1, train
> 0; a zero share leaves that split empty (``"group"``,
``"stratified"``).
cutoffs: ``(validation_start, test_start)``; either may be ``None``
(``"chronological"``).
gap: rows within ``gap`` before a cutoff are excluded; a number, or a
``timedelta`` for dates (``"chronological"``, default 0).
seed: orders groups / ids (``"group"``, ``"stratified"``); ``None``
draws one from the OS and records it. Chronological plans take
no seed.
insufficient: ``"raise"`` (default) or ``"train"`` for a class with
fewer rows than active splits (``"stratified"``).
source: the source's identity โ an ``IdentityRef`` (e.g.
``nnx.provenance.hash_file``), a declared id string, or ``None``.
Raises:
SplitError: duplicate ids, too few groups or class rows, or no
training rows. ``TypeError`` / ``ValueError`` for malformed
arguments, including ones the strategy does not use.
class nnx.data_splits.SplitManifest(strategy: 'str', train: 'tuple[SampleId, ...]', validation: 'tuple[SampleId, ...]', test: 'tuple[SampleId, ...]', excluded: 'tuple[SampleId, ...]' = (), ids: 'str' = 'sample', source: 'IdentityRef' = <factory>, parameters: 'Mapping[str, Any]' = <factory>, seed: 'Optional[int]' = None) -> 'None'Who is in which split, and how that was decided.
Details
Memberships are sample ids (``ids="sample"``) or row positions
(``ids="position"``, which can be replayed only against a recorded source
identity). They are disjoint, stored in sorted order, and ``excluded``
holds the rows a chronological ``gap`` left out. Equality and hashing
follow :meth:`canonical_bytes`, computed once (the manifest is frozen).
property nnx.data_splits.SplitManifest.n_rowsRows the plan covers, excluded ones included.
nnx.data_splits.SplitManifest.state(self) -> 'dict[str, Any]'No public description is currently available.
nnx.data_splits.SplitManifest.from_state(state: 'Mapping[str, Any]') -> 'SplitManifest'No public description is currently available.
nnx.data_splits.SplitManifest.canonical_bytes(self) -> 'bytes'No public description is currently available.
nnx.data_splits.SplitManifest.digest(self) -> 'str'sha256:<hex> of the canonical manifest: the split identity.
nnx.data_splits.SplitManifest.identity(self) -> 'IdentityRef'The split identity as a verified provenance reference.
nnx.data_splits.SplitManifest.to_json(self) -> 'str'No public description is currently available.
nnx.data_splits.SplitManifest.from_json(text: 'str') -> 'SplitManifest'No public description is currently available.
nnx.data_splits.SplitManifest.save(self, path: 'Union[str, os.PathLike[str]]') -> 'None'Write the manifest as JSON, atomically.
nnx.data_splits.SplitManifest.load(path: 'Union[str, os.PathLike[str]]') -> 'SplitManifest'No public description is currently available.
nnx.data_splits.SplitManifest.resolve(self, ids: 'Iterable[Any]', *, source: 'Union[IdentityRef, str, None]' = None) -> 'SplitIndices'Map this manifest onto a source whose rows carry ids.
Details
``ids`` are the source's sample ids in row order (``range(n)`` for a
positional manifest). When the manifest records a source identity,
``source`` must match it; passing one to a manifest that records none
raises too, since nothing could be checked. Raises
:class:`SplitError` on a changed identity, a positional-only
manifest, ids of another type, and duplicate, missing or unexpected
ids, and on an empty train split. Excluded sample ids may be absent
(they are in no split); a positional manifest needs every position,
since a dropped row would shift the rest.
class nnx.data_splits.SplitIndices(train: 'tuple[int, ...]', validation: 'tuple[int, ...]', test: 'tuple[int, ...]', excluded: 'tuple[int, ...]' = ()) -> 'None'Row positions of each split in the source a manifest was resolved on, in the manifest's membership order.
class nnx.data_splits.SplitErrorA split that cannot be planned or replayed as asked.
class nnx.preprocessing.Standardizer(mean: 'tuple[float, ...]', scale: 'tuple[float, ...]', columns: 'Optional[tuple[ColumnLabel, ...]]' = None, dtype: 'str' = 'float32', fit_rows: 'int' = 0, fit_membership: 'IdentityRef' = <factory>) -> 'None'Frozen per-column (x - mean) / scale with a declared schema.
Details
Build one with :meth:`fit`; reload one with :meth:`load` /
:meth:`from_state`. ``columns`` names the fitted DataFrame columns in
order (``str`` or ``int`` labels; ``None`` for unnamed arrays);
``dtype`` is the output dtype of :meth:`transform`; ``fit_rows`` and
``fit_membership`` identify the training rows it was fitted on. Calling
the standardizer on one sample (a 1-D row) standardizes that row, so it
also works as a :class:`SplitView` transform. Equality and hashing
follow :meth:`canonical_bytes`.
nnx.preprocessing.Standardizer.fit(source: 'Any', *, rows: 'Iterable[Any]', columns: 'Optional[Sequence[ColumnLabel]]' = None, dtype: 'Union[str, torch.dtype]' = 'float32', membership: 'Any' = None) -> 'Standardizer'Fit on source's training rows (integer positions) only.
Details
``source`` is a DataFrame (``columns`` selects and orders the schema,
default all columns; only the training rows of those columns are
copied), a 2-D tensor or array, or any row-indexable sequence. No
other row is read. The population mean and standard deviation are
computed in float64; a constant column (all training values equal)
gets its value as the mean and scale 1. ``membership`` identifies the
rows (an ``IdentityRef``, a ``SplitManifest``, a declared string); by
default it is a digest of the positions. Raises
:class:`PreprocessingError` for an empty, repeated or out-of-range
membership and for non-numeric or non-finite training values.
property nnx.preprocessing.Standardizer.n_featuresNo public description is currently available.
nnx.preprocessing.Standardizer.transform(self, data: 'Any') -> 'torch.Tensor'Standardize data with the frozen statistics.
Details
A DataFrame must hold the fitted ``columns`` once each and in the
fitted order (other columns are ignored); a tensor, array or row
sequence must be 2-D with ``n_features`` columns. Values must be
numeric and finite, and so must the result in ``dtype``. Returns a
new tensor; the input is never modified. Raises
:class:`PreprocessingError` otherwise โ before any model sees it.
nnx.preprocessing.Standardizer.state(self) -> 'dict[str, Any]'No public description is currently available.
nnx.preprocessing.Standardizer.from_state(state: 'Mapping[str, Any]') -> 'Standardizer'Rebuild from state, rejecting anything malformed with PreprocessingError (bad lengths, non-finite values, non-positive scales, missing or mistyped entries).
nnx.preprocessing.Standardizer.canonical_bytes(self) -> 'bytes'No public description is currently available.
nnx.preprocessing.Standardizer.digest(self) -> 'str'sha256:<hex> of the canonical state: the schema identity.
nnx.preprocessing.Standardizer.to_json(self) -> 'str'No public description is currently available.
nnx.preprocessing.Standardizer.from_json(text: 'str') -> 'Standardizer'No public description is currently available.
nnx.preprocessing.Standardizer.save(self, path: 'Union[str, os.PathLike[str]]') -> 'None'Write the statistics and schema as JSON, atomically.
nnx.preprocessing.Standardizer.load(path: 'Union[str, os.PathLike[str]]') -> 'Standardizer'No public description is currently available.
class nnx.preprocessing.SplitView(base: 'Any', indices: 'Iterable[int]', *, transform: 'Optional[Callable[..., Any]]' = None) -> 'None'Rows indices of base, with transform applied to each sample's input.
Details
A sample is read from ``base`` (with whatever transform ``base`` already
applies) and only its first element is transformed; targets and any
other elements pass through unchanged, and tuples, named tuples and
lists keep their type. ``base`` itself โ its ``transform``, labels and
order โ is never modified, so a training view with a random
augmentation and an evaluation view with a fixed transform can share one
base and be read alternately or from DataLoader workers. ``indices``
are the view's sample ids in ``base``; ``dataset`` aliases ``base`` like
``torch.utils.data.Subset``, and ``classes`` forwards the base's class
names. Dict samples are rejected: which entry is the input is ambiguous.
property nnx.preprocessing.SplitView.datasetNo public description is currently available.
property nnx.preprocessing.SplitView.classesThe base dataset's class names (dataset-wide, like its labels).
property nnx.preprocessing.SplitView.reconstructibleWhether the transform can be rebuilt from state alone.
nnx.preprocessing.SplitView.state(self) -> 'dict[str, Any]'No public description is currently available.
nnx.preprocessing.describe_transform(transform: 'Optional[Callable[..., Any]]') -> 'Optional[dict[str, Any]]'How a view's transform can be rebuilt: a Standardizer from its state (by digest); any other callable is runtime-only and must be registered again โ passed to the view anew โ to reconstruct it.
class nnx.preprocessing.PreprocessingErrorPreprocessing that cannot be fitted, loaded or applied as declared.
nnx.calibration.fit_temperature(logits: 'Any', targets: 'Any', *, model_id: 'str', split_id: 'str', labels: 'Optional[Sequence[str]]' = None, train_split_id: 'Optional[str]' = None, test_split_id: 'Optional[str]' = None, min_temperature: 'float' = 0.001, max_temperature: 'float' = 1000.0, tolerance: 'float' = 1e-10, max_iterations: 'int' = 200) -> 'CalibrationFit'Fit a scalar temperature on the calibration split split_id.
Details
``logits`` are categorical ``(N, C)`` raw scores (array, tensor, or a
categorical ``nnx.prediction.PredictionResult``, whose spec supplies the
``labels``) and ``targets`` their integer class indices. The NLL of
``softmax(logits / T)`` is minimized over ``T`` in
``[min_temperature, max_temperature]`` in float64 on a copy of the
logits, by bisection on its (monotone) slope in ``1 / T`` until the
log-temperature bracket is narrower than ``tolerance``.
Raises :class:`CalibrationError` for non-finite logits, ``C < 2``,
out-of-range targets, labels that do not name each class once, and a
``split_id`` equal to ``train_split_id`` or ``test_split_id``. Returns a
failed :class:`CalibrationFit` โ never a calibrator with a NaN or
boundary temperature โ when the optimum lies outside the search range (a
separable split, logits no better than uniform), the NLL does not depend
on ``T`` (constant logits), or the search does not converge.
class nnx.calibration.CalibrationFit(status: 'str', calibrator: 'Optional[TemperatureCalibrator]', reason: 'Optional[str]', config: 'Mapping[str, Any]', split: 'Mapping[str, Any]') -> 'None'The outcome of fit_temperature: status == "ok" with a calibrator, or "failed" with calibrator=None and a reason. config and split are recorded either way (read-only). Compared and hashed by identity; compare the calibrators themselves.
property nnx.calibration.CalibrationFit.okNo public description is currently available.
nnx.calibration.CalibrationFit.require(self) -> 'TemperatureCalibrator'The calibrator, or CalibrationFitError with the reason.
class nnx.calibration.TemperatureCalibrator(temperature: 'float', labels: 'tuple[str, ...]', model_id: 'str', split_id: 'str', train_split_id: 'Optional[str]' = None, test_split_id: 'Optional[str]' = None, fit_config: 'Mapping[str, Any]' = <factory>, fit_result: 'Mapping[str, Any]' = <factory>) -> 'None'A fitted scalar temperature bound to its label schema and model.
Details
Build one with :func:`fit_temperature`; reload one with :meth:`load` /
:meth:`from_state`. ``labels`` are the ordered class names the logits'
columns must carry, ``model_id`` the model they must come from,
``split_id`` the calibration split it was fitted on (and, when declared,
``train_split_id`` / ``test_split_id``, which must differ from it).
``fit_config`` / ``fit_result`` record how it was fitted (the result's
``nll_before`` / ``nll_after`` are the calibration split's exact,
unfloored NLL, computed in the log domain). Equality,
hashing and :attr:`id` follow the canonical state.
property nnx.calibration.TemperatureCalibrator.splitThe split ids, and that their disjointness is unverified.
property nnx.calibration.TemperatureCalibrator.idThe calibrator id: digest.
nnx.calibration.TemperatureCalibrator.transform(self, logits: 'Any', *, model_id: 'str', labels: 'Optional[Sequence[str]]' = None, override: 'Optional[str]' = None, sample_ids: 'Any' = None) -> 'CalibratedPrediction'Calibrate logits โ an (N, C) array or tensor, or a categorical nnx.prediction.PredictionResult.
Details
``sample_ids`` names the rows of an array (``0..N-1`` by default); a
prediction keeps its own ids, and ``sample_ids=`` must then equal
them.
``model_id`` and the column ``labels`` (taken from the prediction's
spec when omitted) must equal the fitted ones; otherwise
:class:`CalibrationMismatchError` is raised before the logits are
read, unless ``override`` names the exception โ then the result
records the name and each mismatch. An override exists only to accept
a mismatch: with none, the name is validated and nothing is recorded.
Non-finite logits or a width that differs from the labels raise
:class:`CalibrationError`. The input is never modified.
nnx.calibration.TemperatureCalibrator.report(self, logits: 'Any', targets: 'Any', *, model_id: 'str', split_id: 'str', labels: 'Optional[Sequence[str]]' = None, override: 'Optional[str]' = None, n_bins: 'int' = 10, epsilon: 'Optional[float]' = 1e-12) -> 'CalibrationReport'Held-out NLL, Brier, ECE and reliability bins before and after calibration.
Details
``split_id`` names the held-out split and must differ from the
calibration and training split ids. Identity checks match
:meth:`transform`. The report's :attr:`CalibrationReport.outcome` is
``"improved"`` only when neither NLL nor Brier got worse and one got
better; a worse result is reported as ``"worsened"`` / ``"mixed"``.
nnx.calibration.TemperatureCalibrator.state(self) -> 'dict[str, Any]'No public description is currently available.
nnx.calibration.TemperatureCalibrator.from_state(state: 'Mapping[str, Any]') -> 'TemperatureCalibrator'Rebuild from state, rejecting anything malformed with CalibrationError.
nnx.calibration.TemperatureCalibrator.digest(self) -> 'str'sha256:<hex> of the canonical state.
nnx.calibration.TemperatureCalibrator.from_json(text: 'str') -> 'TemperatureCalibrator'No public description is currently available.
nnx.calibration.TemperatureCalibrator.load(path: 'Union[str, os.PathLike[str]]') -> 'TemperatureCalibrator'No public description is currently available.
nnx.calibration.TemperatureCalibrator.canonical_bytes(self) -> 'bytes'No public description is currently available.
nnx.calibration.TemperatureCalibrator.to_json(self) -> 'str'No public description is currently available.
nnx.calibration.TemperatureCalibrator.save(self, path: 'Union[str, os.PathLike[str]]') -> 'None'Write to_json to path atomically.
class nnx.calibration.CalibratedPrediction(logits: 'np.ndarray', probabilities: 'np.ndarray', calibrated_probabilities: 'np.ndarray', decoded: 'np.ndarray', labels: 'tuple[str, ...]', sample_ids: 'np.ndarray', calibrator_id: 'str', model_id: 'str', temperature: 'float', override: 'Optional[Mapping[str, Any]]' = None) -> 'None'Raw and calibrated views of one categorical prediction, kept apart.
Details
Attributes:
logits: the raw logits as given (a copy; tensors become arrays).
probabilities: uncalibrated float64 ``softmax(logits)``.
calibrated_probabilities: float64 ``softmax(logits / temperature)``.
decoded: argmax class indices of the raw logits โ calibration never
changes the argmax.
labels: the ordered class names of the columns.
sample_ids: ``int64[N]`` row identities (a ``PredictionResult``'s
own ids, else ``transform``'s ``sample_ids=``, else ``0..N-1``).
calibrator_id: :meth:`TemperatureCalibrator.digest` of the calibrator.
model_id: the model id the logits were declared to come from.
temperature: the calibrator's temperature.
override: ``None``, or the named override and the label / model-id
mismatches it accepted.
nnx.calibration.CalibratedPrediction.decoded_labels(self) -> 'np.ndarray'Decoded class names.
class nnx.calibration.CalibrationReport(calibrator_id: 'str', model_id: 'str', labels: 'tuple[str, ...]', temperature: 'float', split_id: 'str', calibration_split_id: 'str', n_samples: 'int', n_bins: 'int', epsilon: 'Optional[float]', before: 'CalibrationMetrics', after: 'CalibrationMetrics', outcome: 'str', override: 'Optional[Mapping[str, Any]]' = None, train_split_id: 'Optional[str]' = None, test_split_id: 'Optional[str]' = None) -> 'None'Held-out metrics before and after calibration.
Details
``outcome`` is ``"improved"`` (neither NLL nor Brier worse, one better),
``"worsened"`` (neither better, one worse), ``"unchanged"`` or
``"mixed"``; :attr:`improved` is true only for ``"improved"``.
``override`` is the named override that accepted a label / model-id
mismatch, or ``None``. Equality follows the canonical state, so a
reloaded calibrator reproduces an equal report.
property nnx.calibration.CalibrationReport.improvedNo public description is currently available.
nnx.calibration.CalibrationReport.summary(self) -> 'str'One line that states the outcome โ never success when the calibrated probabilities are worse.
nnx.calibration.CalibrationReport.state(self) -> 'dict[str, Any]'No public description is currently available.
nnx.calibration.CalibrationReport.from_state(state: 'Mapping[str, Any]') -> 'CalibrationReport'No public description is currently available.
nnx.calibration.CalibrationReport.from_json(text: 'str') -> 'CalibrationReport'No public description is currently available.
nnx.calibration.CalibrationReport.load(path: 'Union[str, os.PathLike[str]]') -> 'CalibrationReport'No public description is currently available.
nnx.calibration.CalibrationReport.canonical_bytes(self) -> 'bytes'No public description is currently available.
nnx.calibration.CalibrationReport.to_json(self) -> 'str'No public description is currently available.
nnx.calibration.CalibrationReport.save(self, path: 'Union[str, os.PathLike[str]]') -> 'None'Write to_json to path atomically.
class nnx.calibration.CalibrationMetrics(nll: 'float', brier: 'float', ece: 'Optional[float]', bins: 'tuple[ReliabilityBin, ...]') -> 'None'NLL, Brier, expected calibration error and reliability bins of one set of probabilities.
nnx.calibration.CalibrationMetrics.of(probabilities: 'Any', targets: 'Any', *, n_bins: 'int' = 10, epsilon: 'Optional[float]' = 1e-12) -> 'CalibrationMetrics'All four metrics of probabilities, validated once.
nnx.calibration.CalibrationMetrics.state(self) -> 'dict[str, Any]'No public description is currently available.
nnx.calibration.CalibrationMetrics.from_state(state: 'Mapping[str, Any]') -> 'CalibrationMetrics'Rebuild from state; malformed entries raise CalibrationError.
class nnx.calibration.ReliabilityBin(lower: 'float', upper: 'float', count: 'int', confidence: 'Optional[float]', accuracy: 'Optional[float]') -> 'None'One confidence bin: [lower, upper) (the last bin is closed at 1), the rows whose top-1 confidence falls in it, their mean confidence and top-1 accuracy. An empty bin has count == 0 and None means.
nnx.calibration.ReliabilityBin.state(self) -> 'dict[str, Any]'No public description is currently available.
nnx.calibration.ReliabilityBin.from_state(state: 'Mapping[str, Any]') -> 'ReliabilityBin'Rebuild from state; malformed entries raise CalibrationError.
nnx.calibration.negative_log_likelihood(probabilities: 'Any', targets: 'Any', *, epsilon: 'Optional[float]' = 1e-12) -> 'float'Mean -log p[target] over the rows of categorical (N, C) probabilities.
Details
``epsilon`` floors the true-class probability (default
:data:`DEFAULT_EPSILON`, the named ``nll`` metric's floor); ``epsilon=None``
is the exact value, which is ``+inf`` when any true-class probability is 0.
nnx.calibration.brier_score(probabilities: 'Any', targets: 'Any') -> 'float'Mean over rows of the class-summed squared error sum_c (p_c - 1[c == target])^2 (range [0, 2]).
nnx.calibration.reliability_bins(probabilities: 'Any', targets: 'Any', *, n_bins: 'int' = 10) -> 'tuple[ReliabilityBin, ...]'Equal-width top-1 reliability bins over [0, 1].
Details
Bin ``i`` covers ``[i / n_bins, (i + 1) / n_bins)``; the last bin is
closed at 1, and a confidence exactly on an inner edge belongs to the
upper bin. The prediction is the first class of maximal probability, and
empty bins are kept with ``count == 0``.
nnx.calibration.expected_calibration_error(bins: 'Iterable[ReliabilityBin]') -> 'Optional[float]'Count-weighted mean |accuracy - confidence| over the bins; None when the bins hold no rows.
nnx.calibration.model_fingerprint(model: 'Any') -> 'str'sha256:<hex> of a model's weights: every state_dict() entry's name, dtype, shape and little-endian bytes, in name order (the same on every host).
Details
``model`` is an ``nn.Module`` or anything with a ``.net`` module (an
``NNModel``). Use it as a ``model_id`` so a calibrator refuses logits
from a model whose weights changed; a declared id such as
``f"{run.id}:BEST"`` works too, but is only as reliable as its naming.
class nnx.calibration.CalibrationErrorCalibration inputs, configuration or a serialized state that cannot be used as declared.
class nnx.calibration.CalibrationFitErrorCalibrationFit.require on a failed fit.
class nnx.calibration.CalibrationMismatchErrorLogits whose label schema or model id differ from the calibrator's, without a named override.
class nnx.abstention.AbstentionPolicy(kind: 'str', threshold: 'float', labels: 'tuple[str, ...]', model_id: 'str', tuning_split_id: 'str', input_field: 'str' = 'probabilities', calibrator_id: 'Optional[str]' = None) -> 'None'Accept a row when its score reaches threshold; abstain otherwise.
Details
Args:
kind: ``"max_probability"`` or ``"margin"``.
threshold: a finite number in ``[0, 1]``; a row is accepted when its
score is at or above it.
labels: the ordered class names the probabilities' columns carry.
model_id: the model the probabilities must come from.
tuning_split_id: the validation split the threshold was chosen on.
input_field: ``"probabilities"`` (raw) or
``"calibrated_probabilities"``.
calibrator_id: the calibrator of a calibrated field (required
then, and ``None`` for raw probabilities).
Equality, hashing and :attr:`id` follow the canonical state.
property nnx.abstention.AbstentionPolicy.idsha256:<hex> of the canonical state.
property nnx.abstention.AbstentionPolicy.reasonThe reason an abstained row carries.
nnx.abstention.AbstentionPolicy.apply(self, source: 'Any', *, labels: 'Optional[Sequence[str]]' = None, model_id: 'Optional[str]' = None, sample_ids: 'Any' = None, input_field: 'Optional[str]' = None, calibrator_id: 'Optional[str]' = None) -> 'AbstentionResult'Accept or abstain on every row of source.
Details
``source`` is a ``CalibratedPrediction`` (the policy's
``input_field`` picks its raw or calibrated view), a categorical
``PredictionResult`` (raw; pass ``model_id=``), or ``(N, C)``
probabilities with ``labels=`` / ``model_id=`` (and
``input_field=`` / ``calibrator_id=`` for calibrated ones). The
declared schema must equal the policy's โ otherwise
:class:`AbstentionSchemaError`, before any probability is read.
Malformed probabilities raise :class:`AbstentionError`. The input is
never modified.
nnx.abstention.AbstentionPolicy.state(self) -> 'dict[str, Any]'No public description is currently available.
nnx.abstention.AbstentionPolicy.from_state(state: 'Mapping[str, Any]') -> 'AbstentionPolicy'Rebuild from state; anything malformed โ including a non-deployable threshold such as a curve's accept-none endpoint โ raises AbstentionError.
nnx.abstention.AbstentionPolicy.from_json(text: 'str') -> 'AbstentionPolicy'No public description is currently available.
nnx.abstention.AbstentionPolicy.load(path: 'Any') -> 'AbstentionPolicy'No public description is currently available.
nnx.abstention.AbstentionPolicy.canonical_bytes(self) -> 'bytes'No public description is currently available.
nnx.abstention.AbstentionPolicy.to_json(self) -> 'str'No public description is currently available.
nnx.abstention.AbstentionPolicy.save(self, path: 'Union[str, os.PathLike[str]]') -> 'None'Write to_json to path atomically.
class nnx.abstention.AbstentionResult(policy_id: 'str', kind: 'str', threshold: 'float', input_field: 'str', calibrator_id: 'Optional[str]', model_id: 'str', labels: 'tuple[str, ...]', sample_ids: 'np.ndarray', probabilities: 'np.ndarray', prediction: 'np.ndarray', score: 'np.ndarray', accepted: 'np.ndarray', calibration_override: 'Optional[Mapping[str, Any]]' = None) -> 'None'A policy applied to N rows.
Details
``probabilities`` is a float64 copy of the field the policy read;
``prediction`` the first class of maximal probability (a prediction's
own ``decoded`` class, for a prediction); ``score`` the
policy score; ``accepted`` whether the row was accepted. The policy's
identity (``policy_id``, ``kind``, ``threshold``, ``input_field``,
``calibrator_id``, ``model_id``, ``labels``) travels with the result, and
so does ``calibration_override``: the named override under which a
calibrated prediction was produced, if any (``nnx.calibration``).
property nnx.abstention.AbstentionResult.abstainedNo public description is currently available.
property nnx.abstention.AbstentionResult.reasonsNo public description is currently available.
property nnx.abstention.AbstentionResult.accepted_idsNo public description is currently available.
property nnx.abstention.AbstentionResult.abstained_idsNo public description is currently available.
nnx.abstention.AbstentionResult.outcomes(self) -> 'tuple[Outcome, ...]'No public description is currently available.
nnx.abstention.AbstentionResult.accepted_rows(self, targets: 'Any') -> 'AcceptedRows'Sample ids, targets and predictions of the accepted rows only.
nnx.abstention.AbstentionResult.report(self, targets: 'Any') -> 'CoverageReport'Coverage and selective risk against integer class targets.
class nnx.abstention.Outcome(sample_id: 'Optional[int]', status: 'str', prediction: 'str', prediction_index: 'int', distribution: 'tuple[tuple[str, float], ...]', score: 'float', reason: 'Optional[str]') -> 'None'One row: status "accepted" or "abstained" (with its reason); the prediction (label) and its index, the ordered distribution, the score and the sample_id are kept either way.
property nnx.abstention.Outcome.acceptedNo public description is currently available.
class nnx.abstention.AcceptedRows(sample_ids: 'np.ndarray', targets: 'np.ndarray', predictions: 'np.ndarray') -> 'None'The accepted rows only โ what a hard-label report (accuracy, a confusion matrix, VisUtils.classification_report) may receive. An abstention is never turned into a pseudo-class.
class nnx.abstention.CoverageReport(policy_id: 'Optional[str]', total: 'int', accepted: 'int', incorrect: 'int', sample_ids: 'np.ndarray', accepted_ids: 'np.ndarray') -> 'None'Coverage over every row and selective risk over the accepted rows.
Details
``coverage = accepted / total`` and ``risk = incorrect / accepted``.
With nothing accepted, ``risk`` is ``None`` and ``status`` is
``"no_accepted"`` โ never a risk of 0; with no rows at all both are
``None`` and ``status`` is ``"empty"``. ``sample_ids`` keeps every
original id and ``accepted_ids`` the accepted ones, in row order, as
read-only int64 arrays.
property nnx.abstention.CoverageReport.statusNo public description is currently available.
nnx.abstention.CoverageReport.summary(self) -> 'str'No public description is currently available.
nnx.abstention.CoverageReport.state(self) -> 'dict[str, Any]'No public description is currently available.
nnx.abstention.CoverageReport.from_state(state: 'Mapping[str, Any]') -> 'CoverageReport'No public description is currently available.
nnx.abstention.CoverageReport.from_json(text: 'str') -> 'CoverageReport'No public description is currently available.
nnx.abstention.CoverageReport.load(path: 'Any') -> 'CoverageReport'No public description is currently available.
property nnx.abstention.CoverageReport.coverageNo public description is currently available.
property nnx.abstention.CoverageReport.riskNo public description is currently available.
nnx.abstention.CoverageReport.canonical_bytes(self) -> 'bytes'No public description is currently available.
nnx.abstention.CoverageReport.to_json(self) -> 'str'No public description is currently available.
nnx.abstention.CoverageReport.save(self, path: 'Union[str, os.PathLike[str]]') -> 'None'Write to_json to path atomically.
class nnx.abstention.CoverageAccumulator(*, allow_repeated_ids: 'bool' = False) -> 'None'Coverage and selective risk aggregated over chunks.
Details
``update(result, targets)`` adds one chunk; :meth:`report` equals the
eager ``AbstentionResult.report`` over the concatenated rows โ the same
accepted / incorrect / total counts and the same risk, including when
nothing is accepted. Every chunk must come from the same policy, and by
default every id must be new โ within its chunk and across chunks โ
since the default ``0..n-1`` repeats from chunk to chunk: pass each
chunk its own ``sample_ids=``. For a split whose ids legitimately repeat
(oversampled), pass ``allow_repeated_ids=True``; ids are then not
checked.
A ``PredictionResult`` or ``CalibratedPrediction`` chunk carries the ids
it was made with (``prediction_from_logits(sample_ids=...)``,
``TemperatureCalibrator.transform(sample_ids=...)``). Ids are kept as
int64 arrays โ in row order, the accepted ones, and sorted for the repeat
check, which costs ``O(k logยฒ n)`` for ``k`` new ids among ``n`` so far.
nnx.abstention.CoverageAccumulator.update(self, result: 'AbstentionResult', targets: 'Any') -> 'None'No public description is currently available.
nnx.abstention.CoverageAccumulator.report(self) -> 'CoverageReport'The aggregated report over every chunk added so far.
nnx.abstention.select_threshold(source: 'Any', targets: 'Any', *, kind: 'str', risk_ceiling: 'float', split_id: 'str', test_split_id: 'Optional[str]' = None, labels: 'Optional[Sequence[str]]' = None, model_id: 'Optional[str]' = None, input_field: 'Optional[str]' = None, calibrator_id: 'Optional[str]' = None, sample_ids: 'Any' = None, candidates: 'Optional[Iterable[float]]' = None) -> 'ThresholdSelection'Choose a kind threshold on the validation split split_id.
Details
``source`` is declared as for :meth:`AbstentionPolicy.apply`; a
``CalibratedPrediction`` needs ``input_field=`` to say which of its two
views to tune on. Every candidate threshold is evaluated: by default
every distinct score, or, beyond :data:`MAX_DEFAULT_CANDIDATES` of them,
that many evenly spaced order statistics of the scores (pass
``candidates=`` for another set). The feasible ones accept at least one
row with ``risk <= risk_ceiling``; among them the highest coverage wins
(equal coverage is the same accepted set, so the same risk), then the
higher, more conservative threshold.
Nothing feasible gives ``status="no_feasible_policy"``. A ``split_id``
equal to ``test_split_id`` is refused: tuning on the test split would
leak it. The ceiling is empirical โ met on this split, not guaranteed.
class nnx.abstention.ThresholdSelection(status: 'str', policy: 'Optional[AbstentionPolicy]', points: 'tuple[CurvePoint, ...]', kind: 'str', risk_ceiling: 'float', split_id: 'str', labels: 'tuple[str, ...]', model_id: 'str', input_field: 'str' = 'probabilities', calibrator_id: 'Optional[str]' = None, test_split_id: 'Optional[str]' = None, basis: 'str' = 'empirical', calibration_override: 'Optional[Mapping[str, Any]]' = None) -> 'None'The outcome of select_threshold.
Details
``status`` is ``"ok"`` with the chosen ``policy`` or
``"no_feasible_policy"`` with ``policy=None``. ``points`` lists every
candidate threshold with its counts, coverage and risk, then the
accept-none endpoint. ``basis`` is ``"empirical"``: the ceiling was met
on ``split_id``, which does not guarantee it anywhere else.
``labels``, ``model_id``, ``input_field`` and ``calibrator_id`` record
what was tuned on โ a chosen policy carries the same โ so even a
``"no_feasible_policy"`` selection says which model and view failed.
``calibration_override`` is the named override (``nnx.calibration``)
under which the calibrated probabilities tuned on were produced, if any.
nnx.abstention.ThresholdSelection.require(self) -> 'AbstentionPolicy'The chosen policy, or AbstentionError.
nnx.abstention.ThresholdSelection.state(self) -> 'dict[str, Any]'No public description is currently available.
nnx.abstention.ThresholdSelection.from_state(state: 'Mapping[str, Any]') -> 'ThresholdSelection'No public description is currently available.
nnx.abstention.ThresholdSelection.from_json(text: 'str') -> 'ThresholdSelection'No public description is currently available.
nnx.abstention.ThresholdSelection.load(path: 'Any') -> 'ThresholdSelection'No public description is currently available.
nnx.abstention.ThresholdSelection.canonical_bytes(self) -> 'bytes'No public description is currently available.
nnx.abstention.ThresholdSelection.to_json(self) -> 'str'No public description is currently available.
nnx.abstention.ThresholdSelection.save(self, path: 'Union[str, os.PathLike[str]]') -> 'None'Write to_json to path atomically.
nnx.abstention.risk_coverage_curve(source: 'Any', targets: 'Any', *, kind: 'str', candidates: 'Optional[Iterable[float]]' = None, input_field: 'Optional[str]' = None) -> 'tuple[CurvePoint, ...]'Coverage and selective risk at each candidate threshold, ascending.
Details
``source`` is ``(N, C)`` probabilities (the prediction is each row's
first class of maximal probability), a categorical ``PredictionResult``
or a ``CalibratedPrediction`` with ``input_field=`` (their own
``decoded`` classes, as :func:`select_threshold` reads them). By default
the candidates are the distinct scores (bounded by
:data:`MAX_DEFAULT_CANDIDATES`). Coverage never rises as the threshold
rises; risk is measured at each point, never assumed monotone. The last
point is the accept-none endpoint.
class nnx.abstention.CurvePoint(threshold: 'Optional[float]', accepted: 'int', incorrect: 'int', total: 'int') -> 'None'One point of a risk-coverage curve: rows with a score at or above threshold are accepted. The final "accept_none" point (threshold=None, coverage 0, risk unavailable) exists only on the curve and is not deployable.
property nnx.abstention.CurvePoint.statusNo public description is currently available.
property nnx.abstention.CurvePoint.deployableNo public description is currently available.
nnx.abstention.CurvePoint.state(self) -> 'dict[str, Any]'No public description is currently available.
property nnx.abstention.CurvePoint.coverageNo public description is currently available.
property nnx.abstention.CurvePoint.riskNo public description is currently available.
nnx.abstention.scores(probabilities: 'Any', *, kind: 'str') -> 'tuple[np.ndarray, np.ndarray]'(prediction, score) per row of categorical (N, C) probabilities: the first class of maximal probability and the policy score (the top probability, or the top-two margin). A prediction's own decoded class is kept by AbstentionPolicy.apply and select_threshold instead.
nnx.abstention.decide(result: 'Any', policy: 'AbstentionPolicy', *, model_id: 'str') -> 'SelectiveDecision'Apply policy to a typed decision (nnx.decisions).
Details
``result`` is a ``ChoiceResult`` or ``ScoreResult``; its option ids, in
order, are the labels. A provider's distribution is raw, so the policy
must read ``"probabilities"``; labels and ``model_id`` must equal the
policy's (:class:`AbstentionSchemaError` otherwise). The result is
returned unchanged inside a :class:`SelectiveDecision`, accepted or not.
class nnx.abstention.SelectiveDecision(result: 'Any', outcome: 'Outcome', policy_id: 'str', input_field: 'str', calibrator_id: 'Optional[str]') -> 'None'A typed decision with a policy's verdict: result is the provider's ChoiceResult / ScoreResult, unchanged โ abstaining is a success, not an error โ and outcome says whether it was accepted. input_field / calibrator_id record which probabilities the verdict read (a provider's distribution is raw).
property nnx.abstention.SelectiveDecision.acceptedNo public description is currently available.
property nnx.abstention.SelectiveDecision.abstainedNo public description is currently available.
class nnx.abstention.AbstentionErrorMalformed probabilities, targets, configuration or serialized state.
class nnx.abstention.AbstentionSchemaErrorA declared schema โ labels, model id, probability field, calibrator or sample ids โ that contradicts the policy's, the prediction's own, or itself (calibrated probabilities without their calibrator, say).
module nnx.plansImmutable fluent experiment plans.
Details
An :class:`ExperimentPlan` holds one experiment's configuration โ the
network parameters (``NNParams``), the model parameters (``NNModelParams``),
the training parameters (``NNTrainParams``), the data, the seed and the
callbacks โ and compiles it to the existing ``NNModel(...)`` +
``NNModel.train(...)`` loop. Every ``with_*`` method returns a **new** plan
and leaves the source unchanged, so branching a configuration is::
base = ExperimentPlan().with_net(net).with_model(model).with_train(train).with_data(loader)
small, large = base.with_epochs(2), base.with_epochs(8) # base, small and large are all intact
Three boundaries are kept apart:
- :meth:`ExperimentPlan.validate` is pure. It aggregates every
configuration problem as a field-path :class:`Diagnostic`, and consumes
no loader, calls no factory, builds no model, reads no weights and writes
no directory.
- :meth:`ExperimentPlan.probe` is effectful but leaves no trace (bar a
CUDA model's first CUDA use, below): on one
example batch it seeds, builds a temporary model and runs a forward pass
(and the loss when the batch has a target) under ``torch.no_grad()``. It
then restores the ambient RNG streams, the cuDNN / deterministic-algorithm
settings and the seeding environment variables, even when the forward
pass fails. It never touches a callback or a run directory.
- :meth:`ExperimentPlan.fit` trains. It seeds, calls the loader and callback
factories once, builds a fresh model and calls ``NNModel.train``, the
same loop an imperative script calls. Each fit is a distinct **attempt**:
its id is folded into the run's ``salt``, so repeated fits of one plan
get distinct run ids and directories, and a plan never sets
``overwrite_existing``. It returns a :class:`FitResult` ``(model, run,
metrics, attempt_id)``.
**Ownership.** The plan's parameter objects are immutable and freely shared
between branches. Everything else is **borrowed**: shared by every branch
and every fit, never copied or cloned, and never reset by the plan.
- Borrowed: loader instances passed to :meth:`~ExperimentPlan.with_data`,
callback instances passed to :meth:`~ExperimentPlan.with_callbacks`, the
``extra_metrics`` callables, step functions, the objective, components
and the provenance manifest.
- A stateful borrowed object carries its state from one fit to the next.
``EarlyStopping`` resets itself; ``LRMonitor`` accumulates.
- For fresh objects per fit, pass zero-argument **factories**: a callable
as ``with_data(train=...)`` / ``val=``, or
:meth:`~ExperimentPlan.with_callback_factories`.
- Each fit builds a new model; ``FitResult.model`` is the trained one.
class nnx.plans.ExperimentPlan(net: 'Optional[NNParams]' = None, model: 'Optional[NNModelParams]' = None, train: 'Optional[NNTrainParams]' = None, train_data: 'Any' = None, val_data: 'Any' = INHERIT, data_identity: 'Any' = INHERIT, seed: 'Any' = None, callbacks: 'tuple[Any, ...]' = (), callback_factories: 'tuple[Any, ...]' = (), train_step_fn: 'Any' = None, eval_step_fn: 'Any' = None, objective: 'Any' = None, components: 'tuple[Any, ...]' = (), provenance: 'Any' = None, resume: 'Optional[_Resume]' = None, batch_adapter: 'Any' = None) -> 'None'An immutable experiment configuration that compiles to NNModel(net, model).train(train, ...); see the module docstring for the boundaries and the ownership rules.
Details
Build it with the ``with_*`` methods, each of which returns a new plan:
- ``with_net(NNParams)``, ``with_model(NNModelParams)``,
``with_train(NNTrainParams)``;
- ``with_epochs``, ``with_optim``, ``with_scheduler``, ``with_metrics``
and ``with_extra_metrics``, which edit the training parameters;
- ``with_data(train, val, identity=...)`` (what is not given is kept,
and validation data and identity otherwise come from the training
parameters) and ``with_seed(seed)``;
- ``with_callbacks`` (borrowed instances) and
``with_callback_factories`` (called once per fit);
- ``with_step_fns``, ``with_objective``, ``with_components`` and
``with_provenance``, which are borrowed, and ``with_batch_adapter``
for a registered module's inputs;
- ``resuming(run_id, checkpoint="last", mode=None, epochs=None)``
(``mode`` and ``epochs`` default to the training parameters'
``resume_mode`` and ``resume_epochs``).
Plan-level arguments (data, seed, callbacks, factories, steps, resume)
are recorded as given and checked by :meth:`validate`, so every problem
is reported at once. The ``NNParams`` / ``NNModelParams`` /
``NNTrainParams`` objects validate themselves when constructed, so the
methods that edit the training parameters (``with_epochs``,
``with_metrics``, ...) raise :class:`PlanError` naming the field at
once.
nnx.plans.ExperimentPlan.with_net(self, net: 'Optional[NNParams]') -> 'ExperimentPlan'The network parameters (None for a ModelSpec model).
nnx.plans.ExperimentPlan.with_model(self, model: 'NNModelParams') -> 'ExperimentPlan'No public description is currently available.
nnx.plans.ExperimentPlan.with_train(self, train: 'NNTrainParams') -> 'ExperimentPlan'The training parameters. Loaders inside them are borrowed, and with_data overrides them. extra_metrics is copied into a read-only mapping.
nnx.plans.ExperimentPlan.with_epochs(self, n_epochs: 'int') -> 'ExperimentPlan'No public description is currently available.
nnx.plans.ExperimentPlan.with_optim(self, optim: 'Any') -> 'ExperimentPlan'No public description is currently available.
nnx.plans.ExperimentPlan.with_scheduler(self, scheduler: 'Any') -> 'ExperimentPlan'No public description is currently available.
nnx.plans.ExperimentPlan.with_metrics(self, metrics: 'Iterable[Any]' = KEEP, *, monitor: 'Any' = KEEP) -> 'ExperimentPlan'Declared metrics (MetricSpec) and the monitor (MonitorSpec), as on NNTrainParams; the one not given is kept (pass monitor=None to drop a monitor).
nnx.plans.ExperimentPlan.with_extra_metrics(self, extra_metrics: 'Optional[Mapping[str, Callable[..., float]]]') -> 'ExperimentPlan'Borrowed name -> callable(y_true, y_pred) metrics, copied into a read-only mapping.
nnx.plans.ExperimentPlan.with_data(self, train: 'Any' = KEEP, val: 'Any' = KEEP, *, identity: 'Any' = KEEP) -> 'ExperimentPlan'The training data and, optionally, validation data and identity.
Details
Each source is a re-iterable of batches (a ``DataLoader`` or a list,
which is borrowed) or a zero-argument factory that returns one. A
factory is called once per fit, never by :meth:`validate`.
``identity`` is the caller-supplied data identity; it becomes
``NNTrainParams.data_id``, which is part of the run id.
As with the other ``with_*`` methods, what is not given is kept (so
``with_data(val=...)`` changes only the validation source): the
current training and validation sources and identity โ the plan's
own, else the training parameters' ``val_loader`` / ``data_id``,
resolved when the plan compiles, whatever the order of the calls.
``train=None`` keeps the training parameters' loader. Pass
``val=None`` to run train-only (a plan without validation data
invents none) and ``identity=`` for new data โ or ``identity=None``
to record none. Without ``with_data`` the loaders and ``data_id``
inside the training parameters are used (the loaders may be
factories too).
nnx.plans.ExperimentPlan.with_seed(self, seed: 'Optional[int]') -> 'ExperimentPlan'Seed every RNG before the factories run and the model is built, and again (as NNTrainParams.seed) when training starts. The plan's seed overrides the training parameters' own seed, so branches can differ by seed; None falls back to it.
nnx.plans.ExperimentPlan.with_callbacks(self, *callbacks: 'Any') -> 'ExperimentPlan'Borrowed callback instances (or legacy fn(idps) callables), shared by every fit; they replace any earlier ones. A callable whose one parameter is required is a legacy callback, called with the history each epoch. One that can be called with no arguments reads as a factory and is reported: pass callback-making functions to with_callback_factories.
nnx.plans.ExperimentPlan.with_callback_factories(self, *factories: 'Callable[[], Any]') -> 'ExperimentPlan'Zero-argument callables, each called once per fit to make a fresh callback. Their callbacks run after the borrowed ones, in order.
nnx.plans.ExperimentPlan.with_step_fns(self, train_step_fn: 'Any' = KEEP, eval_step_fn: 'Any' = KEEP) -> 'ExperimentPlan'Borrowed step functions; the one not given is kept (pass None to drop one).
nnx.plans.ExperimentPlan.with_objective(self, objective: 'Any') -> 'ExperimentPlan'No public description is currently available.
nnx.plans.ExperimentPlan.with_components(self, *components: 'Any') -> 'ExperimentPlan'No public description is currently available.
nnx.plans.ExperimentPlan.with_batch_adapter(self, adapter: 'Any') -> 'ExperimentPlan'How a registered ModelSpec module reads a batch (nnx.models.PositionalInputs / KeywordInputs), passed to NNModel(..., batch_adapter=...) on every build.
nnx.plans.ExperimentPlan.with_provenance(self, manifest: 'Any') -> 'ExperimentPlan'No public description is currently available.
nnx.plans.ExperimentPlan.resuming(self, run_id: 'str', *, checkpoint: 'str' = 'last', mode: 'Optional[str]' = None, epochs: 'Optional[str]' = None) -> 'ExperimentPlan'Warm-resume from the run run_id's checkpoint. The source run is recorded as the new run's parent lineage and is never overwritten: the resumed fit writes its own run directory. mode ("auto" / "stateful" / "weights_only") defaults to the training parameters' own resume_mode. epochs sets what n_epochs means on the resume and defaults to the training parameters' own resume_epochs: "additional" trains n_epochs more; "planned" continues the source run's plan up to n_epochs in total, which needs its training state (a "weights_only" mode is refused).
nnx.plans.ExperimentPlan.without_resume(self) -> 'ExperimentPlan'No public description is currently available.
nnx.plans.ExperimentPlan.validate(self) -> 'PlanValidation'Every configuration problem as a field-path Diagnostic.
Details
Pure: it consumes no loader (iterables are never iterated), calls
no data or callback factory, builds no model, reads no weights and
writes no directory. Declared metrics and the monitor are resolved
against their registries, as ``NNModel.train`` does before
reserving a run. It constructs the built-in loss module, as
``NNModel`` does first, and resolves each borrowed callback's monitor
on a shallow copy; with ``nnx.streaming.streaming_eval_step`` it also
builds each declared metric's accumulator to check that it is
bounded.
nnx.plans.ExperimentPlan.probe(self, example_batch: 'Any') -> 'ProbeResult'Build a temporary model and run one forward pass on example_batch under torch.no_grad() in eval mode, plus a train-mode pass for the loss when the batch has a target (None for an all-masked task batch, which the default step records no loss for either).
Details
Effectful but restored: the plan's seed is applied first, and
afterwards the ambient Python / NumPy / torch RNG streams, the
cuDNN ``deterministic`` / ``benchmark`` flags, the
deterministic-algorithms setting and the seeding environment
variables are put back, whether or not the forward pass succeeds.
The model is discarded. No callback, factory or loader is touched,
and nothing is written. A CPU model's probe seeds no CUDA stream, so
nothing waits for CUDA's first use. A CUDA model's probe starts CUDA
first when it is not yet in use (running any seed queued for its
first use), so its streams are restored like the others. A
registered factory's module is built as ``NNModel`` builds it
(:func:`nnx.models.build_module`), which saves and restores every
RNG stream around the factory call โ on a GPU host that reads, and
so initializes, CUDA even for a CPU model; its streams are then put
back as the build left them.
A probe needs only the model, the network and the seed: when those
are invalid it raises :class:`PlanError` first (the data, callbacks
and training parameters are :meth:`validate`'s and :meth:`fit`'s
concern); a failing forward pass raises its own error โ including a
batch the training step cannot take either, such as a single example
for a ``BatchNorm`` net in train mode.
nnx.plans.ExperimentPlan.fit(self, *, attempt: 'Optional[str]' = None) -> 'FitResult'Train one attempt and return its FitResult.
Details
The steps are the ones an equally seeded imperative script takes:
validate (an invalid plan raises :class:`PlanError` before
anything runs); seed; call each data factory and callback factory
exactly once (what they return is checked next, and a
:class:`PlanError` then leaves the seed applied, as the script's
``set_seed`` would); build ``NNModel(net, model)``; and call
``model.train(train, callbacks, ...)``. The ``train`` given to
``model.train`` carries the plan's seed, data, data identity and
resume. The run's ``salt`` is ``"plan-attempt:<attempt>"``, where
``attempt`` defaults to a fresh random id, so every fit gets its own
run id and directory. ``overwrite_existing`` is never set; reusing
an ``attempt`` id for the same configuration raises
``FileExistsError`` from ``NNModel.train``.
The fit trains in this process (no ``distributed=``): inside a
``torch.distributed`` process group of more than one rank (a
``torchrun`` launch) it raises ``RuntimeError`` before seeding,
calling a factory or creating a run; a one-rank group behaves as no
group.
class nnx.plans.FitResult(model: 'NNModel', run: 'NNRun', metrics: 'Mapping[str, SplitMetrics]', attempt_id: 'str') -> 'None'One fit: the trained model, its persisted run (the NNModel.train return value), the final epoch's metrics per split ("train" and "val", see SplitMetrics) and the attempt_id folded into the run's salt.
class nnx.plans.SplitMetrics(split: 'str', available: 'bool', epoch: 'Optional[int]' = None, source: 'Optional[str]' = None, values: 'Mapping[str, float]' = <factory>, reason: 'Optional[str]' = None) -> 'None'One split's metrics from a fit's final epoch, read from the run's own history โ never from a second pass over a loader.
Details
``available`` says whether the split was measured: a plan without
validation data reports ``val`` as unavailable, with the ``reason``,
rather than inventing it. ``source`` is ``"epoch"`` for a whole-epoch
record (validation, or a training summary when the run declares metrics
or a monitor) and ``"last_batch"`` when only the final training batch's
record exists. ``values`` maps metric names to floats: the recorded
``loss`` / ``error`` / classification fields, the declared ``metrics``
and the ``extra_metrics`` as ``extra/<name>``, skipping any that were
not recorded (a non-finite value is kept). A declared metric named like
a classification field (``f1``, ``accuracy``, ...) appears as
``metrics/<name>`` beside the record's own field, as the loggers write
it; a monitor on that name compares the declared value. A whole-epoch training
summary carries the declared metrics but not the extra metrics, which
the loop computes per batch (``run.idps`` has them).
class nnx.plans.ProbeResult(output_shape: 'tuple[int, ...]', output_dtype: 'str', target_shape: 'Optional[tuple[int, ...]]', loss: 'Optional[float]', n_parameters: 'int', n_trainable: 'int') -> 'None'A probe's forward pass on one example batch: the network's raw output shape and dtype (in eval mode, as predict sees it, before any reshaping the loss applies), the batch's target shape as given, the loss the default training step would compute on the batch (in train mode, under its mixed-precision autocast, without a gradient โ so the batch is scored as a training batch: a BatchNorm net needs more than one row) when the batch has a target โ None for a plan with its own train_step_fn or objective, whose loss a probe cannot know โ and the temporary model's parameter counts.
class nnx.plans.PlanValidation(diagnostics: 'tuple[Diagnostic, ...]' = ()) -> 'None'The result of ExperimentPlan.validate: every Diagnostic at once, in field order.
property nnx.plans.PlanValidation.okNo public description is currently available.
property nnx.plans.PlanValidation.pathsNo public description is currently available.
nnx.plans.PlanValidation.raise_for_errors(self) -> 'None'Raise PlanError listing every diagnostic, if any.
class nnx.plans.Diagnostic(path: 'str', message: 'str') -> 'None'One configuration problem at a field path ("data.train", "train.monitor", "callbacks[1]", ...).
class nnx.plans.PlanError(diagnostics: 'tuple[Diagnostic, ...]') -> 'None'A plan that cannot be fitted or probed. diagnostics lists every problem ExperimentPlan.validate found.
Details
It is a ``ValueError`` and a ``TypeError``, the two errors the params
classes raise, so code catching either around a configuration still
catches it.
module nnx.streamingStreaming prediction and mergeable metrics.
Details
``NNModel.predict()`` and ``NNModel.evaluate()`` are eager: they return only
after the whole loader has run, holding every batch's outputs until then.
This module adds the bounded counterparts.
**A prediction stream.** ``NNModel.iter_predict(loader)`` returns a
:class:`PredictionStream` that yields one result per loader batch, in loader
order, and holds nothing once a batch has been handed over::
with model.iter_predict(loader) as stream:
for batch in stream: # PredictionBatch(logits, classes, sample_ids)
write(batch.sample_ids, batch.classes)
Concatenating the batches gives exactly what the eager call returns:
``predict(loader)``'s logits and classes, and ``predict_proba(loader)``'s
sample ids โ the same seed-row slicing for graph loaders and the same
categorical / multilabel / continuous decoding. With a
:class:`~nnx.prediction.ProbabilitySpec`, or ``rich=True`` for a model with a
task, every batch is a :class:`~nnx.prediction.PredictionResult` instead.
- Each batch runs in eval mode under ``no_grad``, and every submodule's
training mode is restored before the batch is yielded, so the network is in
its own mode between batches โ also when the forward pass raises.
- Closing the stream (leaving the ``with`` block, ``close()``, an exception)
drops its references to the loader's iterator and to the model. A closed or
consumed stream refuses to be iterated again. The loader stays the caller's:
it is never closed and can be iterated again.
- The stream holds at most the batch in flight. What the consumer keeps is
the consumer's memory: store only what you need (for example the sample
ids and classes), not the batches.
**Mergeable metrics.** :class:`StreamingMetrics` accumulates declared
:class:`~nnx.MetricSpec` metrics over any number of batches with ``update``,
combines two accumulations over disjoint samples with ``merge`` and returns a
read-only :class:`MetricSnapshot` from ``finalize``. Built-in metrics keep
sums and counts (``accuracy``, ``nll``, ``brier``, ``mae``, ``mse``) or
confusion counts (``f1``), so memory never grows with the number of samples,
and a merge adds them โ the result does not depend on the order. A metric that
needs every stored score (a rank metric such as AUROC is never additive) is
rejected unless ``materialize=True`` asks for O(N) storage.
**Bounded validation.** :func:`streaming_eval_step` is a ready-made
``eval_step_fn`` for ``NNModel.train``: the default validation record from
counts and sums instead of stored predictions. ``evaluate()`` itself is
unchanged and eager.
class nnx.streaming.PredictionStream(batches: 'Iterator[Any]') -> 'None'A context-managed iterator of prediction batches; see the module docstring. Obtain one with NNModel.iter_predict.
property nnx.streaming.PredictionStream.closedWhether the stream is closed or consumed (it cannot be iterated again).
nnx.streaming.PredictionStream.close(self) -> 'None'Stop the stream and drop its references to the loader's iterator and the model. Idempotent; the loader itself is left to the caller.
class nnx.streaming.PredictionBatch(logits: 'np.ndarray', classes: 'np.ndarray', sample_ids: 'np.ndarray') -> 'None'One loader batch of NNModel.iter_predict.
Details
Attributes:
logits: the raw network output for the batch's rows (graph loaders:
seed rows only), as ``predict()`` returns them.
classes: the decoded predictions, as ``predict().classes``: argmax
classes, 0/1 indicators for a multilabel or ``BCEWithLogitsLoss``
model, the values themselves for a regression task.
sample_ids: ``int64`` identity of each row, as ``predict_proba()``
reports it: the position in iteration order, or the global node
index for graph seed rows.
nnx.streaming.concatenate_predictions(batches: 'Iterable[Union[PredictionBatch, PredictionResult]]') -> 'Union[PredictionBatch, PredictionResult]'Concatenate streamed batches into one result, in order โ what the eager predict() / predict_proba() returns for the same loader. Materializes every batch (O(N) memory).
class nnx.streaming.StreamingMetrics(metrics: 'Sequence[MetricSpec]', semantics: 'str', *, labels: 'Optional[Sequence[str]]' = None, threshold: 'float' = 0.5, materialize: 'bool' = False, ignore_index: 'Optional[int]' = None, num_outputs: 'Optional[int]' = None) -> 'None'Mergeable accumulators for declared metrics (see the module docstring).
Details
Args:
metrics: the :class:`~nnx.MetricSpec` s to accumulate (unique names).
semantics: how predictions are read โ ``"categorical"`` (class
probabilities over the last axis of ``probabilities``, argmax
labels), ``"bernoulli"`` (independent per-output probabilities,
labels at ``threshold``) or ``"continuous"`` (values). Every
metric's input must be one these semantics provide.
labels: optional ordered class / output names; a categorical
``probabilities`` width must match them. Part of the merge schema.
threshold: the bernoulli decision threshold for labels derived from
probabilities (default 0.5).
materialize: store every score of a metric that has no bounded form
(O(N) memory) instead of rejecting it.
ignore_index: categorical only โ the target value that is never
scored (e.g. ``-100`` for padding).
num_outputs: the class count (categorical) or the output width along
axis 1 (bernoulli, continuous), when no ``labels`` name them;
batches of another width are refused.
Two accumulators merge only when their metrics (id, version, config and
name), semantics, labels, output count and ignore index are equal, and,
for bernoulli semantics, the decision threshold. If a
metric's update raises part-way through a batch, the accumulation no
longer describes one set of samples and refuses further use.
nnx.streaming.StreamingMetrics.for_task(metrics: 'Sequence[MetricSpec]', task: 'TaskSpec', *, materialize: 'bool' = False) -> 'StreamingMetrics'Accumulators for a model's TaskSpec: categorical (with the task's ignore_index), multilabel (bernoulli at the task's threshold) or regression (continuous), with the task's labels.
property nnx.streaming.StreamingMetrics.metricsNo public description is currently available.
property nnx.streaming.StreamingMetrics.semanticsNo public description is currently available.
property nnx.streaming.StreamingMetrics.labelsNo public description is currently available.
property nnx.streaming.StreamingMetrics.thresholdNo public description is currently available.
property nnx.streaming.StreamingMetrics.ignore_indexNo public description is currently available.
property nnx.streaming.StreamingMetrics.num_outputsThe class count (categorical) or output width, if declared (num_outputs, else the labels').
property nnx.streaming.StreamingMetrics.countThe valid samples scored so far.
property nnx.streaming.StreamingMetrics.boundedWhether no metric stores its scores (memory independent of N).
nnx.streaming.StreamingMetrics.update(self, target: 'Any', *, probabilities: 'Any' = None, labels: 'Any' = None, values: 'Any' = None, valid: 'Any' = None) -> 'int'Add one batch and return the number of valid samples scored.
Details
``target`` holds class indices ``(N,)`` (categorical), 0/1 outcomes
(bernoulli; soft targets count as 1 from 0.5) or values (continuous).
Give each input a declared metric reads: ``probabilities`` โ
``(N, C)`` class probabilities (categorical) or per-output
probabilities shaped like ``target`` (bernoulli); ``labels`` โ the
decoded predictions (derived from ``probabilities`` when omitted);
``values`` โ continuous predictions shaped like ``target``.
``valid`` (boolean, shaped like ``target``) excludes entries; a NaN
target, or a categorical target equal to ``ignore_index``, is
excluded too. Arrays and tensors are accepted.
nnx.streaming.StreamingMetrics.update_logits(self, target: 'Any', logits: 'Any', *, valid: 'Any' = None) -> 'int'Add one batch of raw model outputs (class axis 1): softmax (categorical), sigmoid decided at threshold (bernoulli) or the values (continuous) โ the inputs evaluate() derives. Returns the number of valid samples scored.
nnx.streaming.StreamingMetrics.merge(self, other: 'StreamingMetrics') -> 'StreamingMetrics'A new accumulation covering both inputs' samples; neither input changes, and later updates to either never reach the result. Raises MetricMergeError for different declarations.
nnx.streaming.StreamingMetrics.finalize(self) -> 'MetricSnapshot'The metrics over every sample seen, as a read-only snapshot. Repeatable, and never changes the accumulation.
class nnx.streaming.MetricSnapshot(count: 'int', values: 'Mapping[str, float]' = <factory>, unavailable: 'tuple[str, ...]' = ()) -> 'None'A finalized StreamingMetrics: read-only values that later updates never change.
Details
Attributes:
count: the valid samples scored (categorical rows; multilabel and
continuous entries). ``0`` for an empty or fully masked stream.
values: each available metric's value, by name.
unavailable: the declared metrics with no value (every one when
``count`` is 0).
property nnx.streaming.MetricSnapshot.availableNo public description is currently available.
nnx.streaming.streaming_eval_step(ctx: 'EvalStepContext') -> 'NNEvaluationDataPoint'An eval_step_fn for NNModel.train that builds the default validation record from counts and sums.
Details
The record is the one NNx's own ``evaluate()`` returns โ the same task
counts and status, loss (each batch's numerator over the summed loss
denominators), classification or task metrics and declared metrics โ
without storing a target or prediction per sample, so memory does not
grow with the validation set. A model subclass that overrides
``evaluate()`` keeps its override only on the default validation step. It cannot compute ``extra_metrics`` (callables on the
full arrays) or a declared metric that needs every stored score; both are
rejected when training starts. ``evaluate()`` and the default validation
step are unchanged.
class nnx.streaming.StreamClosedErrorA closed or already consumed PredictionStream was used again.
class nnx.streaming.MetricMergeErrorTwo StreamingMetrics with different declarations were merged: other metrics (id, version or config), other probability semantics, other task labels, another ignore index or (bernoulli) another decision threshold.
nnx.bundles.export_bundle(run_id: 'str', destination: 'Union[str, os.PathLike[str]]', *, checkpoint: 'str' = 'last', root: 'Optional[str]' = None, calibrators: 'Iterable[TemperatureCalibrator]' = ()) -> 'BundleInfo'Publish run_id's checkpoint as a run bundle at destination.
Details
Args:
run_id: a run under ``<root>/runs`` (the current directory by default)
โ your own run: its pickle checkpoint is read locally.
destination: an empty or missing directory, or an existing bundle,
whose generation this export replaces.
checkpoint: a ``Checkpoints`` tag (``"last"``, ``"best"``, โฆ) or a
``ModelCheckpoint`` file stem ``"<tag>_e<epoch>"``.
calibrators: ``TemperatureCalibrator`` records to ship with the
model. Their labels must match the task's, and a fingerprint
``model_id`` (``nnx.calibration.model_fingerprint``) must be the
bundled weights'.
Returns the published bundle's :class:`BundleInfo` (``verified=True``).
A checkpoint written by ``train()`` gives a ``"resume"`` bundle; a
weights-only one an ``"inference"`` bundle. A runtime-only module, a
recorded topology transform NNx cannot replay from data (anything but an
``nnx.transforms`` recipe operation of a known version or a torchao QAT
conversion โ the error names its index and id), module extra state that
is not a tensor, and training state that is not tensors and JSON
primitives are refused before anything is written.
nnx.bundles.inspect_bundle(path: 'Union[str, os.PathLike[str]]') -> 'BundleInfo'Summarize the bundle at path from its manifest, state.json and calibrator records (each checked against its manifest size and SHA-256). Tensor payloads are not read or checked โ validate_bundle checks them. Never unpickles, calls a factory or downloads.
nnx.bundles.validate_bundle(path: 'Union[str, os.PathLike[str]]') -> 'BundleInfo'Check the whole bundle at path and summarize it: every listed payload present, a regular file inside the published generation directory with the manifest's size and SHA-256, nothing unlisted, one generation id throughout, strict JSON without duplicate keys, and safetensors headers holding exactly the tensors state.json references. No tensor is read before all of that passes (a calibrator with a fingerprint model_id is then checked against the weights); nothing is unpickled, and no factory is called. Raises BundleIntegrityError (or BundleError for a path that is not a bundle).
nnx.bundles.reconstruct_bundle(path: 'Union[str, os.PathLike[str]]', *, factories: 'Optional[Mapping[tuple[str, int], ModelFactory]]' = None, components: 'Optional[Iterable[Any]]' = None, device: 'Any' = None, batch_adapter: 'Optional[BatchAdapter]' = None, exclude_submodules: 'Sequence[str]' = ()) -> 'ReconstructedBundle'Validate the bundle at path and rebuild its model.
Details
Args:
factories: the model factories to rebuild a registered ``ModelSpec``
from, ``{(id, version): factory}`` โ used instead of the process
registry. ``None`` uses the process registry
(``nnx.models.register_model_factory``). A built-in net needs none.
components: for a ``"resume"`` bundle, the stateful components the
resumed run will register (e.g. the same callbacks): their saved
state is checked against them. ``None`` skips the check (for
inference use; :meth:`ReconstructedBundle.resume` checks again).
device: a ``Devices`` member to build the model on instead of the
saved one.
batch_adapter: how a registered module sees a batch
(``nnx.models.BatchAdapter``), as passed to ``NNModel`` โ it is
runtime-only, never stored.
exclude_submodules: top-level submodules the training run attached
to the net that are not part of the rebuilt architecture โ a
JEPA predictor registered as ``model.net._jepa_predictor``, say
โ whose weights are left out, as in
``NNModel.from_checkpoint``; every other weight still loads
strictly. A model rebuilt without them serves inference and
cannot :meth:`~ReconstructedBundle.resume`.
Everything missing โ the model factory, a required or incompatible
component โ is reported in one :class:`BundleReconstructionError` before
any model is allocated. Calibrators with a fingerprint ``model_id`` are
checked against the rebuilt weights.
class nnx.bundles.BundleInfo(path: 'str', version: 'int', generation: 'str', capability: 'str', source_run_id: 'str', source_checkpoint: 'str', epoch: 'int', model: 'str', model_params: 'Mapping[str, Any]', net_params: 'Optional[Mapping[str, Any]]' = None, components: 'Mapping[str, Any]' = <factory>, calibrators: 'tuple[Mapping[str, Any], ...]' = (), payloads: 'Mapping[str, Any]' = <factory>, verified: 'bool' = False) -> 'None'A bundle's summary, from inspect_bundle (verified=False: only the manifest and state.json were checked) or validate_bundle / reconstruct_bundle (verified=True).
Details
Attributes:
path: the bundle directory.
version: the bundle format version.
generation: the published generation id.
capability: ``"resume"`` (weights and training state) or
``"inference"`` (weights only).
source_run_id / source_checkpoint: the run and checkpoint it came from.
epoch: the checkpoint's epoch.
model: the network descriptor (a built-in ``Nets`` name or a
registered ``ModelSpec``).
model_params: ``NNModelParams.state()`` of the checkpoint.
net_params: the built-in net's params ``state()`` (its dimensions:
the architecture reconstruction builds); ``None`` for a
registered module, whose architecture its factory builds.
components: saved component metadata, ``{name: {"version", "required"}}``.
calibrators: each calibrator's ``{"payload", "id", "labels", "model_id"}``.
payloads: ``{name: {"sha256", "size"}}`` from the manifest.
verified: whether every payload was checked.
class nnx.bundles.ReconstructedBundle(model: 'NNModel', info: 'BundleInfo', calibrators: 'tuple[TemperatureCalibrator, ...]', _checkpoint: 'NNCheckpoint', _training_state: 'Optional[dict[str, Any]]', _excluded: 'tuple[str, ...]' = ()) -> 'None'A model rebuilt from a validated bundle, its calibrators and โ for a "resume" bundle โ the training state resume continues from.
property nnx.bundles.ReconstructedBundle.capabilityNo public description is currently available.
property nnx.bundles.ReconstructedBundle.resume_checkpointThe checkpoint label a resumed run records as its parent checkpoint ("bundle-<generation[:16]>_e<epoch>": the first 16 hex characters of the generation).
nnx.bundles.ReconstructedBundle.resume(self, params: 'NNTrainParams', **train_kwargs: 'Any') -> 'NNRun'Continue training model from the bundle's training state: model.train(params, **train_kwargs) as a stateful resume of the bundle's run and checkpoint โ the optimizer, scheduler, GradScaler, RNG and component state are restored and epoch numbering continues, exactly as resuming the original run on disk would. params must not name a resume source of its own.
Details
An ``"inference"`` bundle raises :class:`BundleCapabilityError`
before anything changes, and so does a ``Trainer`` run's bundle (named
optimizers): this continues ``NNModel.train`` runs. ``resume_mode`` is
always ``"stateful"`` here.
class nnx.bundles.BundleErrorA run bundle that cannot be written, read or used.
class nnx.bundles.BundleIntegrityErrorA bundle that fails validation: a missing, extra, altered, symlinked or out-of-root payload, a generation mismatch, duplicate JSON keys, an unknown format or version, or malformed state.
class nnx.bundles.BundleCapabilityErrorAn operation the bundle's capability does not support โ resuming an "inference" bundle, which has no training state.
class nnx.bundles.BundleReconstructionError(problems: 'Iterable[str]') -> 'None'What a reconstruction needs and was not given (a model factory, a component), every problem listed in problems; raised before any model is allocated.
class nnx.transforms.TransformRecipe(operations: 'tuple[TransformOp, ...]', materialization: "Literal['fresh', 'in_place']" = 'in_place') -> 'None'An ordered, immutable list of TransformOp.
Details
``materialization`` says what :meth:`materialize` does: ``"in_place"``
(the default) transforms the given model itself โ its trained weights
are what LoRA wraps and SVD factorizes; ``"fresh"`` builds a fresh,
randomly initialized registered base from the given model's descriptor
and transforms that, leaving the given model untouched. The operations
are copied: changing the list they came from changes nothing here.
nnx.transforms.TransformRecipe.checkpoint_transforms(self) -> 'tuple[NNCheckpointTransform, ...]'The operations as checkpoint transforms, in order.
nnx.transforms.TransformRecipe.validate(self, model: 'NNModel', *, optimizers: 'Iterable[torch.optim.Optimizer]' = ()) -> 'None'Check the whole recipe as materialize would โ against model in place, or against the fresh base model describes โ mutating nothing, and raise one RecipeError naming every problem. optimizers are checked for an in-place materialization (a fresh base's parameters belong to no existing optimizer).
nnx.transforms.TransformRecipe.materialize(self, model: 'NNModel', *, optimizers: 'Iterable[torch.optim.Optimizer]' = ()) -> 'NNModel'Validate the whole recipe (as validate), then apply it โ to model itself ("in_place", returned) or to a fresh registered base built from model's descriptor ("fresh", returned). The operations are recorded on the returned model, so its checkpoints and Hub saves rebuild the same topology. Transactional: a failure leaves the model โ and the global random streams a fresh base or a LoRA initialization draws from โ as they were.
class nnx.transforms.TransformOp(id: 'str', targets: 'tuple[str, ...]', config: 'Mapping[str, Any]' = <factory>, version: 'int' = 1) -> 'None'One recorded operation: id ("lora" or "low_rank"), version, the explicit targets (dotted paths under model.net) and an immutable config. Build one with lora or low_rank.
nnx.transforms.TransformOp.state(self) -> 'dict[str, Any]'The JSON-ready form a checkpoint records (options of its NNCheckpointTransform).
nnx.transforms.TransformOp.checkpoint_transform(self) -> 'NNCheckpointTransform'No public description is currently available.
nnx.transforms.TransformOp.from_checkpoint_transform(transform: 'NNCheckpointTransform') -> 'TransformOp'The operation a checkpoint recorded; malformed options raise a RecipeError.
nnx.transforms.lora(*targets: 'str', r: 'int' = 8, alpha: 'float' = 16.0, dropout: 'float' = 0.0) -> 'TransformOp'Wrap each nn.Linear at targets in a LoRALinear (r, alpha, dropout as for apply_lora_to). Targets are explicit module paths, not globs.
nnx.transforms.low_rank(*targets: 'str', rank: 'int', method: 'str' = 'svd') -> 'TransformOp'Replace each nn.Linear at targets by its rank-rank factorization (nnx.surgery.low_rank_factorize).
nnx.transforms.check_optimizer(model: 'NNModel', optimizer: 'torch.optim.Optimizer') -> 'None'Refuse an optimizer built before the model's recipe: one holding parameters the recipe replaced (a low-rank operation's layers, or the model a fresh materialization started from), or holding a LoRA target's frozen base weights but none of the adapter built around them โ whenever it was built, since the adapter would never train. An optimizer built afterwards over the trainable parameters โ all, a subset, with parameters outside model.net, or BitFit-style over unfrozen base tensors โ passes.
class nnx.transforms.RecipeError(problems: 'Sequence[_Problem]') -> 'None'A recipe that does not fit a model, or a malformed operation. problems lists (operation index, operation id, target, reason); the message names each.
module nnx.precisionExplicit FP32 / FP16 / BF16 execution policy.
Details
``NNModelParams.precision`` takes a :class:`PrecisionPolicy`: the precision
training, evaluation and prediction run in. A policy is *resolved* against
the device it runs on โ once per run, before the run is reserved โ into a
:class:`ResolvedPrecision`, which says what is requested, what runs, and
why a fallback happened:
======== =========================================== ==================
mode runs as where
======== =========================================== ==================
``fp32`` full precision (no autocast, no scaler) every device
``fp16`` float16 autocast + a ``GradScaler`` CUDA
``bf16`` bfloat16 autocast, no scaler CPU; CUDA devices
with bf16 support
======== =========================================== ==================
A request the device cannot run fails before any run is reserved, unless
the policy names ``fallback="fp32"``: the run then trains in full precision
and records why. The model's parameters stay float32 in every mode
(autocast only โ never ``model.half()``), the backward pass runs outside
autocast, and the update keeps its unscale โ normalize โ finite-check โ
clip โ step order.
The legacy ``NNModelParams(mixed_precision=True)`` keeps its meaning โ FP16
autocast with a scaler on CUDA, silently full precision elsewhere โ and its
run id; combining it with a policy other than ``fp16`` is refused.
The policy covers ``NNModel.train`` (the default step and objectives),
``Trainer.train(objective=...)``, ``evaluate()`` and prediction. It does
not cover :mod:`nnx.lr_finder`, :mod:`nnx.diffusion.sampling` or
generation, and TF32 is reported (never set) as separate metadata.
class nnx.precision.PrecisionPolicy(mode: 'PrecisionMode' = 'fp32', fallback: 'PrecisionFallback' = 'error') -> 'None'The requested execution precision.
Details
Args:
mode: ``"fp32"`` (the default), ``"fp16"`` or ``"bf16"``.
fallback: what happens when the device cannot run ``mode``:
``"error"`` (the default) fails before any run is reserved;
``"fp32"`` runs in full precision and records why.
nnx.precision.PrecisionPolicy.state(self) -> 'dict[str, str]'{"mode": ...}, plus "fallback" when it is not the default "error".
nnx.precision.PrecisionPolicy.from_state(state: 'Mapping[str, Any]') -> 'PrecisionPolicy'No public description is currently available.
nnx.precision.PrecisionPolicy.resolve(self, device: 'Union[str, torch.device]') -> 'ResolvedPrecision'What this policy runs as on device (see the module table).
class nnx.precision.ResolvedPrecision(requested: 'str', effective: 'str', device_type: 'str', source: 'str' = 'default', fallback_reason: 'Optional[str]' = None, tf32: 'Mapping[str, Optional[bool]]' = <factory>, covers: 'Optional[tuple[str, ...]]' = None) -> 'None'A policy resolved against a device: what was requested, what runs, and why a fallback happened.
Details
``autocast()`` is the forward context, ``build_scaler()`` the
``GradScaler`` (FP16 only) and ``record()`` the run-inspection
mapping (``NNRun.precision``).
nnx.precision.ResolvedPrecision.scoped(self, covers: 'tuple[str, ...]') -> 'ResolvedPrecision'This resolution, recording that it applies to covers only.
property nnx.precision.ResolvedPrecision.applies_toNo public description is currently available.
property nnx.precision.ResolvedPrecision.autocast_dtypeNo public description is currently available.
property nnx.precision.ResolvedPrecision.reducedWhether anything runs below full precision.
property nnx.precision.ResolvedPrecision.uses_scalerNo public description is currently available.
nnx.precision.ResolvedPrecision.autocast(self) -> 'contextlib.AbstractContextManager[Any]'The forward-pass context: autocast to the effective dtype, or nothing in full precision.
nnx.precision.ResolvedPrecision.build_scaler(self) -> 'Optional[Any]'A fresh GradScaler for FP16, None otherwise.
nnx.precision.ResolvedPrecision.output(self, tensor: 'torch.Tensor') -> 'torch.Tensor'tensor as a caller sees it: a reduced-precision floating output of an autocast forward is returned as float32, so outputs keep their full-precision schema (and BF16 converts to a dtype NumPy can hold).
nnx.precision.ResolvedPrecision.record(self) -> 'dict[str, Any]'The run-inspection mapping: requested and effective precision, the fallback reason, TF32 (separately), and what the policy covers โ never nnx.lr_finder or nnx.diffusion.sampling.
nnx.precision.ResolvedPrecision.from_record(record: 'Mapping[str, Any]') -> 'ResolvedPrecision'No public description is currently available.
class nnx.precision.PrecisionUnsupportedErrorA precision the device cannot run, with no fallback chosen โ or a step function that cannot apply a reduced precision.
class nnx.precision.ReferenceTolerance(loss: 'float', weights: 'float') -> 'None'How far a reduced-precision run may drift from its FP32 reference on NNx's seeded fixtures: the largest absolute difference of a per-epoch training loss, and of any trained parameter.
nnx.precision.resolve_precision(params: 'Any', device: 'Union[str, torch.device, Any]') -> 'ResolvedPrecision'The precision a model's parameters (NNModelParams) run in on device: its policy, the legacy mixed_precision flag (FP16 on CUDA, full precision elsewhere โ its historical meaning), or full precision.
nnx.precision.precision_support(device: 'Union[str, torch.device, None]' = None) -> 'dict[str, dict[str, str]]'The precision matrix as this host sees it: for each device type ("cpu", "cuda", "mps", or just device's), each mode's status โ
Details
- ``"verified"``: NNx's seeded reference fixtures hold it within
:data:`REFERENCE_TOLERANCES` (CPU FP32 and BF16);
- ``"supported"``: the hardware is present and the mode resolves, but
no NNx fixture vouches for it here;
- ``"unverified"``: the hardware is absent, so nothing is claimed;
- ``"unsupported"``: the device type cannot run the mode.
nnx.precision.full_precision_only(step: 'Any') -> 'Any'Mark step as running in full precision only and return it.
Details
``NNModel.train`` refuses a reduced precision policy for a marked step
before anything is reserved. A wrapper around a built-in paradigm step
keeps that refusal when it is marked too (or built with
``functools.wraps``, which copies the marker).
class nnx.comparison.Metric(name: 'str', direction: 'str', unit: 'Optional[str]' = None) -> 'None'What a value measures: its name, whether larger or smaller is better (direction: "maximize" / "minimize") and its unit (None: unitless or not declared).
nnx.comparison.Metric.state(self) -> 'dict[str, Any]'No public description is currently available.
nnx.comparison.Metric.from_state(state: 'Mapping[str, Any]') -> 'Metric'No public description is currently available.
class nnx.comparison.Observation(run_id: 'str', metric: 'Metric', value: 'Optional[float]', split: 'str', selection: 'str', status: 'str' = 'completed', attempt_id: 'Optional[str]' = None, config: 'Optional[str]' = None, data: 'Optional[str]' = None, split_id: 'Optional[str]' = None, replicate: 'Optional[str]' = None, evaluation: 'Optional[str]' = None) -> 'None'One attempt's committed evaluation of one metric.
Details
Attributes:
run_id: the run that produced it.
attempt_id: the attempt within that run, when known.
metric: what ``value`` measures.
value: the value (``None`` when the attempt has none; NaN and
infinities are kept and counted as non-finite).
status: the attempt's completion status: ``"completed"``,
``"failed"``, ``"cancelled"``, ``"running"`` or ``"unknown"``.
split: the evaluation split the value was measured on (for example
``"validation"``).
selection: the checkpoint-selection rule behind the value (for
example ``"last"``, or ``"best:loss"`` for a monitor's election).
config: the configuration identity.
data: the data identity.
split_id: the split identity (a split manifest digest, for example).
replicate: the replicate key (for example ``"seed=3"``) โ what
:func:`compare` pairs on.
evaluation: the committed evaluation behind the value (for example
the epoch record and the checkpoint it was committed with).
``config``, ``data``, ``split_id``, ``replicate``, ``attempt_id`` and
``evaluation`` are ``None`` when unknown.
property nnx.comparison.Observation.idThe attempt id, or run:<run id> when the attempt is unknown.
property nnx.comparison.Observation.finiteWhether this is a completed attempt with a finite value.
nnx.comparison.Observation.state(self) -> 'dict[str, Any]'No public description is currently available.
nnx.comparison.Observation.from_state(state: 'Mapping[str, Any]') -> 'Observation'No public description is currently available.
nnx.comparison.summarize(observations: 'Iterable[Observation]') -> 'Summary'Group observations into poolable groups (same metric, split, selection rule, configuration, data and split identity) and summarize each; see GroupSummary. Duplicate attempts are refused: an attempt gives one observation per metric, so the same attempt observed on two splits or under two selection rules belongs in two summaries. A known identity never pools with an unknown one.
class nnx.comparison.Summary(groups: 'tuple[GroupSummary, ...]') -> 'None'A stratified summary: one GroupSummary per poolable group, and the fields that differ between groups (differing).
property nnx.comparison.Summary.differingNo public description is currently available.
property nnx.comparison.Summary.observationsNo public description is currently available.
nnx.comparison.Summary.pooled(self) -> 'GroupSummary'The single group, when every observation may be pooled; otherwise IncompatibleObservations naming the differing fields.
nnx.comparison.Summary.state(self) -> 'dict[str, Any]'No public description is currently available.
class nnx.comparison.GroupSummary(metric: 'Metric', split: 'str', selection: 'str', config: 'Optional[str]', data: 'Optional[str]', split_id: 'Optional[str]', observations: 'tuple[Observation, ...]') -> 'None'One poolable group: the shared fields (metric, split, selection, config, data, split_id), every observation (sorted), and the statistics over its completed, finite values.
Details
``n`` counts those values; ``n_attempts`` every observation,
``n_failed`` those not completed (failed, cancelled, running or of
unknown status) and ``n_nonfinite`` completed ones without a finite
value. ``std`` is the sample standard deviation (``n - 1``): ``None``
when ``n < 2``; ``mean`` is ``None`` when ``n == 0``. ``unknown`` names
the identity fields that are not known.
property nnx.comparison.GroupSummary.unknownNo public description is currently available.
nnx.comparison.GroupSummary.label(self) -> 'str'No public description is currently available.
nnx.comparison.GroupSummary.state(self) -> 'dict[str, Any]'No public description is currently available.
nnx.comparison.compare(a: 'Iterable[Observation]', b: 'Iterable[Observation]', *, pairing: 'str', bootstrap: 'Optional[Bootstrap]' = None) -> 'PairedComparison'Pair configuration b against a by replicate key.
Details
Each side must be one poolable group with a unique replicate key per
observation, and the sides must agree on everything but the
configuration (metric, split, selection rule, data and split identity).
``pairing`` declares what a shared replicate key means (for example
``"same seed for initialisation and data order, same split"``): NNx
cannot prove it, so it is required and recorded. Deltas are signed
``B - A``.
class nnx.comparison.PairedComparison(a: 'GroupSummary', b: 'GroupSummary', pairing: 'str', bootstrap: 'Optional[Bootstrap]' = None) -> 'None'Configuration B against A over shared replicate keys.
Details
``pairs`` holds every matched replicate (``delta`` is ``B - A``, never
flipped: for a metric to minimize, a negative delta means B is lower);
``unmatched_a`` / ``unmatched_b`` list the replicate keys found on one
side only, with their attempt ids. ``n``, ``mean`` and ``std`` (``n - 1``)
summarize the finite deltas; ``n_nonfinite`` counts the pairs of two
finite values whose delta overflows a float (excluded from ``n`` and
reported); ``interval`` is the bootstrap's, when one was requested and
``n >= 2``.
property nnx.comparison.PairedComparison.metricNo public description is currently available.
nnx.comparison.PairedComparison.state(self) -> 'dict[str, Any]'No public description is currently available.
class nnx.comparison.Pair(replicate: 'str', a: 'str', b: 'str', delta: 'Optional[float]') -> 'None'One replicate key on both sides: the observations' ids and the signed delta b - a (None unless both are completed and finite).
nnx.comparison.Pair.state(self) -> 'dict[str, Any]'No public description is currently available.
class nnx.comparison.Bootstrap(seed: 'int', resamples: 'int' = 2000, level: 'float' = 0.95) -> 'None'A percentile bootstrap of the mean paired delta: resamples resamples of the replicate pairs (the unit), drawn with replacement by numpy.random.default_rng(seed), giving a level interval. It describes seed variability over these replicates โ not a population-level or significance claim.
nnx.comparison.Bootstrap.state(self) -> 'dict[str, Any]'No public description is currently available.
nnx.comparison.Bootstrap.interval(self, deltas: 'Sequence[float]') -> 'Optional[tuple[float, float]]'No public description is currently available.
class nnx.comparison.ComparisonReport(summary: 'Summary', comparisons: 'tuple[PairedComparison, ...]' = ()) -> 'None'A summary of observations and any paired comparisons, as one artifact: table (machine-readable rows), text (a concise view), state / save / load (strict JSON, nnx.comparison/1).
nnx.comparison.ComparisonReport.build(observations: 'Iterable[Observation]', comparisons: 'Sequence[tuple[Iterable[Observation], Iterable[Observation], str]]' = (), *, bootstrap: 'Optional[Bootstrap]' = None) -> 'ComparisonReport'Summarize observations and run each (a, b, pairing) comparison (every compared observation must be among them).
nnx.comparison.ComparisonReport.table(self) -> 'list[dict[str, Any]]'One row per observation, group, pair and unmatched replicate, plus one comparison summary row per comparison (n, mean, std, bootstrap, interval), each with a row kind; values are plain JSON types.
nnx.comparison.ComparisonReport.text(self) -> 'str'A concise, fixed-order text view.
nnx.comparison.ComparisonReport.state(self) -> 'dict[str, Any]'No public description is currently available.
nnx.comparison.ComparisonReport.to_json(self) -> 'str'No public description is currently available.
nnx.comparison.ComparisonReport.save(self, path: 'Union[str, os.PathLike[str]]') -> 'None'No public description is currently available.
nnx.comparison.ComparisonReport.from_state(state: 'Mapping[str, Any]') -> 'ComparisonReport'Rebuild a report from its observations and comparison specs; the stored results must equal the re-derived ones.
nnx.comparison.ComparisonReport.load(path: 'Union[str, os.PathLike[str]]') -> 'ComparisonReport'No public description is currently available.
nnx.comparison.observations_from_runs(run_ids: 'Iterable[str]', *, metric: 'Metric', split: 'str' = 'validation', selection: 'str' = 'last', root: 'Optional[str]' = None, replicate: 'Optional[str]' = 'seed', config: 'Optional[Mapping[str, str]]' = None) -> 'list[Observation]'Observations of metric read from saved runs, without loading a model or a checkpoint (run.yaml, idps.csv โ or, for a run trained with a HistoryJournal, the journal's committed records, each chunk checked โ and the provenance files are read once each โ plus, for a run with a parent, its metadata.yaml and every ancestor's run.yaml and provenance files, whose data and split identities join the configuration identity and whose attempt records decide which parent attempt a child started from; an ancestor's unreadable file is refused โ and nothing is written).
Details
Args:
run_ids: the runs (under ``root``'s ``runs/``).
metric: the metric to read โ a named metric (``train.metrics``), a
built-in field (``loss``, ``error``, ``accuracy``, ``f1``,
``recall``, ``precision``) or an ``extra_metrics`` name.
split: ``"validation"`` (the epoch's validation record) or
``"train"`` (its whole-epoch training summary โ recorded only by
runs that declare metrics or a monitor; never a last batch).
selection: ``"last"`` (the last committed epoch) or ``"best"`` (the
last committed epoch the run's monitor elected; the rule names
the declared monitor, and the value is unknown for a run without
one or whose monitor elected nothing).
replicate: ``"seed"`` keys each observation ``seed=<seed>`` (the
training or ``Trainer`` seed), plus ``init_seed=<seed>`` for a
registered ``ModelSpec`` whose own seed differs; unknown for a
run without a training seed. ``None`` leaves it unknown.
config: run id โ declared configuration label, for every run; by
default, a digest of the run's configuration without its salt,
seeds and device, in which a parent run's id is replaced by the
parent's own identity, the checkpoint tag, the resume mode and,
when it is part of the procedure, the epoch the run started at โ
so a continuation, fine-tune or later generation pools only
with runs descended the same way from the same configuration
(an unknown lineage pools with nothing).
Runs should be trained with ``provenance=``: the status and
attempt id come from the run's attempt record (``"unknown"`` without
one) and the data and split identities from its manifest. A run's
history is committed by its LAST checkpoint, so without an attempt
record naming that checkpoint's epoch the committed epoch โ and so the
value โ is unknown. The recorded epoch is used as is (LAST itself is
never opened, so nothing is unpickled), and a history journal is read
only up to it โ not at all while it is unknown, so an uncommitted tail
is never read. A legacy run (no commit marker) keeps its value,
but its status is unknown, so it is never counted in a group's ``n``.
A NaN metric is read back as missing: ``idps.csv`` writes NaN as an
empty cell.
class nnx.comparison.ComparisonErrorA malformed observation, a duplicate attempt, an invalid pairing or a report file that does not hold what it claims.
class nnx.comparison.IncompatibleObservations(message: 'str', *, fields: 'Sequence[str]') -> 'None'Observations that may not be pooled or paired: fields names what differs.
module nnx.lm_tasksA causal language-model task: declared alignment, masks and valid-token normalization.
Details
A next-token step written by hand flattens ``(B, T, V)`` logits before any
mask exists, divides by the batch count when it accumulates and returns
placeholder classification fields. :class:`CausalLMTask` declares instead:
- **alignment** โ ``"shift_inputs"``: a batch is token ids ``(B, T)`` (or
``(ids, loss_mask)``) and the task shifts once, predicting token ``t + 1``
from tokens ``<= t``; ``"pre_shifted"``: a batch is ``(inputs, targets)``
(or ``(inputs, targets, loss_mask)``) already aligned, never shifted again;
- the **vocabulary** size and axis (the logits' last axis), the **ignore**
id (``-100``) and an optional **padding** id, an optional per-position
**loss mask** (a loss mask is never an attention mask), label
**smoothing** for the objective, and the task **version**.
The ignore id, the padding id and the loss mask select one set of **valid
positions** โ the objective's denominator, the NLL and the token accuracy
all use exactly those. Shapes, dtypes, lengths and every non-ignored id are
checked before any forward pass.
- :meth:`CausalLMTask.objective` โ an objective: one ``"token_ce"``
term per microbatch, the (smoothed) cross-entropy **sum** over valid
positions with the valid count as its denominator, so accumulation
windows normalize by the window's valid tokens. An all-masked microbatch
contributes nothing, and an all-masked window takes no optimizer or
scaler step (its gradients are cleared; schedulers still advance once per
epoch on the default epoch clock, or once per committed update on the
``optimizer_update`` clock, and a plateau scheduler skips an epoch whose validation record is
unavailable). The task configuration is
checkpointed component state (``"lm.causal_task"``): a resume with another
configuration is refused before the first resumed update.
- :meth:`CausalLMTask.eval_step` โ an ``eval_step_fn``: the epoch's
**unsmoothed** NLL is the total valid-token negative log likelihood over
the valid-token count (independent of batching and right padding; the
model never receives the attention mask, so a mapping batch whose
``attention_mask`` shows left padding is refused; ``pad_id`` alone does not
detect left padding, so right-pad the batch), perplexity is
``exp(NLL)`` (``+inf`` when it overflows, never a clipped finite value) and
token accuracy is the top-1 rate. They are the record's ``loss`` and its
``metrics`` ``nll`` / ``perplexity`` / ``token_accuracy``; an epoch whose
every position is masked is reported unavailable (no loss, no metrics).
No classification field is fabricated.
class nnx.lm_tasks.CausalLMTask(vocab_size: 'int', alignment: 'str' = 'shift_inputs', ignore_id: 'int' = -100, pad_id: 'Optional[int]' = None, smoothing: 'float' = 0.0, vocab_axis: 'int' = -1, tokenizer: 'Optional[str]' = None, version: 'int' = 1) -> 'None'A next-token prediction task.
Details
Args:
vocab_size: the vocabulary size; the logits' class axis must have
exactly this width.
alignment: ``"shift_inputs"`` (the task shifts token ids once) or
``"pre_shifted"`` (batches carry aligned inputs and targets).
ignore_id: the target id that marks a position as not scored.
pad_id: an optional padding id: target positions holding it are not
scored either (input positions may hold it).
smoothing: label smoothing for the **objective** only, in ``[0, 1)``;
reported NLL and perplexity are always unsmoothed.
vocab_axis: the logits' vocabulary axis (``-1``, the last).
tokenizer: an optional declared tokenizer identity (for example the
tokenizer file's digest), checkpointed with the task.
version: the task schema version.
nnx.lm_tasks.CausalLMTask.state(self) -> 'dict[str, Any]'No public description is currently available.
nnx.lm_tasks.CausalLMTask.from_state(state: 'Mapping[str, Any]') -> 'CausalLMTask'No public description is currently available.
nnx.lm_tasks.CausalLMTask.split(self, batch: 'Any') -> 'tuple[torch.Tensor, torch.Tensor, Optional[torch.Tensor]]'(inputs, targets, loss_mask) for one batch, aligned once (see the module docstring), with shapes, dtypes and ids checked.
Details
``"shift_inputs"`` takes token ids ``(B, T)``, ``(ids,)``, ``(ids,
loss_mask)`` with a boolean mask, or a mapping ``{"input_ids",
"labels"?, "loss_mask"?}`` โ HuggingFace-style ``labels`` sit at the
ids' positions (unshifted, ``ignore_id`` where not scored) and are
shifted with them. ``"pre_shifted"`` takes ``(inputs, targets[,
loss_mask])`` or a mapping ``{"inputs", "targets", "loss_mask"?}``.
A loss mask (boolean, or 0/1 except in the ``(ids, loss_mask)``
tuple) covers the ids (``"shift_inputs"``) or the targets
(``"pre_shifted"``). A mapping's ``attention_mask`` aligns the same
way (the ids, or the targets when pre-shifted). It is never a loss
mask, but a position it pads that would still be scored is refused,
and so is padding before a real token (left or interior padding): the
model never receives the mask. Without one the task cannot detect
left padding (``pad_id`` only masks pad targets), so right-pad the
batch.
nnx.lm_tasks.CausalLMTask.valid(self, targets: 'torch.Tensor', loss_mask: 'Optional[torch.Tensor]') -> 'torch.Tensor'The scored positions: not ignore_id, not pad_id and, when given, inside the loss mask.
nnx.lm_tasks.CausalLMTask.logits(self, model: 'Any', inputs: 'torch.Tensor', targets: 'torch.Tensor') -> 'torch.Tensor'The model's logits (B, T, V) for inputs, checked.
nnx.lm_tasks.CausalLMTask.token_sums(self, logits: 'torch.Tensor', targets: 'torch.Tensor', valid: 'torch.Tensor', *, with_ce: 'bool' = True) -> 'tuple[torch.Tensor, float, int, int]'(smoothed CE sum (differentiable), unsmoothed NLL sum, correct, valid count) over the valid positions; flattened once, here. with_ce=False (evaluation) skips the cross-entropy pass and returns an empty sum in its place.
nnx.lm_tasks.CausalLMTask.objective(self, *, nonfinite: 'str' = 'fail') -> 'CausalLMObjective'No public description is currently available.
nnx.lm_tasks.CausalLMTask.eval_step(self) -> 'CausalLMEval'No public description is currently available.
class nnx.lm_tasks.CausalLMObjective(task: 'CausalLMTask', *, nonfinite: 'str' = 'fail') -> 'None'The task's training objective (see CausalLMTask.objective): a "token_ce" term per microbatch โ the cross-entropy sum over valid positions (smoothed when the task smooths) over the valid count. Its record's loss is that microbatch's term value and metrics its unsmoothed nll and token_accuracy. Checkpointed as component "lm.causal_task": the task configuration and the valid tokens the run's objective has scored (counted from 0 for each fresh run, restored on a stateful resume; a window the engine then skips โ a non-finite loss, an AMP overflow โ is still counted). Pass a fresh objective to a weights_only resume: it restores no component state, so a reused instance keeps its own count.
nnx.lm_tasks.CausalLMObjective.component_spec(self) -> 'ComponentSpec'No public description is currently available.
nnx.lm_tasks.CausalLMObjective.component_state(self) -> 'dict[str, Any]'No public description is currently available.
nnx.lm_tasks.CausalLMObjective.check_component_state(self, state: 'Mapping[str, Any]', *, version: 'int') -> 'list[str]'No public description is currently available.
nnx.lm_tasks.CausalLMObjective.load_component_state(self, state: 'Mapping[str, Any]', *, version: 'int') -> 'None'No public description is currently available.
class nnx.lm_tasks.CausalLMEval(task: 'CausalLMTask') -> 'None'The task's eval_step_fn (see CausalLMTask.eval_step): total valid-token NLL over the valid-token count for the whole validation loader, its perplexity and token accuracy. The model runs in eval mode and every submodule's training flag is restored.
nnx.lm_tasks.perplexity(nll: 'Optional[float]') -> 'Optional[float]'exp(nll); +inf when it overflows (never a clipped value).
class nnx.lm_tasks.LMTaskErrorA task configuration or a batch the task rejects.
module nnx.rankingQuery-grouped ranking and retrieval evaluation.
Details
A scorer ranks the **candidates of each query**; it is judged per query,
never per batch. :class:`RankingTask` declares the relevance grades, the
cutoffs ``k``, the relevance threshold, how the pairwise objective weights
queries and pairs, the candidate-set kind and the buffer limit, and checks
every batch โ query ids, candidate ids, relevance grades and an optional
mask โ before any update.
A batch is ``(features, query_ids, candidate_ids, relevance)`` or
``(..., mask)`` (or a mapping with those keys): the scorer maps
``features`` to one score per candidate row; ids are integer tensors or
lists of strings; relevance grades are integers in ``0..max_relevance``.
- :meth:`RankingTask.objective` โ the **pairwise logistic** objective: for each query, every pair of its candidates with unequal
relevance contributes ``log(1 + exp(-(s_hi - s_lo)))``; equal-relevance
pairs (ties) contribute nothing and a query without such a pair is
skipped. ``weighting="query"`` averages each query's pairs and then the
queries (a query weighs the same however many pairs it has);
``weighting="pair"`` weighs every pair equally. Pairs are formed within a
microbatch. Checkpointed as component ``"ranking.task"``.
- :meth:`RankingTask.eval_step` โ an ``eval_step_fn``: it buffers the whole
validation stream (at most ``max_buffered`` candidate rows), groups it by
query โ so a query split across batches is joined, in any order โ and
rejects a candidate seen twice for one query. Each query's candidates are
ranked by score, **ties broken by candidate id** (ascending); then
``MRR@k`` (the reciprocal rank of the first relevant candidate in the top
``k``, else 0), ``Recall@k`` (relevant candidates in the top ``k`` over
all relevant) and ``NDCG@k`` (gain ``2^rel - 1``, discount
``log2(rank + 1)``) are averaged over queries. A candidate is relevant
when its grade is at least ``relevance_threshold``; a query with no
relevant candidate is **excluded** (and counted), never scored 0. ``k``
above a query's candidate count is rejected.
The record (``kind="ranking"``) carries ``mrr_at_<k>``, ``recall_at_<k>``,
``ndcg_at_<k>``, the counts of scored and excluded queries and candidates,
and ``exhaustive_candidates`` (``1.0`` when every query's candidate set is
its full corpus, ``0.0`` when it is sampled โ then the metrics measure the
candidate set, not full-corpus retrieval). :meth:`RankingTask.metric_specs`
declares the metrics so a :class:`~nnx.MonitorSpec` can name them (for
example ``MonitorSpec("ndcg_at_10")``) for BEST, early stopping and plateau
scheduling.
class nnx.ranking.RankingTask(k: 'tuple[int, ...]' = (10,), max_relevance: 'int' = 1, relevance_threshold: 'int' = 1, weighting: 'str' = 'query', candidate_sets: 'str' = 'sampled', max_buffered: 'int' = 1000000, group_size: 'Optional[int]' = None, version: 'int' = 1) -> 'None'A query-grouped ranking task.
Details
Args:
k: the cutoffs the metrics are reported at (each ``>= 1``).
max_relevance: grades are integers in ``0..max_relevance``
(``1``: binary relevance).
relevance_threshold: a candidate is relevant (for MRR and Recall,
and for excluding queries) when its grade is at least this.
weighting: the objective's ``"query"`` or ``"pair"`` weighting.
candidate_sets: ``"sampled"`` (each query sees a sample of its
corpus) or ``"exhaustive"`` (its whole corpus); recorded with
the metrics.
max_buffered: the most candidate rows the evaluation buffers to
join each query's candidates.
group_size: when set, every evaluated query must have exactly this
many candidates โ an incomplete (or over-full) query is
rejected rather than scored.
version: the task schema version.
nnx.ranking.RankingTask.state(self) -> 'dict[str, Any]'No public description is currently available.
nnx.ranking.RankingTask.from_state(state: 'Mapping[str, Any]') -> 'RankingTask'No public description is currently available.
nnx.ranking.RankingTask.metric_names(self) -> 'list[str]'No public description is currently available.
nnx.ranking.RankingTask.metric_specs(self) -> 'list[Any]'MetricSpecs declaring this task's metrics โ registered ids ranking.mrr / ranking.recall / ranking.ndcg with config={"k": k}, reported as <metric>_at_<k> โ so a MonitorSpec can select on them. eval_step computes them; a declaration under another name finds no value.
nnx.ranking.RankingTask.split(self, batch: 'Any') -> 'tuple[Any, list[Id], list[Id], torch.Tensor, Optional[torch.Tensor]]'(features, query_ids, candidate_ids, relevance, mask), checked.
nnx.ranking.RankingTask.scores(self, model: 'Any', features: 'Any', n: 'int') -> 'torch.Tensor'The scorer's one score per row, checked.
nnx.ranking.RankingTask.objective(self, *, nonfinite: 'str' = 'fail') -> 'RankingObjective'No public description is currently available.
nnx.ranking.RankingTask.eval_step(self) -> 'RankingEval'No public description is currently available.
nnx.ranking.RankingTask.evaluate_rows(self, rows: 'Iterable[tuple[Id, Id, float, int]]') -> 'tuple[dict[str, float], int, int, int]'Metrics over complete queries from (query, candidate, score, relevance) rows in any order: (means, scored queries, excluded queries, candidates of the scored queries). Duplicate candidates of a query raise.
class nnx.ranking.RankingObjective(task: 'RankingTask', *, nonfinite: 'str' = 'fail') -> 'None'The pairwise logistic objective of a RankingTask (see the module docstring); checkpointed as component "ranking.task" with the task configuration and the run's training units โ pairs (weighting="pair") or queries ("query") scored, from 0 for each fresh run and restored on resume (a window the engine then skips is still counted).
nnx.ranking.RankingObjective.component_spec(self) -> 'ComponentSpec'No public description is currently available.
nnx.ranking.RankingObjective.component_state(self) -> 'dict[str, Any]'No public description is currently available.
nnx.ranking.RankingObjective.check_component_state(self, state: 'Mapping[str, Any]', *, version: 'int') -> 'list[str]'No public description is currently available.
nnx.ranking.RankingObjective.load_component_state(self, state: 'Mapping[str, Any]', *, version: 'int') -> 'None'No public description is currently available.
class nnx.ranking.RankingEval(task: 'RankingTask') -> 'None'The RankingTask's eval_step_fn: buffers the validation stream (at most max_buffered rows), groups it by query and reports the per-query metrics (see the module docstring), with the pairwise loss over the same queries as the record's loss.
nnx.ranking.pairwise_logistic_loss(scores: 'torch.Tensor', relevance: 'torch.Tensor', query_ids: 'Sequence[Id]', *, weighting: 'str' = 'query') -> 'tuple[torch.Tensor, int]'(numerator, denominator) of the pairwise logistic loss over the unequal-relevance pairs within each query (see the module docstring).
Details
Pairs are formed grade by grade (each grade's candidates against every
lower-graded one) in blocks of at most :data:`PAIR_BLOCK` pairs, so no
query's full pair matrix is ever held โ in training too: a block of at
least :data:`CHECKPOINT_PAIRS` pairs is recomputed in backward
(``torch.utils.checkpoint``) rather than kept, while a smaller one costs
less to keep than to recompute. ``relevance`` may live on any device; it
is moved to the scores'.
nnx.ranking.rank(scores: 'Sequence[float]', candidate_ids: 'Sequence[Id]') -> 'list[int]'Positions of the candidates in ranked order: by score, highest first, ties broken by candidate id (ascending) โ a stable, documented order. A non-finite score has no place in that order and is refused.
nnx.ranking.mrr_at_k(relevance: 'Sequence[int]', scores: 'Sequence[float]', candidate_ids: 'Sequence[Id]', k: 'int', *, threshold: 'int' = 1) -> 'float'Reciprocal rank of the first relevant candidate within the top k (0 when none is).
nnx.ranking.recall_at_k(relevance: 'Sequence[int]', scores: 'Sequence[float]', candidate_ids: 'Sequence[Id]', k: 'int', *, threshold: 'int' = 1) -> 'float'Relevant candidates in the top k over all relevant candidates (the query must have at least one).
nnx.ranking.ndcg_at_k(relevance: 'Sequence[int]', scores: 'Sequence[float]', candidate_ids: 'Sequence[Id]', k: 'int') -> 'float'DCG@k / IDCG@k with gain 2^rel - 1 and discount log2(rank + 1) (the query must have a nonzero grade).
class nnx.ranking.RankingErrorA configuration, batch or query the ranking task rejects.
class nnx.graph_tasks.GraphCollection(graphs: 'Sequence[Any]', ids: 'Sequence[int]', *, targets: 'Optional[Sequence[Optional[int]]]' = None, unlabeled: 'Sequence[int]' = (), num_classes: 'Optional[int]' = None) -> 'None'Whole graphs with stable ids and graph-level targets.
Details
Args:
graphs: PyG ``Data`` objects with ``x`` (``(nodes, features)``,
floating) and ``edge_index`` (``(2, edges)``, node indices of the
same graph); each graph's target is its ``y`` (one integer
class) unless ``targets`` gives them.
ids: one stable non-negative integer id per graph (unique).
targets: optional class per graph, overriding ``y``.
unlabeled: ids of graphs deliberately without a target (their
target becomes :data:`IGNORE`); any other graph without one is
refused.
num_classes: when given, every target must be below it.
property nnx.graph_tasks.GraphCollection.labeledHow many graphs carry a target.
nnx.graph_tasks.GraphCollection.subset(self, ids: 'Sequence[int]') -> 'GraphCollection'The graphs with these ids, in the order given.
nnx.graph_tasks.GraphCollection.loader(self, batch_size: 'int', *, shuffle: 'bool' = False, seed: 'Optional[int]' = None) -> 'Any'A PyG loader over whole graphs (in collection order unless shuffle; a seed makes the shuffle reproducible and is refused without it).
class nnx.graph_tasks.GraphClassifier(encoder: 'nn.Module', pool: 'Any' = 'mean', head: 'Optional[nn.Module]' = None, *, input_dim: 'Optional[int]' = None) -> 'None'encoder (node rows from (x, edge_index)) โ GraphPool โ optional head: one output row per graph.
Details
``unpack_batch`` validates a graph-collection batch (see
:func:`check_graph_batch`) and returns its graph-level targets;
``sample_ids`` returns its graph ids, which ``predict_proba`` reports as
the rows' sample ids.
nnx.graph_tasks.GraphClassifier.forward(self, x: 'torch.Tensor', edge_index: 'torch.Tensor', batch: 'torch.Tensor', ptr: 'torch.Tensor') -> 'torch.Tensor'No public description is currently available.
nnx.graph_tasks.GraphClassifier.unpack_batch(self, batch: 'Any') -> 'tuple[tuple[torch.Tensor, ...], Optional[torch.Tensor]]'No public description is currently available.
nnx.graph_tasks.GraphClassifier.sample_ids(self, batch: 'Any') -> 'torch.Tensor'The batch's graph ids, one per output row, in batch order.
class nnx.graph_tasks.GraphPool(mode: 'str' = 'mean') -> 'None'Pools node rows into one row per graph: "mean" or "sum" over each graph's own nodes (graph-local, invariant to node order).
nnx.graph_tasks.GraphPool.forward(self, x: 'torch.Tensor', batch: 'torch.Tensor', num_graphs: 'int') -> 'torch.Tensor'No public description is currently available.
nnx.graph_tasks.GraphPool.extra_repr(self) -> 'str'No public description is currently available.
nnx.graph_tasks.graph_classifier_spec(*, input_dim: 'int', num_classes: 'int', hidden_dims: 'Sequence[int]' = (32,), encoder: 'str' = 'graph_conv', pool: 'str' = 'mean', activation: 'str' = 'relu', dropout: 'float' = 0.0, seed: 'int' = 0) -> 'Any'The registered recipe of a GraphClassifier: encoder convolutions ("graph_conv" GCN, "graph_sage", "graph_att" GAT) over hidden_dims, pool and a linear head to num_classes. Pass it as NNModelParams(net=...); a reload rebuilds the same module from the run.
nnx.graph_tasks.check_graph_batch(batch: 'Any') -> 'tuple[torch.Tensor, torch.Tensor, torch.Tensor, torch.Tensor]'(x, edge_index, batch_vector, ptr) of a graph-collection batch, checked: graph ids (unique), at least one node per graph, ptr and the batch vector consistent, and no edge across graphs. An edge-label (link) batch is refused: pooling it would score candidate edges as graphs.
nnx.graph_tasks.is_graph_collection_batch(batch: 'Any') -> 'bool'Whether batch is a batch of whole graphs (it carries graph ids).
class nnx.graph_tasks.GraphTaskErrorA graph collection, batch or classifier setting NNx rejects.
module nnx.link_tasksLeakage-aware link and edge prediction on a static homogeneous graph.
Details
Node masks cannot hold out edges: a held-out edge left in the message graph
is a leaked answer. This module splits **edges**, keeps the graph the model
passes messages over apart from the edges it is asked about, and checks
every batch against that split before any forward pass.
- :class:`LinkSplit` โ a versioned manifest: the node count, canonical
positive edges per split (``train`` / ``val`` / ``test``; an undirected
edge is ``(min, max)``, so a duplicate, or in an undirected split a
reverse, can never sit in two splits), fixed ``val`` / ``test`` negatives, directedness, the self-loop
policy, the seed, and โ for edge-label tasks โ the categories, each
edge's category and whether labelled edges' **existence** is visible
context. :func:`split_links` derives one from an ``edge_index`` with a
local seeded generator (never the global RNG); :meth:`LinkSplit.replay`
re-derives it and refuses a mismatch.
- **Topology.** :meth:`LinkSplit.message_edge_index` is the only graph
messages pass over: the training edges (and their reverses when
undirected) โ for every split, so evaluation never sees held-out
positives. In edge-label mode with ``label_existence="visible"`` every
labelled edge's existence is context (their categories never are).
- **Negatives** come from the static complement โ never a positive of any
split, its reverse (in an undirected split), a duplicate or a barred
self-loop. A request beyond
the complement's capacity fails before anything is sampled; ``val`` /
``test`` negatives are fixed in the manifest; training negatives are
re-drawn every pass from ``(seed, pass)``.
- :class:`LinkTask` โ ``"binary"`` (link existence: one logit per
candidate, targets 0/1) or ``"edge_label"`` (categories in their declared
order; no negatives, so a non-edge is never a category). ``loader()``
builds batches of candidates over the message graph; ``objective()``,
``eval_step()`` and ``predict()`` check each batch against the split โ
a message edge outside the training topology (a hidden positive) or a
candidate outside its split fails before any update. Evaluation
materialises every candidate (up to ``max_candidates``) for exact
AUROC / AP, scored from the float64 logits (a probability rounds to 0 or
1 for a confident model). For a one-class set, ``link_metrics`` reports
them unavailable with the reason, and the evaluator's record omits them.
The manifest is checkpointed component state ``"link.task"``: a resume
with another split fails before the first resumed update.
- :func:`link_predictor_spec` โ a registered recipe
(``ModelSpec("nnx.link_predictor", 1, config)``): a GCN / GraphSAGE /
GAT encoder and a ``"dot"`` or ``"mlp"`` edge decoder, rebuilt on reload.
Homogeneous static graphs only: no temporal or heterogeneous graphs, and
unobserved pairs are negatives only as the split declares them.
nnx.link_tasks.split_links(edge_index: 'Any', num_nodes: 'int', *, val: 'Any' = 0.1, test: 'Any' = 0.1, seed: 'int', directed: 'bool' = False, self_loops: 'str' = 'bar', negatives: 'int' = 1, edge_labels: 'Optional[Sequence[int]]' = None, categories: 'Optional[Sequence[str]]' = None, label_existence: 'str' = 'hidden') -> 'LinkSplit'Split the graph's edges with a local generator seeded by seed.
Details
Edges are canonicalised and de-duplicated first (an undirected reverse
is the same edge), so no duplicate, and in an undirected split no
reverse, can cross splits (a directed split keeps ``uโv`` and ``vโu``
apart).
``val`` / ``test`` are fractions of the edges or exact counts.
``negatives`` fixed non-edges per ``val`` / ``test`` positive come from
the complement of every positive (and barred self-loops); a request
beyond the complement's capacity fails before sampling. With
``categories`` (and one category per input edge in ``edge_labels``) the
split is an edge-label split: no negatives, and a duplicate edge must
keep its category.
class nnx.link_tasks.LinkSplit(num_nodes: 'int', train: 'tuple[Edge, ...]', val: 'tuple[Edge, ...]' = (), test: 'tuple[Edge, ...]' = (), val_negatives: 'tuple[Edge, ...]' = (), test_negatives: 'tuple[Edge, ...]' = (), directed: 'bool' = False, self_loops: 'str' = 'bar', seed: 'Optional[int]' = None, categories: 'Optional[tuple[str, ...]]' = None, edge_labels: 'tuple[tuple[Edge, int], ...]' = (), label_existence: 'str' = 'hidden', version: 'int' = 1) -> 'None'A versioned edge split (see the module docstring). Every edge is canonical; validation refuses an edge outside the graph, a barred self-loop, a positive in two splits (or twice in one), and a negative that is a positive or repeats.
property nnx.link_tasks.LinkSplit.modeNo public description is currently available.
nnx.link_tasks.LinkSplit.positives(self, name: 'str') -> 'tuple[Edge, ...]'No public description is currently available.
nnx.link_tasks.LinkSplit.negatives(self, name: 'str') -> 'tuple[Edge, ...]'No public description is currently available.
nnx.link_tasks.LinkSplit.candidate_ids(self, name: 'str') -> 'dict[Edge, int]'Stable integer ids of a split's fixed candidates (positives, then negatives): one global numbering over the manifest.
nnx.link_tasks.LinkSplit.labels(self) -> 'dict[Edge, int]'No public description is currently available.
nnx.link_tasks.LinkSplit.message_edge_index(self) -> 'torch.Tensor'The only edges messages pass over, for every split: the training edges (with reverses when undirected) โ plus every labelled edge's existence in an edge-label split with label_existence="visible".
nnx.link_tasks.LinkSplit.state(self) -> 'dict[str, Any]'No public description is currently available.
nnx.link_tasks.LinkSplit.from_state(state: 'Mapping[str, Any]') -> 'LinkSplit'No public description is currently available.
nnx.link_tasks.LinkSplit.digest(self) -> 'str'No public description is currently available.
nnx.link_tasks.LinkSplit.replay(self, edge_index: 'Any', *, edge_labels: 'Optional[Sequence[int]]' = None) -> 'LinkSplit'Re-derive this split from the graph with its own seed and policy; a mismatch (different graph, settings or code) raises.
class nnx.link_tasks.LinkTask(split: 'LinkSplit', mode: 'Optional[str]' = None, train_negatives: 'Optional[int]' = None, max_candidates: 'int' = 1000000, version: 'int' = 1) -> 'None'Link existence (mode="binary") or edge categories ("edge_label") over a LinkSplit (see the module docstring).
Details
Args:
split: the manifest.
mode: ``"binary"`` or ``"edge_label"``; by default the split's (an
edge-label split has categories).
train_negatives: non-edges sampled per training positive, per pass
(binary only; by default 1, and 0 in edge-label mode).
max_candidates: the most candidates evaluation materialises.
Training negatives are drawn per epoch from ``(seed, epoch)``, so a
stateful resume continues them as if uninterrupted โ when the training
loop's ``set_epoch`` reaches the task's loader (directly, or through a
wrapper that forwards it). The objective checkpoints the seed and
refuses a resumed loader with another one.
nnx.link_tasks.LinkTask.state(self) -> 'dict[str, Any]'No public description is currently available.
nnx.link_tasks.LinkTask.loader(self, name: 'str', x: 'torch.Tensor', batch_size: 'int', *, seed: 'Optional[int]' = None) -> '_Loader'Batches of name's candidates over the message graph. Training negatives and order are re-drawn every pass from (seed, pass) (seed, an integer >= 0, defaults to the split's); a seed on a val / test loader, whose candidates are fixed, is refused.
nnx.link_tasks.LinkTask.check_batch(self, batch: 'Any') -> 'None'Refuse a batch that would leak or does not fit the split: message edges outside the training topology (a held-out positive is named); candidates outside the batch's split, with the wrong target or id; a barred self-loop; and a training negative that is a held-out (val / test) negative.
nnx.link_tasks.LinkTask.objective(self, *, nonfinite: 'str' = 'fail') -> 'LinkObjective'No public description is currently available.
nnx.link_tasks.LinkTask.eval_step(self) -> 'LinkEval'No public description is currently available.
nnx.link_tasks.LinkTask.metric_specs(self) -> 'list[Any]'MetricSpecs for MonitorSpec("auroc") / ("ap") (binary) โ a one-class set leaves them unavailable, so it never wins BEST.
nnx.link_tasks.LinkTask.predict(self, model: 'Any', batches: 'Any') -> 'LinkPrediction'Every candidate's prediction, in batch order, aligned with its id, pair and target (eval mode, no gradients, modes restored).
class nnx.link_tasks.LinkPredictor(encoder: 'nn.Module', decoder: 'str' = 'dot', *, width: 'int' = 0, outputs: 'int' = 1) -> 'None'encoder (node rows from (x, edge_index)) and an edge decoder: "dot" (one logit per candidate: h_u ยท h_v) or "mlp" (a linear head on [h_u * h_v, |h_u - h_v|]: one logit, or one per category).
nnx.link_tasks.LinkPredictor.forward(self, x: 'torch.Tensor', edge_index: 'torch.Tensor', edge_label_index: 'torch.Tensor') -> 'torch.Tensor'No public description is currently available.
nnx.link_tasks.LinkPredictor.unpack_batch(self, batch: 'Any') -> 'tuple[tuple[torch.Tensor, ...], Optional[torch.Tensor]]'Refused: the default train / evaluate / predict paths cannot check a batch against its split, so a leak would pass unseen.
nnx.link_tasks.LinkPredictor.sample_ids(self, batch: 'Any') -> 'torch.Tensor'The candidates' ids, one per output row.
nnx.link_tasks.link_predictor_spec(*, input_dim: 'int', hidden_dims: 'Sequence[int]' = (32,), encoder: 'str' = 'graph_conv', decoder: 'str' = 'dot', num_categories: 'Optional[int]' = None, activation: 'str' = 'relu', dropout: 'float' = 0.0, seed: 'int' = 0) -> 'Any'The registered recipe of a LinkPredictor (pass it as NNModelParams(net=...); a reload rebuilds the same module).
class nnx.link_tasks.LinkObjective(task: 'LinkTask', *, nonfinite: 'str' = 'fail') -> 'None'The task's training objective: binary cross-entropy (or categorical cross-entropy) summed over a batch's candidates, over the candidate count, after LinkTask.check_batch. Checkpointed as component "link.task" with the manifest (topology policy, candidate ids, fixed negatives).
nnx.link_tasks.LinkObjective.component_spec(self) -> 'ComponentSpec'No public description is currently available.
nnx.link_tasks.LinkObjective.component_state(self) -> 'dict[str, Any]'No public description is currently available.
nnx.link_tasks.LinkObjective.check_component_state(self, state: 'Mapping[str, Any]', *, version: 'int') -> 'list[str]'No public description is currently available.
nnx.link_tasks.LinkObjective.load_component_state(self, state: 'Mapping[str, Any]', *, version: 'int') -> 'None'The manifest is configuration (checked, not restored). The training negatives' seed is restored and must match the resumed loader's. The passes need no state: the training loop announces each epoch to the task's loader (set_epoch), so a resumed run draws what the uninterrupted one would have โ through any wrapper that forwards set_epoch.
class nnx.link_tasks.LinkEval(task: 'LinkTask') -> 'None'The task's eval_step_fn: every validation candidate materialised (at most max_candidates), then exact metrics over the whole set โ never per-batch averages โ from the float64 logits, never rounded probabilities (link_metrics with from_logits=True). The record (kind="link") counts the candidates; its loss is the BCE (or NLL).
class nnx.link_tasks.MetricValue(value: 'Optional[float]', reason: 'Optional[str]' = None) -> 'None'A metric's value, or None with the reason it is unavailable.
nnx.link_tasks.is_link_batch(batch: 'Any') -> 'bool'Whether batch asks about candidate edges (it has edge_label_index).
nnx.link_tasks.link_metrics(probabilities: 'Any', targets: 'Any', *, from_logits: 'bool' = False) -> 'dict[str, MetricValue]'Exact metrics over a whole candidate set. Binary (probabilities of shape (K,)): bce, auroc, ap (unavailable, with the reason, for a one-class set) and accuracy at 0.5. Categorical ((K, C)): nll and accuracy.
Details
``from_logits=True`` reads raw logits instead (as
:meth:`LinkTask.predict` gives them) and never forms probabilities: in
float64, the BCE comes from the logits, the NLL from their log-softmax,
and AUROC / AP rank the logits themselves. A probability rounds to
exactly 0 or 1 for a confident model (beyond ``|logit|`` โ 16.6 in
float32, 36.7 in float64), tying candidates the logits tell apart and
clipping their loss; logits stay exact at any confidence. The task's
evaluation scores the logits.
class nnx.link_tasks.LinkPrediction(ids: 'np.ndarray', pairs: 'np.ndarray', logits: 'np.ndarray', probabilities: 'np.ndarray', targets: 'np.ndarray', split: 'str') -> 'None'Candidates and their predictions, aligned row for row: ids, pairs (K, 2), logits / probabilities (float64; (K,) binary, (K, C) categorical) and targets โ all of one split. Score it with link_metrics(logits, targets, from_logits=True): a confident model's probabilities round to 0 or 1.
class nnx.link_tasks.LinkTaskErrorA split, batch or setting that would leak or cannot be served.
class nnx.nn.params.nn_params.NNParams(*, dropout_prob: 'float', n_heads: 'Optional[int]' = None, activation: 'Optional[Activations]' = leaky_relu, activations: 'Optional[list[Activations]]' = None, dropout_probs: 'Optional[list[float]]' = None, input_dim: 'int', output_dim: 'int', hidden_dims: 'Optional[list[int]]' = None) -> 'None'Architecture of a built-in feed-forward or graph network: input and output sizes, hidden layer widths, activation and dropout (net-wide or per layer), and attention heads (n_heads, GRAPH_ATT only). Subclasses add the convolutional, MoE, ViT and transformer fields.
property nnx.nn.params.nn_params.NNParams.dimsNo public description is currently available.
nnx.nn.params.nn_params.NNParams.activation_for(self, layer_idx: 'int') -> 'Activations'The activation for hidden layer layer_idx โ the per-layer entry when activations is set, else the net-wide scalar.
nnx.nn.params.nn_params.NNParams.dropout_for(self, layer_idx: 'int') -> 'float'The dropout prob for hidden layer layer_idx โ per-layer entry when dropout_probs is set, else the net-wide scalar.
nnx.nn.params.nn_params.NNParams.state(self) -> 'dict'No public description is currently available.
nnx.nn.params.nn_params.NNParams.from_state(state: 'dict') -> 'NNParams'No public description is currently available.
nnx.nn.params.nn_params.NNParams.resolve_from_state(state: 'dict') -> 'NNParams'Dispatch to the params subclass that wrote state.
Details
``NNTransformerParams.state()`` always emits its required
architectural keys (``vocab_size`` among them); base
``NNParams.state()`` never does. Without this dispatch a
transformer state is silently downgraded to base ``NNParams`` โ
the subclass keys are dropped, the reloaded run re-hashes to a
different id, and net rebuilding crashes. Every loader
(``NNRun.load``, the ``NNCheckpoint`` readers, hub
``from_pretrained``) resolves through here.
class nnx.nn.params.nn_model_params.NNModelParams(*, net: 'Optional[Union[Nets, ModelSpec, RuntimeModule]]' = None, device: 'Devices' = cpu, loss: 'Losses' = cross_entropy, mixed_precision: 'bool' = False, task: 'Optional[TaskSpec]' = None, precision: 'Optional[PrecisionPolicy]' = None) -> 'None'How an NNModel is built and run: the network (a Nets member, a registered ModelSpec or a runtime module), the device, the loss and the task.
nnx.nn.params.nn_model_params.NNModelParams.is_valid(self) -> 'bool'No public description is currently available.
property nnx.nn.params.nn_model_params.NNModelParams.builtinWhether the net is a built-in Nets member.
nnx.nn.params.nn_model_params.NNModelParams.state(self) -> 'dict'No public description is currently available.
nnx.nn.params.nn_model_params.NNModelParams.from_state(state: 'dict') -> 'NNModelParams'No public description is currently available.
class nnx.nn.params.nn_train_params.NNTrainParams(*, n_epochs: 'int', scheduler: 'NNSchedulerParams' = NNSchedulerParams(min_lr=1e-07, factor=0.95, patience=8, cooldown=2, threshold=0.001, kind=None, step_size=None, T_max=None, max_lr=None, total_steps=None, warmup_steps=None, clock='epoch'), optim: 'Union[NNOptimParams, NNOptimFactoryParams]' = NNOptimParams(name=adam, max_lr=0.01, weight_decay=5e-05, momentum=(0.9, 0.999), grad_clip_norm=None, accumulate_grad_batches=1, param_groups=None, eps=1e-08), seed: 'Optional[int]' = None, data_id: 'Optional[str]' = None, save_phase_checkpoints: 'bool' = True, train_loader: 'Optional[Iterable[Any]]' = None, val_loader: 'Optional[Iterable[Any]]' = None, metrics: 'tuple[MetricSpec, ...]' = (), monitor: 'Optional[MonitorSpec]' = None, extra_metrics: 'Optional[Mapping[str, Callable]]' = None, resume_from_run_id: 'Optional[str]' = None, resume_from_checkpoint: 'Optional[str]' = 'last', parent_run_id: 'Optional[str]' = None, overwrite_existing: 'bool' = False, resume_mode: 'str' = 'auto', resume_epochs: 'str' = 'additional') -> 'None'Training configuration.
Details
`seed` reseeds the global RNG streams (Python random, NumPy, torch
CPU+CUDA, cuDNN) when NNModel.train() runs. None disables seeding
(default). A loader or sampler with its own `torch.Generator` is not
reseeded: rebuild it (or reseed its generator) before each fit for
the same shuffle order.
To preserve back-compat with previously-saved runs, `seed` is included
in state() ONLY when set โ so existing runs with no seed continue to
hash to the same `run.id`.
`extra_metrics` maps a name to ``callable(y_true, y_pred) -> float``
(truth first, decoded class predictions second). The default
classification training step calls each per batch, and `evaluate()`
(the default validation pass) calls each once on the aggregate
predictions; a custom `train_step_fn` / `eval_step_fn` decides whether
and how to call them. Runtime-only: not part of `state()`.
`metrics` declares named, registered metrics (:class:`~nnx.MetricSpec`)
computed over the full sample of each epoch โ on the validation set by
`evaluate()` and on the training epoch by the default step โ and
reported in the records' `metrics` under each spec's name. `monitor`
(:class:`~nnx.MonitorSpec`) names the split and metric that BEST
selection and a `ReduceLROnPlateau` scheduler track, with one shared
improvement rule. Both are serialized only when set, so a
configuration without them keeps its `state()` and run id.
nnx.nn.params.nn_train_params.NNTrainParams.with_train_loader(self, value: 'Iterable[Any]') -> 'NNTrainParams'No public description is currently available.
nnx.nn.params.nn_train_params.NNTrainParams.with_val_loader(self, value: 'Iterable[Any]') -> 'NNTrainParams'No public description is currently available.
nnx.nn.params.nn_train_params.NNTrainParams.state(self)No public description is currently available.
nnx.nn.params.nn_train_params.NNTrainParams.from_state(state: 'dict') -> 'NNTrainParams'No public description is currently available.
class nnx.nn.params.nn_optim_params.NNOptimParams(*, name: 'Optims', max_lr: 'float', weight_decay: 'float', momentum: 'Union[float, tuple[float, float]]', grad_clip_norm: 'Optional[float]' = None, accumulate_grad_batches: 'int' = 1, param_groups: 'Optional[list[NNParamGroupSpec]]' = None, eps: 'float' = 1e-08) -> 'None'Optimizer config.
Details
`momentum` is overloaded by optimizer kind:
- For SGD / SGD_NESTEROV: a single float, the SGD momentum coefficient.
- For ADAM / ADAM_AMSGRAD / ADAMW: a (beta1, beta2) tuple, passed as
the `betas=` argument. The name is retained for backwards
compatibility โ `is_valid()` enforces the per-optim shape.
`weight_decay` follows the chosen optimizer: ADAM / ADAM_AMSGRAD add it
to the gradient (coupled L2 penalty, rescaled by the adaptive
denominator), while ADAMW decays the weights directly
(``p -= lr * weight_decay * p``) before the Adam update โ the
decoupled decay of Loshchilov & Hutter.
`eps` is the Adam-family denominator term (``1e-8``, torch's default).
It is only meaningful for ADAM / ADAM_AMSGRAD / ADAMW; setting it on
an SGD variant raises. It is omitted from `state()` at its default so
existing run ids are unchanged.
`grad_clip_norm` clips gradients by global L2 norm before optimizer.step().
None = no clipping (back-compat default). Typical values: 1.0 for
transformers, 5.0 for RNNs.
`accumulate_grad_batches` enables gradient accumulation โ the effective
batch size becomes batch_size * accumulate_grad_batches. Each update
averages the loss over every valid sample in the window (sum of
numerators over sum of denominators), so uneven or partial windows give
the full-batch gradient.
Default 1 (back-compat: step every batch).
`param_groups` enables per-layer-group LR / weight_decay overrides โ the
fine-tuning idiom of "small LR on the backbone, large LR on the head."
None = single-group behavior (every parameter at `max_lr` / `weight_decay`).
When set, the optimizer factory dispatches via
:func:`nnx.finetune.param_groups.build_param_groups` to construct
per-group dicts.
nnx.nn.params.nn_optim_params.NNOptimParams.state(self)No public description is currently available.
nnx.nn.params.nn_optim_params.NNOptimParams.from_state(state: 'dict') -> 'NNOptimParams'No public description is currently available.
nnx.nn.params.nn_optim_params.NNOptimParams.is_valid(self) -> 'bool'No public description is currently available.
nnx.nn.params.nn_optim_params.NNOptimParams.builder() -> 'NNOptimParamsBuilder'Return a variant-aware builder. See NNOptimParamsBuilder.
class nnx.nn.params.nn_optim_params_builder.NNOptimParamsBuilder() -> 'None'Variant-aware builder for NNOptimParams.
Details
Reach via `NNOptimParams.builder()`. Pick exactly one variant
method (`adam`, `adam_amsgrad`, `adamw`, `sgd`, `sgd_nesterov`), then chain
optional methods (`grad_clip`, `accumulate_grad`, `param_groups`),
then `.build()`. Method-call order is independent โ a modifier
called before a variant survives the variant call, and the last
variant always wins.
`copy()` branches a (possibly partial) builder and `from_params()`
rebuilds one from an existing `NNOptimParams`, so shared setup is
written once and varied per branch.
nnx.nn.params.nn_optim_params_builder.NNOptimParamsBuilder.copy(self) -> 'NNOptimParamsBuilder'Return an independent branch of this builder, complete or partial.
Details
The branch starts with the same fields; afterwards setters on
either builder never affect the other, and neither touches values
already built. Configuration containers (the `param_groups` list)
are copied; the immutable `NNParamGroupSpec` rows are shared.
Nothing is built or validated here โ `build()` validates each
branch on its own.
nnx.nn.params.nn_optim_params_builder.NNOptimParamsBuilder.from_params(params: 'NNOptimParams') -> 'NNOptimParamsBuilder'Return a builder pre-loaded with every field of params.
Details
`from_params(params).build()` equals `params`, with the same
`state()` (key order and omitted defaults included). The builder
is ordinary afterwards: a new variant call replaces the variant
fields (`name` / `max_lr` / `momentum` / `weight_decay` / `eps`)
and keeps the modifiers, exactly as in a hand-written chain.
Raises:
TypeError: if `params` is not exactly an `NNOptimParams` โ a
registered-factory `NNOptimFactoryParams` has no builder
(pass it straight to `NNTrainerParamsBuilder.optimizer`),
and subclasses are rejected rather than downgraded.
ValueError: if `params` carries a field this builder cannot
reproduce.
nnx.nn.params.nn_optim_params_builder.NNOptimParamsBuilder.adam(self, *, max_lr: 'float', betas: 'tuple[float, float]' = (0.9, 0.999), weight_decay: 'float' = 0.0, eps: 'Optional[float]' = None) -> 'NNOptimParamsBuilder'torch.optim.Adam. betas is PyTorch's name for the (beta1, beta2) tuple; the Builder maps it onto the underlying NNOptimParams.momentum field (which holds the tuple for Adam variants). weight_decay is Adam's coupled L2 term (added to the gradient); use adamw() for decoupled decay. eps defaults to torch's 1e-8 and is then omitted from state().
nnx.nn.params.nn_optim_params_builder.NNOptimParamsBuilder.adam_amsgrad(self, *, max_lr: 'float', betas: 'tuple[float, float]' = (0.9, 0.999), weight_decay: 'float' = 0.0, eps: 'Optional[float]' = None) -> 'NNOptimParamsBuilder'torch.optim.Adam with amsgrad=True. Same betas / eps mapping as adam().
nnx.nn.params.nn_optim_params_builder.NNOptimParamsBuilder.adamw(self, *, max_lr: 'float', betas: 'tuple[float, float]' = (0.9, 0.999), weight_decay: 'float' = 0.01, eps: 'Optional[float]' = None) -> 'NNOptimParamsBuilder'torch.optim.AdamW โ Adam with decoupled weight decay: each step first scales the weights by 1 - lr * weight_decay and then applies the Adam update, instead of adding weight_decay * p to the gradient. weight_decay defaults to torch's 1e-2; betas / eps map as in adam().
nnx.nn.params.nn_optim_params_builder.NNOptimParamsBuilder.sgd(self, *, max_lr: 'float', momentum: 'float' = 0.9, weight_decay: 'float' = 0.0) -> 'NNOptimParamsBuilder'torch.optim.SGD. The float momentum stays as momentum (no rename) โ betas is an Adam-family term.
nnx.nn.params.nn_optim_params_builder.NNOptimParamsBuilder.sgd_nesterov(self, *, max_lr: 'float', momentum: 'float' = 0.9, weight_decay: 'float' = 0.0) -> 'NNOptimParamsBuilder'torch.optim.SGD with nesterov=True. Same momentum shape as sgd().
nnx.nn.params.nn_optim_params_builder.NNOptimParamsBuilder.grad_clip(self, norm: 'Optional[float]') -> 'NNOptimParamsBuilder'Global-L2 gradient-norm clipping. None = no clipping (the dataclass default; this method is the opt-in path).
nnx.nn.params.nn_optim_params_builder.NNOptimParamsBuilder.accumulate_grad(self, batches: 'int') -> 'NNOptimParamsBuilder'Accumulate gradients over batches mini-batches before stepping. Default (no call) leaves the dataclass at 1.
nnx.nn.params.nn_optim_params_builder.NNOptimParamsBuilder.param_groups(self, groups: 'list[NNParamGroupSpec]') -> 'NNOptimParamsBuilder'Per-layer-group LR / weight_decay overrides (the fine-tuning idiom). Default (no call) leaves the dataclass at None (single-group behavior).
nnx.nn.params.nn_optim_params_builder.NNOptimParamsBuilder.build(self) -> 'NNOptimParams'Construct the dataclass from the fields the user touched.
Details
Checks for the required fields first: when no variant method was
called, it raises a ValueError that names the variant methods,
rather than the dataclass's missing-argument TypeError. Every
params Builder's ``build()`` behaves this way.
Only the fields the caller set are passed to the dataclass, so
every untouched optional field keeps its default. ``state()``
omits fields at their default, so the built value has the same
``state()`` and run id as one constructed directly.
Raises:
ValueError: if no variant method (`.adam`, `.adam_amsgrad`,
`.adamw`, `.sgd`, `.sgd_nesterov`) was called before
`.build()`. The message names the five methods so the user
can fix the chain without consulting the dataclass schema.
class nnx.optimizers.NNOptimFactoryParams(*, factory: 'OptimizerFactorySpec', max_lr: 'float', weight_decay: 'float' = 0.0, grad_clip_norm: 'Optional[float]' = None, accumulate_grad_batches: 'int' = 1, param_groups: 'Optional[list[NNParamGroupSpec]]' = None) -> 'None'Optimizer config backed by a registered factory.
Details
The registered-variant counterpart of
:class:`~nnx.nn.params.nn_optim_params.NNOptimParams`: it shares
``max_lr`` / ``weight_decay`` (defaults for every resolved group),
``grad_clip_norm``, ``accumulate_grad_batches`` and ``param_groups``
with the same validation, but names its optimizer through ``factory``
instead of an :class:`~nnx.nn.enum.optims.Optims` value and carries no
``momentum`` / ``name`` fields. Its ``state()`` holds a ``factory``
entry, which is how run decoding tells the two variants apart.
nnx.optimizers.NNOptimFactoryParams.is_valid(self) -> 'bool'Always True: every field was validated at construction (the built-in variant's momentum-shape check has no counterpart here). Whether the factory is registered is checked when training starts.
nnx.optimizers.NNOptimFactoryParams.state(self) -> 'dict[str, Any]'No public description is currently available.
nnx.optimizers.NNOptimFactoryParams.from_state(state: 'Mapping[str, Any]') -> 'NNOptimFactoryParams'No public description is currently available.
class nnx.nn.params.nn_scheduler_params.NNSchedulerParams(*, min_lr: 'float', factor: 'float', patience: 'int', cooldown: 'int', threshold: 'float', kind: 'Optional[Schedulers]' = None, step_size: 'Optional[int]' = None, T_max: 'Optional[int]' = None, max_lr: 'Optional[float]' = None, total_steps: 'Optional[int]' = None, warmup_steps: 'Optional[int]' = None, clock: 'SchedulerClockUnit' = 'epoch') -> 'None'A learning-rate scheduler: its kind (ReduceLROnPlateau when kind is None), the plateau fields, the kind-specific horizons and the clock that one step counts (an epoch or an optimizer update).
nnx.nn.params.nn_scheduler_params.NNSchedulerParams.state(self) -> 'dict'No public description is currently available.
nnx.nn.params.nn_scheduler_params.NNSchedulerParams.from_state(state: 'dict') -> 'NNSchedulerParams'No public description is currently available.
nnx.nn.params.nn_scheduler_params.NNSchedulerParams.builder() -> 'NNSchedulerParamsBuilder'Return a variant-aware builder. See NNSchedulerParamsBuilder.
class nnx.nn.params.nn_scheduler_params_builder.NNSchedulerParamsBuilder() -> 'None'Variant-aware builder for NNSchedulerParams.
Details
Reach this via `NNSchedulerParams.builder()`. Each variant method
is self-contained โ the user calls exactly one of them per builder
instance. Calling a second variant overwrites the first (last
write wins); `.build()` produces the dataclass.
`copy()` branches a (possibly empty) builder and `from_params()`
rebuilds one from an existing `NNSchedulerParams`.
nnx.nn.params.nn_scheduler_params_builder.NNSchedulerParamsBuilder.copy(self) -> 'NNSchedulerParamsBuilder'Return an independent branch of this builder.
Details
The branch starts with the same fields; a later variant call on
either builder replaces only that builder's fields. Nothing is
built or validated here โ `build()` validates each branch.
nnx.nn.params.nn_scheduler_params_builder.NNSchedulerParamsBuilder.from_params(params: 'NNSchedulerParams') -> 'NNSchedulerParamsBuilder'Return a builder pre-loaded with every field of params.
Details
`from_params(params).build()` equals `params`, with the same
`state()` (key order and omitted defaults included). A variant
call afterwards replaces the whole configuration, as usual,
except the clock, which it keeps.
Raises:
TypeError: if `params` is not exactly an `NNSchedulerParams`.
ValueError: if `params` carries a field this builder cannot
reproduce.
nnx.nn.params.nn_scheduler_params_builder.NNSchedulerParamsBuilder.clock(self, clock: 'str') -> 'NNSchedulerParamsBuilder'What one scheduler step counts: "epoch" (the default) or "optimizer_update" โ once per committed update of the scheduler's optimizer, with horizons counted in updates. Call before or after the variant method; a variant call keeps the clock (a loaded one too).
nnx.nn.params.nn_scheduler_params_builder.NNSchedulerParamsBuilder.reduce_on_plateau(self, *, min_lr: 'float', factor: 'float', patience: 'int', cooldown: 'int', threshold: 'float') -> 'NNSchedulerParamsBuilder'ReduceLROnPlateau โ the default scheduler.
Details
Sets the five plateau fields (``factor`` in ``(0, 1)``: it reduces
the LR). `kind` is left at None (the
dataclass default), which preserves the omit-when-default
state() invariant for callers who used the original pre-enum
config.
nnx.nn.params.nn_scheduler_params_builder.NNSchedulerParamsBuilder.step(self, *, step_size: 'int', min_lr: 'float', factor: 'float', patience: 'int', cooldown: 'int', threshold: 'float') -> 'NNSchedulerParamsBuilder'torch.optim.lr_scheduler.StepLR โ decay LR by factor every step_size epochs. The plateau-shape fields (min_lr, patience, cooldown, threshold) are not consumed by StepLR but are required by the underlying NNSchedulerParams dataclass and serialised for back-compat.
nnx.nn.params.nn_scheduler_params_builder.NNSchedulerParamsBuilder.cosine_annealing(self, *, T_max: 'int', min_lr: 'float', factor: 'float', patience: 'int', cooldown: 'int', threshold: 'float') -> 'NNSchedulerParamsBuilder'torch.optim.lr_scheduler.CosineAnnealingLR โ anneal LR over T_max steps.
nnx.nn.params.nn_scheduler_params_builder.NNSchedulerParamsBuilder.one_cycle(self, *, max_lr: 'float', total_steps: 'int', min_lr: 'float', factor: 'float', patience: 'int', cooldown: 'int', threshold: 'float') -> 'NNSchedulerParamsBuilder'torch.optim.lr_scheduler.OneCycleLR โ Smith one-cycle schedule with peak LR max_lr over total_steps steps.
nnx.nn.params.nn_scheduler_params_builder.NNSchedulerParamsBuilder.linear_warmup_decay(self, *, warmup_steps: 'int', total_steps: 'int', min_lr: 'float', factor: 'float', patience: 'int', cooldown: 'int', threshold: 'float') -> 'NNSchedulerParamsBuilder'Linear warm-up to the optimizer's LR over warmup_steps, linear decay to 0 over the remaining total_steps - warmup_steps. Used by most transformer training recipes.
nnx.nn.params.nn_scheduler_params_builder.NNSchedulerParamsBuilder.build(self) -> 'NNSchedulerParams'Construct the dataclass from the fields the user touched.
Details
Checks for the required fields first: when no variant method was
called, it raises a ValueError that names the variant methods,
rather than the dataclass's missing-argument TypeError. Every
params Builder's ``build()`` behaves this way.
Only the fields the caller set are passed to the dataclass, so
every untouched field keeps its default. ``state()`` omits fields
at their default, so the built value has the same ``state()`` and
run id as one constructed directly.
Raises:
ValueError: if no variant method (`.reduce_on_plateau`,
`.step`, `.cosine_annealing`, `.one_cycle`,
`.linear_warmup_decay`) was called before `.build()`.
The message names the five methods so the user can
fix the chain without consulting the dataclass schema.
class nnx.nn.params.nn_transformer_params.NNTransformerParams(*, dropout_prob: 'float', n_heads: 'Optional[int]' = None, activation: 'Optional[Activations]' = leaky_relu, activations: 'Optional[list[Activations]]' = None, dropout_probs: 'Optional[list[float]]' = None, input_dim: 'int', output_dim: 'int', hidden_dims: 'Optional[list[int]]' = None, vocab_size: 'int', n_layers: 'int', d_model: 'int', max_seq_len: 'int', ffn_mult: 'int' = 4, rope_base: 'float' = 10000.0, tie_embeddings: 'bool' = True, attn_dropout: 'float' = 0.0, resid_dropout: 'float' = 0.0) -> 'None'Architecture of the decoder-only TransformerNN: vocabulary size, depth, width, attention heads and the maximum sequence length.
nnx.nn.params.nn_transformer_params.NNTransformerParams.state(self) -> 'dict'No public description is currently available.
nnx.nn.params.nn_transformer_params.NNTransformerParams.from_state(state: 'dict') -> 'NNTransformerParams'No public description is currently available.
nnx.nn.params.nn_transformer_params.NNTransformerParams.builder() -> 'NNTransformerParamsBuilder'Return a fluent LM-path builder. See NNTransformerParamsBuilder.
class nnx.nn.params.nn_transformer_params_builder.NNTransformerParamsBuilder() -> 'None'Builder for NNTransformerParams.
Details
Reach via `NNTransformerParams.builder()`. The six methods can be
chained in any order; `.build()` collects them, fills in the
LM-path defaults for the dead parent-NNParams fields, and
constructs the dataclass.
`copy()` branches a (possibly partial) builder and `from_params()`
rebuilds one from an existing `NNTransformerParams` โ including the
inherited `NNParams` fields (`hidden_dims`, `activation(s)`,
`dropout_prob(s)`, custom `input_dim` / `output_dim`) no setter
exposes.
nnx.nn.params.nn_transformer_params_builder.NNTransformerParamsBuilder.copy(self) -> 'NNTransformerParamsBuilder'Return an independent branch of this builder, complete or partial.
Details
The branch starts with the same fields; afterwards setters on
either builder never affect the other. List fields carried from
`from_params` are copied. Nothing is built or validated here โ
`build()` validates each branch on its own.
nnx.nn.params.nn_transformer_params_builder.NNTransformerParamsBuilder.from_params(params: 'NNTransformerParams') -> 'NNTransformerParamsBuilder'Return a builder pre-loaded with every field of params.
Details
`from_params(params).build()` equals `params`, with the same
`state()` (key order and omitted defaults included) โ the
inherited `hidden_dims`, `activation`, `activations`,
`dropout_prob` and `dropout_probs` survive because `build()`
applies its LM-path defaults *under* the carried fields. Setters
keep their usual rules afterwards: `.vocab(size)` still resets
`input_dim` / `output_dim` to `size`.
Raises:
TypeError: if `params` is not exactly an `NNTransformerParams`
(base `NNParams` or a subclass).
ValueError: if `params` carries a field this builder cannot
reproduce.
nnx.nn.params.nn_transformer_params_builder.NNTransformerParamsBuilder.vocab(self, size: 'int') -> 'NNTransformerParamsBuilder'Set the vocabulary size. Mirrors into both input_dim and output_dim on the parent NNParams (the LM convention).
nnx.nn.params.nn_transformer_params_builder.NNTransformerParamsBuilder.layers(self, *, n: 'int', heads: 'int', d_model: 'int') -> 'NNTransformerParamsBuilder'Set depth (n_layers), attention head count (n_heads), and hidden dimension (d_model). All three must be positive integers โ Python or NumPy integers, normalized to plain int; floats (even 128.0), booleans and strings raise ValueError naming the argument โ and the check runs before the divisibility arithmetic so the builder rejects exactly what the direct constructor rejects. Enforces d_model % heads == 0 and an even head width (d_model / heads, required by RoPE) immediately โ this is the Builder's safety value-add over the direct-kwarg ctor, which only catches the mismatch at post_init time after all kwargs have already been typed.
Details
Raises:
ValueError: for a non-integral or non-positive `n`, `heads`
or `d_model`, for `d_model % heads != 0`, or for an odd
`d_model // heads`.
nnx.nn.params.nn_transformer_params_builder.NNTransformerParamsBuilder.ffn(self, *, mult: 'int') -> 'NNTransformerParamsBuilder'FFN expansion ratio. Default is 4 (the SwiGLU-friendly ratio); only call this method to override.
nnx.nn.params.nn_transformer_params_builder.NNTransformerParamsBuilder.context(self, *, max_seq_len: 'int', rope_base: 'Optional[float]' = None) -> 'NNTransformerParamsBuilder'Context-length and RoPE base. max_seq_len is required; rope_base=None is the sentinel for "use the dataclass default (10000.0, the LLaMA / GPT convention)". The fluent contract is "last call wins": .context(rope_base=500000.0).context(max_seq_len=128) resets rope_base to the default โ the second call's implicit rope_base=None drops the prior override.
nnx.nn.params.nn_transformer_params_builder.NNTransformerParamsBuilder.dropout(self, *, attn: 'float' = 0.0, resid: 'float' = 0.0) -> 'NNTransformerParamsBuilder'Attention and residual dropout rates. Defaults are both 0.0 (modern LLM convention; regularization comes from data scale, not dropout).
Details
Like `.context()`, a `dropout()` call specifies BOTH rates
together โ each call fully replaces the pair, and a rate left
at its 0.0 default is reset, not carried over from a prior
call. So `.dropout(resid=0.3).dropout(attn=0.5)` yields
`attn=0.5, resid=0.0` (the second call's implicit `resid=0.0`
drops the prior override); call `.dropout(attn=0.5, resid=0.3)`
once to set both. Same-field last-call-wins still holds:
`.dropout(attn=0.5).dropout(attn=0.0)` resets to `attn=0.0`.
The dataclass's omit-when-default `state()` then handles
run.id stability automatically.
nnx.nn.params.nn_transformer_params_builder.NNTransformerParamsBuilder.tied_embeddings(self, value: 'bool') -> 'NNTransformerParamsBuilder'Toggle weight-tying between input embeddings and LM head. Default is True. The fluent contract is "last call wins" โ a prior .tied_embeddings(False) followed by .tied_embeddings(True) leaves the dataclass at the default (which state() then omits).
nnx.nn.params.nn_transformer_params_builder.NNTransformerParamsBuilder.build(self) -> 'NNTransformerParams'Construct the dataclass.
Details
Checks for the required fields first: when a required setter was
not called, it raises a ValueError that names the missing setter
methods, rather than the dataclass's missing-argument TypeError.
Every params Builder's ``build()`` behaves this way.
Supplies `dropout_prob=0.0` โ the one dead parent-NNParams field
the TransformerNN net never reads but the parent dataclass
requires โ *under* the set fields, so a value carried by
`from_params` wins; `hidden_dims` / `activation` fall to the
NNParams defaults (`None` / `LEAKY_RELU`) unless carried. Using
the parent's own defaults keeps the builder's `state()` /
`run.id` identical to the direct-kwarg ctor's.
Raises:
ValueError: if `.vocab(size=...)`, `.layers(n=..., heads=...,
d_model=...)`, or `.context(max_seq_len=...)` was not
called before `.build()`. The message names the
specific setter methods that are still missing so the
user can complete the chain without consulting the
dataclass schema.
class nnx.nn.params.nn_conv_params.NNConvParams(*, dropout_prob: 'float', n_heads: 'Optional[int]' = None, activation: 'Optional[Activations]' = leaky_relu, activations: 'Optional[list[Activations]]' = None, dropout_probs: 'Optional[list[float]]' = None, input_dim: 'int', output_dim: 'int', hidden_dims: 'Optional[list[int]]' = None, conv_channels: 'list[int]', in_channels: 'int' = 1, kernel_size: 'int' = 5, stride: 'int' = 1, padding: 'int' = 0, pool_size: 'int' = 2) -> 'None'Parameters for a ConvNN with a required conv-block activation.
nnx.nn.params.nn_conv_params.NNConvParams.image_side(self) -> 'int'Spatial side of the (square) input image.
Details
Always a plain ``int``: every count that feeds the conv arithmetic
(``input_dim``, ``in_channels``, ``kernel_size``, ``stride``,
``padding``, ``pool_size``, ``conv_channels``) is validated at
construction as a non-boolean integer โ NumPy integers accepted and
normalized โ so a fractional or boolean knob raises ``ValueError``
naming the field before any shape helper or layer runs.
nnx.nn.params.nn_conv_params.NNConvParams.spatial_sizes(self) -> 'list[int]'Feature-map side after each ConvโPool block (floor arithmetic, matching Conv2d/MaxPool2d).
nnx.nn.params.nn_conv_params.NNConvParams.flatten_dim(self) -> 'int'Input width of the first FC layer: last block's channels ร sideยฒ.
nnx.nn.params.nn_conv_params.NNConvParams.state(self) -> 'dict'No public description is currently available.
nnx.nn.params.nn_conv_params.NNConvParams.from_state(state: 'dict') -> 'NNConvParams'No public description is currently available.
class nnx.nn.params.nn_vit_params.NNViTParams(*, dropout_prob: 'float', n_heads: 'Optional[int]' = None, activation: 'Optional[Activations]' = leaky_relu, activations: 'Optional[list[Activations]]' = None, dropout_probs: 'Optional[list[float]]' = None, input_dim: 'int', output_dim: 'int', hidden_dims: 'Optional[list[int]]' = None, image_size: 'int', patch_size: 'int', d_model: 'int', n_layers: 'int', in_channels: 'int' = 3, ffn_mult: 'int' = 4, attn_dropout: 'float' = 0.0, resid_dropout: 'float' = 0.0) -> 'None'Parameters for ViTNN, built by Nets.VIT.
property nnx.nn.params.nn_vit_params.NNViTParams.n_patchesPatch tokens per image (the encoder adds one CLS token).
nnx.nn.params.nn_vit_params.NNViTParams.state(self) -> 'dict'No public description is currently available.
nnx.nn.params.nn_vit_params.NNViTParams.from_state(state: 'dict') -> 'NNViTParams'No public description is currently available.
class nnx.nn.params.nn_moe_params.NNMoEParams(*, dropout_prob: 'float', n_heads: 'Optional[int]' = None, activation: 'Optional[Activations]' = leaky_relu, activations: 'Optional[list[Activations]]' = None, dropout_probs: 'Optional[list[float]]' = None, input_dim: 'int', output_dim: 'int', hidden_dims: 'Optional[list[int]]' = None, num_experts: 'int', top_k: 'int' = 2) -> 'None'Serializable parameters for an expert-bearing feed-forward MoE.
Details
Unlike base :class:`NNParams`, ``hidden_dims`` must contain at least one
layer because only hidden layers are replaced by ``MoELinear`` modules.
nnx.nn.params.nn_moe_params.NNMoEParams.state(self) -> 'dict'No public description is currently available.
nnx.nn.params.nn_moe_params.NNMoEParams.from_state(state: 'dict') -> 'NNMoEParams'No public description is currently available.
class nnx.nn.params.nn_tokenizer_params.NNTokenizerParams(*, path: 'str', tokenizer: 'object') -> 'None'Frozen dataclass holding a tokenizer + its on-disk pointer.
Details
The dataclass is frozen so it can sit alongside NNTransformerParams /
NNModelParams in an NNRun without inviting in-place mutation. The
actual ``tokenizers.Tokenizer`` object is held in a repr=False field
so it doesn't bloat the str() output.
nnx.nn.params.nn_tokenizer_params.NNTokenizerParams.of(tokenizer: 'object', path: 'str') -> 'NNTokenizerParams'Construct from a live Tokenizer instance and persist it to path.
Details
This is the train-time entry point: train a tokenizer, then call
``NNTokenizerParams.of(tk, path="runs/tok.json")`` to wrap it
with a paired on-disk artifact.
nnx.nn.params.nn_tokenizer_params.NNTokenizerParams.from_state(state: 'dict') -> 'NNTokenizerParams'Load from a state dict produced by state. The single required key is path; the tokenizer is reconstructed from the file the path points to.
Details
The path is stored exactly as the caller gave it to :meth:`of` โ
typically cwd-relative โ so loading from a different working
directory requires the same relative layout. That's deliberate:
storing an absolute path would break run portability across
machines, which is the more common need.
nnx.nn.params.nn_tokenizer_params.NNTokenizerParams.state(self) -> 'dict'Return the serializable view โ only the path goes into run.yaml.
property nnx.nn.params.nn_tokenizer_params.NNTokenizerParams.vocab_sizeNo public description is currently available.
nnx.nn.params.nn_tokenizer_params.NNTokenizerParams.encode(self, text: 'str') -> 'list[int]'No public description is currently available.
nnx.nn.params.nn_tokenizer_params.NNTokenizerParams.decode(self, ids: 'list[int]', skip_special_tokens: 'bool' = True) -> 'str'No public description is currently available.
nnx.nn.params.nn_tokenizer_params.train_bpe(files: 'Optional[list[str]]' = None, *, vocab_size: 'int' = 8192, texts: 'Optional[list[str]]' = None, special_tokens: 'Optional[list[str]]' = None, min_frequency: 'int' = 2) -> 'TokenizerType'Train a BPE tokenizer on either a list of files or a list of texts.
Details
Mirrors the HF "quick BPE" recipe โ Whitespace pre-tokenizer + BPE
model + BpeTrainer. Returns the trained Tokenizer instance; the
caller is responsible for persisting via
``NNTokenizerParams.of(tk, path=...)``.
Args:
files: paths to plaintext files (one corpus line per file row).
If None, ``texts`` is consulted instead.
vocab_size: target vocab. Actual size may be smaller for tiny
corpora.
texts: in-memory list of training strings โ useful for unit
tests and the examples without writing a temp file.
special_tokens: e.g. ``["<unk>", "<pad>", "<bos>", "<eos>"]`` (the
default). Included in the vocab and not split during
tokenization. ``"<unk>"`` is the model's unknown token: it is
appended when missing, so an unseen character encodes instead
of raising.
min_frequency: minimum pair frequency to merge โ higher values
give smaller, more conservative vocabs.
Returns:
Tokenizer: a trained ``tokenizers.Tokenizer`` ready for encode/decode + save.
class nnx.nn.params.nn_run.NNRun(*, net: 'Optional[NNParams]', train: 'NNTrainParams', model: 'NNModelParams', trainer: 'Optional[NNTrainerParams]' = None, salt: 'Optional[str]' = None, transforms: 'tuple[NNCheckpointTransform, ...]' = (), idps: 'Optional[list[NNIterationDataPoint]]' = None, resume_status: 'Optional[ResumeStatus]' = None, provenance: 'Optional[ProvenanceRecord]' = None, history: 'Optional[str]' = None, precision: 'Optional[ResolvedPrecision]' = None, compile: 'Optional[CompileRecord]' = None) -> 'None'A training run: its network, training and model parameters, its id and its persisted history and checkpoints under runs/<id>/. Load one with NNRun.load.
property nnx.nn.params.nn_run.NNRun.idNo public description is currently available.
nnx.nn.params.nn_run.NNRun.state(self) -> 'dict'No public description is currently available.
nnx.nn.params.nn_run.NNRun.with_resume_status(self, value: 'Optional[ResumeStatus]') -> 'NNRun'No public description is currently available.
nnx.nn.params.nn_run.NNRun.with_precision(self, value: 'Optional[ResolvedPrecision]') -> 'NNRun'No public description is currently available.
nnx.nn.params.nn_run.NNRun.with_compile(self, value: 'Optional[CompileRecord]') -> 'NNRun'No public description is currently available.
nnx.nn.params.nn_run.NNRun.with_idps(self, value: 'list[NNIterationDataPoint]') -> 'NNRun'No public description is currently available.
nnx.nn.params.nn_run.NNRun.with_provenance(self, value: 'Optional[ProvenanceRecord]') -> 'NNRun'No public description is currently available.
nnx.nn.params.nn_run.NNRun.with_history(self, value: 'Optional[str]') -> 'NNRun'No public description is currently available.
nnx.nn.params.nn_run.NNRun.ensure_writable(self, root: 'Optional[str]' = None, *, overwrite: 'bool' = False) -> 'None'No public description is currently available.
nnx.nn.params.nn_run.NNRun.writable_lease(self, root: 'Optional[str]' = None, *, overwrite: 'bool' = False) -> 'Iterator[None]'Reserve this run ID and hold exclusive ownership until training ends.
nnx.nn.params.nn_run.NNRun.checkpoints(self, root: 'Optional[str]' = None) -> 'list[Optional[NNCheckpoint]]'Load this run's five phase checkpoints, in cadence order (FIRST, Q1, Q2, Q3, LAST). Entries are None when the tag was never written โ e.g. runs trained with save_phase_checkpoints=False write only LAST and BEST.
Details
BEST is not a cadence checkpoint, so it is not in this list (its
epoch is often none of the phase epochs). Load it directly via
``NNCheckpoint.load(run=run.id, type=Checkpoints.BEST)``.
nnx.nn.params.nn_run.NNRun.save(self, root: 'Optional[str]' = None, *, update_best: 'bool' = True) -> 'NNRun'No public description is currently available.
nnx.nn.params.nn_run.NNRun.load(id: 'str', root: 'Optional[str]' = None) -> 'NNRun'Load a saved run. Its history is idps.csv read whole โ or, for a run with a history journal, the journal's committed tail: the last retention records, from the chunks that hold them. Either way records past the LAST checkpoint's epoch are left out.
nnx.nn.params.nn_run.NNRun.all(root: 'Optional[str]' = None) -> 'list[NNRun]'List every saved NNRun under the runs root, skipping the best pointer. Returns [] when the runs/ directory doesn't exist yet. Non-directory entries (stray files, .DS_Store) are filtered out so they don't trigger spurious NNRun.load failures.
class nnx.nn.params.nn_checkpoint.NNCheckpointTransform(*, name: 'str', version: 'int' = 1, options: 'dict[str, Any]' = <factory>) -> 'None'A versioned recipe for rebuilding a checkpoint's module topology.
nnx.nn.params.nn_checkpoint.NNCheckpointTransform.state(self) -> 'dict[str, Any]'No public description is currently available.
nnx.nn.params.nn_checkpoint.NNCheckpointTransform.from_state(state: 'dict[str, Any]') -> 'NNCheckpointTransform'No public description is currently available.
class nnx.nn.params.nn_checkpoint.NNCheckpoint(*, net_params: 'Optional[NNParams]', net_state: 'dict[str, Any]', model_params: 'NNModelParams', idp: 'NNIterationDataPoint', transforms: 'tuple[NNCheckpointTransform, ...]' = (), training_state_id: 'Optional[str]' = None, training_state_present: 'Optional[bool]' = None) -> 'None'Model state plus the recipes needed to rebuild its module topology.
Details
``transforms`` is empty for ordinary and legacy checkpoints. Training
callbacks that replace modules at ``on_train_end`` can persist ordered,
versioned recipes here; :meth:`NNModel.from_checkpoint` replays recognized
recipes before loading ``net_state``.
property nnx.nn.params.nn_checkpoint.NNCheckpoint.reconstructibleWhether a model can be rebuilt from this checkpoint alone: False for a runtime-only module, whose weights need the module again (NNModel.from_checkpoint(ckpt, module=...)).
nnx.nn.params.nn_checkpoint.NNCheckpoint.to_file(self, path: 'str', format: "Literal['pickle', 'safetensors']" = 'pickle') -> 'None'Atomically write this NNCheckpoint to path.
Details
Args:
path: destination path. Parent directory is created if missing.
format: one of:
- ``"pickle"`` (default): a ``torch.save`` of the whole
NNCheckpoint dataclass. Bit-exact round-trip including
the OrderedDict state and the dataclass identity. The
on-disk format NNx has always written; back-compat
default for existing callers.
- ``"safetensors"``: a ``.safetensors`` file with the
net's tensors as the data section and
NNParams + NNModelParams + NNIterationDataPoint + transform
recipes
JSON-serialized into the metadata dict (strโstr only,
per the safetensors spec). Safe to mmap, readable by
ComfyUI/vLLM/AutoGPTQ/HF tools, and proof against
arbitrary-code-execution on load. Requires the
``thekaveh-nnx[hub]`` extra. Portable, so a runtime-only
module (``reconstructible=False``) is rejected
with ``MissingModelFactoryError`` before anything is
written.
Both formats write to a hidden temporary file beside ``path``
(``.<name>.<random>``), flush it to disk and rename it into place,
so an interrupted save can never leave a half-written checkpoint at
the destination โ matching the atomicity guarantee NNRun.save
offers for YAML/CSV.
nnx.nn.params.nn_checkpoint.NNCheckpoint.save(self, run: 'str', type: 'Checkpoints', root: 'Optional[str]' = None, optimizer_state: 'Optional[dict[str, Any]]' = None, scheduler_state: 'Optional[dict[str, Any]]' = None, scaler_state: 'Optional[dict[str, Any]]' = None, rng_state: 'Optional[dict[str, Any]]' = None, completed_epoch: 'Optional[int]' = None, resume_net_state: 'Optional[dict[str, Any]]' = None, optimizer_type: 'Optional[str]' = None, scheduler_type: 'Optional[str]' = None, optimizer_topology: 'Optional[list[list[dict[str, Any]]]]' = None, optimizer_factory: 'Optional[dict[str, Any]]' = None, components: 'Optional[dict[str, Any]]' = None, optimizers_state: 'Optional[dict[str, Any]]' = None, schedulers_state: 'Optional[dict[str, Any]]' = None, optimizer_types: 'Optional[dict[str, str]]' = None, scheduler_types: 'Optional[dict[str, str]]' = None, optimizer_topologies: 'Optional[dict[str, list[list[dict[str, Any]]]]]' = None, optimizer_factories: 'Optional[dict[str, Optional[dict[str, Any]]]]' = None, precision: 'Optional[dict[str, Any]]' = None, compile: 'Optional[dict[str, Any]]' = None, distributed: 'Optional[dict[str, Any]]' = None, counters: 'Optional[dict[str, Any]]' = None) -> 'None'Save the checkpoint to disk atomically.
Details
Every save is a resume point: a manifest
(``<tag>.pt.manifest.json``, written last) stamps it with the tag's
generation ordinal, the checkpoint id and the SHA-256 of each file it
consists of, so a resume refuses a torn, mixed or corrupted point
(:meth:`verify`). Files are flushed to stable storage
(``F_FULLFSYNC`` on macOS, ``fsync`` elsewhere) before they are
renamed into place, and a Ctrl-C arriving meanwhile is held until the point is
complete. ``counters`` (the run's logical position: ``global_step``,
``committed_updates``, ``planned_n_epochs``) rides in the training
state.
``components`` is the ``ComponentRegistry.collect()``
mapping of every registered component's versioned state; it lives
in the same generation sidecar as the optimizer state, so a model
and component state from different generations are never paired.
``optimizers_state`` / ``schedulers_state`` (with their
``*_types``, ``optimizer_topologies`` and ``optimizer_factories``)
carry a multi-optimizer ``Trainer``'s name-keyed states; either
``optimizer_state`` or ``optimizers_state`` makes the checkpoint
stateful.
When `optimizer_state` is supplied, a generation-addressed sibling
file holds the training state, plus a fixed-name compatibility copy.
This sidecar is used by NNModel.train when NNTrainParams'
resume_from_run_id / resume_from_checkpoint name this checkpoint, to
warm-resume with the prior optimizer momentum / Adam state.
The immutable generation sidecar is committed first and the checkpoint
second. The checkpoint names the sidecar it owns, so interruption
between replacements leaves the previous generation resumable.
nnx.nn.params.nn_checkpoint.NNCheckpoint.resume_point(run: 'str', type: 'Checkpoints', root: 'Optional[str]' = None) -> 'Optional[dict[str, Any]]'The manifest stamping this checkpoint's resume point โ generation, checkpoint_id, completed_epoch, global_step, committed_updates, planned_n_epochs and the files with their SHA-256 โ or None for a checkpoint without a manifest. Read as is: after an interrupted publish the live manifest may name the previous checkpoint while a staged one describes the published files โ verify checks both and returns the one that matches.
nnx.nn.params.nn_checkpoint.NNCheckpoint.verify(run: 'str', type: 'Checkpoints', root: 'Optional[str]' = None) -> 'Optional[dict[str, Any]]'Check a resume point before anything is restored from it and return its manifest (None for a checkpoint without one, which loads as before). Raises ResumePointError naming the reason: a listed file missing or a manifest naming another checkpoint (torn), files of different generations (mixed), or a file whose SHA-256 differs (digest mismatch). Every listed file is hashed; only the matching training-state sidecar is deserialized, with torch.load(weights_only=True), to compare its generation; no checkpoint data is written (only the checkpoint's lock file is taken, created when missing): a point whose writer stopped between publishing the checkpoint and its live manifest is recognized by its staged manifest โ every digest matching โ and that manifest is returned (the writer's next save makes it live).
nnx.nn.params.nn_checkpoint.NNCheckpoint.load_training_state(run: 'str', type: 'Checkpoints', root: 'Optional[str]' = None, map_location: 'Any' = 'cpu') -> 'Optional[dict[str, Any]]'Load and validate the resumable optimizer/scheduler/scaler bundle.
Details
Legacy optimizer-only sidecars are normalized into the new mapping so
checkpoints written by older NNx versions remain resumable.
Returns ``None`` โ without creating ``runs/<run>/`` โ when the run
directory does not exist; the run ID is still validated first.
nnx.nn.params.nn_checkpoint.NNCheckpoint.load_with_training_state(run: 'str', type: 'Checkpoints', root: 'Optional[str]' = None, map_location: 'Any' = 'cpu') -> 'tuple[Optional[NNCheckpoint], Optional[dict[str, Any]]]'Atomically load a checkpoint and its matching training-state bundle.
Details
Returns ``(None, None)`` โ without creating ``runs/<run>/`` โ when
the run directory does not exist; the run ID is still validated.
nnx.nn.params.nn_checkpoint.NNCheckpoint.load_optimizer_state(run: 'str', type: 'Checkpoints', root: 'Optional[str]' = None) -> 'Optional[dict[str, Any]]'Load the optimizer state sidecar for a checkpoint. Returns None when no sidecar exists (e.g., checkpoints written without resume support).
Details
The sidecar itself is read with ``weights_only=True``. Locating it
reads the checkpoint through :meth:`load_training_state`, which
loads a pickle-format checkpoint with ``NNCheckpoint.from_file``:
trust the run directory as you would for ``from_file``.
nnx.nn.params.nn_checkpoint.NNCheckpoint.from_file(path: 'str', map_location: 'Any' = 'cpu') -> 'Optional[NNCheckpoint]'Load an NNCheckpoint from disk, auto-detecting pickle vs safetensors.
Details
Returns ``None`` if the path doesn't exist or the loaded pickle
object isn't an NNCheckpoint instance.
Dispatch is by magic bytes:
- ``torch.save`` writes a ZIP archive in modern PyTorch
(``_use_new_zipfile_serialization=True`` is the default since
PyTorch 1.6), so the file starts with ``b"PK\x03\x04"``.
- Legacy ``torch.save`` (with the zipfile serialization disabled)
and bare pickle files begin with ``\x80`` (the pickle PROTO
opcode for protocol >= 2).
- safetensors files begin with a little-endian u64 header length
followed by a JSON object โ byte 8 is always ``{``. The u64's
LOW byte can legitimately be ``0x80`` (any header length
โก 128 mod 256), which would collide with the pickle PROTO
opcode โ so safetensors is positively identified by byte 8
BEFORE the ``\x80`` pickle check. The ZIP magic is checked
first of all (a ZIP's byte 8 is the compression method, never
``{``; a torch-LEGACY pickle has the fixed magic byte ``0xf9``
at offset 8, and a protocol โฅ 4 bare pickle has a frame-length
byte there, ``0x00`` for any file under a terabyte. A
protocol-2/3 *bare* pickle's byte 8 is content-dependent, but
NNx never produces bare pickles and such a file failed under
the old routing too).
Anything matching none of the positive sniffs falls through to
the safetensors loader, whose error on a genuinely corrupt file
is clearer than a misleading unpickle attempt.
SECURITY: the pickle branch calls ``torch.load(weights_only=False)``,
which unpickles arbitrary Python objects. NEVER call this on a
checkpoint file from an untrusted source โ a malicious .pt file
can execute arbitrary code at load time. The default
``./runs/<id>/checkpoints/`` layout assumes the files were
produced locally by NNCheckpoint.save. For untrusted sources,
use the safetensors path on save and load: safetensors has no
arbitrary-code path.
nnx.nn.params.nn_checkpoint.NNCheckpoint.load(run: 'str', type: 'Checkpoints', root: 'Optional[str]' = None, map_location: 'Any' = 'cpu') -> 'Optional[NNCheckpoint]'No public description is currently available.
class nnx.nn.params.nn_checkpoint.ResumePointErrorA resume point that cannot be trusted: torn (its manifest names another checkpoint), mixed (files from different generations) or corrupted (a file's digest differs). Raised before anything is restored; the message says which and why.
class nnx.nn.params.nn_iteration_data_point.NNIterationDataPoint(*, lr: 'float', iter_idx: 'int', epoch_idx: 'int', batch_idx: 'int', train_edp: 'NNEvaluationDataPoint', val_edp: 'Optional[NNEvaluationDataPoint]' = None, train_summary: 'Optional[NNEvaluationDataPoint]' = None, selection: 'Optional[MonitorRecord]' = None, update_count: 'Optional[int]' = None) -> 'None'One row in the per-iteration training log.
Details
`train_edp` is computed from the current batch only. `val_edp` is the
per-epoch validation evaluation โ populated **only on the last idp of
each epoch** (the idp at which the validation loop ran). Other idps in
the same epoch have `val_edp=None`. When reading idps.csv, group by
epoch_idx and take the row with val_edp set for per-epoch validation
metrics.
**Epoch summary.** A run that declares metrics or a monitor
also records, on the same last idp of each epoch, `train_summary` โ
the whole-epoch training record (loss and error averaged with every
batch's own denominator, declared metrics over the full sample) โ and
`selection`, the epoch's :class:`~nnx.MonitorRecord` (monitor identity,
value, status and whether the epoch improved). Both are omitted from
`state()` otherwise, so legacy history is unchanged.
**Update counter.** Objective runs also record
`update_count` โ the committed optimizer updates so far โ distinct from
the microbatch counter `iter_idx` and the epoch `epoch_idx`; omitted
otherwise.
nnx.nn.params.nn_iteration_data_point.NNIterationDataPoint.with_val_edp(self, value: 'Optional[NNEvaluationDataPoint]') -> 'NNIterationDataPoint'No public description is currently available.
nnx.nn.params.nn_iteration_data_point.NNIterationDataPoint.with_epoch_summary(self, train_summary: 'Optional[NNEvaluationDataPoint]', selection: 'Optional[MonitorRecord]') -> 'NNIterationDataPoint'No public description is currently available.
nnx.nn.params.nn_iteration_data_point.NNIterationDataPoint.monitored_train_edp(self) -> 'NNEvaluationDataPoint'The epoch's training record for monitors: the whole-epoch summary when recorded, else this (last) batch's record.
nnx.nn.params.nn_iteration_data_point.NNIterationDataPoint.state(self) -> 'dict'No public description is currently available.
nnx.nn.params.nn_iteration_data_point.NNIterationDataPoint.from_state(state: 'dict') -> 'NNIterationDataPoint'No public description is currently available.
class nnx.nn.params.nn_evaluation_data_point.NNEvaluationDataPoint(*, f1: 'Optional[float]' = None, recall: 'Optional[float]' = None, accuracy: 'Optional[float]' = None, precision: 'Optional[float]' = None, loss: 'Optional[float]' = None, error: 'Optional[float]' = None, extra: 'Mapping[str, float]' = <factory>, kind: 'Optional[str]' = None, count: 'Optional[int]' = None, status: 'Optional[str]' = None, metrics: 'Mapping[str, float]' = <factory>) -> 'None'Per-batch / per-epoch evaluation metrics.
Details
The four classification fields (f1, recall, accuracy, precision) are
computed by `of()` via sklearn. `loss` and `error` are typically
attached after the fact by NNModel during training / evaluation.
`extra` is a free-form dict of user-supplied custom metric names to
floats. Populated when NNTrainParams.extra_metrics or evaluate(extra_metrics=)
is set; empty by default (and omitted from state() when empty so that
pre-extra runs hash to the same run.id and pre-extra YAML loads cleanly).
**Task records.** A model with a ``TaskSpec`` writes records
that also carry the task `kind` (``"categorical"`` / ``"multilabel"`` /
``"regression"``), the `count` of valid (unmasked) targets they
summarize, a `status` (``"ok"``, or ``"empty"`` when every target was
masked) and the task's own `metrics` (``mse`` / ``mae`` for regression,
``subset_accuracy`` / ``element_accuracy`` for multilabel). A
regression record leaves the classification fields and `error` as
``None`` rather than fabricating them; an empty record has no loss or
metrics at all. All four task fields are omitted from `state()` on
legacy records, whose serialization is unchanged.
nnx.nn.params.nn_evaluation_data_point.NNEvaluationDataPoint.with_loss(self, value: 'float')No public description is currently available.
nnx.nn.params.nn_evaluation_data_point.NNEvaluationDataPoint.with_error(self, value: 'float')No public description is currently available.
nnx.nn.params.nn_evaluation_data_point.NNEvaluationDataPoint.with_extra(self, name: 'str', value: 'float') -> 'NNEvaluationDataPoint'No public description is currently available.
nnx.nn.params.nn_evaluation_data_point.NNEvaluationDataPoint.of(Y: 'np.ndarray', Y_hat: 'np.ndarray', average: 'str' = 'macro', extra_metrics: 'Optional[Mapping[str, Callable]]' = None)Compute per-batch evaluation metrics.
Details
`average` controls how f1/precision/recall reduce across classes.
Default "macro" treats all classes equally โ the right choice for
multi-class classification and the only one that makes f1/precision/
recall mathematically distinct from accuracy. Pass "micro" to
recover the legacy behavior (numerically identical to accuracy for
single-label multi-class). Accuracy itself is not affected.
`extra_metrics` is a {name -> callable(y_true, y_pred) -> float} map
of user-supplied custom metrics, called as ``fn(Y, Y_hat)`` โ truth
first, decoded predictions second โ once on the arrays passed here
(one batch in the default training step, the aggregate in
`NNModel.evaluate`) and stored in the returned object's `extra` dict.
nnx.nn.params.nn_evaluation_data_point.NNEvaluationDataPoint.mean_of(edps: 'list[NNEvaluationDataPoint]') -> 'NNEvaluationDataPoint'Unweighted-mean reduce a list of EDPs across every metric.
Details
.. warning::
This is a **simple mean across edps**, NOT a sample-weighted
mean. With unequal batch sizes (the common case), the result
is statistically incorrect โ a 1024-sample batch counts the
same as an 8-sample tail batch. For correct sample-weighted
metrics across batches, use :meth:`NNModel.evaluate`, which
concatenates predictions across the loader and computes once
on the full sample.
``mean_of`` is kept for back-compat with callers that already
depend on the unweighted-mean semantics; new code should
prefer :meth:`NNModel.evaluate` unless the unweighted form is
specifically what's wanted (e.g., averaging across runs, not
across batches within a run).
An ``extra`` key present on some but not all edps is averaged over
the edps where it IS present (skipped on the rest).
nnx.nn.params.nn_evaluation_data_point.NNEvaluationDataPoint.state(self) -> 'dict'No public description is currently available.
nnx.nn.params.nn_evaluation_data_point.NNEvaluationDataPoint.from_state(state: 'dict') -> 'NNEvaluationDataPoint'No public description is currently available.
class nnx.history.HistoryJournal(retention: 'int' = 1000, chunk_size: 'Optional[int]' = None) -> 'None'Opt-in bounded history for one training run.
Details
Args:
retention: how many of the most recent records stay in memory (the
``ctx.idps`` window, ``NNRun.idps`` of the returned and of a
loaded run).
chunk_size: records per journal chunk file, at most ``retention``
(so the records waiting for their chunk are always inside the
window). ``None`` (the default) means ``min(250, retention)``;
:attr:`chunk` is the resolved size.
property nnx.history.HistoryJournal.chunkRecords per chunk file: chunk_size, or min(250, retention).
nnx.history.iter_history(run_id: 'str', root: 'Optional[str]' = None, *, lineage: 'bool' = False) -> 'Iterator[NNIterationDataPoint]'Stream a run's committed history in order: a journal chunk by chunk (each checked against its SHA-256), a legacy idps.csv whole (floats parsed round-trip, so its export reproduces the file). Records past the LAST checkpoint's epoch are never yielded.
Details
With ``lineage=True`` a resumed run is preceded by the committed records
of the run it resumed from, up to and including the epoch it resumed
from (recorded in its resume status; for runs recorded before that, the
epoch of that checkpoint now) and never past the run's own first record,
recursively: each epoch once, never the parent's later epochs. When
that epoch cannot be known (the checkpoint, or the parent's LAST, is
unreadable), the parent's records before the run's first record are
used, with a ``RuntimeWarning``; when neither is known, or the parent
run is gone, the lineage starts at the run, with a ``RuntimeWarning``.
Lineage follows resumes: a run that started fresh (a born-again
generation naming its teacher's run as ``parent_run_id``, say) starts
its own epochs, and its lineage is its own history.
nnx.history.export_history_csv(run_id: 'str', path: 'Union[str, os.PathLike[str]]', root: 'Optional[str]' = None, *, lineage: 'bool' = False) -> 'int'Write a run's committed history (see iter_history) to path in the legacy idps.csv layout โ the same columns, order and index NNRun.save writes โ atomically. Returns the number of records. The export holds the records in memory while it builds the table; stream iter_history for histories that do not fit.
nnx.history.migrate_history(run_id: 'str', root: 'Optional[str]' = None, *, spec: 'Optional[HistoryJournal]' = None, discard_uncommitted: 'bool' = False) -> 'None'Move a legacy run's idps.csv into a history journal, explicitly.
Details
The committed records are written as a journal (with per-epoch summary
rows), published and read back, and only then is ``idps.csv`` removed;
``run.yaml`` โ and so the run id โ is untouched. A leftover journal from
an interrupted migration (never published) is replaced. Refused: a run
that already keeps a journal, a run that is being trained (its lease is
held), and โ unless ``discard_uncommitted=True`` โ a CSV holding records
past the LAST checkpoint's epoch, which readers hide but a migration
would delete.
class nnx.history.HistoryCorruptionErrorA history journal whose committed files do not match its manifest: a chunk whose bytes changed, a broken index chain, a missing file.
class nnx.nn.net.feed_fwd_nn.FeedFwdNN(params: 'NNParams')Feed-forward classifier built from NNParams: flatten, a stack of hidden nn.Linear layers (per-layer activation and dropout), then a linear head. forward(x) returns logits.
property nnx.nn.net.feed_fwd_nn.FeedFwdNN.paramsNo public description is currently available.
nnx.nn.net.feed_fwd_nn.FeedFwdNN.forward(self, X: 'torch.Tensor') -> 'torch.Tensor'No public description is currently available.
nnx.nn.net.feed_fwd_nn.FeedFwdNN.seed_count(self, batch) -> 'Optional[int]'The seed rows of a PyG NeighborLoader / full-graph split batch, as GraphNNBase.seed_count: this net also reads PyG node batches, whose other rows belong to other splits and must not be scored.
nnx.nn.net.feed_fwd_nn.FeedFwdNN.unpack_batch(self, batch)No public description is currently available.
nnx.nn.net.feed_fwd_nn.FeedFwdNN.to_file(self, path: 'str') -> 'None'No public description is currently available.
nnx.nn.net.feed_fwd_nn.FeedFwdNN.from_file(path: 'str', params: 'NNParams', map_location='cpu') -> 'FeedFwdNN'No public description is currently available.
nnx.nn.net.feed_fwd_nn.FeedFwdNN.from_state(state_dict: 'dict', params: 'NNParams') -> 'FeedFwdNN'No public description is currently available.
class nnx.nn.net.feed_fwd_moe_nn.FeedFwdMoENN(params: 'NNMoEParams')Feed-forward classifier built from NNMoEParams whose hidden layers are top-k routed MoELinear experts; the head stays a plain nn.Linear. forward(x) returns logits.
nnx.nn.net.feed_fwd_moe_nn.FeedFwdMoENN.forward(self, X: 'torch.Tensor') -> 'torch.Tensor'No public description is currently available.
property nnx.nn.net.feed_fwd_moe_nn.FeedFwdMoENN.paramsNo public description is currently available.
nnx.nn.net.feed_fwd_moe_nn.FeedFwdMoENN.unpack_batch(self, batch)No public description is currently available.
class nnx.nn.net.conv_nn.ConvNN(params: 'NNConvParams')LeNet-style convolutional classifier built from NNConvParams: conv and pool blocks, then fully connected layers. forward(x) takes (B, C, H, W) images or flattened rows and returns logits.
property nnx.nn.net.conv_nn.ConvNN.paramsThe net's configuration; setting it (as model surgery does) re-resolves the per-layer state the forward reads.
nnx.nn.net.conv_nn.ConvNN.forward(self, X: 'torch.Tensor') -> 'torch.Tensor'No public description is currently available.
nnx.nn.net.conv_nn.ConvNN.unpack_batch(self, batch)No public description is currently available.
class nnx.nn.net.graph_nn_base.GraphNNBase(params: 'NNParams')Abstract base for GNN architectures.
Details
Subclasses must implement `_build_layers()` returning an `nn.ModuleList`
of PyG message-passing layers. The forward loop applies all-but-last
layers with the configured activation + dropout, then a bare final layer.
nnx.nn.net.graph_nn_base.GraphNNBase.forward(self, X: 'torch.Tensor', E: 'torch.Tensor') -> 'torch.Tensor'No public description is currently available.
nnx.nn.net.graph_nn_base.GraphNNBase.unpack_batch(self, batch) -> 'tuple[tuple[torch.Tensor, torch.Tensor], torch.Tensor]'No public description is currently available.
nnx.nn.net.graph_nn_base.GraphNNBase.seed_count(self, batch) -> 'Optional[int]'Number of seed rows at the head of a NeighborLoader subgraph.
Details
NeighborLoader puts the ``batch_size`` seed nodes first and
appends their sampled neighbors โ which can belong to *other*
splits. Loss and metrics must be computed on the seed rows only;
scoring neighbor rows leaks val/test labels into the training
loss and train labels into val metrics.
Returns None (no slicing) for anything that isn't a
NeighborLoader subgraph: plain full-graph ``Data`` has no
``batch_size``, and a multi-graph ``Batch.from_data_list``
collation DOES carry ``batch_size`` (= ``num_graphs``) but no
``input_id`` โ slicing there would truncate node-level output
to the graph count. ``input_id`` is the NeighborLoader-specific
marker (the seed indices), so it gates the slice.
class nnx.nn.net.graph_conv_nn.GraphConvNN(params: 'NNParams')Node-level graph net of PyG GCNConv layers (the graph extra: pip install "thekaveh-nnx[graph]").
class nnx.nn.net.graph_sage_nn.GraphSageNN(params: 'NNParams')Node-level graph net of PyG SAGEConv layers (the graph extra: pip install "thekaveh-nnx[graph]").
class nnx.nn.net.graph_att_nn.GraphAttNN(params: 'NNParams')Node-level graph net of PyG GATConv layers (the graph extra: pip install "thekaveh-nnx[graph]").
class nnx.nn.net.transformer_nn.TransformerNN(params: 'NNTransformerParams')Decoder-only language model built from NNTransformerParams. forward(tokens) maps (B, T) token ids to (B, T, vocab) logits; forward_with_cache decodes with a per-layer KV cache.
nnx.nn.net.transformer_nn.TransformerNN.forward(self, tokens: 'torch.Tensor') -> 'torch.Tensor'Args: tokens: (batch, seq) long tensor of token ids.
Details
Returns:
(batch, seq, vocab_size) logits โ pre-softmax.
nnx.nn.net.transformer_nn.TransformerNN.forward_with_cache(self, tokens: 'torch.Tensor', past_kvs: 'Optional[list[LayerKV]]' = None) -> 'tuple[torch.Tensor, list[LayerKV]]'Cache-threading forward used by GenerativeNNModel.generate.
Details
Behaves like ``forward`` but additionally accepts a per-layer
list of (k, v) caches (or ``None`` entries on the first call)
and returns the updated per-layer caches alongside the logits.
The total attended-to length per layer is
``past_kv_len + tokens.shape[1]`` โ the caller is responsible
for ensuring that this stays within ``max_seq_len`` (the
generate loop slides a window when it would otherwise overflow).
Args:
tokens: (batch, seq) long tensor of token ids. During
incremental decode, ``seq == 1``; on the prefill step
the prompt's full length is fed in one shot.
past_kvs: list of length ``n_layers`` with each entry a
``(k, v)`` tuple or ``None``. ``None`` means "no
history for this layer" (i.e., first call).
Returns:
A tuple ``(logits, new_kvs)`` where ``logits`` is
``(batch, seq, vocab)`` โ the *new* tokens' logits (with
``past_kvs != None`` and ``seq=1`` the returned
``logits[:, -1, :]`` is the next-token distribution
conditioned on the full cached prefix) โ and ``new_kvs``
is a list of length ``n_layers`` of updated ``(k, v)``
tuples; pass this back in for the next step.
nnx.nn.net.transformer_nn.TransformerNN.unpack_batch(self, batch)Make TransformerNN compatible with the standard supervised NNModel training loop.
Details
For an LM the canonical batch is ``(tokens, targets)`` where
``targets = tokens[:, 1:]`` shifted by one. We don't shift here โ
the caller assembles the tuple โ but we accept either a 2-tuple
``(X, Y)`` or a plain tensor of tokens (next-token loss is then
computed in the train step).
nnx.nn.net.transformer_nn.TransformerNN.to_file(self, path: 'str') -> 'None'No public description is currently available.
nnx.nn.net.transformer_nn.TransformerNN.from_file(path: 'str', params: 'NNTransformerParams', map_location='cpu') -> 'TransformerNN'No public description is currently available.
nnx.nn.net.transformer_nn.TransformerNN.from_state(state_dict: 'dict', params: 'NNTransformerParams') -> 'TransformerNN'No public description is currently available.
class nnx.nn.net.vit_nn.ViTNN(*, image_size: 'int' = 32, patch_size: 'int' = 4, in_channels: 'int' = 3, d_model: 'int' = 64, n_layers: 'int' = 4, n_heads: 'int' = 4, ffn_mult: 'int' = 4, attn_dropout: 'float' = 0.0, resid_dropout: 'float' = 0.0)Small Vision Transformer encoder.
Details
Forward contract:
``forward(x: (B, C, H, W), mask: Optional[BoolTensor[B, n_patches]]=None)``
โ ``(B, T_kept + 1, d_model)`` if ``mask`` provided (T_kept = mask.sum())
โ ``(B, n_patches + 1, d_model)`` otherwise.
The leading token is the learned CLS. Patches are flattened in
raster order (row-major over the patch grid). The optional ``mask``
is the I-JEPA "context" mask: True positions are kept, False ones
are dropped before any attention runs, so gradients do not flow
through masked patches.
``__init__`` requires ``image_size``, ``patch_size``, and
``in_channels`` for the patch-embedding convolution. ``image_size``
must be divisible by ``patch_size`` โ validated at construction.
nnx.nn.net.vit_nn.ViTNN.patch_positions(self) -> 'torch.Tensor'Return LongTensor[n_patches] of patch-token positions in the full sequence (i.e., arange(1, n_patches + 1) โ CLS is position 0).
Details
Exposed so the I-JEPA step factory can derive its context /
target position indices by boolean-masking this tensor instead
of rebuilding the arange (see ``jepa_train_step_factory``).
nnx.nn.net.vit_nn.ViTNN.forward(self, x: 'torch.Tensor', mask: 'Optional[torch.Tensor]' = None) -> 'torch.Tensor'Run the encoder.
Details
Args:
x: (B, C, H, W) input image tensor.
mask: optional BoolTensor of shape ``(B, n_patches)`` โ
True positions are kept, False ones are dropped *before*
attention. Per-sample masks may have different
``True``-counts, but the resulting batch must have the
same kept-count per row (asserted). I-JEPA's typical
context mask is uniform across the batch (same set of
patches kept on every sample in a step).
Returns:
``(B, T_kept + 1, d_model)`` where T_kept is the number of
kept patches (or ``n_patches`` when mask is None). The +1
is the CLS token at position 0.
nnx.nn.net.vit_nn.ViTNN.unpack_batch(self, batch)Standard (X-tuple, Y) adapter. JEPA doesn't use Y but the supervised linear-probe path on top of a frozen ViTNN does.
class nnx.nn.net.vit_nn.ViTBlock(d_model: 'int', n_heads: 'int', ffn_mult: 'int' = 4, attn_dropout: 'float' = 0.0, resid_dropout: 'float' = 0.0)Pre-norm ViT block: x = x + attn(RMSNorm(x)); x = x + ffn(RMSNorm(x)).
Details
Same shape as :class:`nnx.nn.net.transformer_layers.TransformerBlock`
but with bidirectional attention instead of causal. SwiGLU is
reused unchanged.
nnx.nn.net.vit_nn.ViTBlock.forward(self, x: 'torch.Tensor') -> 'torch.Tensor'No public description is currently available.
class nnx.nn.moe.MoELinear(in_features: 'int', out_features: 'int', *, num_experts: 'int', top_k: 'int' = 2)Sparse top-k Mixture-of-Experts drop-in for nn.Linear.
Details
Forward pass:
1. Router (a bias-less :class:`nn.Linear`) projects input
``(B, in_features) โ (B, num_experts)`` logits.
2. ``top_k`` largest logits per row are kept; a softmax over
those ``k`` values produces the per-expert gating weight.
3. Each token is dispatched to its top-``k`` experts; expert
outputs are weighted by the gating weights and summed into
the output tensor.
4. ``self.last_aux_loss`` is populated with the Switch-style
load-balancing penalty
``num_experts ยท ฮฃ_i f_i ยท P_i``. This is a scalar tensor with
gradients wired to the router so optimization of the main
loss + this term pushes routing toward uniform expert usage.
Args:
in_features: input feature dimension (matches ``nn.Linear``).
out_features: output feature dimension (matches ``nn.Linear``).
num_experts: number of expert sub-networks. Must be โฅ 2.
(``num_experts=1`` collapses to a plain linear with extra
book-keeping; the layer rejects it to surface the misuse.)
top_k: number of experts each input is routed through. Must
be โฅ 1 and โค ``num_experts``. Defaults to 2 โ the
Switch-Transformer paper uses ``k=1``, but ``k=2`` is the
broader MoE convention and tolerates a single misrouted
expert without losing the entire token.
Attributes:
router: bias-less :class:`nn.Linear` of shape
``(in_features, num_experts)``.
experts: :class:`nn.ModuleList` of ``num_experts``
:class:`nn.Linear` layers, each ``(in_features, out_features)``.
top_k: how many experts run per token.
num_experts: total expert count.
last_aux_loss: scalar ``torch.Tensor`` set after each
:meth:`forward`. ``None`` before the first forward.
Raises:
ValueError: if ``in_features <= 0``, ``out_features <= 0``,
``num_experts <= 1``, ``top_k <= 0``, or ``top_k > num_experts``.
nnx.nn.moe.MoELinear.forward(self, x: 'torch.Tensor') -> 'torch.Tensor'No public description is currently available.
nnx.nn.moe.MoELinear.extra_repr(self) -> 'str'No public description is currently available.
class nnx.nn.dataset.nn_dataset_base.NNDatasetBase() -> 'None'Base of the NNx datasets: a name, the input and output sizes and the train / validation / test loaders, all set by the subclass at construction.
nnx.nn.dataset.nn_dataset_base.NNDatasetBase.state(self) -> 'dict'No public description is currently available.
class nnx.nn.dataset.nn_dataset.NNDataset(*, ds_class: 'type[VisionDataset]', root_dir: 'str' = './data', download: 'bool' = True, transform: 'Optional[Callable]' = None, batch_sizes: 'tuple[Optional[int], Optional[int], Optional[int]]' = (None, None, None), val_proportion: 'float' = 0.1, seed: 'Optional[int]' = None, train_transform: 'Optional[Callable]' = None, eval_transform: 'Optional[Callable]' = None) -> 'None'Vision dataset wrapper (the vision extra: pip install "thekaveh-nnx[vision]"). val_proportion carves a validation slice out of the source train=True split (NOT out of the test split, which stays untouched for final evaluation).
Details
``batch_sizes`` is a ``(train, val, test)`` tuple. ``None`` โ the
default for every slot โ means *one batch holding the complete split*:
the default train loader therefore yields a single full-split batch per
epoch, i.e. **one optimizer step per epoch**. That suits small
full-batch fits but not stochastic training (diffusion, MoE routing,
self-supervision); pass an explicit train size such as
``batch_sizes=(128, None, None)`` for mini-batches. A positive integer
is used verbatim (a size larger than the split simply yields one smaller
batch); NumPy integers are normalized to ``int``. Zero, ``False``,
negatives, floats, strings and anything but a 3-tuple are rejected with
the ``batch_sizes[i] (split)`` slot named before ``ds_class`` is
instantiated โ zero never disables a split; ``val_proportion=0.0`` does
(``val_loader`` is then ``None``; the resolved val size is a placeholder
``1`` when requested as ``None``, and an explicit size is kept).
``transform`` is handed to both torchvision factories and applies to
every split. Split transforms: ``train_transform`` and
``eval_transform`` add a per-split input transform on top of it โ e.g. a
random augmentation for training and a fixed resize for validation and
test. Each split then becomes a ``nnx.preprocessing.SplitView`` over
the untouched base dataset: targets pass through, the base
``transform`` / labels / order never change, and views are safe to read
alternately and from DataLoader workers. ``input_dim`` is probed through
``eval_transform`` when one is set. Both default to ``None`` (the
existing single-transform behavior, plain ``Subset`` splits); ``state()``
gains ``train_transform`` / ``eval_transform`` descriptions only when
set โ runtime callables are recorded by name and must be passed again
to rebuild the dataset.
class nnx.nn.dataset.nn_graph_dataset.NNGraphDataset(*, ds_class: 'type[Dataset]', n_neighbors: 'Optional[list[int]]' = None, root_dir: 'str' = './data', transform: 'Optional[Callable]' = None, n_workers: 'int' = 4, batch_sizes: 'tuple[Optional[int], Optional[int], Optional[int]]' = (None, None, None), seed: 'Optional[int]' = None, sampler: "Literal['neighbor', 'full']" = 'neighbor') -> 'None'Single-graph node-classification wrapper over a PyG dataset class (the graph extra: pip install "thekaveh-nnx[graph]").
Details
``sampler="neighbor"`` (default) builds one ``NeighborLoader`` per split
from the graph's ``train_mask`` / ``val_mask`` / ``test_mask``;
``sampler="full"`` yields the whole graph as one batch per split (no
pyg-lib / torch-sparse needed).
``batch_sizes`` is a ``(train, val, test)`` tuple of *seed-node* counts
for the neighbor sampler. ``None`` (the default for every slot) means
*every node of the split mask in one batch* โ one optimizer step per
epoch for the default train loader; pass an explicit train size such as
``batch_sizes=(256, None, None)`` for mini-batches. A positive integer
is kept verbatim; NumPy integers are normalized to ``int``. Zero,
``False``, negatives, floats, strings and anything but a 3-tuple are
rejected with the ``batch_sizes[i] (split)`` slot named before
``ds_class`` is instantiated. ``sampler="full"`` rejects every explicit
size (the complete split is always one batch).
An empty ``val_mask`` / ``test_mask`` is an *absent* split in both
sampler modes: that loader is ``None`` โ the optional-loader
contract the tabular / preference wrappers already follow, which
``NNModel.train`` / ``Trainer.train`` honour by skipping validation โ
its resolved size is ``0`` (derived from absence, never an accepted
explicit zero) and ``state()`` reports ``"0"``. An empty ``train_mask``
raises ``ValueError`` at construction. The caller's masks are only read.
class nnx.nn.dataset.nn_tabular_dataset.NNTabularDataset(*, df: 'pd.DataFrame', feature_cols: 'list[str]', target_col: 'str', batch_sizes: 'tuple[Optional[int], Optional[int], Optional[int]]' = (None, None, None), val_proportion: 'float' = 0.15, test_proportion: 'float' = 0.15, name_override: 'Optional[str]' = None, feature_dtype: 'torch.dtype' = torch.float32, target_dtype: 'Optional[torch.dtype]' = None, seed: 'Optional[int]' = None, split: 'Optional[SplitManifest]' = None, id_col: 'Optional[str]' = None, source_identity: 'Optional[Union[IdentityRef, str]]' = None, standardize: 'Union[bool, Standardizer]' = False) -> 'None'Wrap a pandas DataFrame as train/val/test DataLoaders.
Details
`feature_cols` columns are stacked into the input tensor; `target_col`
is the target column. By default, targets are coerced to int64 (long)
and validated as contiguous integer classes 0..K-1 (classification);
the loaders yield 1-D class-index targets `(batch,)` (the
`CrossEntropyLoss` convention). Set `target_dtype` to a floating-point
dtype (e.g. `torch.float32`) to skip the integer cast and contiguity
check and fix `output_dim=1` for regression; the loaders then yield
targets of shape `(batch, 1)` so they line up with a model whose
final linear layer has one output. Integer dtypes are rejected โ
leave `target_dtype` unset (`None`) for classification.
``batch_sizes`` is a ``(train, val, test)`` tuple. ``None`` (the
default for every slot) means *one batch holding the complete split* โ
the default train loader yields one full-split batch per epoch, i.e.
one optimizer step per epoch; pass an explicit train size such as
``batch_sizes=(64, None, None)`` for mini-batches. A positive integer
is kept verbatim (larger than the split โ one smaller batch); NumPy
integers are normalized to ``int``. Zero, ``False``, negatives, floats,
strings and anything but a 3-tuple are rejected with the
``batch_sizes[i] (split)`` slot named before any tensor conversion or
split. Zero never disables a split: `val_proportion=0.0` /
`test_proportion=0.0` do, and that split's loader is then ``None``. Its
resolved ``batch_sizes`` slot is a placeholder ``1`` when it was
requested as ``None``; an explicit size is kept.
Admission: only the selected feature and target columns are inspected.
NaN in any of them, ``ยฑinf`` in a feature (checked in source precision,
before the ``feature_dtype`` cast that could absorb it), finite features
outside an integer ``feature_dtype``'s range (the cast would wrap them)
and a non-finite target raise ``ValueError`` naming the columns and
dtypes; so does a finite value that overflows a narrower floating /
complex ``feature_dtype`` or floating ``target_dtype`` during conversion.
All checks, the classification label check included, run before the
split, so a rejection leaves the DataFrame and the global RNG untouched
(the one exception: with ``standardize``, a standardized feature that
overflows ``feature_dtype`` can only be detected after the split, once
the training statistics exist).
Explicit membership: ``split=`` takes a ``SplitManifest``
(``nnx.data_splits``) instead of the seeded ``random_split``. Rows are
matched to the plan by the sample ids in the ``id_col`` column (required;
never inferred from the index), so reordered rows keep their split; a
positional manifest matches row positions and needs its recorded
``source_identity``. Duplicate, missing or unexpected ids, a changed
``source_identity`` and an empty train split raise ``SplitError`` before
any tensor or loader exists. Only the planned rows are admitted and
counted (``output_dim`` included): rows the plan excluded (a
chronological ``gap``) may be absent and are never inspected. An empty
validation or test membership gives a ``None`` loader. ``seed``,
``val_proportion`` and ``test_proportion`` do not apply and must be left
at their defaults; the train loader still shuffles, and validation /
test follow the manifest's order. ``state()`` gains ``split`` (the
manifest digest) only then.
Train-only standardization: ``standardize=True`` fits a
``nnx.preprocessing.Standardizer`` on the training split's rows of the
feature columns only โ after the split, so validation and test values
never reach the statistics โ and applies those frozen statistics to
every split. ``standardize=<fitted Standardizer>`` reuses one without
refitting; its columns must equal ``feature_cols`` (same order) and its
dtype ``feature_dtype``, checked before the split. Either way the
standardizer in use is ``ds.standardizer`` (save it to serve the model).
Standardizing needs a floating ``feature_dtype`` and unique
``feature_cols``; the raw features are never converted, targets are never
transformed, and the split membership (and RNG draw) is the same as
without standardization. ``state()`` gains ``standardizer`` (its digest)
only then.
class nnx.nn.dataset.nn_preference_dataset.NNPreferenceDataset(*, prompts: 'list[str]', chosen: 'list[str]', rejected: 'list[str]', tokenizer: 'object', max_prompt_len: 'int' = 64, max_response_len: 'int' = 64, pad_token_id: 'int' = 0, batch_sizes: 'tuple[Optional[int], Optional[int], Optional[int]]' = (None, None, None), val_proportion: 'float' = 0.1, test_proportion: 'float' = 0.1, name_override: 'Optional[str]' = None, seed: 'Optional[int]' = None) -> 'None'Wrap parallel lists of (prompt, chosen, rejected) strings as DPO loaders.
Details
Tokenizes every triple through ``tokenizer.encode`` once at
construction, pads/truncates to fixed lengths, then splits into
train / val / test ``DataLoader``s with the same shape as the
rest of :class:`NNDatasetBase` (so callbacks and the standard
training loop work unchanged).
Each batch yielded is ``(prompt_ids, chosen_ids, rejected_ids)``
where each entry is ``(B, T_*)`` ``torch.LongTensor``.
``pad_token_id`` defaults to ``0``. Under :func:`train_bpe`'s default
special tokens, id 0 is ``"<unk>"`` and ``"<pad>"`` is id 1: pass the
tokenizer's pad id (``tokenizer.token_to_id("<pad>")``), or the DPO
step's padding mask would also hide every real ``<unk>`` token.
``batch_sizes`` is a ``(train, val, test)`` tuple. ``None`` (the
default for every slot) means *one batch holding the complete split* โ
one DPO step per epoch for the default train loader; pass an explicit
train size such as ``batch_sizes=(8, None, None)`` for mini-batches. A
positive integer is kept verbatim (larger than the split โ one smaller
batch); NumPy integers are normalized to ``int``. Zero, ``False``,
negatives, floats, strings and anything but a 3-tuple are rejected with
the ``batch_sizes[i] (split)`` slot named before ``tokenizer.encode``
is called. Zero never disables a split: `val_proportion=0.0` /
`test_proportion=0.0` do (that loader is then ``None``; its resolved
``batch_sizes`` slot is a placeholder ``1`` when requested as ``None``,
and an explicit size is kept).
class nnx.nn.enum.activations.Activations(Enum)Enum values: ELU, SELU, TANH, RELU, SOFTMAX, SIGMOID, SOFTPLUS, LEAKY_RELU.
nnx.nn.enum.activations.Activations.ELU = 'elu'Enum value elu.
nnx.nn.enum.activations.Activations.SELU = 'selu'Enum value selu.
nnx.nn.enum.activations.Activations.TANH = 'tanh'Enum value tanh.
nnx.nn.enum.activations.Activations.RELU = 'relu'Enum value relu.
nnx.nn.enum.activations.Activations.SOFTMAX = 'softmax'Enum value softmax.
nnx.nn.enum.activations.Activations.SIGMOID = 'sigmoid'Enum value sigmoid.
nnx.nn.enum.activations.Activations.SOFTPLUS = 'softplus'Enum value softplus.
nnx.nn.enum.activations.Activations.LEAKY_RELU = 'leaky_relu'Enum value leaky_relu.
class nnx.nn.enum.checkpoints.Checkpoints(Enum)Enum values: Q1, Q2, Q3, BEST, LAST, FIRST.
nnx.nn.enum.checkpoints.Checkpoints.Q1 = 'q1'Enum value q1.
nnx.nn.enum.checkpoints.Checkpoints.Q2 = 'q2'Enum value q2.
nnx.nn.enum.checkpoints.Checkpoints.Q3 = 'q3'Enum value q3.
nnx.nn.enum.checkpoints.Checkpoints.BEST = 'best'Enum value best.
nnx.nn.enum.checkpoints.Checkpoints.LAST = 'last'Enum value last.
nnx.nn.enum.checkpoints.Checkpoints.FIRST = 'first'Enum value first.
class nnx.nn.enum.devices.Devices(Enum)Enum values: CPU, MPS, CUDA.
nnx.nn.enum.devices.Devices.CPU = 'cpu'Enum value cpu.
nnx.nn.enum.devices.Devices.MPS = 'mps'Enum value mps.
nnx.nn.enum.devices.Devices.CUDA = 'cuda'Enum value cuda.
nnx.nn.enum.devices.Devices.torch_device(self) -> 'torch.device'Explicit alias for self() โ more readable in code that mixes the enum and torch.device usage.
nnx.nn.enum.devices.Devices.get() -> 'Devices'No public description is currently available.
nnx.nn.enum.devices.Devices.get_torch_device() -> 'torch.device'Convenience: auto-detect and return the corresponding torch.device directly. Equivalent to Devices.get().torch_device().
class nnx.nn.enum.losses.Losses(Enum)Enum values: CROSS_ENTROPY, MEAN_SQUARED_ERROR, BINARY_CROSS_ENTROPY, NEGATIVE_LOG_LIKELIHOOD.
nnx.nn.enum.losses.Losses.CROSS_ENTROPY = 'cross_entropy'Enum value cross_entropy.
nnx.nn.enum.losses.Losses.MEAN_SQUARED_ERROR = 'mean_squared_error'Enum value mean_squared_error.
nnx.nn.enum.losses.Losses.BINARY_CROSS_ENTROPY = 'binary_cross_entropy'Enum value binary_cross_entropy.
nnx.nn.enum.losses.Losses.NEGATIVE_LOG_LIKELIHOOD = 'negative_log_likelihood'Enum value negative_log_likelihood.
class nnx.nn.enum.nets.Nets(Enum)Enum values: CONV, FEED_FWD, FEED_FWD_MOE, GRAPH_ATT, GRAPH_CONV, GRAPH_SAGE, TRANSFORMER, VIT.
nnx.nn.enum.nets.Nets.CONV = 'conv'Enum value conv.
nnx.nn.enum.nets.Nets.FEED_FWD = 'feed_fwd'Enum value feed_fwd.
nnx.nn.enum.nets.Nets.FEED_FWD_MOE = 'feed_fwd_moe'Enum value feed_fwd_moe.
nnx.nn.enum.nets.Nets.GRAPH_ATT = 'graph_att'Enum value graph_att.
nnx.nn.enum.nets.Nets.GRAPH_CONV = 'graph_conv'Enum value graph_conv.
nnx.nn.enum.nets.Nets.GRAPH_SAGE = 'graph_sage'Enum value graph_sage.
nnx.nn.enum.nets.Nets.TRANSFORMER = 'transformer'Enum value transformer.
nnx.nn.enum.nets.Nets.VIT = 'vit'Enum value vit.
class nnx.nn.enum.optims.Optims(Enum)Enum values: SGD, ADAM, ADAM_AMSGRAD, SGD_NESTEROV, ADAMW.
nnx.nn.enum.optims.Optims.SGD = 'sgd'Enum value sgd.
nnx.nn.enum.optims.Optims.ADAM = 'adam'Enum value adam.
nnx.nn.enum.optims.Optims.ADAM_AMSGRAD = 'adam_amsgrad'Enum value adam_amsgrad.
nnx.nn.enum.optims.Optims.SGD_NESTEROV = 'sgd_nesterov'Enum value sgd_nesterov.
nnx.nn.enum.optims.Optims.ADAMW = 'adamw'Enum value adamw.
class nnx.nn.enum.schedulers.Schedulers(Enum)Enum values: REDUCE_LR_ON_PLATEAU, STEP, COSINE_ANNEALING, ONE_CYCLE, LINEAR_WARMUP_DECAY.
nnx.nn.enum.schedulers.Schedulers.REDUCE_LR_ON_PLATEAU = 'reduce_lr_on_plateau'Enum value reduce_lr_on_plateau.
nnx.nn.enum.schedulers.Schedulers.STEP = 'step'Enum value step.
nnx.nn.enum.schedulers.Schedulers.COSINE_ANNEALING = 'cosine_annealing'Enum value cosine_annealing.
nnx.nn.enum.schedulers.Schedulers.ONE_CYCLE = 'one_cycle'Enum value one_cycle.
nnx.nn.enum.schedulers.Schedulers.LINEAR_WARMUP_DECAY = 'linear_warmup_decay'Enum value linear_warmup_decay.
class nnx.nn.callbacks.Callback()Base class for training callbacks. Override any subset of the hooks.
Details
``distributed`` declares the callback's rank behaviour under
``train(distributed=DDP())``: ``"all"`` โ run on every rank
(it must not write files: records and decisions are identical on every
rank); ``"writer"`` โ run on the writer rank only (it may write; it must
not change control flow such as ``ctx.should_stop``); ``"unsupported"``
โ refused (it writes when constructed: use
``nnx.distributed.writer_only``). ``None`` (undeclared) is refused under
DDP and ignored otherwise.
nnx.nn.callbacks.Callback.on_train_begin(self, ctx: '_CallbackContext') -> 'None'No public description is currently available.
nnx.nn.callbacks.Callback.on_epoch_begin(self, ctx: '_CallbackContext') -> 'None'No public description is currently available.
nnx.nn.callbacks.Callback.on_epoch_end(self, ctx: '_CallbackContext') -> 'None'No public description is currently available.
nnx.nn.callbacks.Callback.on_train_end(self, ctx: '_CallbackContext') -> 'None'No public description is currently available.
nnx.nn.callbacks.Callback.on_optimizer_update(self, ctx: '_CallbackContext', event: 'Any') -> 'None'Called once per committed optimizer update of an objective run โ never per microbatch, for an all-masked window or a skipped step. event is a detached nnx.objectives.UpdateEvent (optimizer name, its update index, epoch / batch, microbatches, loss values); ctx.update_count is the run's committed-update count.
nnx.nn.callbacks.Callback.checkpoint_transforms(self) -> 'tuple[NNCheckpointTransform, ...]'Completed topology transforms to persist on the final checkpoint.
class nnx.nn.callbacks.EarlyStopping(monitor: 'Optional[Union[str, MonitorSpec]]' = None, patience: 'int' = 10, min_delta: 'float' = 0.0, mode: 'str' = 'min', name: 'Optional[str]' = None)Stop training when the monitored metric stops improving.
Details
Args:
monitor: which data-point field to track. ``None`` (default) selects
automatically from the validation data point, once per
``train()`` call: ``val_edp.error`` when the first validated
epoch reports a finite error, otherwise ``val_edp.loss`` when it
reports a finite loss (regression and other evaluators that
leave ``error`` as ``None``). The choice then stays fixed for
that run; ``selected_monitor`` reports it. The default never
reads training metrics and only supports ``mode="min"``.
An explicit ``"val_edp.error"``, ``"val_edp.loss"``,
``"train_edp.error"`` or ``"train_edp.loss"`` reads exactly that
field with no fallback. Unlike BEST selection and
ReduceLROnPlateau, there is no validationโtraining fallback.
When the tracked field (or its whole data point) is absent, the
epoch is not counted toward patience and one ``RuntimeWarning``
per run names the monitor and what is missing, instead of the
callback going silently inactive. A NaN/ยฑinf value never
becomes the best and counts as an epoch without improvement
(also reported once per run).
patience: epochs with no improvement before stopping โ a nonnegative
integer count (NumPy integers accepted and normalized; zero
stops on the first non-improving epoch). Fractional, boolean
or string values raise ``ValueError`` at construction.
min_delta: minimum change to qualify as improvement.
mode: improvement direction for the monitored field. ``"min"``
(default): lower is better โ the meaning of every accepted
monitor (loss / error), and the direction BEST selection and
ReduceLROnPlateau also assume, so it is almost always right.
``"max"`` only reverses this callback's comparison for one of
the four accepted keys; it does not enable accuracy/F1
monitors, which are rejected, and it does not change how the
rest of NNx ranks ``error`` / ``loss``. ``"max"`` requires an
explicit ``monitor``.
**Named monitors.** ``monitor`` may instead be a
:class:`~nnx.MonitorSpec` โ a split and metric (``loss``,
``error`` or a metric declared in ``NNTrainParams.metrics``)
with its own direction, ``min_delta`` and missing / non-finite
policy. It reads the epoch's whole-epoch training summary or
validation record and applies
:meth:`~nnx.MonitorSpec.improved`, the rule BEST selection and
plateau scheduling use when the run declares the same monitor,
so all three agree. ``mode`` and ``min_delta`` must then stay
at their defaults (set them on the spec).
name: component name under which the patience state is checkpointed.
By default ``"early_stopping"``, numbered
(``early_stopping.2`` โฆ) in callback order when a run has
several; an explicit name must be unique within the run.
Checkpointable: the best value, the epochs waited and the selected
monitor are saved with every checkpoint's training state and restored
on a stateful warm resume *after* ``on_train_begin`` resets them, so a
resumed run stops at the same epoch an uninterrupted one would. It is
an optional component: resuming from a checkpoint written without it
starts with fresh patience.
Example::
EarlyStopping(monitor="val_edp.loss", mode="min", patience=5)
property nnx.nn.callbacks.EarlyStopping.selected_monitorField compared in the current run (None until the automatic default has seen a validated epoch with a finite error or loss).
nnx.nn.callbacks.EarlyStopping.component_spec(self) -> 'ComponentSpec'No public description is currently available.
nnx.nn.callbacks.EarlyStopping.component_state(self) -> 'dict[str, Any]'No public description is currently available.
nnx.nn.callbacks.EarlyStopping.check_component_state(self, state: 'Mapping[str, Any]', *, version: 'int') -> 'list[str]'Reject, before anything is restored, patience tracked for a different monitor or direction.
nnx.nn.callbacks.EarlyStopping.load_component_state(self, state: 'Mapping[str, Any]', *, version: 'int') -> 'None'No public description is currently available.
nnx.nn.callbacks.EarlyStopping.on_train_begin(self, ctx: '_CallbackContext') -> 'None'No public description is currently available.
nnx.nn.callbacks.EarlyStopping.on_epoch_end(self, ctx: '_CallbackContext') -> 'None'No public description is currently available.
class nnx.nn.callbacks.LRMonitor(bounded: 'bool' = True)Logs the current LR each epoch. History exposed at .history.
Details
`.history` keeps one entry per epoch (the primary optimizer's LR at the
epoch's end) whatever the scheduler's clock. An ``optimizer_update``-clock
scheduler also records `.update_history`: ``(update index, LR
after that update's scheduler step)`` for the primary optimizer, a
separate trace of the steps NNx takes โ empty on the epoch clock and
under ``Trainer``'s ``auto_step_schedulers=False``, where the step
function steps the schedule itself.
In a run with a history journal (``nnx.history``) the log is
bounded too: it keeps the LRs of the last ``retention`` epochs (the
journal's bound, applied per epoch), and `.update_history` the updates
of those epochs. ``bounded=False`` keeps every epoch's LR (one float
each), and every update's, in any run.
nnx.nn.callbacks.LRMonitor.on_epoch_end(self, ctx: '_CallbackContext') -> 'None'No public description is currently available.
class nnx.nn.callbacks.ModelCheckpoint(epochs: 'Optional[list[int]]' = None, tag: 'str' = 'custom')Save a custom-tagged checkpoint at user-specified epochs.
Details
The standard train() loop already saves FIRST / Q1 / Q2 / Q3 / LAST / BEST
via the Checkpoints enum. This callback adds ad-hoc save points outside
that cycle โ useful for sampling at fixed milestones (e.g., epoch 10,
20, 50) for downstream inspection.
Each match writes ``<cwd>/runs/<run.id>/checkpoints/<tag>_e<epoch>.pt``
โ cwd-relative, matching what :meth:`NNRun.save` and :class:`NNCheckpoint`
use when called from inside :meth:`NNModel.train` (the train() entry
point doesn't accept a ``root=`` parameter). The epoch suffix
prevents successive matches from overwriting each other when
``epochs`` has multiple entries.
Files are **weights-only** and say so (``training_state_present`` is
``False``): pass ``resume_from_checkpoint="<tag>_e<epoch>"`` with
``resume_mode="weights_only"`` (or the default ``"auto"``) to
warm-start from one; ``resume_mode="stateful"`` rejects it before
anything is restored.
Args:
epochs: list of 0-indexed epoch numbers at which to save. Empty /
None means the callback never fires (and never saves anything).
tag: prefix in the filename, defaults to ``"custom"``.
nnx.nn.callbacks.ModelCheckpoint.on_epoch_end(self, ctx: '_CallbackContext') -> 'None'No public description is currently available.
class nnx.nn.callbacks.TensorBoardCallback(log_dir: 'Optional[str]' = None, flush_each_epoch: 'bool' = True)Stream train/val metrics + LR to a TensorBoard SummaryWriter.
Details
Requires `tensorboard` to be installed โ imported lazily so users who
don't use this callback don't pay the dependency cost.
Args:
log_dir: directory passed to SummaryWriter. None lets TensorBoard
pick its default (runs/<datetime>).
flush_each_epoch: when True (default), calls writer.flush() so
partial training is visible in TB even if the process crashes.
nnx.nn.callbacks.TensorBoardCallback.on_epoch_end(self, ctx: '_CallbackContext') -> 'None'No public description is currently available.
nnx.nn.callbacks.TensorBoardCallback.on_train_end(self, ctx: '_CallbackContext') -> 'None'No public description is currently available.
class nnx.nn.callbacks.WandbCallback(project: 'Optional[str]' = None, wandb_run=None, **init_kwargs)Stream train/val metrics + LR to Weights & Biases.
Details
Requires `wandb` โ lazily imported. Pass `project=` to start a new run,
or `wandb_run=` to attach to an externally-managed run.
nnx.nn.callbacks.WandbCallback.on_epoch_end(self, ctx: '_CallbackContext') -> 'None'No public description is currently available.
nnx.nn.callbacks.WandbCallback.on_train_end(self, ctx: '_CallbackContext') -> 'None'No public description is currently available.
nnx.finetune.freezing.freeze(module: 'nn.Module', *patterns: 'str') -> 'int'Set requires_grad=False on every parameter under module whose dotted name matches any of patterns.
Details
Patterns use ``fnmatch`` shell-glob semantics: ``*`` matches any
sequence of characters **including dots** (not just one path segment),
``?`` matches a single character, ``[seq]`` matches one character
from the set. Match is against the parameter's full dotted name,
e.g., ``encoder.layer.5.weight``. So ``"encoder.*"`` matches every
parameter under the encoder subtree, including deeply nested ones
like ``encoder.layer.5.weight``.
Args:
module: any ``nn.Module``.
*patterns: one or more fnmatch globs. If no patterns are
given, raises ``ValueError`` (freeze-all-by-default is too
dangerous to be the no-arg behavior).
Returns:
The number of parameters newly frozen (i.e., previously had
``requires_grad=True``). Useful for assertion / logging.
nnx.finetune.freezing.unfreeze(module: 'nn.Module', *patterns: 'str') -> 'int'Mirror of freeze โ set requires_grad=True on matching parameters. Returns the count newly unfrozen.
nnx.finetune.freezing.frozen(module: 'nn.Module') -> 'list[str]'List the dotted parameter names currently frozen under module.
Details
Returned list is sorted by name for stable test assertions. Useful
for logging at ``train()`` entry so users can see exactly which
parameters are excluded from training.
nnx.finetune.loading.load_pretrained(module: 'nn.Module', source: 'Union[str, Path, dict, nn.Module]', *, key_map: 'Optional[dict[str, str]]' = None, strict: 'bool' = False, prefix: 'Optional[str]' = None) -> 'LoadPretrainedResult'Load weights into module from an external source.
Details
The source can be:
- a path (str or Path) to a ``.pt`` / ``.pth`` file holding a
state-dict (loaded with ``weights_only=True`` for safety);
- a state-dict (``dict``) already in memory;
- another ``nn.Module``, in which case its state-dict is used.
Args:
module: target module to load into. Mutated in place.
source: see above.
key_map: optional remapping from source keys to target keys,
applied AFTER ``prefix`` stripping and BEFORE matching.
Each entry is a **prefix** substitution: for the first
key in ``key_map`` whose prefix matches the source key,
that prefix is replaced with the mapped value. E.g.,
``{"backbone.": "net."}`` rewrites ``backbone.conv1.weight``
to ``net.conv1.weight``; later occurrences of ``backbone.``
mid-string are NOT touched. First-match-wins; subsequent
entries don't fire once a key has been remapped.
strict: when True, raise if any source key has no target match
OR any target key has no source. Default False (fine-tuning
commonly partial-loads). Either way a key both sides hold
with different shapes raises ``RuntimeError`` before anything
is copied.
prefix: optional prefix to strip from source keys before
matching. E.g., ``prefix="model."`` turns ``model.layer.0``
into ``layer.0``. Applied BEFORE ``key_map``.
Returns:
:class:`LoadPretrainedResult` with the loaded / missing /
unexpected key sets.
class nnx.finetune.loading.LoadPretrainedResult(loaded_keys: 'list[str]', missing_keys: 'list[str]', unexpected_keys: 'list[str]') -> 'None'Outcome of a load_pretrained call.
Details
Compared with :meth:`torch.nn.Module.load_state_dict`, this gives
you back not just the missing/unexpected keys but also the list
of keys actually applied (after any remapping) โ useful for
confirming the load did what you intended.
class nnx.finetune.param_groups.NNParamGroupSpec(*, name_pattern: 'str', lr: 'Optional[float]' = None, lr_multiplier: 'Optional[float]' = None, weight_decay: 'Optional[float]' = None) -> 'None'One row in NNOptimParams.param_groups.
Details
Matches parameters whose dotted name matches ``name_pattern``
(fnmatch glob) and applies the specified ``lr`` (absolute) or
``lr_multiplier`` (multiplied by ``NNOptimParams.max_lr``) and
optional ``weight_decay`` override.
Exactly one of ``lr`` and ``lr_multiplier`` may be set. If both are
None the matched parameters use the optimizer's default LR โ handy
when you only want to override ``weight_decay`` for a group.
Specs are matched in list order and the **first** matching spec wins โ
rules never merge. Put specific rules before broad ones: a parameter
that should get settings from two rules needs its own combined rule.
Example:
# Freeze nothing, but train the encoder at 1/100th the head's LR
# and disable weight_decay on every bias term. `encoder.*bias`
# must come first: with `encoder.*` first, the encoder biases
# would match it and keep weight decay.
NNOptimParams(
name=Optims.ADAM,
max_lr=1e-3,
momentum=(0.9, 0.999),
weight_decay=5e-4,
param_groups=[
NNParamGroupSpec(name_pattern="encoder.*bias", lr_multiplier=0.01, weight_decay=0.0),
NNParamGroupSpec(name_pattern="encoder.*", lr_multiplier=0.01),
NNParamGroupSpec(name_pattern="*.bias", weight_decay=0.0),
],
)
# encoder.bias: lr 1e-5, wd 0 encoder.weight: lr 1e-5, wd 5e-4
# head.bias: lr 1e-3, wd 0 head.weight: lr 1e-3, wd 5e-4
nnx.finetune.param_groups.NNParamGroupSpec.state(self) -> 'dict'No public description is currently available.
nnx.finetune.param_groups.NNParamGroupSpec.from_state(state: 'dict') -> 'NNParamGroupSpec'No public description is currently available.
nnx.finetune.param_groups.build_param_groups(module: 'nn.Module', specs: 'list[NNParamGroupSpec]', *, default_lr: 'float', default_weight_decay: 'float', strict: 'bool' = False) -> 'list[dict]'Walk module's parameters, bucket them by the first matching spec (or into a fallback default group), and return the list of param-group dicts the optimizer expects.
Details
Parameters with ``requires_grad=False`` are dropped โ they're
frozen, the optimizer doesn't need to see them. (Without this, the
optimizer would still hold them in its state but they'd never
update; harmless but wasteful and confusing in `optimizer.param_groups`.)
Args:
module: source of parameters to bucket.
specs: list of :class:`NNParamGroupSpec` in priority order.
The first spec whose ``name_pattern`` matches a parameter's
dotted name wins; later matches are ignored, never merged, so
list specific patterns (``encoder.*bias``) before broad ones
(``encoder.*``, ``*.bias``).
default_lr: LR for parameters that don't match any spec, or
for specs that omit both ``lr`` and ``lr_multiplier``.
default_weight_decay: WD for parameters that don't match any
spec's ``weight_decay`` override.
strict: when False (default, fine-tuning semantics), parameters
that match no spec go into a default group at ``default_lr``
so every trainable parameter ends up in the optimizer. When
True (multi-optimizer Trainer semantics), unmatched parameters
are DROPPED from the optimizer entirely โ the contract is
"this optimizer owns only what the specs explicitly select",
which is what allows disjoint optimizers in
:class:`nnx.trainer.Trainer`.
Returns:
A list of dicts suitable for ``torch.optim.Optimizer(
params, ...)`` โ each entry has ``"params"`` plus any overrides.
LoRA + DoRA + IA3 + Prefix-Tuning + Prompt-Tuning + Adapters. The apply_*_to helpers wrap layers in place. LoRA, IA3, Prefix and Prompt ship save_*_weights / load_*_weights, which persist only the trainable delta. DoRA persists with the full state_dict() (the LoRA helpers omit magnitude), and AdapterLayer is inserted by hand.
class nnx.peft.lora.LoRALinear(base: 'nn.Linear', *, r: 'int' = 8, alpha: 'float' = 16.0, dropout: 'float' = 0.0)Linear layer wrapped with a LoRA low-rank residual.
Details
The original :class:`nn.Linear` lives at ``self.base`` with its
parameters frozen (``requires_grad=False``) on construction.
``lora_A`` and ``lora_B`` are trainable; ``lora_A`` uses
Kaiming-uniform init and ``lora_B`` is zero-initialized so the
layer's output at step 0 equals the base layer's output exactly
โ fine-tuning starts from the pretrained behavior and diverges
only as B picks up gradient.
The wrapper preserves the base layer's ``in_features`` /
``out_features``, so consumers that read ``base.weight.shape`` or
pass tensors through the layer don't change.
``lora_A`` / ``lora_B`` are allocated with the base weight's dtype
and device, so convert or move the base *before* wrapping and the
adapter composes immediately (a meta-device base yields meta
adapters). The base is never moved, recast or replaced.
The wrapper and its dropout inherit the base layer's train/eval mode
at construction, so wrapping an eval layer keeps inference
deterministic; a later ``.train()`` / ``.eval()`` switches them as
usual. Modes are runtime state and never serialized.
property nnx.peft.lora.LoRALinear.in_featuresNo public description is currently available.
property nnx.peft.lora.LoRALinear.out_featuresNo public description is currently available.
nnx.peft.lora.LoRALinear.forward(self, x: 'torch.Tensor') -> 'torch.Tensor'No public description is currently available.
nnx.peft.lora.LoRALinear.extra_repr(self) -> 'str'No public description is currently available.
nnx.peft.lora.apply_lora_to(module: 'nn.Module', *name_patterns: 'str', r: 'int' = 8, alpha: 'float' = 16.0, dropout: 'float' = 0.0) -> 'int'Wrap every nn.Linear submodule whose dotted name matches any of name_patterns with a LoRALinear. Returns the number of layers wrapped.
Details
Patterns use shell-style globs (``fnmatch``) against the dotted
submodule name โ e.g., ``"layers.0"``, ``"encoder.*"``, ``"*"`` for
every Linear. Every registration path is considered, including the
non-first path through a shared container.
**Shared modules:** a Linear inside a container that is
registered under several names has one registration slot; it is
wrapped once and every path sees the same wrapper. A Linear itself
registered under more than one name (``m.a = m.b = linear``) cannot be
wrapped without splitting it into independent layers, so selecting
any of its names raises ``ValueError`` naming every alias โ after the
whole matched set is validated and before any wrapper is built, so a
rejected call modifies nothing. Tied tensors between distinct modules
are not aliases and are outside this check โ but wrapping one of those
modules freezes the shared tensor for both (wrapping a tied output
head also freezes the tied token embedding).
The wrap is in-place: each matched layer is removed from its parent
and replaced with a :class:`LoRALinear` wrapping it. The base
layer's parameters end up frozen as a side effect of LoRALinear's
construction; the LoRA parameters (``lora_A`` / ``lora_B``) are
trainable by default.
Args:
module: root module to walk. The function mutates ``module``
in place.
name_patterns: at least one fnmatch glob. Empty raises.
r: LoRA rank โ passed through to :class:`LoRALinear`.
alpha: LoRA scaling numerator โ passed through.
dropout: dropout on the LoRA path โ passed through.
Returns:
The count of layers wrapped โ unique registration slots, so a
shared container's Linear counts once (may be 0 if no patterns
match).
Raises:
ValueError: if ``name_patterns`` is empty, if a selected Linear
is registered under more than one name, or if its parent reads
its weight directly (``nn.MultiheadAttention.out_proj``, a torch
Transformer layer's ``linear1`` / ``linear2``); nothing is
modified.
**Idempotency note:** if a layer is already a :class:`LoRALinear`,
its inner ``.base`` is skipped โ re-applying ``apply_lora_to``
against the same patterns is a no-op for layers that already
carry a LoRA wrapper. The function returns the count of NEW wraps.
nnx.peft.lora.save_lora_weights(module: 'nn.Module', path: 'Union[str, Path]') -> 'str'Save ONLY the LoRA parameters of module to path.
Details
The output is a plain ``torch.save`` of a dict-subset of the full
state_dict, containing exactly the ``lora_A`` / ``lora_B`` tensors
owned by the :class:`LoRALinear` wrappers in ``module`` โ selected by
registered ownership, not by key substring, so a module *named*
``lora_A_projection`` never leaks its frozen base weights. Loadable
via :func:`load_lora_weights`. An adapter-only export records no
transformation recipe (``nnx.transforms``): it cannot rebuild the
wrapped topology alone โ apply the same adapters (or materialize the
recipe) first.
Args:
module: any module that has been processed by
:func:`apply_lora_to`. If no LoRA params exist, an empty
dict is saved (the caller decides whether that's an error).
path: destination file path.
Returns:
The path written (so calls can be chained).
nnx.peft.lora.load_lora_weights(module: 'nn.Module', source: 'Union[str, Path, dict]') -> 'int'Load LoRA parameters into module from source.
Details
Args:
module: must already have :class:`LoRALinear` wrappers in the
same positions as the source โ apply_lora_to FIRST, then
call this. Otherwise the keys won't match and 0 params load.
source: either a path to a file produced by
:func:`save_lora_weights`, or a state-dict dict directly.
Returns:
The number of parameter tensors loaded.
Loads via ``module.load_state_dict(..., strict=False)`` so the
base layer's frozen weights โ which are NOT in the LoRA-only
checkpoint โ don't trigger a missing-keys error. The source is
first restricted to the keys ``module``'s own LoRA wrappers register
(derived from the destination, not from the source's key names), so
a full checkpoint โ or one carrying colliding ``*.base.*`` keys โ
can never overwrite a frozen base tensor. A wrong-shaped owned key
raises a size-mismatch ``RuntimeError`` before anything is copied.
class nnx.peft.dora.DoRALinear(base: 'nn.Linear', *, r: 'int' = 8, alpha: 'float' = 16.0, dropout: 'float' = 0.0)Linear layer wrapped with a DoRA weight decomposition.
Details
Subclasses :class:`LoRALinear` to inherit the frozen-base + trainable
low-rank residual machinery (``lora_A``, ``lora_B``, alpha/r scaling,
optional dropout, base-freeze-on-construction). Adds a trainable
per-output-row ``magnitude`` parameter (shape: ``out_features``)
initialized from the column-wise L2 norm of the base weight.
The forward composes the LoRA residual into a combined weight
``V = W_0 + (ฮฑ/r) ยท BA``, normalizes ``V`` row-wise, then re-scales
by the trainable magnitude:
``W = magnitude.unsqueeze(1) * V / ||V||_c``
``y = W ยท x + b``
At step 0, ``B`` is zero-initialized (inherited from LoRALinear)
so ``V = W_0`` and ``||V||_c == magnitude``, giving ``W == W_0``
exactly โ fine-tuning starts from the pretrained behavior.
The row normalization is carried out in FP32 for FP16/BF16 layers
(FP64 stays FP64), so a zero or tiny row of ``V`` โ from pruning,
explicit zero initialization or a zero row in a loaded base โ yields
a finite, zero output equal to the base instead of NaN, and learned
nonzero updates stay differentiable. The effective weight is cast
back to the layer dtype before the matmul, so the output dtype is
unchanged. Persist a DoRA adapter with the full ``state_dict()``:
the LoRA-only helpers omit ``magnitude``. Like :class:`LoRALinear`,
the wrapper and its update dropout inherit the base layer's
train/eval mode at construction.
Args:
base: the :class:`nn.Linear` to wrap. Its parameters are frozen
on construction (inherited from LoRALinear).
r: low-rank dim for the LoRA residual. Must be positive.
alpha: scaling numerator. Effective LoRA scale is ``alpha / r``.
dropout: dropout on the LoRA update path. Range ``[0, 1)``.
nnx.peft.dora.DoRALinear.forward(self, x: 'torch.Tensor') -> 'torch.Tensor'No public description is currently available.
nnx.peft.dora.DoRALinear.extra_repr(self) -> 'str'No public description is currently available.
nnx.peft.dora.apply_dora_to(module: 'nn.Module', *name_patterns: 'str', r: 'int' = 8, alpha: 'float' = 16.0, dropout: 'float' = 0.0) -> 'int'Wrap every nn.Linear submodule whose dotted name matches any of name_patterns with a DoRALinear. Returns the number of layers wrapped.
Details
Mirrors :func:`nnx.peft.apply_lora_to` โ same fnmatch glob conventions,
same two-phase (collect-then-mutate) traversal, same idempotency
contract (existing DoRA/LoRA wrappers are not re-wrapped โ the
parent-is-LoRALinear check covers DoRALinear by inheritance).
Args:
module: root module to walk. Mutated in place.
name_patterns: at least one fnmatch glob.
r: LoRA rank โ passed through.
alpha: LoRA scaling numerator โ passed through.
dropout: dropout on the LoRA update path โ passed through.
Returns:
The count of layers wrapped (may be 0 if no patterns match
or every match is already wrapped).
Raises:
ValueError: if ``name_patterns`` is empty, if a selected Linear
is read directly by its parent (``nn.MultiheadAttention.out_proj``,
a torch Transformer layer's ``linear1`` / ``linear2``), or if it
is registered under more than one name โ the whole matched set
is validated before any wrapper is built, so nothing is
modified. A Linear inside a shared container is one slot and is
wrapped once; tied tensors between distinct modules are outside
this check (see :func:`nnx.peft.apply_lora_to`).
TypeError: if a selected Linear is a subclass with its own
``forward`` (a QAT fake-quantized layer), which DoRA's recomposed
matmul would bypass. Every wrapper is built before any slot is
replaced, so a refused call modifies nothing.
class nnx.peft.ia3.IA3Linear(base: 'nn.Linear')Linear layer wrapped with an IA3 per-output-dim scaling vector.
Details
The original :class:`nn.Linear` lives at ``self.base`` with its
parameters frozen (``requires_grad=False``) on construction.
``scaling`` is the only trainable parameter: a length-``out_features``
vector initialized to all-ones so the layer's output at step 0
equals the base layer's output exactly.
Forward: ``y = base(x) * scaling`` (broadcast over the trailing dim).
``scaling`` takes the base weight's dtype and device, so the output
keeps the base dtype (a half base stays half) and a pre-converted or
pre-moved base composes without a second ``.to()``.
The wrapper inherits the base layer's train/eval mode at construction
(modes are runtime state and never serialized).
Args:
base: the :class:`nn.Linear` to wrap.
property nnx.peft.ia3.IA3Linear.in_featuresNo public description is currently available.
property nnx.peft.ia3.IA3Linear.out_featuresNo public description is currently available.
nnx.peft.ia3.IA3Linear.forward(self, x: 'torch.Tensor') -> 'torch.Tensor'No public description is currently available.
nnx.peft.ia3.IA3Linear.extra_repr(self) -> 'str'No public description is currently available.
nnx.peft.ia3.apply_ia3_to(module: 'nn.Module', *name_patterns: 'str') -> 'int'Wrap every nn.Linear submodule whose dotted name matches any of name_patterns with an IA3Linear. Returns the number of layers wrapped.
Details
Mirrors :func:`nnx.peft.apply_lora_to` โ same fnmatch glob conventions,
same two-phase (collect-then-mutate) traversal, same idempotency
contract (existing IA3 wrappers are skipped via the parent-is-IA3Linear
check).
Args:
module: root module to walk. Mutated in place.
name_patterns: at least one fnmatch glob.
Returns:
The count of layers wrapped (may be 0 if no patterns match
or every match is already wrapped).
Raises:
ValueError: if ``name_patterns`` is empty, if a selected Linear
is read directly by its parent (``nn.MultiheadAttention.out_proj``,
a torch Transformer layer's ``linear1`` / ``linear2``), or if it
is registered under more than one name โ the whole matched set
is validated before any wrapper is built, so nothing is
modified. A Linear inside a shared container is one slot and is
wrapped once; tied tensors between distinct modules are outside
this check (see :func:`nnx.peft.apply_lora_to`). Also raised,
before any change, when a selected Linear is the base of a
:class:`DoRALinear` (DoRA reads its base's weight directly).
nnx.peft.ia3.save_ia3_weights(module: 'nn.Module', path: 'Union[str, Path]') -> 'str'Save ONLY the IA3 scaling parameters of module to path.
Details
The output is a ``torch.save`` of a dict-subset of the full
state_dict, containing exactly the ``scaling`` tensors owned by the
:class:`IA3Linear` wrappers in ``module`` (selected by registered
ownership, not by key substring). Loadable via :func:`load_ia3_weights`.
Args:
module: any module that has been processed by
:func:`apply_ia3_to`. If no IA3 params exist, an empty
dict is saved.
path: destination file path.
Returns:
The path written (so calls can be chained).
nnx.peft.ia3.load_ia3_weights(module: 'nn.Module', source: 'Union[str, Path, dict]') -> 'int'Load IA3 scaling parameters into module from source.
Details
Args:
module: must already have :class:`IA3Linear` wrappers in the
same positions as the source โ apply_ia3_to FIRST, then
call this. Otherwise the keys won't match and 0 params load.
source: either a path to a file produced by
:func:`save_ia3_weights`, or a state-dict dict directly.
Returns:
The number of parameter tensors loaded.
Loads via ``module.load_state_dict(..., strict=False)`` so the
base layer's frozen weights โ which are NOT in the IA3-only
checkpoint โ don't trigger a missing-keys error.
class nnx.peft.prefix.PrefixTuner(model: 'TransformerNN', *, n_prefix: 'int' = 10, n_layers: 'Optional[int]' = None)Wrap a TransformerNN with learnable per-layer K/V prefixes.
Details
Freezes every parameter of the wrapped model on construction and
registers ``n_layers`` pairs of ``(n_prefix, n_heads, head_dim)``
K / V tensors as the only trainable parameters.
Args:
model: a :class:`TransformerNN` instance. Its parameters are
mutated in place (set to ``requires_grad=False``); the
attention forward of each targeted block is monkey-patched.
n_prefix: number of virtual prefix tokens per layer. Must be > 0.
n_layers: number of leading transformer blocks to attach a prefix
to. ``None`` (default) targets every block in
``model.blocks``. When set, the first ``n_layers`` blocks
are targeted; later blocks run un-prefixed.
Note on shape: the prefix uses ``n_heads`` and ``head_dim`` taken
from the model's ``params`` โ there's no per-block override, since
every block in a TransformerNN shares the same attention shape.
Each layer's prefix takes the dtype and device of that block's
``attn.w_qkv`` weight. A projection already adapted by
:class:`~nnx.peft.LoRALinear` / :class:`~nnx.peft.DoRALinear` /
:class:`~nnx.peft.IA3Linear` (``apply_*_to`` before wrapping) is
resolved to the :class:`nn.Linear` it wraps; the adapter stays in the
attention path, so its delta or scaling still shapes K/V, and its
parameters are frozen with the rest of the model.
Raises:
TypeError: if ``model`` is not a :class:`TransformerNN`.
ValueError: if ``n_prefix`` or ``n_layers`` is out of range, or
if ``model`` is already prefix-tuned โ a second tuner would
silently hijack the patched forwards (they read the MHA's
``_nnx_prefix_tuner`` ref, which the second tuner overwrites,
so the first tuner's parameters stop receiving gradients).
nnx.peft.prefix.PrefixTuner.forward(self, *args, **kwargs)Delegate to the wrapped model. The prefix injection happens inside each block's monkey-patched MHA forward.
nnx.peft.prefix.PrefixTuner.trainable_parameters(self) -> 'Iterator[nn.Parameter]'Yield only the learned prefix tensors.
Details
The wrapped model's parameters are frozen on construction; this
is the iterator you hand to an optimizer.
nnx.peft.prefix.PrefixTuner.prefix_state_dict(self) -> 'dict'Return a state-dict containing only the prefix tensors, keyed for round-trip via load_prefix_weights.
Details
The keys are the same as ``self.state_dict()`` filtered to the
prefix entries โ i.e., ``prefix_keys.0``, ``prefix_values.0``,
``prefix_keys.1``, โฆ
nnx.peft.prefix.save_prefix_weights(tuner: 'PrefixTuner', path: 'Union[str, Path]') -> 'str'Save ONLY the prefix tensors of tuner to path.
Details
Args:
tuner: a :class:`PrefixTuner` instance.
path: destination file path.
Returns:
The path written, so calls can be chained.
nnx.peft.prefix.load_prefix_weights(tuner: 'PrefixTuner', source: 'Union[str, Path, dict]') -> 'int'Load prefix tensors into tuner from source.
Details
Args:
tuner: must already have the same prefix shape as the source
(same n_prefix, n_heads, head_dim, n_layers). Otherwise
``load_state_dict`` will surface the mismatch.
source: a path to a file produced by :func:`save_prefix_weights`,
or a state-dict dict directly.
Returns:
The number of parameter tensors loaded.
class nnx.peft.prompt.PromptTuner(model: 'TransformerNN', *, n_prompt_tokens: 'int' = 20)Wrap a TransformerNN with a learnable soft prompt.
Details
Freezes every base parameter and allocates an
``(n_prompt_tokens, d_model)`` embedding tensor. The wrapper's
forward prepends the prompt to the token embeddings, runs the
stack, then trims the prompt positions off the logits before
returning.
Args:
model: a :class:`TransformerNN` instance. Its parameters are
mutated in place (set to ``requires_grad=False``).
n_prompt_tokens: number of soft-prompt slots. Must be > 0.
The soft prompt is initialized with ``nn.init.normal_(std=0.02)``
โ the same scale Lester et al. use as their "random init" baseline โ
and allocated with the dtype/device of the wrapped model's token
embedding, so convert or move the model *before* wrapping.
property nnx.peft.prompt.PromptTuner.effective_max_seq_lenWindow available for REAL tokens: the soft prompt occupies n_prompt_tokens of the wrapped model's max_seq_len slots. GenerativeNNModel.generate reads this so its sliding window never overflows the wrapped model mid-generation.
nnx.peft.prompt.PromptTuner.forward(self, tokens: 'torch.Tensor') -> 'torch.Tensor'Run the wrapped model with the soft prompt prepended.
Details
Args:
tokens: (batch, seq) long tensor of token ids.
Returns:
(batch, seq, vocab_size) logits over the REAL token
positions only. The soft-prompt positions are scaffolding
and their logits are discarded.
Raises:
ValueError: if ``seq + n_prompt_tokens`` exceeds the
wrapped model's ``max_seq_len``. The soft prompt
consumes positions in the RoPE table just like real
tokens do.
nnx.peft.prompt.PromptTuner.trainable_parameters(self) -> 'Iterator[nn.Parameter]'Yield only the soft-prompt tensor.
Details
The wrapped model's parameters are frozen on construction; this
is the iterator you hand to an optimizer.
nnx.peft.prompt.PromptTuner.prompt_state_dict(self) -> 'dict'Return a state-dict containing only the soft-prompt tensor, keyed for round-trip via load_prompt_weights. Only the tuner-owned soft_prompt entry qualifies โ never anything under model.*, even if a nested base module's name happens to contain the marker.
nnx.peft.prompt.save_prompt_weights(tuner: 'PromptTuner', path: 'Union[str, Path]') -> 'str'Save ONLY the soft-prompt tensor of tuner to path.
Details
Args:
tuner: a :class:`PromptTuner` instance.
path: destination file path.
Returns:
The path written, so calls can be chained.
nnx.peft.prompt.load_prompt_weights(tuner: 'PromptTuner', source: 'Union[str, Path, dict]') -> 'int'Load the soft-prompt tensor into tuner from source.
Details
Args:
tuner: must already have the same prompt shape as the source
(same n_prompt_tokens, d_model). A shape mismatch is
surfaced by ``load_state_dict``.
source: a path to a file produced by :func:`save_prompt_weights`,
or a state-dict dict directly.
Returns:
The number of parameter tensors loaded.
class nnx.peft.adapters.AdapterLayer(dim: 'int', bottleneck: 'int', activation: 'Callable[[], nn.Module]' = torch.nn.modules.activation.GELU)Bottleneck residual block: y = x + up(act(down(x))).
Details
``up.weight`` and ``up.bias`` are zero-initialized so at step 0
the layer's output equals its input exactly. ``up`` receives a
gradient from the first step; ``down`` receives none until ``up``
moves away from zero (its gradient flows through ``up.weight``).
Args:
dim: input and output feature dimension. The adapter is
shape-preserving.
bottleneck: hidden dimension. Typically much smaller than
``dim`` (e.g., dim=768 โ bottleneck=64 in the original
Houlsby setup). Lower bottleneck = fewer params,
potentially less expressive.
activation: ``nn.Module`` factory (called with no args inside
``__init__`` to produce the activation module). Defaults to
``torch.nn.GELU`` โ the modern adapter choice; ``nn.ReLU``
works too.
nnx.peft.adapters.AdapterLayer.forward(self, x: 'torch.Tensor') -> 'torch.Tensor'No public description is currently available.
nnx.peft.adapters.AdapterLayer.extra_repr(self) -> 'str'No public description is currently available.
nnx.prune.magnitude.magnitude_prune(net: 'nn.Module', sparsity: 'float', *, layer_pattern: 'str' = '*', bake: 'bool' = True) -> 'int'Zero the smallest-magnitude entries of every matched layer's weight.
Details
For each :class:`nn.Linear` submodule of ``net`` whose dotted name
matches ``layer_pattern`` (fnmatch glob), call
:func:`torch.nn.utils.prune.l1_unstructured` with
``amount=sparsity``. PyTorch's implementation zeros
``round(sparsity ยท weight.numel())`` entries โ the ones with the
smallest absolute value โ per layer.
Args:
net: root module to walk. The function mutates ``net`` in place.
sparsity: fraction of weights to zero, in ``[0, 1)``. ``0.0``
is a valid no-op; ``1.0`` is rejected here โ a fully-zeroed
Linear is never useful (torch itself would accept
``amount=1.0`` and silently zero the whole weight).
layer_pattern: fnmatch glob against dotted submodule name.
``"*"`` (the default) matches every :class:`nn.Linear`.
bake: when ``True`` (default), call
:func:`torch.nn.utils.prune.remove` immediately after each
layer is pruned. The mask is baked into a plain ``weight``
tensor, the reparameterization is dropped, and the
``state_dict`` keys stay identical to the pre-prune layout.
When ``False``, the reparameterization stays in place โ the
``state_dict`` carries ``<name>.weight_orig`` +
``<name>.weight_mask`` instead of ``<name>.weight``. Use
``False`` for iterative pruning schedules (e.g., 10% per
epoch for N epochs) where successive ``magnitude_prune``
calls need to compose with the existing mask. ``NNModel.train``
refuses a registered net that still carries the masks: run
such a schedule on ``NNModel(module=net)`` or a custom loop.
Returns:
The number of :class:`nn.Linear` submodules that were pruned.
``0`` if ``layer_pattern`` matched nothing.
Raises:
ValueError: if ``sparsity`` is outside ``[0, 1)``, or if
``bake=False`` selects a Linear whose parent reads its weight
directly (``nn.MultiheadAttention``'s ``out_proj``).
**Idempotency note:** with ``bake=True``, calling ``magnitude_prune``
twice at the same ``sparsity`` is a no-op for the second call โ
l1_unstructured picks the smallest-magnitude entries, which after the
first prune are exactly the already-zeroed positions. With
``bake=False`` the masks compose: a second call prunes ``sparsity`` of
the remaining weights (0.5 twice leaves 0.75 zero).
nnx.prune.semi_structured.semi_structured_24(net: 'nn.Module', *, layer_pattern: 'str' = '*') -> 'int'Swap each matched nn.Linear's weight with a 2:4 semi-structured sparse tensor via torchao.sparsity.sparsify_.
Details
Args:
net: root module to walk. The function mutates ``net`` in place.
layer_pattern: fnmatch glob against dotted submodule name.
``"*"`` (the default) matches every :class:`nn.Linear`.
Returns:
The number of :class:`nn.Linear` submodules that were swapped.
``0`` if ``layer_pattern`` matched nothing (in which case the
underlying ``torchao.sparsity.sparsify_`` is NOT invoked โ this
avoids an unnecessary torchao dispatch and the CUDA-only kernel
error on CPU runners with no Linear targets to swap).
Raises:
ImportError: if ``torchao`` isn't installed. The import happens
inside the function body so :mod:`nnx.prune` doesn't pull
torchao at package-import time; users on the magnitude-only
path pay no dep cost.
RuntimeError: surfaced from the underlying
``torch.sparse.SparseSemiStructuredTensor`` constructor on
unsupported hardware (CPU / pre-Ampere GPU) or on weights
whose inner dimension isn't a multiple of 4. The error
originates in torch / torchao; we don't intercept it.
**Pattern semantics:** same fnmatch convention as
:func:`nnx.peft.apply_lora_to` and
:func:`nnx.prune.magnitude_prune` โ dotted submodule names against
shell wildcards. Only :class:`nn.Linear` submodules are eligible
(Conv2d / BatchNorm / Embedding / etc. are skipped even under a
wildcard pattern).
**Note on weights:** ``torchao.sparsity.sparsify_`` does NOT enforce
the 2:4 mask before the swap, and the conversion encodes each group
of 4 by the *positions* of its nonzeros, not by magnitude: extra
nonzeros are dropped by position. Prune the weight to a valid 2:4
pattern first (a custom 2:4 mask; :func:`magnitude_prune` alone gives
no per-group structure).
**Inference only:** torchao replaces each matched weight with a
``requires_grad=False`` parameter, so the matched Linears stop
training after this call.
Walkthrough at Model surgery. Every primitive returns a fresh module (expand_embedding also returns a frozen-row mask) and composes with NNModel.train() for the "load checkpoint โ surgery โ refine" loop.
nnx.surgery.widen.widen(model: 'nn.Module', *, layer_name: 'str', new_width: 'int', rng_seed: 'Optional[int]' = 0) -> 'nn.Module'Net2WiderNet: grow a Linear's out_features to new_width.
Details
Returns a deep copy of ``model`` with the named layer expanded and
the downstream Linear's ``in_features`` adjusted so the overall
forward output is preserved exactly (within FP rounding).
Args:
model: any :class:`nn.Module`. The function deep-copies it so
the caller's reference survives.
layer_name: dotted name (as produced by ``named_modules()``) of
the :class:`nn.Linear` to widen. Must be a Linear whose
consumer can be *proven*: the target lives directly inside an
``nn.Sequential`` (the following siblings are walked) or in
``FeedFwdNN.layers`` (the effective per-layer activation /
dropout is resolved), and every op between it and the next
Linear is elementwise โ the built-in activations except
Softmax, ``Dropout``, ``Identity``. Anything else raises
before any allocat