Training¶
Optimizers¶
All optimizers live in snn.optimizers. Pass an instance to model.compile() or use a string shorthand.
String key |
Class |
Key hyperparams |
|---|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
from snn.optimizers import SGD, Adam, AdamW, RMSprop
# SGD with Nesterov momentum
opt = SGD(learning_rate=0.01, momentum=0.9, nesterov=True)
# AdamW with weight decay
opt = AdamW(learning_rate=3e-4, weight_decay=0.01)
# Centred RMSprop
opt = RMSprop(learning_rate=1e-3, centered=True)
Loss functions¶
String key |
Class |
Notes |
|---|---|---|
|
|
Regression |
|
|
Regression, robust to outliers |
|
|
Blend of MSE + MAE |
|
|
Binary classification |
|
|
Multi-class, one-hot targets |
|
|
Multi-class, integer targets |
|
|
Distribution matching |
Pass from_logits=True to BCE or CCE when your last layer has no activation
(e.g., Dense(10) with no softmax). The loss will apply softmax internally for
numerical stability.
Callbacks¶
Callbacks are callables invoked at the end of each epoch by model.fit().
The signature expected by the built-in utilities is:
def my_callback(epoch, history):
...
EarlyStopping¶
from snn.utils import EarlyStopping
early_stop = EarlyStopping(
monitor="val_loss",
patience=10,
min_delta=1e-4,
mode="auto", # "min" / "max" / "auto"
restore_best_weights=False,
verbose=1,
)
model.fit(X, y, epochs=200, callbacks=[early_stop])
When patience is exhausted, the callback raises StopIteration which fit()
catches and stops training gracefully.
ReduceLROnPlateau¶
from snn.utils import ReduceLROnPlateau
reduce_lr = ReduceLROnPlateau(
monitor="val_loss",
factor=0.5,
patience=5,
min_lr=1e-6,
)
# ReduceLROnPlateau needs the optimizer, so wrap it:
opt = Adam(learning_rate=1e-3)
model.compile(optimizer=opt, loss="mse")
def lr_callback(epoch, history):
reduce_lr(epoch, history, optimizer=opt)
model.fit(X, y, epochs=100, callbacks=[lr_callback])
Learning rate schedules¶
from snn.utils import (
learning_rate_schedule,
cosine_annealing,
warmup_schedule,
)
# Exponential decay
schedule = learning_rate_schedule(initial_lr=1e-3, decay_rate=0.96, decay_steps=1000)
# Cosine annealing
schedule = cosine_annealing(initial_lr=1e-3, t_max=100, eta_min=1e-6)
# Linear warmup → cosine annealing
base = cosine_annealing(initial_lr=1e-3, t_max=200)
schedule = warmup_schedule(initial_lr=1e-3, warmup_steps=10, base_schedule=base)
# Apply manually each epoch:
opt = Adam(learning_rate=1e-3)
def lr_sched_callback(epoch, history):
opt.learning_rate = schedule(epoch)
model.fit(X, y, epochs=200, callbacks=[lr_sched_callback])
Advanced Trainer¶
Trainer extends the training loop with three power-user features.
Gradient accumulation¶
Accumulate gradients over N micro-batches before updating weights.
Effective batch size = batch_size × gradient_accumulation_steps.
from snn.trainer import Trainer
trainer = Trainer(model, gradient_accumulation_steps=8)
trainer.fit(X, y, epochs=20, batch_size=16)
# effective batch size = 128
Mixed-precision simulation¶
Cast activations and gradients to float16 and back to float64 during
forward/backward passes — simulates the precision loss of real FP16 hardware
without requiring a GPU.
trainer = Trainer(model, mixed_precision=True)
Gradient clipping¶
Clip the global gradient norm before each optimizer step:
trainer = Trainer(model, clip_grad_norm=1.0)
Checkpointing¶
Save best weights for multiple metrics independently:
from snn.trainer import Checkpoint
ckpt_loss = Checkpoint(monitor="val_loss", mode="min")
ckpt_acc = Checkpoint(monitor="val_accuracy", mode="max")
trainer = Trainer(model, checkpoints=[ckpt_loss, ckpt_acc])
trainer.fit(X, y, epochs=50, validation_data=(X_val, y_val))
ckpt_acc.restore(model) # apply best-accuracy weights
ckpt_loss.save_to_disk(model) # persist to .npz
Full Trainer example¶
from snn.trainer import Trainer, Checkpoint
from snn.utils import EarlyStopping
model.compile(
optimizer=AdamW(learning_rate=3e-4, weight_decay=1e-4),
loss="categorical_crossentropy",
metrics=["categorical_accuracy"],
)
ckpt = Checkpoint(monitor="val_categorical_accuracy", mode="max")
def lr_warmup(epoch, history, model, optimizer):
if epoch <= 5:
optimizer.learning_rate = 3e-4 * epoch / 5
trainer = Trainer(
model,
gradient_accumulation_steps=4,
mixed_precision=True,
clip_grad_norm=1.0,
checkpoints=[ckpt],
verbose=1,
)
history = trainer.fit(
X_train, y_train,
epochs=100,
batch_size=32,
validation_data=(X_val, y_val),
callbacks=[lr_warmup],
)
ckpt.restore(model)
model.evaluate(X_test, y_test)