mojolearn.training#
Optimizers, schedules, losses and training primitives over float32 arrays.
These functions work on explicit float32 buffers: mojolearn.Array, NumPy arrays,
or any buffer-protocol object. NumPy is not required.
SGD, Adam and AdamW update the parameter arrays you pass in place, from gradients
you supply in the same order. ConstantLR, WarmupLinearLR and WarmupCosineLR give
the learning rate of each step. clip_grad_norm_ scales gradients in place (L2 norm
only). cross_entropy takes float32 logits and integer class targets and can return
the logits gradient in the same call. embedding_forward, rms_norm_forward,
linear_forward, chunked_lm_head_loss and their backward functions are the layer
operations of a small language-model stack, and Generator draws reproducible
initializations and dropout masks.
These are building blocks, not an autograd framework or a drop-in replacement for
PyTorch: you compute and pass the gradients. SmallMLPTrainer and the byte language
model trainers provide complete training loops for their fixed model shapes.
numeric_mode selects the compiled tier where a function takes it. No compiled
extension is loaded until a function or optimizer step needs it.
Examples
>>> import numpy as np
>>> from mojolearn.training import AdamW, WarmupCosineLR
>>> w = np.zeros((64, 8), dtype=np.float32)
>>> opt = AdamW([w], lr=3e-4, weight_decay=0.01,
... lr_schedule=WarmupCosineLR(3e-4, warmup_steps=10, total_steps=100))
>>> opt.step([np.ones_like(w)])
Functions
|
Clip gradients by their global L2 norm, as |
|
Cross-entropy loss over class indices, as |
|
Return the numeric mode the training binding will run in. |
|
Return the accelerator API of the training binding: 'metal', 'cuda' or 'hip'. |
|
Combine microbatch gradients into one, on the device. |
|
Whether a split into microbatches gives exactly the unsplit gradient. |
|
Embedding lookup: |
|
Gradient of |
|
RMS normalization of each row: |
|
Gradients |
|
Linear layer without bias: |
|
Gradients |
|
Mean cross-entropy of a language-model head without materializing the logits. |
|
Run several independent batches of rows through one Linear-ReLU-Linear model. |
|
Return |
|
Return |
Classes
|
Stochastic gradient descent, as |
|
Adam optimizer, as |
|
AdamW optimizer, as |
|
Constant learning rate, after an optional linear warmup. |
|
Linear warmup, then linear decay to |
|
Linear warmup, then cosine decay to |
|
Seeded random stream for neural-network initialization and dropout. |
|
Shape of a |
|
A Samba-style language model with its own training loop. |
- class mojolearn.training.SGD(params, lr=0.001, momentum=0.0, dampening=0.0, weight_decay=0.0, nesterov=False, lr_schedule=None, accumulation_steps=1, maximize=False, resident=None, **kwargs)[source]#
Bases:
_OptimizerStochastic gradient descent, as
torch.optim.SGD, over float32 buffers (NumPy arrays,array.arrayormojolearn.Array; any writable buffer-protocol object).Supports momentum, dampening, Nesterov momentum and weight decay. Weight decay is coupled, as in torch: it is added to the gradient and so passes through the momentum buffer. Parameters are updated in place.
- Parameters:
params (buffer or list of buffers) – float32 parameter arrays, updated in place.
lr (float, default 1e-3) – Learning rate. torch’s SGD has no default; this one is 1e-3.
momentum (float, default 0.0) – Momentum factor.
dampening (float, default 0.0) – Dampening of the momentum.
weight_decay (float, default 0.0) – L2 penalty, added to the gradient.
nesterov (bool, default False) – Nesterov momentum. Needs
momentum > 0anddampening == 0, otherwiseValueError(as torch).lr_schedule (object or None, default None) – A schedule with a one-based
lr_at(t)method; it setslrbefore every step. Anything withoutlr_atraisesTypeError.accumulation_steps (int, default 1) – Number of microbatches combined by
step_accumulated; a power of two >= 1, otherwiseValueError.maximize (bool, default False) – Maximize the objective (the gradient’s sign is flipped inside the step; the caller’s gradient arrays are not negated). Must be a bool.
resident (bool or None, default None) – Keep the optimizer state on the device between steps when the install supports it (None or True). False keeps it on the host, which suits an optimizer that is rebuilt every step.
Notes
step(grads, max_norm=None)takes one gradient per parameter, matchingparamsin count, shape and order. Withmax_normset, the global L2 gradient norm is clipped first: the caller’s gradient arrays are scaled in place (astorch.nn.utils.clip_grad_norm_) andstepreturns the pre-clip total norm; without it,stepreturns None and the gradients are untouched. With clipping on, the order ofparamsis part of the result, because it fixes the summation order of the norm.lris read at every step, so you can assignopt.lrbetween steps, or pass anlr_schedule(ConstantLR,WarmupLinearLR,WarmupCosineLR, or any object with a one-basedlr_at(t)).step_accumulated(microbatch_grads, tokens)combinesaccumulation_stepsmicrobatch gradient lists before one step.zero_grad(grads)writes +0.0 into the given gradient arrays.state_dict()/load_state_dict()save and restore the step count and the optimizer state.Refused with
TypeError:foreach,fused,capturable,differentiable, parameter groups, sparse gradients, float16/bfloat16 and float64 buffers, and any unknown keyword.Examples
>>> import numpy as np, mojolearn >>> w = np.zeros((64, 8), dtype=np.float32) >>> opt = mojolearn.SGD([w], lr=1e-2, momentum=0.9) >>> g = np.ones_like(w) >>> norm = opt.step([g], max_norm=1.0)
- class mojolearn.training.Adam(params, lr=0.001, betas=(0.9, 0.999), eps=1e-08, weight_decay=0.0, lr_schedule=None, accumulation_steps=1, maximize=False, resident=None, **kwargs)[source]#
Bases:
_OptimizerAdam optimizer, as
torch.optim.Adam, over float32 buffers (NumPy arrays,array.arrayormojolearn.Array; any writable buffer-protocol object).Weight decay is coupled, as in torch’s Adam: it is added to the gradient and so passes through both moments.
AdamWis the decoupled form; atweight_decay=0the two compute the same thing. The bias corrections are computed in float32, not in torch’s float64, so results are close to torch’s but not bit-equal to them. Parameters are updated in place.- Parameters:
params (buffer or list of buffers) – float32 parameter arrays, updated in place.
lr (float, default 1e-3) – Learning rate.
betas (tuple of float, default (0.9, 0.999)) – Decay rates of the first and second moments, each in [0, 1); 1.0 is refused.
eps (float, default 1e-8) – Added to
sqrt(v)in the denominator (sqrt(v) + eps).weight_decay (float, default 0.0) – L2 penalty, added to the gradient.
lr_schedule (object or None, default None) – A schedule with a one-based
lr_at(t)method; it setslrbefore every step. Anything withoutlr_atraisesTypeError.accumulation_steps (int, default 1) – Number of microbatches combined by
step_accumulated; a power of two >= 1, otherwiseValueError.maximize (bool, default False) – Maximize the objective (the gradient’s sign is flipped inside the step; the caller’s gradient arrays are not negated). Must be a bool.
resident (bool or None, default None) – Keep the optimizer state on the device between steps when the install supports it (None or True). False keeps it on the host, which suits an optimizer that is rebuilt every step.
- Variables:
t (int) – Number of steps taken.
exp_avg (array) – First moment, flat over all parameters.
exp_avg_sq (array) – Second moment, flat over all parameters.
Notes
step(grads, max_norm=None)takes one gradient per parameter, matchingparamsin count, shape and order. Withmax_normset, the global L2 gradient norm is clipped first: the caller’s gradient arrays are scaled in place (astorch.nn.utils.clip_grad_norm_) andstepreturns the pre-clip total norm; without it,stepreturns None and the gradients are untouched. With clipping on, the order ofparamsis part of the result, because it fixes the summation order of the norm.lris read at every step, so you can assignopt.lrbetween steps, or pass anlr_schedule(ConstantLR,WarmupLinearLR,WarmupCosineLR, or any object with a one-basedlr_at(t)).step_accumulated(microbatch_grads, tokens)combinesaccumulation_stepsmicrobatch gradient lists before one step.zero_grad(grads)writes +0.0 into the given gradient arrays.state_dict()/load_state_dict()save and restore the step count and the optimizer state.Refused with
TypeError:foreach,fused,capturable,differentiable, parameter groups, sparse gradients, float16/bfloat16 and float64 buffers, and any unknown keyword.amsgradis not supported and raisesTypeError.Examples
>>> import numpy as np, mojolearn >>> w = np.zeros((64, 8), dtype=np.float32) >>> opt = mojolearn.Adam([w], lr=1e-3) >>> opt.step([np.ones_like(w)])
- class mojolearn.training.AdamW(params, lr=0.001, betas=(0.9, 0.999), eps=1e-08, weight_decay=0.01, lr_schedule=None, accumulation_steps=1, maximize=False, resident=None, **kwargs)[source]#
Bases:
AdamAdamW optimizer, as
torch.optim.AdamW, over float32 buffers (NumPy arrays,array.arrayormojolearn.Array; any writable buffer-protocol object).Identical to
Adamexcept that weight decay is decoupled: the parameter is multiplied by1 - lr * weight_decayand the gradient is left untouched, so the decay does not pass through the moments. Parameters, attributes and refusals areAdam’s.Note that the default
weight_decayhere is 0.0, while torch’s AdamW default is 0.01; at 0.0 AdamW and Adam compute the same thing. Passweight_decay=0.01to match torch’s default.Examples
>>> import numpy as np, mojolearn >>> w = np.zeros((64, 8), dtype=np.float32) >>> opt = mojolearn.AdamW([w], lr=1e-3, weight_decay=0.01) >>> opt.step([np.ones_like(w)])
- mojolearn.training.clip_grad_norm_(grads, max_norm, norm_type=2.0, error_if_nonfinite=True, numeric_mode=None, **kwargs)[source]#
Clip gradients by their global L2 norm, as
torch.nn.utils.clip_grad_norm_.Works over float32 buffers (NumPy arrays,
array.arrayormojolearn.Array; any writable buffer-protocol object). Computes a norm per tensor, then the norm of those norms, and scales every gradient in place bymax_norm / totalwhen the total exceedsmax_norm. Returns the pre-clip total norm, as torch does. The order of the tensors is part of the result, because it fixes the summation order.- Parameters:
grads (buffer or list of buffers) – float32 gradient arrays, scaled in place.
max_norm (float) – Maximum total norm; must be > 0, otherwise
ValueError.norm_type (float, default 2.0) – Only 2.0 is supported; any other value (including inf) raises
NotImplementedError.error_if_nonfinite (bool, default True) – Only True is supported (False raises
NotImplementedError): a non-finite total norm always raises.numeric_mode ({‘identical’, ‘deterministic’, ‘fast’} or None, default None) – Numeric mode. None uses the library default.
- Returns:
total_norm (float) – The total gradient norm before clipping.
Notes
Not supported:
foreach, per-tensor norms in the result, float16/bfloat16 and float64. Each tensor costs one device synchronization, so many small tensors are slower than a few large ones.Examples
>>> import numpy as np, mojolearn >>> g1 = np.ones((64, 8), dtype=np.float32); g2 = np.ones(8, dtype=np.float32) >>> total = mojolearn.clip_grad_norm_([g1, g2], max_norm=1.0)
- mojolearn.training.cross_entropy(logits, targets, ignore_index=-100, reduction='mean', label_smoothing=0.0, num_items=None, return_grad=False, numeric_mode=None, **kwargs)[source]#
Cross-entropy loss over class indices, as
torch.nn.functional.cross_entropy.logitsis (N, V) float32 andtargetsis (N,) int32 or int64 holding a class index orignore_index. Returns a float forreduction='mean'or'sum'and an (N,) float32 array for'none'. Withreturn_grad=Truethe forward and the gradient with respect to the logits come from one call:(loss, dlogits),dlogitsof shape (N, V).- Parameters:
logits (buffer, shape (N, V)) – float32 logits, row-major.
targets (buffer, shape (N,)) – Integer class indices; a value that is neither a valid class nor
ignore_indexraises an error naming its row.ignore_index (int, default -100) – Targets equal to this are ignored, even when it is a valid class index (as torch).
reduction ({‘mean’, ‘sum’, ‘none’}, default ‘mean’) – Reduction over rows.
'none'has no gradient:return_grad=Truewith it is refused.label_smoothing (float, default 0.0) – Label smoothing in [0, 1).
num_items (int or None, default None) – Divisor of the mean, as Hugging Face’s
num_items_in_batch. None uses the number of non-ignored targets.return_grad (bool, default False) – Also return the gradient with respect to
logits.numeric_mode ({‘identical’, ‘deterministic’, ‘fast’} or None, default None) – Numeric mode. None uses the library default.
- Returns:
loss (float or ndarray) – The loss; (N,) per-row values under
reduction='none'.dlogits (array, shape (N, V)) – Only with
return_grad=True.
Notes
Refused: per-class
weight, the deprecatedsize_averageandreduce, class-probability (soft) targets, float16/bfloat16 and float64. Memory use is several (N, V) float32 buffers; for a large vocabulary, split the rows into chunks (withreduction='none'the chunked result equals the unsplit one).Examples
>>> import numpy as np, mojolearn >>> logits = np.random.default_rng(0).random((32, 10), dtype=np.float32) >>> targets = np.arange(32, dtype=np.int64) % 10 >>> loss, dlogits = mojolearn.cross_entropy(logits, targets, return_grad=True)
- mojolearn.training.numeric_mode_used(numeric_mode=None)[source]#
Return the numeric mode the training binding will run in.
Read from the compiled binding itself, not from the requested string.
- Parameters:
numeric_mode ({‘identical’, ‘deterministic’, ‘fast’} or None, default None) – Numeric mode. None uses the library default.
- Returns:
str
- mojolearn.training.vendor_used(numeric_mode=None)[source]#
Return the accelerator API of the training binding: ‘metal’, ‘cuda’ or ‘hip’.
Read from the binding’s compile-time constant, not from the platform.
- Parameters:
numeric_mode ({‘identical’, ‘deterministic’, ‘fast’} or None, default None) – Numeric mode. None uses the library default.
- Returns:
str
- class mojolearn.training.ConstantLR(peak_lr, warmup_steps=0, total_steps=None, min_lr=0.0)[source]#
Bases:
_ScheduleConstant learning rate, after an optional linear warmup.
Returns
peak_lrat every step afterwarmup_stepssteps of linear warmup starting frompeak_lr / warmup_steps.total_stepsandmin_lrare accepted and ignored.- Parameters:
peak_lr (float) – Learning rate after warmup; finite and >= 0 (rounded to float32).
warmup_steps (int, default 0) – Steps of linear warmup; >= 0.
total_steps (int or None, default None) – Step at which the decay reaches
min_lr; >= max(1, warmup_steps).min_lr (float, default 0.0) – Final learning rate; finite and >= 0.
Notes
lr_at(t)takes the optimizer’s one-based step:lr_at(1)is the first step’s rate. Each value is the float32 nearest the exact expression, so a schedule gives the same bits on every machine.config()returns the schedule’s settings as a dict, whichfrom_configturns back into a schedule. Invalid arguments raiseValueError. Pass a schedule aslr_schedule=toSGD,AdamorAdamW.Examples
>>> import mojolearn >>> sched = mojolearn.ConstantLR(1e-3, warmup_steps=100) >>> lr = sched.lr_at(1)
- class mojolearn.training.WarmupLinearLR(peak_lr, warmup_steps=0, total_steps=None, min_lr=0.0)[source]#
Bases:
_ScheduleLinear warmup, then linear decay to
min_lr.Rises linearly over
warmup_steps, falls in a straight line frompeak_lrtomin_lrattotal_steps, then stays atmin_lr.- Parameters:
peak_lr (float) – Learning rate after warmup; finite and >= 0 (rounded to float32).
warmup_steps (int, default 0) – Steps of linear warmup; >= 0.
total_steps (int or None, default None) – Step at which the decay reaches
min_lr; >= max(1, warmup_steps).min_lr (float, default 0.0) – Final learning rate; finite and >= 0.
Notes
lr_at(t)takes the optimizer’s one-based step:lr_at(1)is the first step’s rate. Each value is the float32 nearest the exact expression, so a schedule gives the same bits on every machine.config()returns the schedule’s settings as a dict, whichfrom_configturns back into a schedule. Invalid arguments raiseValueError. Pass a schedule aslr_schedule=toSGD,AdamorAdamW.Examples
>>> import mojolearn >>> sched = mojolearn.WarmupLinearLR(1e-3, warmup_steps=100, total_steps=1000) >>> lr = sched.lr_at(500)
- class mojolearn.training.WarmupCosineLR(peak_lr, warmup_steps=0, total_steps=None, min_lr=0.0)[source]#
Bases:
_ScheduleLinear warmup, then cosine decay to
min_lr.Rises linearly over
warmup_steps, then followsmin_lr + (peak_lr - min_lr) * (1 + cos(pi * p)) / 2withp = (t - warmup_steps) / (total_steps - warmup_steps), then stays atmin_lr.- Parameters:
peak_lr (float) – Learning rate after warmup; finite and >= 0 (rounded to float32).
warmup_steps (int, default 0) – Steps of linear warmup; >= 0.
total_steps (int or None, default None) – Step at which the decay reaches
min_lr; >= max(1, warmup_steps).min_lr (float, default 0.0) – Final learning rate; finite and >= 0.
Notes
lr_at(t)takes the optimizer’s one-based step:lr_at(1)is the first step’s rate. Each value is the float32 nearest the exact expression, so a schedule gives the same bits on every machine.config()returns the schedule’s settings as a dict, whichfrom_configturns back into a schedule. Invalid arguments raiseValueError. Pass a schedule aslr_schedule=toSGD,AdamorAdamW.Examples
>>> import mojolearn >>> sched = mojolearn.WarmupCosineLR(3e-4, warmup_steps=200, total_steps=10000, min_lr=3e-5) >>> lr = sched.lr_at(5000)
- class mojolearn.training.Generator(seed, numeric_mode=None)[source]#
Bases:
objectSeeded random stream for neural-network initialization and dropout.
Every value drawn is a function of (seed, stream id, element index) only, so the same call at the same counter gives the same bits on every device. Each draw uses the next stream id.
state_dict()returns{'seed', 'counter'}, andload_state_dict()resumes the stream exactly.- Parameters:
seed (int) – Seed in [0, 2**64); otherwise
ValueError.numeric_mode ({‘identical’, ‘deterministic’, ‘fast’} or None, default None) – Numeric mode. None uses the library default.
Examples
>>> import mojolearn >>> gen = mojolearn.Generator(0) >>> w = gen.kaiming_uniform((128, 64), fan_in=64) >>> y, key = gen.dropout(w, p=0.1)
- load_state_dict(state)[source]#
Restore a state from
state_dict.The counter must fit in 32 bits unsigned, otherwise
ValueError.
- next_stream()[source]#
Consume and return the next stream id.
Use it for operations that draw several slices of one stream, such as dropout over microbatches. Raises
RuntimeErrorwhen the 32-bit counter is exhausted.
- uniform(shape, low=0.0, high=1.0)[source]#
Draw float32 values uniform in [low, high).
- Parameters:
shape (int or tuple of int) – Output shape; every dimension must be positive.
low (float, default 0.0) – Lower bound.
high (float, default 1.0) – Upper bound; must be >=
low.
- normal(shape, mean=0.0, std=1.0)[source]#
Draw float32 normal values (Box-Muller).
- Parameters:
shape (int or tuple of int) – Output shape.
mean (float, default 0.0) – Mean.
std (float, default 1.0) – Standard deviation; must be >= 0.
- kaiming_uniform(shape, fan_in, a=None)[source]#
Kaiming-uniform initialization, as
torch.nn.init.kaiming_uniform_.The bound is
sqrt(2 / (1 + a**2)) * sqrt(3 / fan_in). The defaulta = sqrt(5)is torch’snn.Lineardefault and gives the bound1 / sqrt(fan_in).- Parameters:
shape (int or tuple of int) – Output shape.
fan_in (int) – Fan-in of the layer.
a (float or None, default None) – Negative slope; None means sqrt(5).
- xavier_uniform(shape, fan_in, fan_out, gain=1.0)[source]#
Xavier-uniform initialization with bound
gain * sqrt(6 / (fan_in + fan_out)).
- kaiming_normal(shape, fan_in, gain=None)[source]#
Kaiming-normal initialization with std
gain / sqrt(fan_in);gainNone means sqrt(2).
- xavier_normal(shape, fan_in, fan_out, gain=1.0)[source]#
Xavier-normal initialization with std
gain * sqrt(2 / (fan_in + fan_out)).
- dropout(x, p, offset=0, stream=None)[source]#
Inverted dropout of a float32 array.
Keeps each element whose uniform draw is
>= pand scales it by1 / (1 - p). Returns(y, key); passkeytodropout_backwardto replay the same mask.- Parameters:
x (array of float32) – Input.
p (float) – Drop probability in [0, 1); otherwise
ValueError.offset (int, default 0) – Element index of
x[0]within the stream, so that a microbatch of a larger batch draws the same values it would draw unsplit.stream (int or None, default None) – Stream id; None takes the next one.
- Returns:
y (array of float32)
key (dict)
- mojolearn.training.accumulate_grads(microbatch_grads, tokens, numeric_mode=None)[source]#
Combine microbatch gradients into one, on the device.
Adds the microbatch gradients by a balanced tree in ascending microbatch order (not a running sum), so the result does not depend on the device. When
tokensis given, a split thataccumulation_is_alignedrejects raisesValueError; passtokens=Noneto combine without that check.- Parameters:
microbatch_grads (list) – One entry per microbatch, each a float32 array or a list of arrays with the same shapes across entries.
tokens (int or None) – Token count of the whole step, or None.
numeric_mode ({‘identical’, ‘deterministic’, ‘fast’} or None, default None) – Numeric mode. None uses the library default.
- Returns:
array or list of arrays – The combined gradient, in the structure of one entry.
- mojolearn.training.accumulation_is_aligned(tokens, accumulation_steps, numeric_mode=None)[source]#
Whether a split into microbatches gives exactly the unsplit gradient.
True when
accumulation_stepsmicrobatches overtokenstokens, combined byaccumulate_grads, give the same weight-gradient bits as one unsplit batch.- Parameters:
tokens (int) – Token count of the whole step.
accumulation_steps (int) – Number of microbatches.
numeric_mode ({‘identical’, ‘deterministic’, ‘fast’} or None, default None) – Numeric mode. None uses the library default.
- Returns:
bool
- class mojolearn.training.SambaConfig(vocab, d_model, layers, n_heads=None, n_kv_heads=None, head_dim=None, intermediate=None, tie_embeddings=True, norm_eps=1e-05, dropout=0.0)[source]#
Bases:
objectShape of a
SambaStack: vocabulary, width and the sequence of layer kinds.- Parameters:
vocab (int) – Vocabulary size, >= 2.
d_model (int) – Model dimension, >= 1. Must be a multiple of 32 when any layer is ‘mamba3’, and equal
n_heads * head_dimwhen any layer is ‘attention’.layers (sequence of {‘mamba3’, ‘attention’}) – Layer kinds in stack order; at least one.
n_heads (int or None, default None) – Attention heads. Required when any layer is ‘attention’.
n_kv_heads (int or None, default None) – Key/value heads for grouped-query attention. None uses
n_heads.head_dim (int or None, default None) – Attention head dimension. None uses
d_model // n_heads.intermediate (int or None, default None) – Hidden size of the attention layers’ gated MLP. Required when any layer is ‘attention’.
tie_embeddings (bool, default True) – Share the embedding matrix with the output head.
norm_eps (float, default 1e-5) – Epsilon of the final RMSNorm (>= 0). Each block keeps its own constants.
dropout (float, default 0.0) – Dropout rate after the embedding during
SambaStack.train_step, in [0, 1).Invalid values raise `ValueError`.
Examples
>>> import mojolearn >>> cfg = mojolearn.SambaConfig(vocab=256, d_model=64, layers=("mamba3", "attention"), ... n_heads=4, intermediate=128)
- block_shapes(kind)[source]#
Names and shapes of one block’s tensors.
- Parameters:
kind ({‘mamba3’, ‘attention’}) – Layer kind.
- Returns:
list of (str, tuple) –
(name, shape)pairs in the block’s weight order.
- registry()[source]#
Names and shapes of every parameter tensor of the stack, in order.
The order is
embed.weight, thenlayers.{i}.<block tensor>for each layer, thennorm_f.weight, thenlm_head.weightwhen embeddings are not tied. It is also the summation order of gradient clipping.- Returns:
list of (str, tuple)
- class mojolearn.training.SambaStack(config, weights=None, generator=None, lr=0.001, betas=(0.9, 0.999), eps=1e-08, weight_decay=0.01, lr_schedule=None, max_norm=None, accumulation_steps=1, numeric_mode=None)[source]#
Bases:
objectA Samba-style language model with its own training loop.
Embedding, then a stack of
Mamba3BlockandTransformerBlocklayers (each with its own pre-norm and residual), a final RMSNorm, a tied or untied LM head and the cross-entropy loss, with a full backward pass. Training uses AdamW over the parameter registry (SambaConfig.registry), optional gradient-norm clipping, an optional learning-rate schedule and gradient accumulation. All parameters live in one flat float32 buffer. Attention layers use full causal attention.Pass exactly one of
weightsorgeneratorto supply the initial values; otherwiseValueError.With
accumulation_steps > 1the batch rows are split into microbatches whose gradients are combined in a fixed tree. A split the accumulation rule does not allow is refused withValueErrorbefore any gradient is computed. An allowed split reproduces the unsplit step’s block, final-norm and LM-head gradients bit for bit, but not the embedding gradient’s, which is summed per token occurrence: after a step with accumulation the embedding can differ from the unsplit step in the last bits.- Parameters:
config (SambaConfig) – Model shape. Anything else raises
TypeError.weights (dict of str to float32 buffer or None, default None) – Initial parameters keyed by the registry names, with exact names and shapes.
generator (Generator or None, default None) – Seeded
mojolearn.training.Generatorused to initialize the parameters and to draw dropout masks.lr (float, default 1e-3) – Learning rate, used when
lr_scheduleis None.betas (tuple of float, default (0.9, 0.999)) – AdamW moment decays.
eps (float, default 1e-8) – AdamW denominator term.
weight_decay (float, default 0.01) – AdamW decoupled weight decay.
lr_schedule (schedule or None, default None) – For example
WarmupCosineLRorWarmupLinearLR.max_norm (float or None, default None) – Clip the global gradient L2 norm to this value; None turns clipping off.
accumulation_steps (int, default 1) – Number of microbatches per optimizer step; must divide the batch rows.
numeric_mode ({‘identical’, ‘deterministic’, ‘fast’} or None, default None) – Numeric mode. None uses the library default.
- Variables:
config (SambaConfig)
optimizer (AdamW) – The optimizer.
Examples
>>> import numpy as np, mojolearn >>> from mojolearn.training import Generator, WarmupCosineLR >>> cfg = mojolearn.SambaConfig(vocab=256, d_model=64, layers=("mamba3", "mamba3")) >>> stack = mojolearn.SambaStack(cfg, generator=Generator(7), lr=1e-3, ... lr_schedule=WarmupCosineLR(1e-3, 8, 64), ... max_norm=1.0, accumulation_steps=2) >>> ids = np.random.default_rng(0).integers(0, 256, (4, 32)).astype(np.int32) >>> out = stack.train_step(ids[:, :-1], ids[:, 1:]) >>> stack.save_checkpoint("run.ckpt")
- load_weights(weights)[source]#
Copy parameters in from a dict keyed by registry name.
The key set and every shape must match exactly; values must be float32.
- forward(inputs, state=None, *, lengths=None)[source]#
Compute logits for a batch of token ids, without dropout.
- Parameters:
inputs (int array, shape (B, L)) – Token ids.
state (SambaState or None, default None) – None runs every block from a zero state. Pass a state from
allocate_stateto carry it: each block’s state is read at entry and updated in place, so a laterforwardorstepcontinues the sequence.lengths (sequence of int or None, default None) – Makes the batch ragged: B integers in [1, L]. Row i is real at positions [0, lengths[i]) and padding after. Each real position’s logits equal the row run alone at its own length, and every padding position’s logits are exactly +0.0. Not allowed together with
state.
- Returns:
logits (mojolearn.Array, shape (B, L, vocab))
- allocate_state(batch_size, max_tokens)[source]#
Return a zero decode state for
batch_sizesequences of up tomax_tokenspositions.The state holds one block state per layer in stack order (
Mamba3StateorTransformerState), owned by the caller.- Parameters:
batch_size (int)
max_tokens (int)
- Returns:
SambaState
- step(inputs, state)[source]#
Decode one token per row, updating
statein place.The stateful
forwardat L = 1. No dropout.- Parameters:
inputs (int array, shape (B,) or (B, 1)) – Token ids.
state (SambaState) – Decode state from
allocate_state.
- Returns:
logits (mojolearn.Array, shape (B, vocab))
- loss(inputs, targets)[source]#
Mean cross-entropy of
targetsgiveninputs, both (B, L) ids; no dropout, no gradient.
- loss_and_grads(inputs, targets, num_items=None, dropout_stream=None, token_offset=0)[source]#
Run one forward and the full backward pass.
- Parameters:
inputs, targets (int array, shape (B, L)) – Token ids and next-token targets. Shapes must match.
num_items (int or None, default None) – Divisor of the summed loss and gradients. None uses this call’s own number of targets; pass the whole step’s count to make a microbatch’s gradient its slice of the unsplit one.
dropout_stream (int or None, default None) – Generator stream for the dropout mask; None applies no dropout.
token_offset (int, default 0) – Position offset of this batch in the dropout stream.
- Returns:
loss (float)
grads (list of mojolearn.Array) – One gradient per parameter, in registry order.
- train_step(inputs, targets)[source]#
Run one optimizer step on a batch.
The batch rows are split into
accumulation_stepsmicrobatches; each one’s gradient uses the whole step’s divisor and the pieces are combined in a fixed tree before clipping and the AdamW update. B must be divisible byaccumulation_steps, otherwiseValueError.- Parameters:
inputs, targets (int array, shape (B, L)) – Token ids and next-token targets.
- Returns:
dict –
loss,lr,stepandtotal_norm(the gradient norm before clipping, or None whenmax_normis None).
- state_dict(*, _copy_arrays=True)[source]#
The complete training state as a dict.
Holds the configuration, parameter registry and values, optimizer moments and step, schedule, generator state and numeric mode, so
load_state_dictresumes training exactly.
- load_state_dict(state)[source]#
Restore a state made by
state_dict; returns self.Raises
ValueErrorwhen the schema, configuration, numeric profile or sizes do not match this stack.
- save_checkpoint(path)[source]#
Write the training state to
pathas a checksummed checkpoint.The file is written atomically. Saving reads the live parameter and optimizer storage, so do not train or modify the stack while it saves.
- classmethod from_checkpoint(path, numeric_mode=None)[source]#
Build a stack from a checkpoint written by
save_checkpoint.- Parameters:
path (str or path-like)
numeric_mode (str or None, default None) – None uses the mode stored in the checkpoint.
- Returns:
SambaStack
Raises
ValueErrorwhen the checkpoint fails its integrity or schema checks.
- mojolearn.training.embedding_forward(weight, ids, numeric_mode=None)[source]#
Embedding lookup:
weight[ids].- Parameters:
weight (array, shape (V, D)) – float32 embedding table.
ids (array, shape (N,)) – Integer row indices.
numeric_mode ({‘identical’, ‘deterministic’, ‘fast’} or None, default None) – Numeric mode. None uses the library default.
- Returns:
array, shape (N, D)
- mojolearn.training.embedding_backward(dy, ids, vocab, numeric_mode=None)[source]#
Gradient of
embedding_forwardwith respect to the table.Returns a fresh (vocab, D) gradient from
dy. Rows with equal ids are summed in a fixed ascending order (no atomics), so the result is reproducible.- Parameters:
dy (array, shape (N, D)) – float32 gradient of the output.
ids (array, shape (N,)) – The forward’s ids; the row counts must match, otherwise
ValueError.vocab (int) – Number of table rows V.
numeric_mode ({‘identical’, ‘deterministic’, ‘fast’} or None, default None) – Numeric mode. None uses the library default.
- Returns:
array, shape (vocab, D)
- mojolearn.training.rms_norm_forward(x, weight, eps, numeric_mode=None)[source]#
RMS normalization of each row:
weight * x / sqrt(mean(x**2) + eps).- Parameters:
x (array, shape (M, D)) – float32 input.
weight (array, shape (D,)) – float32 scale.
eps (float) – Added to the mean square.
numeric_mode ({‘identical’, ‘deterministic’, ‘fast’} or None, default None) – Numeric mode. None uses the library default.
- Returns:
array, shape (M, D)
- mojolearn.training.rms_norm_backward(dy, x, weight, eps, numeric_mode=None)[source]#
Gradients
(dx, dweight)ofrms_norm_forward.- Parameters:
dy (array, shape (M, D)) – Gradient of the output.
x (array, shape (M, D)) – The forward input.
weight (array, shape (D,)) – The forward’s scale.
eps (float) – The forward’s epsilon.
numeric_mode ({‘identical’, ‘deterministic’, ‘fast’} or None, default None) – Numeric mode. None uses the library default.
- Returns:
dx (array, shape (M, D))
dweight (array, shape (D,))
- mojolearn.training.linear_forward(a, weight, numeric_mode=None)[source]#
Linear layer without bias:
a @ weight.T, astorch.nn.Linearwithbias=False.- Parameters:
a (array, shape (M, K)) – float32 input.
weight (array, shape (N, K)) – float32 weight, in torch’s layout.
numeric_mode ({‘identical’, ‘deterministic’, ‘fast’} or None, default None) – Numeric mode. None uses the library default.
- Returns:
array, shape (M, N)
- mojolearn.training.linear_backward(dc, a, weight, numeric_mode=None)[source]#
Gradients
(da, dweight)oflinear_forward.- Parameters:
dc (array, shape (M, N)) – Gradient of the output.
a (array, shape (M, K)) – The forward input.
weight (array, shape (N, K)) – The forward weight.
numeric_mode ({‘identical’, ‘deterministic’, ‘fast’} or None, default None) – Numeric mode. None uses the library default.
- Returns:
da (array, shape (M, K))
dweight (array, shape (N, K))
- mojolearn.training.chunked_lm_head_loss(hidden, weight, targets, return_grad=False, numeric_mode=None)[source]#
Mean cross-entropy of a language-model head without materializing the logits.
Computes the mean over rows of the cross-entropy of
hidden @ weight.Tagainsttargets, and optionally its gradients, without holding the (rows, vocab) logits. The result does not depend on how the vocabulary is chunked internally. There is no bias, ignore index, label smoothing or other reduction; uselinear_forwardandcross_entropywhen you need those. Non-finite inputs and out-of-range targets raise before any output is written.- Parameters:
hidden (array, shape (rows, width)) – float32 hidden states.
weight (array, shape (vocab, width)) – float32 output embedding,
vocab >= 2.targets (array, shape (rows,)) – Integer class indices in [0, vocab).
return_grad (bool, default False) – Also return the gradients.
numeric_mode ({‘identical’, ‘deterministic’, ‘fast’} or None, default None) – Numeric mode. None uses the library default.
- Returns:
loss (float)
d_hidden (array, shape (rows, width)) – Only with
return_grad=True.d_weight (array, shape (vocab, width)) – Only with
return_grad=True.
Examples
>>> import numpy as np, mojolearn >>> rng = np.random.default_rng(0) >>> hidden = rng.random((64, 32), dtype=np.float32) >>> weight = rng.random((1000, 32), dtype=np.float32) >>> targets = rng.integers(0, 1000, 64) >>> loss, dh, dw = mojolearn.chunked_lm_head_loss(hidden, weight, targets, return_grad=True)
- mojolearn.training.mlp_inference_sessions(inputs, weight1, bias1, weight2, bias2, numeric_mode='identical')[source]#
Run several independent batches of rows through one Linear-ReLU-Linear model.
Computes
relu(x @ weight1.T + bias1) @ weight2.T + bias2for every session and returns the logits of all sessions concatenated in the order given. Empty sessions are allowed. The model is stateless (no caches, no dropout).- Parameters:
inputs (list of arrays) – float32 sessions, each (rows_i, width).
weight1 (array, shape (hidden, width)) – First layer weight, in torch’s layout.
bias1 (array, shape (hidden,)) – First layer bias.
weight2 (array, shape (out_width, hidden)) – Second layer weight.
bias2 (array, shape (out_width,)) – Second layer bias.
numeric_mode ({‘identical’, ‘deterministic’, ‘fast’}, default ‘identical’) – Numeric mode.
- Returns:
array, shape (sum of rows_i, out_width)
- Raises:
ValueError – When the weight, bias and session shapes do not agree.
- mojolearn.training.residual_dropout(values, residual, *, p=0.5, seed=0, stream=0, offset=0)[source]#
Return
residual + dropout(values)as one explicit IDENTICAL layer.Inputs must have identical shapes and C-contiguous float32 storage. The native implementation admits finite values and p in [0,1), computes scale=1/(1-p), and preserves the rounded dropout intermediate before add. RNG coordinates are (64-bit seed, 32-bit stream, offset+flat_index). NN59 selects the fused schedule; OFF/ALL_OFF use two native stages. No existing model is implicitly changed and no validation is claimed.
- mojolearn.training.residual_dropout_backward(gradient, *, p=0.5, seed=0, stream=0, offset=0)[source]#
Return
(d_values, d_residual)for the explicit residual layer.Pass exactly the forward p/seed/stream/offset to regenerate its mask. d_values applies the same rounded keep-and-scale operation to gradient; d_residual preserves the upstream gradient words. This is an explicit backward operation, not an automatic differentiation registration.