Training
SageUncompiledTraining is the reference training
loop used in all production runs. It constructs each batch explicitly, injects
signals into random noise positions, preprocesses the combined batch, runs the
forward pass, and updates the model.
Initialisation
from sage.factory.training import SageUncompiledTraining
train_sage = SageUncompiledTraining(
signal_sampler, # e.g. IMRPhenomPv2 with SNR rescaler
noise_sampler, # e.g. MemmapNoiseSampler with RecolourPostprocess
processor, # Preprocessor([FiducialWhitening(), MultirateSampler(...)])
model, # MSCNN1D_2DResNetCBAM_Heteroscedastic (compiled or not)
loss_function, # BCEWithPEsigmaLoss(...)
optimiser, # optim.Adam(...)
scheduler, # CosineAnnealingWarmRestarts(...)
scaler, # torch.amp.GradScaler(...) or None
num_iterations=cfg.training_iterations, # batches per epoch
num_epochs=cfg.num_epochs, # total epochs
scheduler_mode="batch", # step scheduler every batch
)
Constructor arguments
Argument |
Description |
|---|---|
|
Any callable returning |
|
Any callable returning |
|
Applied to the combined batch after signal injection. |
|
The network (may be a |
|
Returns a stacked loss-component tensor; its |
|
|
|
Any |
|
|
|
Number of gradient steps per epoch. In the O3b run:
|
|
Total number of epochs. Pre-allocates the |
|
|
What happens inside each batch
The forward method (called via train_sage(nepoch=i)) runs
num_iterations batches per epoch. For each batch:
Sample —
signal_sampler()andnoise_sampler()produceS = batch_size * class_balancesignals andB = batch_sizenoise windows.Inject —
Ssignals are injected intoSrandomly chosen positions of theB-size noise batch. The injection positions are sampled without replacement viatorch.randperm.Preprocess —
processor(x)whitens and multirate-compresses the batch.Forward pass — the model runs under
torch.autocastifcfg.autocastis enabled, producing(ranking_stat, point_estimates).Loss —
loss_function(out, targets)returns a stacked component tensor.Backward —
loss[0].backward()with optional AMP scaling.Clip — gradient norms clipped to
cfg.clip_norm.Step — optimiser and scheduler updated.
Accumulate —
loss_components[nepoch] += loss.detach().
Running the training loop
for nepoch in range(cfg.num_epochs):
train_sage(nepoch=nepoch)
train_sage(nepoch=i) runs one full epoch and updates train_sage.loss_components[i]
with the per-component average loss.
Inspecting loss components
After each epoch, loss_components contains the mean losses for all completed epochs:
print(train_sage.loss_components)
# tensor([[0.705, 0.689, 0.054], ← epoch 0: [total, bce, reg]
# [0.477, 0.461, 0.054], ← epoch 1
# ...], device='cuda:0')
For BCEWithPEsigmaLoss with 2 PE targets, the shape is (num_epochs, 4):
[total, bce, nll_reg, coupling].
Logging losses to HDF5
The production run uses HDF5LossLogger to persist losses
to disk:
from sage.core.logger import HDF5LossLogger
logger = HDF5LossLogger(
path=os.path.join(cfg.export_dir, "losses.h5"),
num_epochs=cfg.num_epochs,
num_components=train_sage.loss_function.num_components,
)
for nepoch in range(cfg.num_epochs):
train_sage(nepoch=nepoch)
logger.log(train_sage.loss_components, nepoch, split="training")