Time Series Forecasting using Pytorch (original) (raw)

Last Updated : 23 Jul, 2025

Time series forecasting plays a major role in data analysis, with applications ranging from anticipating stock market trends to forecasting weather patterns. In this article, we'll dive into the field of time series forecasting using PyTorch and LSTM (Long Short-Term Memory) neural networks. We'll uncover the critical preprocessing procedures that underpin the accuracy of our forecasts along the way.

Time Series Forecasting

Time series data is essentially a set of observations taken at regular periods of time. Time series forecasting attempts to estimate future values based on patterns and trends detected in historical data. Moving averages and traditional approaches like ARIMA have trouble capturing long-term dependencies in the data. LSTM is a type of recurrent neural network, that excels at capturing dependencies through time and able to intricate patterns.

Implementation of Time Series Forecasting

Prerequisites

Datasets

Here, we have used Yahoo Finance to get the share market dataset.

To install the Yahoo Finance, we can use the following command

!pip install yfinance

Step 1: Import Libraries and Set Configuration

This step involves importing various libraries essential for data processing, visualization, machine learning, and deep learning tasks.

Python `

import seaborn as sns from pylab import rcParams import matplotlib.pyplot as plt from matplotlib import rc

import pandas as pd import numpy as np from tqdm.notebook import tqdm

from sklearn.preprocessing import MinMaxScaler

import torch import torch.nn as nn import torch.optim as optim from torch.utils.data import DataLoader, TensorDataset from sklearn.metrics import mean_squared_error, r2_score from collections import defaultdict

%matplotlib inline

sns.set(style='whitegrid', palette='muted', font_scale=1.2)

Colour_Palette = ['#01BEFE', '#FF7D00', '#FFDD00', '#FF006D', '#ADFF02', '#8F00FF'] sns.set_palette(sns.color_palette(Colour_Palette))

tqdm.pandas()

`

Step 2: Load and Inspect Data

Download the historical stock price data for Apple Inc. (AAPL) from Yahoo Finance. Inspect the data using df.head() and df.info() to understand its structure and contents.

Python `

import yfinance as yf from datetime import date

end_date = date.today().strftime("%Y-%m-%d") start_date = '1990-01-01'

df = yf.download('AAPL', start=start_date, end=end_date)

df.columns = df.columns.droplevel() df.columns = ['Open', 'High', 'Low', 'Close', 'Volume']

print(df.head()) print(df.info())

`

**Output:

[100%**] 1 of 1 completed

            Open      High       Low     Close     Volume  

Date
1990-01-02 0.261498 0.263253 0.245703 0.247458 183198400
1990-01-03 0.263253 0.266764 0.263253 0.266764 207995200
1990-01-04 0.264132 0.272029 0.261498 0.268519 221513600
1990-01-05 0.265009 0.268519 0.259744 0.265009 123312000
1990-01-08 0.266764 0.266764 0.259744 0.263253 101572800
<class 'pandas.core.frame.DataFrame'>
DatetimeIndex: 8940 entries, 1990-01-02 to 2025-07-01
Data columns (total 5 columns):

Column Non-Null Count Dtype


0 Open 8940 non-null float64
1 High 8940 non-null float64
2 Low 8940 non-null float64
3 Close 8940 non-null float64
4 Volume 8940 non-null int64
dtypes: float64(4), int64(1)
memory usage: 419.1 KB
None

Step 3: Plot Data

Define a function to plot the data using line plots for each column in the DataFrame. This helps visualize trends and patterns in the data.

Python `

import matplotlib.dates as mdates

def data_plot(df): # Plot line charts df_plot = df.copy()

ncols = 2
nrows = int(round(df_plot.shape[1] / ncols, 0))

fig, ax = plt.subplots(nrows=nrows, ncols=ncols, sharex=True, figsize=(14, 7))
for i, ax in enumerate(fig.axes):
    sns.lineplot(data=df_plot.iloc[:, i], ax=ax)
    ax.tick_params(axis="x", rotation=30, labelsize=10, length=0)
    ax.xaxis.set_major_locator(mdates.AutoDateLocator())
fig.tight_layout()
plt.show()

Plot the data

data_plot(df)

`

**Output:

plotting-data-min-min

Plotting Columns in Our Data

Step 4: Preprocess Data

In this step, we split the data into training and testing sets, and normalize the values using MinMaxScaler. This preprocessing is essential for preparing the data for machine learning models.

import math from sklearn.preprocessing import MinMaxScaler

Train test split

training_data_len = math.ceil(len(df) * .8) print(training_data_len)

Splitting the dataset

train_data = df[:training_data_len].iloc[:, :1] test_data = df[training_data_len:].iloc[:, :1] print(train_data.shape, test_data.shape)

Selecting Open Price values

dataset_train = train_data.Open.values

Reshaping 1D to 2D array

dataset_train = np.reshape(dataset_train, (-1, 1)) print(dataset_train.shape)

Selecting Open Price values

dataset_test = test_data.Open.values

Reshaping 1D to 2D array

dataset_test = np.reshape(dataset_test, (-1, 1)) print(dataset_test.shape)

scaler = MinMaxScaler(feature_range=(0, 1))

Scaling dataset

scaled_train = scaler.fit_transform(dataset_train) print(scaled_train[:5])

Normalizing values between 0 and 1

scaled_test = scaler.fit_transform(dataset_test) print(scaled_test[:5])

`

**Output:

7152
(7152, 1) (1788, 1)
(7152, 1)
(1788, 1)
[[0.00367675]
[0.00371602]
[0.00373567]
[0.00375531]
[0.00379458]]
[[0.04682367]
[0.04632846]
[0.04759275]
[0.04737147]
[0.04782451]]

Step 5: Create Sequences and Labels for Training and Testing

We structure the data into sequences for the LSTM model. Each sequence contains a specified number of time steps. We then convert the data into PyTorch tensors, which are necessary for input into the PyTorch model.

Create sequences and labels for training data

sequence_length = 50 # Number of time steps to look back X_train, y_train = [], [] for i in range(len(scaled_train) - sequence_length): X_train.append(scaled_train[i:i + sequence_length]) y_train.append(scaled_train[i + sequence_length]) # Predicting the value right after the sequence X_train, y_train = np.array(X_train), np.array(y_train)

Convert data to PyTorch tensors

X_train = torch.tensor(X_train, dtype=torch.float32) y_train = torch.tensor(y_train, dtype=torch.float32) print(X_train.shape, y_train.shape)

Create sequences and labels for testing data

sequence_length = 30 # Number of time steps to look back X_test, y_test = [], [] for i in range(len(scaled_test) - sequence_length): X_test.append(scaled_test[i:i + sequence_length]) y_test.append(scaled_test[i + sequence_length]) # Predicting the value right after the sequence X_test, y_test = np.array(X_test), np.array(y_test)

Convert data to PyTorch tensors

X_test = torch.tensor(X_test, dtype=torch.float32) y_test = torch.tensor(y_test, dtype=torch.float32) print(X_test.shape, y_test.shape)

`

**Output:

torch.Size([7102, 50, 1]) torch.Size([7102, 1])
torch.Size([1758, 30, 1]) torch.Size([1758, 1])

Step 6: Define and Train the LSTM Model

Define an LSTM model for time series forecasting. The model includes an LSTM layer followed by a fully connected layer. Train the model using the training data and evaluate it on the test data.

class LSTMModel(nn.Module): def init(self, input_size, hidden_size, num_layers, output_size, dropout=0.2): super(LSTMModel, self).init() self.lstm = nn.LSTM(input_size, hidden_size, num_layers, batch_first=True, dropout=dropout) self.linear = nn.Linear(hidden_size, output_size)

def forward(self, x):
    out, _ = self.lstm(x)
    out = self.linear(out[:, -1, :])
    return out

device = torch.device('cuda' if torch.cuda.is_available() else 'cpu') print(device)

input_size = 1 num_layers = 3 # Increased number of layers hidden_size = 128 # Increased number of hidden units output_size = 1 dropout = 0.2 # Added dropout for regularization

model = LSTMModel(input_size, hidden_size, num_layers, output_size, dropout).to(device) loss_fn = nn.MSELoss(reduction='mean') optimizer = optim.Adam(model.parameters(), lr=1e-3) # Learning rate

batch_size = 32 # Adjusted batch size train_dataset = TensorDataset(X_train, y_train) train_loader = DataLoader(train_dataset, batch_size=batch_size, shuffle=True) test_dataset = TensorDataset(X_test, y_test) test_loader = DataLoader(test_dataset, batch_size=batch_size, shuffle=False)

num_epochs = 100 # Increased number of epochs train_hist = [] test_hist = []

for epoch in range(num_epochs): total_loss = 0.0 model.train() for batch_X, batch_y in train_loader: batch_X, batch_y = batch_X.to(device), batch_y.to(device) predictions = model(batch_X) loss = loss_fn(predictions, batch_y)

    optimizer.zero_grad()
    loss.backward()
    optimizer.step()

    total_loss += loss.item()

average_loss = total_loss / len(train_loader)
train_hist.append(average_loss)

model.eval()
with torch.no_grad():
    total_test_loss = 0.0

    for batch_X_test, batch_y_test in test_loader:
        batch_X_test, batch_y_test = batch_X_test.to(device), batch_y_test.to(device)
        predictions_test = model(batch_X_test)
        test_loss = loss_fn(predictions_test, batch_y_test)

        total_test_loss += test_loss.item()

    average_test_loss = total_test_loss / len(test_loader)
    test_hist.append(average_test_loss)

if (epoch + 1) % 10 == 0:
    print(f'Epoch [{epoch + 1}/{num_epochs}] - Training Loss: {average_loss:.4f}, Test Loss: {average_test_loss:.4f}')

`

**Output:

cuda
Epoch [10/100] - Training Loss: 0.0003, Test Loss: 0.0008
Epoch [20/100] - Training Loss: 0.0002, Test Loss: 0.0005
Epoch [30/100] - Training Loss: 0.0002, Test Loss: 0.0004
Epoch [40/100] - Training Loss: 0.0001, Test Loss: 0.0003
Epoch [50/100] - Training Loss: 0.0001, Test Loss: 0.0002
Epoch [60/100] - Training Loss: 0.0001, Test Loss: 0.0005
Epoch [70/100] - Training Loss: 0.0001, Test Loss: 0.0002
Epoch [80/100] - Training Loss: 0.0001, Test Loss: 0.0002
Epoch [90/100] - Training Loss: 0.0001, Test Loss: 0.0002
Epoch [100/100] - Training Loss: 0.0001, Test Loss: 0.0002

Python `

x = np.linspace(1,num_epochs,num_epochs) plt.plot(x,train_hist,scalex=True, label="Training loss") plt.plot(x, test_hist, label="Test loss") plt.legend() plt.show()

`

**Output:

2

Comparing Training and Test Losses

Step 7: Forecast Future Values and Evaluate the Model

Use the trained model to forecast future values. Evaluate the model's performance using metrics like RMSE and R² score.

num_forecast_steps = 30 sequence_to_plot = X_test.squeeze().cpu().numpy() historical_data = sequence_to_plot[-1]

forecasted_values = [] with torch.no_grad(): for _ in range(num_forecast_steps): historical_data_tensor = torch.as_tensor(historical_data).view(1, -1, 1).float().to(device) predicted_value = model(historical_data_tensor).cpu().numpy()[0, 0] forecasted_values.append(predicted_value) historical_data = np.roll(historical_data, shift=-1) historical_data[-1] = predicted_value

last_date = test_data.index[-1] future_dates = pd.date_range(start=last_date + pd.DateOffset(1), periods=30)

plt.rcParams['figure.figsize'] = [14, 4] plt.plot(test_data.index[-100:], test_data.Open[-100:], label="test_data", color="b") plt.plot(test_data.index[-30:], test_data.Open[-30:], label='actual values', color='green') plt.plot(test_data.index[-1:].append(future_dates), np.concatenate([test_data.Open[-1:], scaler.inverse_transform(np.array(forecasted_values).reshape(-1, 1)).flatten()]), label='forecasted values', color='red')

plt.xlabel('Time Step') plt.ylabel('Value') plt.legend() plt.title('Time Series Forecasting') plt.grid(True) plt.show()

Evaluate the model and calculate RMSE and R² score

model.eval() with torch.no_grad(): test_predictions = [] for batch_X_test in X_test: batch_X_test = batch_X_test.to(device).unsqueeze(0) # Add batch dimension test_predictions.append(model(batch_X_test).cpu().numpy().flatten()[0])

test_predictions = np.array(test_predictions)

Calculate RMSE and R² score

rmse = np.sqrt(mean_squared_error(y_test.cpu().numpy(), test_predictions)) r2 = r2_score(y_test.cpu().numpy(), test_predictions)

print(f'RMSE: {rmse:.4f}') print(f'R² Score: {r2:.4f}')

`

**Output:

3

Forecasting Values

By plotting the test data, actual values and model's forecasting data. We got a clear idea of how well the forecasted values are aligning with the actual time series.

The intriguing field of time series forecasting using PyTorch and LSTM neural networks has been thoroughly examined in this paper. In order to collect historical stock market data using Yahoo Finance module, we imported the **yfinance library and started the preprocessing step. Then we applied crucial actions like data loading, train-test splitting, and data scaling to make sure our model could accurately learn from the data and make predictions.

For more accurate forecasts, additional adjustments, hyperparameter tuning, and optimization are frequently needed. To improve predicting capabilities, ensemble methods and other cutting-edge methodologies can be investigated.

We have barely begun to explore the enormous field of time series forecasting in this essay. There is a ton more to learn, from managing multi-variate time series to resolving practical problems in novel ways. With this knowledge in hand, you're prepared to use PyTorch and LSTM neural networks to go out on your own time series forecasting adventures.

Enjoy your forecasting!

Get the Complete Notebook:

**Notebook: **click here.