braindecode.models.ZUNA#
- class braindecode.models.ZUNA(n_outputs=None, n_chans=None, chs_info=None, n_times=None, input_window_seconds=None, sfreq=None, *, dim=1024, n_layers=16, n_heads=8, head_dim=64, fine_time_pts=32, latent_dim=32, max_seqlen=256, rope_theta=10000.0, pos_bins=50, pos_half_range=0.12, norm_eps=1e-05, multiple_of=256, ffn_dim_multiplier=None, sandwich_norm=True, qk_norm=True, activation=<class 'torch.nn.modules.activation.SiLU'>)[source]#
ZUNA from Warner et al. (2026) [Warner2026ZUNA].
Foundation Model Channel Attention/Transformer
Added in version 1.7.
ZUNA was introduced as a diffusion autoencoder for masked EEG channel reconstruction and super-resolution [Warner2026ZUNA]. ZUNA1.1 retains that objective and adds query-key normalization, sandwich normalization, pretraining windows from 0.5 to 30 seconds, and eight channel and time dropout schemes [Warner2026ZUNA11].
This Braindecode class contains the ZUNA1.1 encoder followed by a classification head. It does not contain the diffusion decoder, channel masking, or reconstruction sampler shown in the figure.
forward()returns logits of shape(batch, n_outputs). Withreturn_features=True, it returns the per-channel encoder features used by the classification head.Signal size and sampling frequency follow the standard Braindecode model arguments. Supply either
n_timesor bothinput_window_secondsandsfreq; ZUNA does not set these values implicitly. ZUNA1.1 was trained at 256 Hz with 32-sample tokens. This implementation does not resample, filter, or normalize the input. Channel coordinates are read fromchs_infowhen the model is constructed and stored in fixed rotary position buffers.Architecture Overview
PatchTokenizersplits every channel into non-overlapping patches offine_time_ptssamples. The resultingn_chans * (n_times // fine_time_pts)patches are serialized in channel-major order. A learned register is placed before each patch, and both are projected todimbefore entering the transformer blocks.Self-attention is bidirectional. Four-dimensional rotary positions encode each token’s discretized scalp coordinates
(x, y, z)and coarse-time index. The encoder reads the register positions, projects them tolatent_dim, restores the channel and patch axes, and averages over the patch axis. A linear layer maps the concatenated channel features ton_outputs.Macro Components
ZUNA.patch_embedding(torch.nn.Sequential)Operations: split
(batch, channel, time)into patches withPatchTokenizer, then rearrange(channel, temporal_patch)into one token axis.Role: produce one continuous-valued token for every channel and time patch.
ZUNA.encoder(torch.nn.Module)Operations:
tok_embeddings(linear patch embedding) → interleaveregisters→n_layers×_TransformerBlock(RMS-normed multi-head attention with 4D RoPE and QK-norm, SwiGLU feed-forward, sandwich norm) →norm→outputlinear projection tolatent_dimper token.Role: encode channel-time patches while retaining their spatial and temporal coordinates.
ZUNA.final_layer(torch.nn.Sequential)Operations: rearrange the
(n_chans, latent_dim)channel features into one axis → linear map ton_outputs.Role: produce task logits from the pooled encoder features.
Temporal, Spatial, and Spectral Encoding
Temporal: fixed-size patches provide local samples, and the fourth rotary axis identifies each patch’s coarse-time index. The patch axis is averaged after encoding.
Spatial: the first three rotary axes contain bucketed 3D coordinates from
chs_info. A model instance uses the montage supplied at construction.Spectral: there is no Fourier or filter-bank stage. The encoder receives raw time-domain patches.
Additional Mechanisms
Register tokens: one learned register is paired with every patch. Only register outputs enter the latent projection.
ZUNA1.1 normalization: query-key RMS normalization and optional post-attention and post-feed-forward RMS normalization match the changes introduced in ZUNA1.1.
Fixed positional grid: channel and coarse-time rotary values are computed during construction. Inputs passed to
forward()must have the configured channel count and window length.
- Parameters:
n_outputs (
Optional[int]) – Number of outputs of the model. This is the number of classes in the case of classification.chs_info (
Optional[list[dict]]) – Information about each individual EEG channel. This should be filled withinfo["chs"]. Refer tomne.Infofor more details.n_times (
Optional[int]) – Number of time samples of the input window.input_window_seconds (
Optional[float]) – Length of the input window in seconds.sfreq (
Optional[float]) – Sampling frequency of the EEG recordings.dim (
int) – Transformer embedding dimension. The default is1024.n_layers (
int) – Number of transformer blocks. The default is16.n_heads (
int) – Number of attention heads per block. The default is8.head_dim (
int) – Dimension of each attention head. It must be divisible by eight. The default is64.fine_time_pts (
int) – Number of fine time points per token (the encoder input dimension).n_timesmust be divisible by this value. The default is32, or 0.125 seconds for data sampled at 256 Hz.latent_dim (
int) – Per-token output dimension of the encoder. The default is32.max_seqlen (
int) – Length of the rotary frequency table. It must be at leastmax(pos_bins, n_times // fine_time_pts). The default is256.rope_theta (
float) – Base period of the rotary positional embedding. The default is10000.0.pos_bins (
int) – Number of buckets per spatial coordinate. The default is50.pos_half_range (
float) – Half-range (in metres) used to normalise channel coordinates before bucketing. Coordinates at or beyond this range are clipped to the first or last bucket. The default is0.12.norm_eps (
float) – Epsilon of the RMS normalization layers. The default is1e-5.multiple_of (
int) – Feed-forward hidden dimension is rounded up to a multiple of this value. The default is256.ffn_dim_multiplier (
Optional[float]) – Multiplier applied before rounding the feed-forward hidden dimension. The default isNone.sandwich_norm (
bool) – Apply RMS normalization after attention and the feed-forward layer. The default isTrue.qk_norm (
bool) – Apply RMS normalization to queries and keys. The default isTrue.activation (
type[Module]) – Feed-forward activation class. The default istorch.nn.SiLU.
- Raises:
ValueError – If some input signal-related parameters are not specified: and can not be inferred.
Notes
The full ZUNA1.1 pretraining model has an encoder and a rectified-flow decoder. Its encoder latent is regularized with a maximum mean discrepancy loss. The decoder and both pretraining losses are outside this class.
In the paper’s downstream experiments, token latents were averaged and the encoder was fine-tuned with the classifier. A frozen encoder with a linear head performed worse. The authors also found that checkpoints with lower reconstruction error were not necessarily better for classification [Warner2026ZUNA11].
References
Hugging Face Hub integration
When the optional
huggingface_hubpackage is installed, all models automatically gain the ability to be pushed to and loaded from the Hugging Face Hub. Install with:pip install braindecode[hub]
Pushing a model to the Hub:
from braindecode.models import ZUNA # Train your model model = ZUNA(n_chans=22, n_outputs=4, n_times=1000) # ... training code ... # Push to the Hub model.push_to_hub( repo_id="username/my-zuna-model", commit_message="Initial model upload", )
Loading a model from the Hub:
from braindecode.models import ZUNA # Load pretrained model model = ZUNA.from_pretrained("username/my-zuna-model") # Load with a different number of outputs (head is rebuilt automatically) model = ZUNA.from_pretrained("username/my-zuna-model", n_outputs=4)
Extracting features and replacing the head:
import torch x = torch.randn(1, model.n_chans, model.n_times) # Extract encoder features (consistent dict across all models) out = model(x, return_features=True) features = out["features"] # Replace the classification head model.reset_head(n_outputs=10)
Saving and restoring full configuration:
import json config = model.get_config() # all __init__ params with open("config.json", "w") as f: json.dump(config, f) model2 = ZUNA.from_config(config) # reconstruct (no weights)
All model parameters (both EEG-specific and model-specific such as dropout rates, activation functions, number of filters) are automatically saved to the Hub and restored when loading.
See Loading and Adapting Pretrained Foundation Models for a complete tutorial.
Methods
- forward(input_tensor, return_features=False)[source]#
Define the computation performed at every call.
Should be overridden by all subclasses.
Note
Although the recipe for forward pass needs to be defined within this function, one should call the
Moduleinstance afterwards instead of this since the former takes care of running the registered hooks while the latter silently ignores them.