Skip to content

Optimizers, losses, initializers

StableTested and in active use. Breaking changes will be called out.

from noeira.nn.optimizer.adam import Adam
from noeira.nn.optimizer.sgd import SGD
OptimizerNotes
SGDwith momentum and weight decay
Adamthe default throughout the RL agents
AdamWdecoupled weight decay
ScalarAdamfor scalar parameters — SAC’s learned temperature
DreamerOptDreamerV3’s optimizer configuration

An optimizer is a ParamVisitor: it walks the module’s parameters and updates each in place. It carries no per-parameter state of its own, because Adam’s moments live on the Param itself.

var opt = Adam(lr=Scalar[DT](3e-4))

On NVIDIA, Adam applies as a grouped multi-tensor operation — one kernel across many parameters rather than one launch per tensor, which matters when a network has dozens of small parameter blocks.

from noeira.nn.optimizer.grad_clip import clip_grad_norm
from noeira.nn.optimizer.lr_scheduler import LRScheduler

Global gradient-norm clipping is standard in this codebase — PPO uses 0.5, MuZero 10.0. It is a driver-call argument, not an optimizer field:

_ = agent.train(..., max_grad_norm=0.5)

lr_scheduler and schedules provide warmup and decay.

from noeira.nn.loss.cross_entropy import CrossEntropyLoss
from noeira.nn.loss.mse_loss import MSELoss
from noeira.nn.loss.two_hot import TwoHot
LossUsed for
CrossEntropyLossclassification
SequenceCrossEntropylanguage modelling
SoftCrossEntropysoft targets — distillation, MCTS policy targets
MSELoss · MSEregression, value heads
MSEPerSampleper-sample loss, for prioritized-replay weights
GaussianNLLLossprobabilistic dynamics models — MBPO
TwoHotdistributional value/reward heads — DreamerV3, TD-MPC2

TwoHot deserves a note. It encodes a scalar as a distribution over two adjacent bins, so a value head predicts a categorical distribution rather than a point estimate — markedly more stable for value learning.

Huber loss is available through the loss helpers used by the DQN family.

from noeira.nn.core.initializer import Kaiming, Xavier, Zero, Normal
InitializerUse
KaimingReLU-family networks — the default
ScaledKaimingKaiming with an explicit gain
Xaviertanh/sigmoid networks
TruncNormalIntruncated normal, σ = √(1/fan_in) — DreamerV3 reference
Normalplain Gaussian
Zerozero — reference output-layer init for reward/value heads
Deterministicfixed values, for tests

The initializer is a compile-time parameter to make, threaded down through combinators to every leaf:

var trainer = Trainer[Net, NC, IN_DIM, BATCH, "gpu"].make[Kaiming](
Optional(ctx), lr=1e-3
)

Some layers carry their own initialization concerns — PPO’s Gaussian head takes a log_std_init, and the trainer leaves setting it to the caller because Mojo nightly cannot reflect into Sequential’s variadic children generically:

agent.trainer.actor.children[4].set_log_std_init["cpu"](LOG_STD_INIT)