Paper · 2023-04-17 · stat.ML, cs.LG
Long-term Forecasting with TiDE: Time-series Dense Encoder
TiDE is an MLP encoder-decoder for long-horizon forecasting: residual MLP blocks encode the look-back and covariates, decode the whole horizon, and a linear shortcut adds the history. Author-reported: lower Traffic MSE than PatchTST/64 at all four horizons, 5-10x faster than the best Transformer, and a near-optimal linear analogue for linear dynamical systems.
Time series forecastingMulti-layer perceptron (MLP)Residual learningtime-seriesforecastingmlplong-horizon
arXivPDFCodeMarkdown15 min read
AI-compiled by claude-haiku-5-5 on 2026-10-19, not yet reviewed by a human. Every claim cites its source; check it when a detail matters.
In plain words
Long-range forecasting means: given a long stretch of past values for many related series, plus side information that is known in advance (calendar features, promotions, and similar), predict many steps into the future. Transformer models were the usual choice for this, but their cost grows fast with the length of the history. Earlier work also found that a single linear map from history to future could beat them on standard benchmarks.
TiDE keeps the simplicity of that linear approach and adds non-linear layers. It flattens the past values and the known-in-advance side information into one vector, passes it through small neural blocks that each include a straight-through linear shortcut, and then produces the whole forecast in one pass. A plain linear path from the history to the forecast is added at the end, so the linear model is a special case of TiDE.
The authors also prove that a linear version of this design is near-optimal for a simple linear dynamical system. Their main claim is that the model matches or beats strong forecasters on common benchmarks while running much faster.
TL;DR
- An MLP-only encoder-decoder for multivariate long-horizon forecasting with optional covariates. It has no attention, recurrence or convolution §1 §4.
- Look-back is 720 for all horizons and the loss is MSE §5.1 §4.2.
- Author-reported: it matches or beats PatchTST/64 on most of seven benchmarks, beats it on Traffic at every horizon in MSE, and is 5x faster in inference and over 10x faster in training than the best Transformer §1 tab. 2.
- The released code differs from the paper in several ways that change the model (see Paper vs code). The biggest are a learned series-ID embedding and a block count of layers minus one.
Problem
Long-term multivariate forecasting: N time series, a look-back of length L, a horizon of length H, dynamic covariates of dimension r that are known up to L+H, and static attributes §3.2. Accuracy is measured by MSE on the standardized data §5.1 eq. 2.
Transformer forecasters approximate full self-attention with sub-quadratic variants, and the paper argues this hurts accuracy on long horizons §2 §5.1. The vanilla Transformer used by PatchTST needs memory quadratic in L, which is the efficiency issue the paper targets §5.3. Linear models do well on these benchmarks but cannot model non-linear dependencies or covariates §4 §2.
Method
Overview. TiDE is channel-independent: each series goes through the model on its own, and the weights are shared across all series §4. The input for series i is its past values y(1:L), its covariates x(1:L+H) and its static attributes a(i). The output is the H-step forecast §4 §3.2.
Residual block. The basic layer is a residual MLP with one hidden layer and a ReLU. It has a fully linear skip connection. Dropout is applied only on the output linear layer, and layer norm is applied to the sum of the output and the skip §4. Whether layer norm and dropout are on is set per dataset tab. 8.
Steps.
- Feature projection. Each time step's covariate vector (dimension r) goes through a residual block into a small width of 4, the temporal width §4.1 eq. 3 app. B.3.
- Dense encoder. The projected covariates for the past and future steps
are flattened and concatenated with the past values and the static
attributes. A stack of residual blocks of width
hiddenSizemaps this vector to an embedding §4.1 eq. 4. - Dense decoder. Residual blocks map the embedding to a vector of size
H times p, where p is
decoderOutputDim. It is reshaped into a p-by-H matrix whose t-th column is the decoded vector for horizon step t §4.2. - Temporal decoder. For each horizon step, a residual block with a single output combines the decoded vector with the projected covariates of that same step. This gives a direct path from known future covariates to the prediction §4.2.
- Global linear residual. A linear map from the look-back to the horizon is added to the output. A purely linear model is therefore always a special case of TiDE §4.2.
Training uses mini-batches of series, where each batch holds batchSize
series and their windows, with MSE as the loss §4.2. The paper says each
epoch covers all look-back and horizon pairs in the training range, and that
two mini-batches may overlap in time points §4.2.
# Residual block: one ReLU hidden layer, linear skip, dropout, layer norm [§4]
# Input: inputs (..., d_in) Output: (..., d_out)
def residual_block(inputs):
# Hidden layer with ReLU [§4]
hidden = relu(linear_a(inputs))
# Output layer; dropout is applied to this layer only [§4]
output = dropout(linear_b(hidden))
# Fully linear skip connection, added to the output [§4]
output = output + linear_skip(inputs)
# Optional layer norm on the sum; on or off per dataset [§4] [tab. 8]
return layer_norm(output)# TiDE forward pass for one series [§4.1] [§4.2] [eq. 3] [eq. 4]
# Inputs: past_values (L,), past_covariates (L, r), future_covariates (H, r)
# Output: forecast (H,)
def tide_forward(past_values, past_covariates, future_covariates):
# Project each step's covariates to a small width [§4.1] [eq. 3]
past_projected = residual_block(past_covariates) # (L, 4)
future_projected = residual_block(future_covariates) # (H, 4)
# Flatten everything into one vector and encode it [§4.1] [eq. 4]
encoder_input = concat(
past_values,
flatten(past_projected),
flatten(future_projected),
static_attributes,
)
embedding = encoder(encoder_input) # (hidden,)
# Decode one vector per future step [§4.2]
decoded = reshape(decoder(embedding), (p, H)) # (p, H)
# Per step, join the decoded vector with that step's covariates [§4.2]
temporal_inputs = concat(decoded, future_projected.T) # (p + 4, H)
forecast = temporal_decoder(temporal_inputs.T) # (H, 1)
# Add the global linear shortcut from the look-back [§4.2]
return squeeze(forecast) + linear(past_values) # (H,)Key equations. The feature projection and the encoder are numbered in the paper; the decoders are written without numbers §4.1 §4.2.
\tilde{\mathbf{x}}^{(i)}_t = \mathrm{ResidualBlock}\left(\mathbf{x}^{(i)}_t\right) eq. 3
\mathbf{e}^{(i)} = \mathrm{Encoder}\left(\mathbf{y}^{(i)}_{1:L};\; \tilde{\mathbf{x}}^{(i)}_{1:L+H};\; \mathbf{a}^{(i)}\right) eq. 4
\mathrm{MSE} = \frac{1}{NH} \sum_{i=1}^{N} \left\lVert \mathbf{y}^{(i)}_{L+1:L+H} - \hat{\mathbf{y}}^{(i)}_{L+1:L+H} \right\rVert_2^2 eq. 2
Theory. The authors study the linear analogue of the model, which is a linear map from a finite context (the last k values plus covariates) to the horizon. For data generated by a linear dynamical system with bounded norms and a stable transition matrix (0 ⪯ A ⪯ γI with γ < 1), they prove a generalization bound. The excess risk over the best system predictor is at most ε plus a term of order log(1/ε)·√(log(1/δ))/√N, with probability 1−δ, when k = Θ(log(1/ε)) app. A.1. The bound is for this linear autoregressive class, not for the full TiDE network app. A.1.
Ablations and checks. The temporal decoder helps the model adapt to future covariates more quickly, shown on a semi-synthetic electricity variant §5.4 fig. 3. Removing all residual connections lowers accuracy at horizons 96 to 336 on Electricity and leaves horizon 720 unchanged §5.4.
Hyperparameters
Per-dataset settings for the published results tab. 8. The paper sets
numEncoderLayers and numDecoderLayers separately in its tables, but every
row has them equal. Read "layers" against the code before you implement, see
Paper vs code.
| Dataset | hiddenSize | numEncoderLayers = numDecoderLayers | decoderOutputDim | temporalDecoderHidden | dropout | layerNorm | learning rate (max) | revIn | Where |
|---|---|---|---|---|---|---|---|---|---|
| Traffic | 256 | 1 | 16 | 64 | 0.3 | False | 6.55e-5 | True | tab. 8 |
| Electricity | 1024 | 2 | 8 | 64 | 0.5 | True | 9.99e-4 | False | tab. 8 |
| ETTm1 | 1024 | 1 | 8 | 128 | 0.5 | True | 8.39e-5 | False | tab. 8 |
| ETTm2 | 512 | 2 | 16 | 128 | 0.0 | True | 2.52e-4 | True | tab. 8 |
| ETTh1 | 256 | 2 | 8 | 128 | 0.3 | True | 3.82e-5 | True | tab. 8 (see Paper vs code) |
| ETTh2 | 512 | 2 | 32 | 16 | 0.2 | True | 2.24e-4 | True | tab. 8 |
| Weather | 512 | 1 | 8 | 16 | 0.0 | True | 3.01e-5 | False | tab. 8 |
Settings that apply to all experiments, and what the paper leaves open:
| Name | Value | Where | Notes |
|---|---|---|---|
| look-back L | 720 | §5.1 | Paper says 720 for all horizons. The code's ETTh1 script does not (see Paper vs code). |
| horizons H | 96, 192, 336, 720 | §5.1 tab. 2 | |
| batch size (series per batch) | 512 | app. B.3 | |
| temporal width of the projection | 4 | app. B.3 | Hidden width of that projection block: not stated in the paper. |
| optimizer | Adam, default settings | §5.1 | Betas and epsilon: not stated beyond "default". |
| learning-rate schedule | cosine decay from the tuned maximum | app. B.3 | Warm-up: not stated. Decay length: not stated. |
| loss | MSE on normalized targets | §4.2 §5.1 | |
| normalization | per-series standardization using training-period mean and std | §5.1 | |
| reversible instance normalization | tuned on or off per dataset | app. B.3 | |
| time covariates | minute of hour, hour of day, day of week, etc., scaled to [-0.5, 0.5]; "8 such features" in total | app. B.2 | Code has 7 base features plus holiday features (see Paper vs code). |
| epochs and early stopping (main results) | not stated | app. B.1 | M5 used 100 epochs and patience 5. Code defaults: 100 epochs, patience 40 train.py. |
| validation split and selection | tuned on validation loss | app. B.3 | Code selects by validation RMSE (see Paper vs code). |
| seeds | 5 runs for the reported TiDE numbers | tab. 2 tab. 5 | Seed values: not stated. |
| search space | hiddenSize {256, 512, 1024}; layers {1, 2, 3} each; decoderOutputDim {4, 8, 16, 32}; temporalDecoderHidden {32, 64, 128}; dropout {0.0, 0.1, 0.2, 0.3, 0.5}; layerNorm {on, off}; learning rate log-uniform in [1e-5, 1e-2]; revIn {on, off} | tab. 7 | Search method and number of trials: not stated. |
Results
Author-reported. Setup: seven benchmarks (Weather, Traffic, Electricity, four ETT datasets), multivariate, standardized data, MSE and MAE, four horizons, and look-back 720 §5.1 tab. 1. TiDE numbers are means over 5 runs tab. 2. Transformer and N-HiTS baseline numbers come from the PatchTST paper, and the PatchTST numbers are corrected for a test data-loader bug §5.1 tab. 2. DLinear numbers come from the DLinear paper app. B.1. The table below shows three baselines. The full table, with FEDformer, Autoformer, Informer, Pyraformer, LogTrans and N-HiTS, is in tab. 2.
| Dataset | H | TiDE MSE | TiDE MAE | PatchTST/64 MSE | PatchTST/64 MAE | DLinear MSE | DLinear MAE |
|---|---|---|---|---|---|---|---|
| Weather | 96 | 0.166 | 0.222 | 0.149 | 0.198 | 0.176 | 0.237 |
| Weather | 192 | 0.209 | 0.263 | 0.194 | 0.241 | 0.220 | 0.282 |
| Weather | 336 | 0.254 | 0.301 | 0.245 | 0.282 | 0.265 | 0.319 |
| Weather | 720 | 0.313 | 0.340 | 0.314 | 0.334 | 0.323 | 0.362 |
| Traffic | 96 | 0.336 | 0.253 | 0.360 | 0.249 | 0.410 | 0.282 |
| Traffic | 192 | 0.346 | 0.257 | 0.379 | 0.256 | 0.423 | 0.287 |
| Traffic | 336 | 0.355 | 0.260 | 0.392 | 0.264 | 0.436 | 0.296 |
| Traffic | 720 | 0.386 | 0.273 | 0.432 | 0.286 | 0.466 | 0.315 |
| Electricity | 96 | 0.132 | 0.229 | 0.129 | 0.222 | 0.140 | 0.237 |
| Electricity | 192 | 0.147 | 0.243 | 0.147 | 0.240 | 0.153 | 0.249 |
| Electricity | 336 | 0.161 | 0.261 | 0.163 | 0.259 | 0.169 | 0.267 |
| Electricity | 720 | 0.196 | 0.294 | 0.197 | 0.290 | 0.203 | 0.301 |
| ETTh1 | 96 | 0.375 | 0.398 | 0.379 | 0.401 | 0.375 | 0.399 |
| ETTh1 | 192 | 0.412 | 0.422 | 0.413 | 0.429 | 0.412 | 0.420 |
| ETTh1 | 336 | 0.435 | 0.433 | 0.435 | 0.436 | 0.439 | 0.443 |
| ETTh1 | 720 | 0.454 | 0.465 | 0.446 | 0.464 | 0.501 | 0.490 |
| ETTh2 | 96 | 0.270 | 0.336 | 0.274 | 0.337 | 0.289 | 0.353 |
| ETTh2 | 192 | 0.332 | 0.380 | 0.338 | 0.376 | 0.383 | 0.418 |
| ETTh2 | 336 | 0.360 | 0.407 | 0.363 | 0.397 | 0.448 | 0.465 |
| ETTh2 | 720 | 0.419 | 0.451 | 0.393 | 0.430 | 0.605 | 0.551 |
| ETTm1 | 96 | 0.306 | 0.349 | 0.293 | 0.346 | 0.299 | 0.343 |
| ETTm1 | 192 | 0.335 | 0.366 | 0.333 | 0.370 | 0.335 | 0.365 |
| ETTm1 | 336 | 0.364 | 0.384 | 0.369 | 0.392 | 0.369 | 0.386 |
| ETTm1 | 720 | 0.413 | 0.413 | 0.416 | 0.420 | 0.425 | 0.421 |
| ETTm2 | 96 | 0.161 | 0.251 | 0.166 | 0.256 | 0.167 | 0.260 |
| ETTm2 | 192 | 0.215 | 0.289 | 0.223 | 0.296 | 0.224 | 0.303 |
| ETTm2 | 336 | 0.267 | 0.326 | 0.274 | 0.329 | 0.281 | 0.342 |
| ETTm2 | 720 | 0.352 | 0.383 | 0.362 | 0.385 | 0.397 | 0.421 |
The paper groups TiDE with PatchTST, N-HiTS and DLinear as the clear leaders over the Transformer baselines §5.1. TiDE beats PatchTST on Traffic at all horizons by a clear margin §5.1 tab. 2. The paper reports that, apart from Weather, it either outperforms PatchTST or stays within statistical significance for most horizons §5.1. The table above shows the cases where PatchTST has the lower MSE, such as ETTh2 at horizon 720.
Demand forecasting (M5). Test WRMSSE on the private leaderboard, three runs. TiDE with static and dynamic covariates: 0.611 ± 0.009. TiDE with date features only: 0.637 ± 0.005. DeepAR with static and dynamic covariates: 0.789 ± 0.025. PatchTST (no covariates): 0.976 ± 0.014. The authors trained DeepAR and TiDE with a zero-inflated negative binomial loss, following the GluonTS template §5.2 tab. 3 app. B.1.
S4 comparison. On four datasets at horizons 336 and 720, TiDE has lower MSE than S4 in all eight pairs. The S4 numbers come from the S4 paper tab. 6 app. B.1.
Efficiency. The paper gives no numeric timing table in the text. The claims are: inference is about 5x faster and training more than 10x faster than the best Transformer §1. The Electricity timing plots show an order of magnitude gap in inference, and PatchTST runs out of memory for look-back 1440 and longer §5.3 fig. 2. The hardware was one NVIDIA T4 GPU §5.3.
Synthetic linear dynamical system. A 30-dimensional system with a Wishart-based transition matrix, look-back and horizon both 320, and 3 runs. MSE: linear 0.510 ± 0.001, LSTM 1.455 ± 0.455, Transformer 0.731 ± 0.041 tab. 4 app. A.2.
Paper vs code
Official code: the tide/ directory of google-research/google-research at
commit 3ef52111c1fabc301880818f5a547570e926a76b, Apache-2.0. Its README
names this arXiv paper and its authors. The code is TensorFlow. The paper
text has no code link and no project page. A GitHub repository search for
"tide" returned only third-party implementations, and a search inside the
google-research organization found tide/. The release has scripts for seven
datasets and none for M5.
The discrepancies below were found by reading the code and the paper. Each one is a place where the code and the paper disagree or where the paper is silent and the code decides.
- Learned series-ID embedding. The code adds a 16-dimensional embedding per series to the encoder input. The paper's encoder input contains only the past values, projected covariates and static attributes §4.1 eq. 4. models.py models.py. This means a trained model needs the series index at inference time.
- Residual block count is layers minus one.
num_layers = nbuildsn - 1residual blocks in the encoder and in the decoder. Forn = 1, the encoder is one plain linear layer with no activation, no skip, no dropout and no layer norm. The decoder is then also one plain linear layer models.py models.py. The paper describes the encoder as "multiple residual blocks" §4.1 and tunesnumEncoderLayersover {1, 2, 3} tab. 7. The published Traffic, Weather and ETTm1 settings use 1 tab. 8 and so use no residual block in the encoder or decoder traffic.sh weather.sh ettm1.sh. - Encoder and decoder depth are tied. The code has one
num_layersflag for both train.py. The paper tunes them separately tab. 7. All published rows set them equal, so this does not change the published configs. - Projection hidden width. The feature-projection block is hard-coded to hidden width 64 and output width 4 train.py. The paper gives only the output width app. B.3.
- Data splits differ for the ETT datasets. Electricity, Traffic and Weather use 70/10/20 splits, as stated in the paper §5.1. The ETT datasets use fixed 60/20/20 boundaries (for example 8640, 11520, 14400 for ETTh) train.py. The paper says 7:1:2 for all datasets §5.1.
- Dataset length is truncated. The code uses only the data up to the end of the test range. For ETTh that is 14400 points, while the paper's Table 1 lists 17420 train.py tab. 1. For ETTm it is 57600 points, against 69680 in Table 1 train.py tab. 1.
- ETTh1 look-back for horizons 336 and 720. The script sets
--hist_len=720, then sets--hist_len=336on the same command. Absl flags normally keep the last value, so the effective look-back is 336. The paper says 720 for all horizons §5.1 etth1.sh etth1.sh. This is not verified by running the code. - ETTh1 has two configurations. Horizons 96 and 192 use hidden size 512,
decoder output 32, final hidden 16, dropout 0.5, learning rate 9.85e-4, and
holiday=trueetth1.sh. The row in Table 8 matches only the horizon 336 and 720 block tab. 8. - Holiday features. Most scripts set
holiday=true. Electricity and Weather set it to false traffic.sh electricity.sh. The paper describes only time-derived features app. B.2. - Count of time features. The paper says there are 8 time features
app. B.2. The code has 7 base features (minute, hour, day of month,
day of week, day of year, month, week of year), plus holiday columns when
holidayis on time_features.py. - Model selection. The code keeps the epoch with the best validation
RMSE, not validation MSE or loss, and only after
min_num_epochstrain.py. Test metrics are recorded at that epoch train.py. The paper says the validation loss was used for tuning app. B.3. - Early stopping. The code stops once more than 40 epochs pass without improvement (patience 40), with a maximum of 100 epochs train.py train.py. The main-text results do not state this app. B.1.
- Learning-rate schedule.
CosineDecayruns over30 x training lengthoptimizer steps, with the training length measured in time steps, and no warm-up. Adam also has a value clip of 1000 train.py. The paper says only "cosine decay" app. B.3. - Batch composition per epoch. For each window, the loader draws
N // B + 1random batches of B series. Series are sampled without replacement inside a batch but can repeat across batches [code: tide/data_loader.py#L157-L160]. The paper says each epoch covers every look-back and horizon pair §4.2. For N = 862 and B = 512, some pairs appear twice in an epoch and some not at all. - Reversible normalization. The code uses a learned per-series scale and bias, plus the per-window mean and std of the look-back, and inverts both on the output models.py models.py models.py. The paper mentions only "reversible instance normalization" app. B.3.
- Categorical covariates. The code embeds categorical covariates into 4-dimensional vectors per time step, not as static attributes [code: tide/models.py#L163-L169]. The paper's static attributes are used for M5 app. B.1.
- Things the code matches. The residual block (ReLU hidden layer, linear skip, dropout on the output layer, layer norm on the sum), the temporal decoder (per-step block on decoded vector plus projected future covariates), the global linear residual from the look-back, the MSE loss and the Adam optimizer all match the paper models.py models.py §4 §4.2.
Pitfalls
- Match the published numbers or match the paper text, not both. Following
the paper's wording ("n residual blocks") gives a deeper model than the
code's
n - 1. The published Traffic, Weather and ETTm1 settings have no residual block in the encoder or decoder, as the code builds them. - The series-ID embedding limits use. A model trained with it can only forecast series it saw in training, and it needs the series index. The paper does not mention this models.py.
- ETT comparisons need the code's splits. Use 60/20/20 for ETT, not 7:1:2, or the numbers are not comparable with Table 2. Use the code's truncation as well train.py.
- Look-back for ETTh1 is ambiguous. The paper says 720 for all horizons. The script's effective value at horizons 336 and 720 is 336 [code: tide/scripts/etth1.sh#L46-L54].
- Standardized metrics. With
normalize=True, MSE and MAE are in units of the training-period standard deviation. They are not comparable with raw-scale errors on the same data §5.1 data_loader.py. - The learning-rate schedule does not depend on epochs. The decay horizon
is
30 xtraining time steps, counted in optimizer steps. Stopping early leaves the rate high. Adam's value clip is 1000, which the paper does not mention train.py. - Dropout and layer norm vanish in the one-layer case. For
num_layers = 1the encoder and decoder are plain linear layers, so dropout and layer norm apply only in the projection and temporal decoder blocks [code: tide/models.py#L64-L70]. - Theory is for a linear class. The bound in app. A.1 is for a linear autoregressive predictor on bounded LDS data. It does not prove anything about TiDE's non-linear blocks. The constants (c, γ and the O(·) factor) are not given numerical values app. A.1.
- Costs the abstract hides. Training uses batches of 512 series with hidden sizes up to 1024 tab. 8. The efficiency gain is measured on one T4 GPU with a batch size of 8 §5.3. Memory grows with the encoder input width, which is L + 4L + 4H + 16 in the code, so long look-backs raise both cost and memory models.py.
- Baseline numbers. PatchTST numbers are the PatchTST paper's, corrected by the TiDE authors for a data-loader bug that mainly affected ETTh1 and ETTh2 §5.1 tab. 2. Re-running the baseline yourself will differ.
Lineage
- Builds on: the finding that a linear model from history to horizon is strong on long-term benchmarks, which the paper attributes to DLinear §2 §4. The paper also cites N-BEATS and N-HiTS as earlier MLP forecasters §2.
- Compared against: FEDformer, Autoformer, Informer, Pyraformer, LogTrans, N-HiTS, PatchTST/64, DLinear, DeepAR (on M5) and S4 §5.1 §5.2 app. B.1.
- Superseded by: not stated in this wiki's content.
Source and license
Adapted from “Long-term Forecasting with TiDE: Time-series Dense Encoder” by Abhimanyu Das, Weihao Kong, Andrew Leach, Shaan Mathur, Rajat Sen, Rose Yu (arXiv:2304.08424), used under CC BY 4.0. Changes: condensed and restructured for implementation, with tables, equations and algorithms taken from the paper and its code, each cited to its source. Not affiliated with or endorsed by the authors. Code citations refer to https://github.com/google-research/google-research/tree/master/tide at commit 3ef52111c1fa (Apache-2.0). Any quoted code is shown under that license.