Source code for tfts.models.dlinear

"""
`Are Transformers Effective for Time Series Forecasting?
<https://arxiv.org/abs/2205.13504>`_
"""

from typing import Optional

import tensorflow as tf
from tensorflow.keras.layers import Dense, Lambda

from tfts.layers.autoformer_layer import SeriesDecomp

from .base import BaseConfig, BaseModel


[docs] class DLinearConfig(BaseConfig): model_type: str = "dlinear" def __init__( self, kernel_size: int = 25, channels: int = 3, individual: bool = False, ): super().__init__() self.kernel_size = kernel_size self.channels = channels # number of input features self.individual = individual self.activation: Optional[str] = None self.initializer: str = "glorot_uniform" def __post_init__(self): """Validate configuration parameters.""" if self.channels <= 0: raise ValueError(f"channels must be positive, got {self.channels}") if self.kernel_size <= 0 or self.kernel_size % 2 == 0: raise ValueError(f"kernel_size must be positive and odd, got {self.kernel_size}") if not 0 <= self.dropout_rate < 1: raise ValueError(f"dropout_rate must be in [0, 1), got {self.dropout_rate}")
[docs] class DLinear(BaseModel): """DLinear Network for Time Series Forecasting.""" def __init__(self, predict_sequence_length: int = 1, config: Optional[DLinearConfig] = None): super(DLinear, self).__init__() self.config = config or DLinearConfig() self.predict_sequence_length = predict_sequence_length self.decomposition = SeriesDecomp(self.config.kernel_size) if self.config.individual: self.linear_seasonal = [Dense(self.predict_sequence_length) for _ in range(self.config.channels)] self.linear_trend = [Dense(self.predict_sequence_length) for _ in range(self.config.channels)] else: self.linear_seasonal = Dense(self.predict_sequence_length) self.linear_trend = Dense(self.predict_sequence_length) self.project = Dense(1) def __call__( self, inputs: tf.Tensor, output_hidden_states: Optional[bool] = None, return_dict: Optional[bool] = None ): """DLinear model forward pass. Args: inputs: Input time-series data of shape [batch_size, seq_len, channels]. training: Whether the model is in training mode. output_hidden_states: Whether to return intermediate hidden states. return_dict: Whether to return outputs as a dictionary. Returns: If return_dict is False: Forecasted values tensor of shape [batch_size, predict_sequence_length, input_dim] If return_dict is True: Dictionary containing: - 'predictions': Forecasted values - 'seasonal_component': Seasonal component (if output_hidden_states is True) - 'trend_component': Trend component (if output_hidden_states is True) """ # Decompose the input into trend and seasonal components seasonal, trend = self.decomposition(inputs) seasonal = Lambda(lambda x: tf.transpose(x, [0, 2, 1]))(seasonal) trend = Lambda(lambda x: tf.transpose(x, [0, 2, 1]))(trend) if self.config.individual: seasonal_output = [] trend_output = [] for i in range(self.config.channels): seasonal_output.append(self.linear_seasonal[i](seasonal[:, i, :])) trend_output.append(self.linear_trend[i](trend[:, i, :])) seasonal_output = tf.stack(seasonal_output, axis=1) trend_output = tf.stack(trend_output, axis=1) else: seasonal_output = self.linear_seasonal(seasonal) trend_output = self.linear_trend(trend) output = seasonal_output + trend_output output = Lambda(lambda t: tf.transpose(t, [0, 2, 1]))(output) output = self.project(output) return output