N-HiTS: neural hierarchical interpolation for time series forecasting.

Classes

NHITSForecaster

class algorithms.timeseries.deep.nhits.NHITSForecaster(DeepForecaster)

N-HiTS: N-BEATS with multi-rate sampling and hierarchical interpolation.

N-HiTS keeps the doubly-residual skeleton of N-BEATS and adds the two ideas that make long horizons tractable. First, multi-rate signal sampling: each stack max-pools its input at a different rate before the fully connected trunk sees it, so a stack with a large pooling size literally cannot see high-frequency detail and is forced to model the slow component. Second, hierarchical interpolation: a stack predicts only a handful of knots and interpolates them up to the full horizon, so the number of parameters in the output layer no longer grows with the horizon.

Together the two make the stacks specialise by frequency — coarse stacks supply the smooth backbone, fine stacks the detail — while cutting both compute and parameter count against N-BEATS at long horizons. Remove either one and what remains is N-BEATS with extra steps.

Constructor
__init__(
    self,
    lookback: int = 24,
    horizon: int = 8,
    pooling_sizes: Tuple[int, Ellipsis] = (),
    n_freq_downsample: Tuple[int, Ellipsis] = (),
    n_blocks: int = 1,
    n_layers: int = 2,
    hidden_size: int = 64,
    interpolation_mode: str = 'linear',
    backcast_loss_weight: float = 0.5,
    n_epochs: int = 100,
    batch_size: int = 32,
    learning_rate: float = 0.001,
    patience: int = 15,
    device: str = 'cpu',
    random_state: Optional[int] = None,
)

Overview

  1. Slide a (lookback, horizon) window over the series and normalise
each window by its own statistics.
  1. For stack s, max-pool the incoming residual with kernel
k_s — large for the first stack, 1 for the last.
  1. Run the pooled signal through a fully connected trunk and predict
\lceil H / r_s \rceil forecast knots and a pooled backcast.
  1. Interpolate both back to full resolution: the backcast to the lookback,
the forecast to the horizon.
  1. Subtract the backcast, pass the residual on, and sum the forecasts.

Theory

Stack s first pools its input x_s at rate k_s,

x^{p}_s = \text{MaxPool}_{k_s}(x_s),

which acts as an anti-alias filter: frequencies above 1/(2k_s) are removed before the trunk sees them. The trunk emits knots \theta_s \in \mathbb{R}^{\lceil H/r_s \rceil} that are expanded by a temporal interpolation operator g,

\hat{y}_s[t] = g(\theta_s)[t], \qquad t = 1, \dots, H,

with g linear interpolation over the knot grid. Choosing r_s in step with k_s gives each stack a matched input and output bandwidth; the forecast is again the sum, \hat{y} = \sum_s \hat{y}_s, over residuals x_{s+1} = x_s - \hat{x}_s.

Parameters

lookback
int = 24
Length of the input window. Automatically shrunk when the series is too short to yield training windows.
horizon
int = 8
Number of steps the network is trained to emit at once. Longer forecasts are produced by autoregressive rollout.
pooling_sizes
tuple of int, 2, 1) = (4
Max-pooling rate per stack, coarsest first. One entry per stack.
n_freq_downsample
tuple of int, 2, 1) = (4
Interpolation ratio per stack; a stack predicts ceil(horizon / ratio) knots. Must be the same length as pooling_sizes.
n_blocks
int = 1
Blocks per stack.
n_layers
int = 2
Fully connected layers in each block's trunk.
hidden_size
int = 64
Width of the trunk. The paper uses 512; the default here keeps the generic algorithm contract, which fits every model on sixty points, fast. Real use wants 256-512.
interpolation_mode
{"linear", "nearest"} = "linear"
How knots are expanded to full resolution.
backcast_loss_weight
float = 0.5
Weight on a penalty applied to the residual leaving the last block. The published objective supervises only the forecast, which leaves the backcast heads free to grow the residual; a small weight here is what makes the residual cascade real. Set to 0 for the paper's exact objective.
n_epochs
int = 100
Maximum passes over the window dataset. Real use wants several hundred to a few thousand.
batch_size
int = 32
Windows per gradient step.
learning_rate
float = 0.001
Adam step size.
patience
int = 15
Epochs without an improvement in training loss before stopping early.
device
{"cpu", "auto", "cuda", "mps"} = "cpu"
Where to run. The default is "cpu" because it is the only setting that gives bit-identical forecasts across machines; use "auto" when speed matters more than exact reproducibility.
random_state
int = None
Seed for weight initialisation and batch shuffling.

Attributes

module_
torch.nn.Module
The trained network.
lookback_
int
Lookback actually used, after any shrinking.
horizon_
int
Horizon actually used, after any shrinking.
n_windows_
int
Number of training windows built from the series.
offset_
float
Mean of the training series, removed before fitting.
scale_
float
Standard deviation of the training series, divided out before fitting.
series_
np.ndarray of shape (n_samples,)
The globally scaled training series, kept for the rollout.
loss_curve_
np.ndarray of shape (n_epochs_run_,)
Mean training loss per epoch.
n_epochs_run_
int
Epochs actually run before early stopping.
device_
str
Resolved device string.

Notes

Requires PyTorch: pip install 'tuiml[torch]'. The class imports, constructs, registers and reports its schema without torch; only fit needs it.

Complexity:

  • Training: O(E \cdot W \cdot S \cdot B \cdot h^2) for
E epochs, W windows, S stacks and B trunk layers of width h. Unlike N-BEATS the output layer is O(h \cdot H / r) rather than O(h \cdot H).
  • Prediction: O(\lceil s / H \rceil \cdot S \cdot B \cdot h^2).
When to use NHITSForecaster:
  • Long horizons, where N-BEATS output layers become the bottleneck.
  • Series with structure at clearly separated timescales.
  • When you want N-BEATS accuracy at a fraction of the parameters.
  • Not for very short series; a classical model will usually win.

References

Challu2023
Challu, C., Olivares, K. G., Oreshkin, B. N., Garza, F., Mergenthaler-Canseco, M., & Dubrawski, A. (2023). N-HiTS: Neural hierarchical interpolation for time series forecasting. Proceedings of the AAAI Conference on Artificial Intelligence, 37(6), 6989-6997. https://doi.org/10.1609/aaai.v37i6.25854
python
>>> import numpy as np
>>> from tuiml.algorithms.timeseries.deep import NHITSForecaster
>>> from tuiml.utils.torch_backend import has_torch
>>> model = NHITSForecaster(lookback=24, horizon=6, random_state=0)
>>> model.pooling_sizes
(4, 2, 1)
>>> if has_torch():
...     y = np.sin(np.arange(200) / 5.0)
...     _ = model.fit(y)
...     print(model.predict(steps=6).shape)
... else:
...     print("(6,)")
(6,)

Methods

get_parameter_schema (cls) -> Dict[str, Dict[str, Any]]

Return JSON Schema for algorithm parameters.

get_complexity (cls) -> str

Return complexity analysis.

get_references (cls) -> List[str]

Return academic citations.

Functions

Func

pooled_length

Line 17
pooled_length(length: int, pooling_size: int) -> int

Return the length of a max-pooled sequence.

Pooling uses ceil_mode, so a partial final window still yields an output point and no data is silently dropped from the end of the lookback — the end being the part that matters most for a forecast.

Parameters

length
int
Input sequence length.
pooling_size
int
Pooling kernel and stride.

Returns

length
int
Output sequence length, at least 1.
python
>>> from tuiml.algorithms.timeseries.deep.nhits import pooled_length
>>> pooled_length(24, 4), pooled_length(10, 4), pooled_length(3, 8)
(6, 3, 1)
Func

interpolation_length

Line 46
interpolation_length(horizon: int, downsample: int) -> int

Return how many knots a stack predicts before interpolating up.

Parameters

horizon
int
Full forecast length.
downsample
int
Expressiveness ratio for this stack. A large value means few knots and so a smooth, low-frequency contribution.

Returns

n_knots
int
Number of predicted knots, at least 1 and at most horizon.
python
>>> from tuiml.algorithms.timeseries.deep.nhits import interpolation_length
>>> interpolation_length(24, 8), interpolation_length(24, 1)
(3, 24)