pip install flax # brings in jax; add optax★Install JAX for your accelerator separately (jax[cuda12]).from flax import nnx.class MLP(nnx.Module): def __init__(self, din, dh, dout, *, rngs: nnx.Rngs): self.l1 = nnx.Linear(din, dh, rngs=rngs) self.l2 = nnx.Linear(dh, dout, rngs=rngs)★Create sublayers in__init__and store them as attributes — just like PyTorch. Pass annnx.Rngsfor init randomness.def __call__(self, x): return self.l2(nnx.relu(self.l1(x)))★Define the forward in__call__. No@nn.compact, no separate apply — it's a normal method.model = MLP(2, 16, 3, rngs=nnx.Rngs(0)) y = model(x) # eager call, weights already initialized★Instantiation runs initialization (unlike Linen's lazyinit). Then call the model directly.
nnx.Linear(din, dout, rngs=rngs) · nnx.Conv(...) · nnx.Embed(...)★Core layers all takerngs=. Weight init viakernel_init=,bias_init=.nnx.BatchNorm(dim, rngs=rngs) · nnx.LayerNorm(dim) · nnx.Dropout(0.1, rngs=rngs)★Stateful/stochastic layers — they hold running stats / need RNG, handled by the module system.nnx.MultiHeadAttention(...) · nnx.relu / gelu / softmax (via jax.nn)Attention block plus the usual activations (nnxre-exportsjax.nnfunctions).nnx.Sequential(nnx.Linear(...), nnx.relu, nnx.Linear(...))Quick sequential container; or write a custom Module for anything non-linear.
self.w = nnx.Param(jax.random.normal(key, (din, dout)))★Learnable weights arennx.Param. Non-trainable state usesnnx.Variablesubclasses (e.g.nnx.BatchStat).nnx.state(model) · nnx.state(model, nnx.Param)★Extract the model's state (all variables, or filtered by type) as a pytree — for saving, counting, or transforms.model.train() · model.eval()★Toggle Dropout/BatchNorm behavior — same idiom as PyTorch. Sets the deterministic/use-running-stats flags.nnx.display(model) · nnx.tabulate(model, x)Inspect structure & parameter shapes/counts.