Model Usage Examples¶
This page provides a training example for every model configuration available in
conf/model/, followed by shared inference and evaluation commands. All examples
use the WPR Zarr, 12 hourly input frames, 6 hourly targets, one channel, and
320 × 320 spatial patches.
See Workflow Examples for the complete data workflow and Parameter Reference for architecture options and limitations.
Preparation¶
Run from the repository root, using Bash and an environment with the package installed. Run training on a compute node with an allocated GPU.
source .venv/bin/activate
uv pip install -e '.[dl,verification]'
export BENCHMARK_ZARR=/prj/cptec/nowcasting/data/benchmark/benchmark_dataset_wpr.zarr
python -c "import torch; print('CUDA available:', torch.cuda.is_available())"
Create the environment first if needed, following Installation.
Replace the Zarr path on other machines. The store must contain train,
validation, and test groups with gsmap_nrt inputs and gsmap_mvk targets.
Each recipe is a one-epoch functional check, with 8 training samples and 4 validation samples. These are sample counts, not batch counts. A checkpoint from this check is not a validated forecasting model. Batch size 1 reduces memory use, but does not guarantee that every architecture fits every GPU.
The Bash MODEL_ARGS array preserves the selected architecture arguments for
later inference and evaluation. Run a model's block before using those commands.
Each model writes checkpoints to a separate run directory.
UNet — direct¶
Predicts all six future frames in one forward pass. This is the default model configuration.
MODEL_ARGS=(model=unet/direct)
export MODEL_RUN=outputs/model_examples/unet_direct
python main.py task=train "${MODEL_ARGS[@]}" training=default \
loss=hybrid_mse_ssim \
dataset.dataset.zarr_path="$BENCHMARK_ZARR" \
dataset.dataset.patch_height=320 \
dataset.dataset.patch_width=320 \
dataset.train_loader.batch_size=1 \
dataset.val_loader.batch_size=1 \
dataset.overrides.train.steps_per_epoch=8 \
dataset.overrides.validation.steps_per_epoch=4 \
training.epochs=1 \
training.checkpoint.dir="$MODEL_RUN/checkpoints" \
hydra.run.dir="$MODEL_RUN"
UNet — autoregressive¶
Predicts one frame at a time and feeds predictions back into the context window. This YAML fixes 12 input frames and 6 output frames; if changing the root temporal settings, also override model.input_timesteps and model.output_timesteps.
MODEL_ARGS=(model=unet/autoregressive)
export MODEL_RUN=outputs/model_examples/unet_autoregressive
python main.py task=train "${MODEL_ARGS[@]}" training=default \
loss=hybrid_mse_ssim \
dataset.dataset.zarr_path="$BENCHMARK_ZARR" \
dataset.dataset.patch_height=320 \
dataset.dataset.patch_width=320 \
dataset.train_loader.batch_size=1 \
dataset.val_loader.batch_size=1 \
dataset.overrides.train.steps_per_epoch=8 \
dataset.overrides.validation.steps_per_epoch=4 \
training.epochs=1 \
training.checkpoint.dir="$MODEL_RUN/checkpoints" \
hydra.run.dir="$MODEL_RUN"
ConvLSTM¶
Uses recurrent convolutional states to encode the sequence and generate future frames. The configured hidden widths are [32,32,32]; use a quoted override such as 'hidden_channels=[16,16,16]' to reduce them, and reuse that setting when loading the checkpoint.
MODEL_ARGS=(model=convlstm/direct)
export MODEL_RUN=outputs/model_examples/convlstm
python main.py task=train "${MODEL_ARGS[@]}" training=default \
loss=hybrid_mse_ssim \
dataset.dataset.zarr_path="$BENCHMARK_ZARR" \
dataset.dataset.patch_height=320 \
dataset.dataset.patch_width=320 \
dataset.train_loader.batch_size=1 \
dataset.val_loader.batch_size=1 \
dataset.overrides.train.steps_per_epoch=8 \
dataset.overrides.validation.steps_per_epoch=4 \
training.epochs=1 \
training.checkpoint.dir="$MODEL_RUN/checkpoints" \
hydra.run.dir="$MODEL_RUN"
AFNO¶
Uses Fourier-domain blocks. Its Python image-size default is [880,970], so explicitly match model.img_size to the 320 × 320 dataset patches. The internal model.patch_size defaults to 10 and must divide both dimensions; it is separate from the dataset crop size.
MODEL_ARGS=(model=afno/direct '+model.img_size=[320,320]')
export MODEL_RUN=outputs/model_examples/afno
python main.py task=train "${MODEL_ARGS[@]}" training=default \
loss=hybrid_mse_ssim \
dataset.dataset.zarr_path="$BENCHMARK_ZARR" \
dataset.dataset.patch_height=320 \
dataset.dataset.patch_width=320 \
dataset.train_loader.batch_size=1 \
dataset.val_loader.batch_size=1 \
dataset.overrides.train.steps_per_epoch=8 \
dataset.overrides.validation.steps_per_epoch=4 \
training.epochs=1 \
training.checkpoint.dir="$MODEL_RUN/checkpoints" \
hydra.run.dir="$MODEL_RUN"
ResNet50¶
Uses a ResNet50 encoder with a forecasting decoder. This example initializes the encoder without downloaded pretrained weights. Set +model.pretrained=true to use pretrained encoder weights when available in the cache or through network access.
MODEL_ARGS=(model=resnet50/direct +model.pretrained=false)
export MODEL_RUN=outputs/model_examples/resnet50
python main.py task=train "${MODEL_ARGS[@]}" training=default \
loss=hybrid_mse_ssim \
dataset.dataset.zarr_path="$BENCHMARK_ZARR" \
dataset.dataset.patch_height=320 \
dataset.dataset.patch_width=320 \
dataset.train_loader.batch_size=1 \
dataset.val_loader.batch_size=1 \
dataset.overrides.train.steps_per_epoch=8 \
dataset.overrides.validation.steps_per_epoch=4 \
training.epochs=1 \
training.checkpoint.dir="$MODEL_RUN/checkpoints" \
hydra.run.dir="$MODEL_RUN"
InceptionV4¶
Uses an InceptionV4 encoder with a forecasting decoder. +model.pretrained=false avoids an encoder-weight download. Use true to initialize from pretrained encoder weights.
MODEL_ARGS=(model=inceptionv4/direct +model.pretrained=false)
export MODEL_RUN=outputs/model_examples/inceptionv4
python main.py task=train "${MODEL_ARGS[@]}" training=default \
loss=hybrid_mse_ssim \
dataset.dataset.zarr_path="$BENCHMARK_ZARR" \
dataset.dataset.patch_height=320 \
dataset.dataset.patch_width=320 \
dataset.train_loader.batch_size=1 \
dataset.val_loader.batch_size=1 \
dataset.overrides.train.steps_per_epoch=8 \
dataset.overrides.validation.steps_per_epoch=4 \
training.epochs=1 \
training.checkpoint.dir="$MODEL_RUN/checkpoints" \
hydra.run.dir="$MODEL_RUN"
Xception¶
Uses an Xception encoder with a forecasting decoder. +model.pretrained=false avoids an encoder-weight download. Use true to initialize from pretrained encoder weights.
MODEL_ARGS=(model=xception/direct +model.pretrained=false)
export MODEL_RUN=outputs/model_examples/xception
python main.py task=train "${MODEL_ARGS[@]}" training=default \
loss=hybrid_mse_ssim \
dataset.dataset.zarr_path="$BENCHMARK_ZARR" \
dataset.dataset.patch_height=320 \
dataset.dataset.patch_width=320 \
dataset.train_loader.batch_size=1 \
dataset.val_loader.batch_size=1 \
dataset.overrides.train.steps_per_epoch=8 \
dataset.overrides.validation.steps_per_epoch=4 \
training.epochs=1 \
training.checkpoint.dir="$MODEL_RUN/checkpoints" \
hydra.run.dir="$MODEL_RUN"
Run a longer experiment¶
In the selected model's training command, change:
training.epochs=50for up to 50 epochs, subject to early stopping.dataset.overrides.train.steps_per_epoch=500for 500 sampled training items.dataset.overrides.validation.steps_per_epoch=500for sampled validation, ornullfor deterministic validation over the full split.MODEL_RUNto a fresh directory to keep the small check separate.
Keep model, data, loss, and sampling settings recorded when comparing results. These recipes demonstrate usage; they are not tuned hyperparameters for each architecture. See Training Models for model-specific details.
Use any trained model for inference¶
After running one of the blocks above, keep its MODEL_ARGS and MODEL_RUN
values in the same shell. The following command works with each recipe's selected
architecture and checkpoint, including AFNO's image-size override.
export MODEL_CHECKPOINT="$MODEL_RUN/checkpoints/best_model.pt"
if test -f "$MODEL_CHECKPOINT"; then
python main.py task=infer "${MODEL_ARGS[@]}" \
checkpoint="$MODEL_CHECKPOINT" \
dataset.dataset.zarr_path="$BENCHMARK_ZARR" \
+dataset.overrides.test.group=test \
inference.mode=single \
inference.single.output_format=pt \
inference.output_dir="$MODEL_RUN/inference"
else
echo "Checkpoint missing: $MODEL_CHECKPOINT"
fi
The guard matters: the current CLI otherwise continues with fresh weights when
a checkpoint file is missing. Single inference predicts the first test patch and
uses a fixed timestamp in the output path:
$MODEL_RUN/inference/single/2026/03/16/pred_gsmap_20260316_1200.pt.
This timestamp is not the sample's actual date. Use inference.single.output_format=nc
for NetCDF, or follow historical inference
for batch output to Zarr.
If starting a new shell, activate the environment and restore BENCHMARK_ZARR,
MODEL_ARGS, and MODEL_RUN from the chosen recipe. Do not rerun its training
command just to load the model. Any custom architecture overrides must also be
restored before loading the checkpoint.
Evaluate any trained model¶
With the same variables, run a small evaluation on sampled test data:
if test -f "$MODEL_CHECKPOINT"; then
python main.py task=evaluate "${MODEL_ARGS[@]}" \
checkpoint="$MODEL_CHECKPOINT" \
dataset.dataset.zarr_path="$BENCHMARK_ZARR" \
+dataset.overrides.test.group=test \
+dataset.overrides.test.steps_per_epoch=8 \
dataset.val_loader.batch_size=1 \
'evaluation.lead_times_min=[60,120,180,240,300,360]' \
+evaluation.output_dir="$MODEL_RUN/evaluation" \
+visualization.output_dir="$MODEL_RUN/figures"
else
echo "Checkpoint missing: $MODEL_CHECKPOINT"
fi
Remove +dataset.overrides.test.steps_per_epoch=8 for deterministic evaluation
across the test samples. Metrics are written to
$MODEL_RUN/evaluation/evaluation_summary.csv, with figures under
$MODEL_RUN/figures. The evaluator still labels horizons T+1 through T+6;
for this dataset, they represent 1 through 6 hours despite the minute override
being recorded in configuration.
GAN and discriminator availability¶
The repository also contains a GAN training engine and a PatchGAN discriminator.
They are not an additional selectable model=gan configuration. The unified
main.py training handler currently invokes supervised training even when
training=gan is selected, so that override is not a working adversarial-training
recipe. The examples above cover all seven model YAML configurations currently
available through this CLI, including both UNet forecasting strategies.