nn.training

nn.training

Plain training and evaluation loops for variable-length sequence models.

Ported op-for-op from the train/evaluate_many helpers of the schu25a compressor-map study (src/rnn/train.py), so that runs seeded with spotoptim.utils.seed.seed_everything reproduce the original results bit-identically on the same device. The models are called as model(x, lengths) on padded batches (e.g. spotoptim.nn.many_to_many_rnn.ManyToManyRNN with batches collated by spotoptim.data.manydataset.PadSequenceManyToMany); losses and metrics are computed on the squeezed padded tensors, exactly as in the original. The original’s unused seed parameters were dropped.

Requires the torch optional extra (pip install 'spotoptim[torch]'), which includes torchmetrics.

Functions

Name Description
evaluate_sequences Evaluate a sequence model with per-batch MAPE and RMSE.
train_sequences Train a sequence model with a plain epoch loop.

evaluate_sequences

nn.training.evaluate_sequences(
    model,
    val_loader,
    device='cpu',
    metrics_only=False,
)

Evaluate a sequence model with per-batch MAPE and RMSE.

Metrics are the torchmetrics MeanAbsolutePercentageError and MeanSquaredError(squared=False) between model(x, lengths).squeeze() and the squeezed target batch, as in the schu25a original. Inputs, predictions, and targets are additionally returned as nested Python lists for plotting.

Parameters

Name Type Description Default
model nn.Module Model called as model(x, lengths). required
val_loader DataLoader Loader yielding (x, lengths, y) batches. Batches are converted to NumPy, so device should be “cpu” unless metrics_only=True. required
device str Evaluation device. Defaults to “cpu”. 'cpu'
metrics_only bool Return only (mean_mape, mean_rmse). Defaults to False. False

Returns

Name Type Description
tuple Union[Tuple[float, float], Tuple[list, list, list, List[float], List[float]]] (x_ls, y_hat_ls, y_ls, mape_loss_ls, rmse_loss_ls) with one entry per batch, or (mean_mape, mean_rmse) if metrics_only.

Examples

import pandas as pd
import torch
from torch.utils.data import DataLoader
from spotoptim.data.manydataset import ManyToManyDataset, PadSequenceManyToMany
from spotoptim.nn.many_to_many_rnn import ManyToManyRNN
from spotoptim.nn.training import evaluate_sequences
from spotoptim.utils.seed import seed_everything

seed_everything(42)
frames = [pd.DataFrame({"x": [0.1, 0.2, 0.3], "y": [1.0, 2.0, 3.0]})]
ds = ManyToManyDataset(frames, target="y")
dl = DataLoader(ds, batch_size=1, shuffle=False, collate_fn=PadSequenceManyToMany())
model = ManyToManyRNN(input_size=1, rnn_units=8, fc_units=8)
x, y_hat, y, mape, rmse = evaluate_sequences(model, dl)
print(len(y_hat), len(mape), len(rmse))
1 1 1

train_sequences

nn.training.train_sequences(
    model,
    train_loader,
    optimizer,
    criterion,
    val_loader=None,
    epochs=10,
    device='cpu',
    verbose=True,
)

Train a sequence model with a plain epoch loop.

One optimizer step per batch; the loss is computed between model(x, lengths).squeeze() and the (padded) target batch. The recorded training loss is the batch-mean loss per epoch.

Parameters

Name Type Description Default
model nn.Module Model called as model(x, lengths). required
train_loader DataLoader Loader yielding (x, lengths, y) batches, e.g. collated by PadSequenceManyToMany. required
optimizer torch.optim.Optimizer Optimizer over model.parameters(). required
criterion nn.Module Loss module, e.g. nn.MSELoss(). required
val_loader Optional[DataLoader] Optional validation loader; when given, the mean per-batch RMSE from evaluate_sequences is recorded after every epoch. Defaults to None. None
epochs int Number of epochs. Defaults to 10. 10
device str Training device. Defaults to “cpu”. 'cpu'
verbose bool Print per-epoch losses. Defaults to True. True

Returns

Name Type Description
tuple Union[Tuple[nn.Module, List[float]], Tuple[nn.Module, List[float], List[float]]] (model, train_loss_ls) without a validation loader, or (model, train_loss_ls, val_loss_ls) with one.

Examples

import pandas as pd
import torch
import torch.nn as nn
from torch.utils.data import DataLoader
from spotoptim.data.manydataset import ManyToManyDataset, PadSequenceManyToMany
from spotoptim.nn.many_to_many_rnn import ManyToManyRNN
from spotoptim.nn.training import train_sequences
from spotoptim.utils.seed import seed_everything

seed_everything(42)
frames = [
    pd.DataFrame({"x": [0.1, 0.2, 0.3], "y": [1.0, 2.0, 3.0]}),
    pd.DataFrame({"x": [0.4, 0.5], "y": [4.0, 5.0]}),
]
ds = ManyToManyDataset(frames, target="y")
dl = DataLoader(ds, batch_size=2, shuffle=False, collate_fn=PadSequenceManyToMany())
model = ManyToManyRNN(input_size=1, rnn_units=8, fc_units=8)
optimizer = torch.optim.Adam(model.parameters(), lr=1e-3)
model, losses = train_sequences(
    model, dl, optimizer, nn.MSELoss(), epochs=2, verbose=False
)
print(len(losses))
2