Skip to content

Quickstart

An end-to-end CPU example you can paste into a Python REPL. Trains a tiny feed-forward classifier on random data so you can verify the install in under five seconds.

1. Minimal example

import torch
from torch.utils.data import DataLoader, TensorDataset

from nnx import (
    NNModel, NNParams, NNModelParams, NNTrainParams,
    NNOptimParams, NNSchedulerParams,
    Activations, Devices, Losses, Nets, Optims,
    EarlyStopping,
)

# 1. Data
X_train, y_train = torch.randn(256, 8), torch.randint(0, 3, (256,))
X_val,   y_val   = torch.randn(64,  8), torch.randint(0, 3, (64,))
train_loader = DataLoader(TensorDataset(X_train, y_train), batch_size=32, shuffle=True)
val_loader   = DataLoader(TensorDataset(X_val,   y_val),   batch_size=32)

# 2. Model
net_params   = NNParams(input_dim=8, output_dim=3, hidden_dims=[32, 16],
                        dropout_prob=0.1, activation=Activations.RELU)
model_params = NNModelParams(net=Nets.FEED_FWD, device=Devices.CPU,
                             loss=Losses.CROSS_ENTROPY)
model = NNModel(net_params=net_params, params=model_params)

# 3. Train
train_params = NNTrainParams(
    n_epochs=10,
    seed=42,                       # reproducibility
    train_loader=train_loader,
    val_loader=val_loader,
    optim=NNOptimParams(name=Optims.ADAM, max_lr=1e-2,
                        momentum=(0.9, 0.999), weight_decay=5e-5,
                        grad_clip_norm=1.0),
    scheduler=NNSchedulerParams(min_lr=1e-7, factor=0.5,
                                patience=3, cooldown=1, threshold=1e-3),
)
run = model.train(params=train_params, callbacks=[EarlyStopping(patience=5)])

# 4. Use it
print(f"trained {len(run.idps)} iterations; saved under runs/{run.id}/")
result = model.predict(X=X_val)
print(f"predicted {len(result.classes)} samples")

2. Common variations

2.1. GPU / Apple Silicon

from nnx import Devices
NNModelParams(net=Nets.FEED_FWD, device=Devices.get(), loss=Losses.CROSS_ENTROPY)
# Devices.get() picks MPS > CUDA > CPU.

2.2. Mixed precision (CUDA)

NNModelParams(..., mixed_precision=True)   # silently no-op on CPU/MPS

2.3. Warm-resume training

# Train round 1
run = model.train(params=NNTrainParams(n_epochs=10, ...))

# Train round 2 — pick up from where the last run's LAST checkpoint left off.
model.train(params=NNTrainParams(
    n_epochs=10,
    resume_from_run_id=run.id,
    resume_from_checkpoint="last",   # or "best"
    ...
))

2.4. Loading a finished run

from nnx import NNRun, NNCheckpoint, Checkpoints, NNModel

run  = NNRun.load(id="<md5>")                              # rehydrate idps + params
ckpt = NNCheckpoint.load(run=run.id, type=Checkpoints.BEST)
model = NNModel.from_checkpoint(checkpoint=ckpt)

2.5. Custom metrics

from sklearn.metrics import roc_auc_score

NNTrainParams(
    ...,
    extra_metrics={
        "roc_auc": lambda y, y_hat: float(roc_auc_score(y, y_hat, multi_class="ovr")),
    },
)
# Every NNEvaluationDataPoint gets `.extra["roc_auc"]` populated.

2.6. Silencing the progress bar (CI / non-TTY)

The training loop draws a tqdm progress bar by default. Set NNX_TQDM_DISABLE=1 in the environment to silence it — useful in CI, in non-TTY contexts, and in test suites:

NNX_TQDM_DISABLE=1 python your_train_script.py

NNX_TQDM_DISABLE is read by both NNModel.train() and Trainer.train(). Any value of 1 / true / yes (case-insensitive) disables the bar; anything else leaves it enabled.

2.7. TensorBoard

pip install thekaveh-nnx[tensorboard]
from nnx import TensorBoardCallback
model.train(params=..., callbacks=[TensorBoardCallback(log_dir="tb_logs")])

2.8. LR finder pre-flight

Before a long training run, sweep learning rates exponentially and let the Smith-2017 steepest-descent heuristic pick a defensible max_lr for the real run. The sweep is non-destructive — model weights and the training-mode flag are snapshotted and restored on exit.

import torch.nn.functional as F
from nnx import lr_finder

result = lr_finder(
    model.net, train_loader,
    loss_fn=F.cross_entropy,
    start_lr=1e-7, end_lr=10.0, num_iter=100,
)
print(f"Suggested max_lr: {result.suggested_lr:.2e}")
result.figure.show()  # Plotly: loss vs log(LR) with the suggestion marked

# Plug into the real training run:
NNTrainParams(..., optim=NNOptimParams(name=Optims.ADAM, max_lr=result.suggested_lr, ...))

See Concepts → LR finder for the algorithm details and divergence early-exit behavior.

3. Beyond supervised classification

For tasks where loss isn't loss_fn(net(X), Y) — autoencoder reconstruction, VAE composite loss, link prediction with negative sampling, recommendation pairwise loss, diffusion noise prediction — pass train_step_fn= to train(). See Concepts → Custom training paradigms.

The same hook underpins the four specialization-paradigm pointers below.

3.1. Fine-tuning (transfer learning)

Load external pretrained weights, freeze layers by glob pattern, and (optionally) train them at different learning rates. See Concepts → Fine-tuning and examples/06_finetune_with_layer_freezing.py.

3.2. Multi-optimizer training (GANs, actor-critic)

When per-batch updates need multiple optimizers (G + D for GANs, policy + value for actor-critic), use nnx.trainer.Trainer — accepts one NNModel and a dict of NNOptimParams, scoped via NNParamGroupSpec globs. See Concepts → Multi-optimizer training and examples/09_gan_with_trainer.py.

3.3. Diffusion (DDPM)

For DDPM-style diffusion: nnx.diffusion.{NoiseSchedulers, DiffusionMLP, diffusion_train_step_factory, sample}. The training step is a train_step_fn on NNModel.train() — no Trainer, no new params dataclass. See Concepts → Diffusion and examples/08_diffusion_2d_mixture.py.

3.4. Training paradigms (KD, SimCLR, Mixup, CutMix)

nnx.paradigms.{kd, simclr, mixup, cutmix}_train_step_factory return train_step_fns for NNModel.train(). Knowledge distillation freezes the teacher and mixes soft/hard losses; SimCLR runs NT-Xent on paired-view batches; Mixup / CutMix interpolate samples within a batch. See Concepts → Training paradigms and examples/10_knowledge_distillation.py.

3.5. Parameter-efficient fine-tuning (LoRA, DoRA, IA3, Prefix, Prompt, Adapters)

nnx.peft.{LoRALinear, apply_lora_to, save_lora_weights, load_lora_weights, AdapterLayer} plus DoRA / IA3 / PrefixTuner / PromptTuner. LoRA wraps nn.Linear submodules with a frozen base + trainable low-rank residual; DoRA layers in a per-output magnitude vector; IA3 is a per-output scaling; PrefixTuner / PromptTuner attach learned prefixes to a frozen TransformerNN; AdapterLayer is a bottleneck residual the user inserts manually. See Concepts → Parameter-efficient fine-tuning and examples/07_lora_finetuning.py.