N-BEATS: The Unique Interpretable Deep Learning Model for Time Series Forecasting
Introduction
In various sectors such as finance, retail, and meteorology, time series forecasting is pivotal. Traditional models often grapple with flexibility and interpretability, particularly when dealing with complex patterns. Enter N-BEATS, a groundbreaking model developed by researchers at Element AI and the Montreal Institute for Learning Algorithms (MILA). Renowned for its interpretability and robust forecasting capabilities, N-BEATS is a true game-changer.
What is N-BEATS?
N-BEATS (Neural Basis Expansion Analysis for Time Series Forecasting) revolutionizes the approach to time series predictions. Distinct from typical models that depend on recurrent neural networks (RNNs), N-BEATS employs a series of feed-forward neural networks. This structure not only enhances performance but also circumvents the complexities and instabilities often associated with RNNs.
Key Features of N-BEATS
Architecture Deep Dive
How N-BEATS Works
Example: Daily Temperature Data
To demonstrate N-BEATS, let’s use the daily temperature dataset, a common benchmark in time series modeling. It’s a sine wave with a period of one year (365 days) and an amplitude of 10. This example will show how to implement N-BEATS using PyTorch and interpret the model’s outputs to understand the influence of different time series components.
Lets explore the main parts for the exercise: (Link to the full code is in the reference section)
# Step 1: Function to create a synthetic temperature dataset
def create_temperature_dataset(length, num_samples):
np.random.seed(0)
x = np.linspace(0, length, num_samples)
seasonal = 10 + 10 * np.sin(2 * np.pi * x / 365)
noise = np.random.normal(0, 2, num_samples)
y = seasonal + noise
return x, y
# Create dataset
x, y = create_temperature_dataset(365, 3650)
# Plot the dataset
# Step 2: Define a PyTorch Dataset
class TimeSeriesDataset(Dataset):
def __init__(self, data, backcast_length, forecast_length):
self.data = data
self.backcast_length = backcast_length
self.forecast_length = forecast_length
def __len__(self):
return len(self.data) - self.backcast_length - self.forecast_length
def __getitem__(self, index):
x = self.data[index : index + self.backcast_length]
y = self.data[index + self.backcast_length : index + self.backcast_length + self.forecast_length]
return torch.tensor(x, dtype=torch.float32), torch.tensor(y, dtype=torch.float32)
# Parameters
backcast_length = 30
forecast_length = 7
# Create dataset
dataset = TimeSeriesDataset(y, backcast_length, forecast_length)
dataloader = DataLoader(dataset, batch_size=32, shuffle=True)
The TimeSeriesDataset class in PyTorch is designed to handle time series data by leveraging backcasting and forecasting techniques. It requires three parameters for initialization: data (the time series data), backcast_length(the number of past time steps to consider), and forecast_length (the number of future time steps to predict). The class has methods to return the length of the dataset and to fetch data samples, converting slices of the time series into tensors. In this example, if you set backcast_length to 30 and forecast_length to 7, you can create an instance of the dataset and a DataLoader to handle batching and shuffling of the data as follows: dataset = TimeSeriesDataset(y, backcast_length, forecast_length) and dataloader = DataLoader(dataset, batch_size=32, shuffle=True). This setup facilitates the training of models by providing efficient data handling.
# Step 3: Define and Train the N-BEATS Model
device = 'cuda' if torch.cuda.is_available() else 'cpu'
# Define N-BEATS model
model = NBeatsNet(
device=device,
stack_types=(NBeatsNet.GENERIC_BLOCK, NBeatsNet.GENERIC_BLOCK),
forecast_length=forecast_length,
backcast_length=backcast_length,
hidden_layer_units=128
).to(device)
# Define loss and optimizer
criterion = torch.nn.MSELoss()
optimizer = torch.optim.Adam(model.parameters())
# Training loop
epochs = 50
for epoch in range(epochs):
for x_batch, y_batch in dataloader:
x_batch, y_batch = x_batch.to(device), y_batch.to(device)
optimizer.zero_grad()
backcast, forecast = model(x_batch)
loss = criterion(forecast, y_batch)
loss.backward()
optimizer.step()
if (epoch + 1) % 10 == 0:
print(f'Epoch {epoch + 1}/{epochs}, Loss: {loss.item()}')
The N-BEATS model uses a series of fully connected layers organized into blocks, each with sub-networks for backcasting and forecasting. Configured with parameters like backcast and forecast lengths and the number of hidden units, the model captures complex data patterns.
The loss function is set to mean squared error (MSE), and the Adam optimizer adjusts the model’s parameters to minimize this loss. Training involves iterating over the dataset for several epochs, processing data batches, computing forecasts, and updating parameters via backpropagation. Progress is monitored by checking loss values to ensure effective learning.
# Step 4: Visualize Backcast, Forecast, and Model Weights
model.eval()
x_batch, y_batch = next(iter(dataloader))
x_batch, y_batch = x_batch.to(device), y_batch.to(device)
with torch.no_grad():
backcast, forecast = model(x_batch)
# Convert to numpy for plotting
backcast = backcast.cpu().numpy()
forecast = forecast.cpu().numpy()
x_batch = x_batch.cpu().numpy()
y_batch = y_batch.cpu().numpy()
# Plot results
Redracted
# Visualize model weights
weights = [param.cpu().data.numpy() for param in model.parameters()]
# Plot weights of the first few layers for illustration
Redracted
Visualize Backcast, Forecast, and Model Weights
To visualize the backcast, forecast, and model weights, set the model to evaluation mode and obtain a batch of data from the DataLoader. Use this batch to compute the backcast and forecast without updating model parameters. Convert these results to NumPy arrays for plotting.
Create plots to show the backcast input, actual forecast, predicted backcast, and forecast. Use matplotlib to display these results, showing the time series and predictions.
The plot visualizes the N-BEATS model’s performance.
Visualize model weights
Next, extract and visualize the model weights. Convert the model parameters to NumPy arrays and plot the weights of the first few layers for illustration. This helps in understanding the model’s learned features and patterns. The visualizations of the N-BEATS model’s weights reveal the following:
Key Hyper-parameters in the N-BEATS Example
Your model will always not give you the best performance, which you can benchmark with metrics such as MSE, MAE. Hyper parameter tuning is a great way to adapt your model to your particular use case. Here are the common parameter which can be tuned for optimal performance.
2. Number of Epochs
3. Batch Size
4. Number of Layers and Neurons per Layer
5. Network Architecture Adjustments
# Define hyperparameter grid
hyperparams = {
'backcast_length': [15, 30, 60],
'forecast_length': [5, 7, 10],
'hidden_layer_units': [64, 128, 256],
'num_blocks': [2, 3, 4],
'learning_rate': [0.001, 0.01, 0.1],
'batch_size': [16, 32, 64],
'epochs': [50, 100, 200]
}
Conclusion
N-BEATS is more than just a forecasting tool; it’s a robust, interpretable framework that democratizes deep learning for time series analysis. Its capability to provide clear insights into what drives forecasts makes it invaluable for businesses reliant on accurate, explainable predictions.
Call to Action
For those looking to push the boundaries of time series forecasting, implementing N-BEATS provides a perfect blend of performance and interpretability, transforming decision-making processes. Try applying N-BEATS to your data and delve into the depth of insights it can offer.
Helpful Resources
This article bridges the gap between complex theoretical models and practical, actionable insights, helping you harness the power of N-BEATS in your forecasting endeavors.