OTIS base — ONNX
OTIS is an open time series encoder: 7.1 M parameters, matching the accuracy of encoders 54× its size across 162 tasks at 10× less memory, 43× less energy and 37× lower latency. This is the released base checkpoint exported to ONNX so it can run client-side in a browser — the demo page runs it on the visitor's own data without uploading anything.
| file | size | contents |
|---|---|---|
otis_encoder.onnx |
~22 MB | encoder, float32 |
otis_decoder.onnx |
~5 MB | decoder, float32 |
otis_encoder_int8.onnx |
~6 MB | encoder, dynamic int8 |
otis_decoder_int8.onnx |
~1.4 MB | decoder, dynamic int8 |
otis_meta.json |
— | patch size, domain offsets, variate slots |
pos_embed_y.bin |
15 KB | learned variate embeddings, raw float32 (39 x 96) |
pos_embed_x.bin |
17 KB | fixed sincos position table, raw float32 (1 x 43 x 96) |
decoder_pos_embed_x.bin |
14 KB | the decoder's, raw float32 (1 x 43 x 80) |
units_x128_<V>x<T>[_int8].onnx |
3-12 MB | UniTS backbone per demo shape |
units_meta.json |
— | UniTS graph list, token size, prompt count |
moment_large_int8.onnx |
330 MB | MOMENT-large, dynamic V and T, feat + pred + emb |
moment_fc_<dataset>_int8.onnx |
3-6 MB | MOMENT's linear-probing forecasting head per benchmark, applied to emb |
moment_meta.json |
— | MOMENT graph entry |
moment_released_tokens.bin, moment_<sine|chirp|square>_tokens.bin |
37 KB each | MOMENT's mask token + reconstruction head as graph inputs: the released pair, and the pair fitted on sines, chirps or squares from 20 to 120 Hz for the Impute row |
chronos2_fc336_96_int8.onnx |
131 MB | Chronos-2, 336 -> 96 median forecast, dynamic V |
chronos2_embed_int8.onnx |
128 MB | Chronos-2 encoder states for the Explore map |
zero_shot_meta.json |
— | the zero-shot graph(s) hosted here: split, sizes, int8 error |
forecast/<dataset>[_revin]/ |
~7 MB each | OTIS fine-tuned per forecasting benchmark: int8 graphs, meta, tables |
sine_embed.bin, <chirp|square>_embed.bin |
384 B each | one OTIS variate embedding per Impute source, fitted on clean sines, chirps or squares from 20 to 120 Hz |
Signatures
otis_encoder(x, attn_mask, pos_embed_y, pos_embed_x, ids_keep) -> latent
otis_decoder(latent, attn_mask, pos_embed_y, decoder_pos_embed_x, ids_restore) -> pred
pos_embed_y is (B, V, T', 96) vectors, not integer ids: the nn.Embedding
lookup is lifted out of the graph so the caller supplies the variate embeddings.
A domain seen in pre-training uses the learned rows from pos_embed_y.bin
(indexed arange(V) + 1 + domain_offset); an unseen domain gets randomly
initialised ones, trunc_normal(std=0.02), as the paper specifies and as
models_otis.initialize_weights does it.
pos_embed_x is (1, 1+T', 96) and is lifted out for the same reason. It is a
fixed sincos table, requires_grad=False, so baking the checkpoint's 42
positions in would cap a run at 1008 time steps — which the codebase does not
do: util/pos_embed.interpolate_pos_embed_x resamples it (linear,
align_corners=False) for anything longer, and slices it for anything shorter.
Pass the table you want; pos_embed_x.bin is the checkpoint's.
Masking is deliberately outside the graph: the reference random_masking draws from an
RNG and selects with a boolean mask, so its output shape is data dependent and cannot be
expressed in ONNX. The caller picks the visible patches and passes them as ids_keep,
which also lets a mask-ratio slider re-run without re-exporting. Passing
ids_keep = arange(N) with attn_mask = 1 reproduces the all-patches-visible path exactly.
Licenses
The OTIS graphs are released under this repository's license (CC BY-NC 4.0). The comparison models keep their own: UniTS (MIT), MOMENT-large (MIT) and Chronos-2 (Apache-2.0) are converted to ONNX without any change to the weights, and are here only so the demo page can run them in the browser; use them under their own terms. TimesFM-3's license (TimesFM Non-Commercial License v1.0) does not allow the weights or derivatives to be distributed, so its graph is not here: the page runs its rows only from a local copy of the graph.
Note on attention
These weights were trained with an inner nn.MultiheadAttention, i.e. with a second
projection between qkv and proj. util/transformer.py keeps that layout and folds
the two projections into one at inference, which is exact and is why the exported graph
is smaller than the checkpoint's nominal parameter count.