Open In App

Time Series Forecasting using Recurrent Neural Networks (RNN) in TensorFlow

Last Updated : 31 Jul, 2023
Improve
Improve
Like Article
Like
Save
Share
Report

Time Series Data: Each data point in a time series is linked to a timestamp, which shows the exact time when the data was observed or recorded. Many fields, including finance, economics, weather forecasting, and machine learning, frequently employ this kind of data.

The fact that time series data frequently display patterns or trends across time, such as seasonality or cyclical patterns, is an essential feature associated with it. To make predictions or learn more about the underlying processes or occurrences being observed, these patterns can be analyzed and modeled.

Recurrent Neural Networks (RNN) model the temporal dependencies present in the data as it contains an implicit memory of previous inputs. Hence, time series data being sequential in nature is often used in RNN. For working with time series data in RNNs, TensorFlow provides a number of APIs and tools, like tf.keras.layers.RNN API, which allows to create of unique RNN cell classes and use them with data. Several RNN cell types are also supported by this API, including Basic RNN, LSTM, and GRU. 

To demonstrate the same, we’re going the run the following code snippets in Google Colaboratory which comes pre-installed with Machine Learning and Deep Learning Libraries. This example will use stock price data, the most popular type of time series data.

Step 1: Import the required libraries.

  • Numpy & Pandas – For data manipulation and analysis
  • Matplotlib – For data visualization.
  • Yahoo Finance – Provides financial data for analysis.
  • Datetime – For working with dates and times.
  • Math – Provides basic mathematical functions in Python.

Python3




import numpy as np
import pandas as pd
import yfinance as yf
import datetime as dt
import matplotlib.pyplot as plt
import math


Step 2: This code uses the yf.download() method of the yfinance library to download historical stock data for Google from Yahoo Finance. Using the dt.datetime() method of the datetime module, the start and end dates of the time period for which the data has been obtained are given.

The downloaded data is then shown using the print() function, where the Pandas DataFrame’s display options are configured using pd.set_option().

Python3




#the start and end date
start_date = dt.datetime(2020,4,1)
end_date = dt.datetime(2023,4,1)
 
#loading from yahoo finance
data = yf.download("GOOGL",start_date, end_date)
 
pd.set_option('display.max_rows', 4)
pd.set_option('display.max_columns',5)
print(data)


Output:

[*********************100%***********************]  1 of 1 completed
                  Open        High  ...   Adj Close    Volume
Date                                ...                      
2020-04-01   56.200001   56.471001  ...   55.105000  51970000
2020-04-02   55.000000   56.138500  ...   55.851501  56410000
...                ...         ...  ...         ...       ...
2023-03-30  100.910004  101.160004  ...  100.889999  33086200
2023-03-31  101.300003  103.889999  ...  103.730003  36823200

[756 rows x 6 columns]

Step 3: Next, we split the dataset into training and testing in the ratio 80:20. Only the first column of the data is chosen using iloc[:,:1] and the train_data contains the first training_data_len rows of the original data. The test_data, contains all of the remaining rows of the original data starting from training_data_len to the end.

Python3




# Setting 80 percent data for training
training_data_len = math.ceil(len(data) * .8)
training_data_len
 
#Splitting the dataset
train_data = data[:training_data_len].iloc[:,:1]
test_data = data[training_data_len:].iloc[:,:1]
print(train_data.shape, test_data.shape)


 Output:

(605, 1) (151, 1)

Step 4: This code creates a numpy array called dataset_train and populates it with the “Open” pricing values from the training data. The 1-dimensional array is then transformed into a 2-dimensional array. The shape property, which returns the tuple (num_rows, num_columns) denoting the dataset_train array’s final shape.

Python3




# Selecting Open Price values
dataset_train = train_data.Open.values
# Reshaping 1D to 2D array
dataset_train = np.reshape(dataset_train, (-1,1))
dataset_train.shape


Output:

(605, 1)

Step 5: Normalization is a crucial step in data preprocessing to enhance the effectiveness and interpretability of machine learning models. Hence MinMaxScaler from sklearn is imported to scale the dataset from 0 to 1. Using the sklearn fit_transform() method, the training dataset is scaled.

Python3




from sklearn.preprocessing import MinMaxScaler
scaler = MinMaxScaler(feature_range=(0,1))
# scaling dataset
scaled_train = scaler.fit_transform(dataset_train)
 
print(scaled_train[:5])


Output:

[[0.01246754]
 [0.        ]
 [0.00764156]
 [0.01714287]
 [0.0607844 ]]

Step 6: The same data preprocessing is done for test data.

Python3




# Selecting Open Price values
dataset_test = test_data.Open.values
# Reshaping 1D to 2D array
dataset_test = np.reshape(dataset_test, (-1,1)) 
# Normalizing values between 0 and 1
scaled_test = scaler.fit_transform(dataset_test) 
print(*scaled_test[:5])


Output:

[0.98362881] [1.] [0.83867656] [0.84481572] [0.86118691]

Step 7: The time-series data must be divided into X_train and y_train from the training set and X_test and y_test from the testing set in this phase. It is done to turn time series data into a supervised learning problem that can be utilized to train the model. The loop generates input/output sequences of length 50 while iterating through the time series data. Using this method, we can forecast future values while taking into consideration the data’s temporal dependence on prior observations.

For training set:

Python3




X_train = []
y_train = []
for i in range(50, len(scaled_train)):
    X_train.append(scaled_train[i-50:i, 0])
    y_train.append(scaled_train[i, 0])
    if i <= 51:
        print(X_train)
        print(y_train)
        print()


Output:

[array([0.01246754, 0.        , 0.00764156, 0.01714287, 0.0607844 ,
       0.05355843, 0.06139221, 0.05272728, 0.0727117 , 0.0761091 ,
       0.08682596, 0.0943896 , 0.08825454, 0.07413508, 0.0733039 ,
       0.08609869, 0.08051948, 0.09974024, 0.09516887, 0.12727273,
       0.12018702, 0.11641037, 0.1081195 , 0.12337662, 0.13402599,
       0.13574544, 0.14640004, 0.14378702, 0.16011432, 0.14345973,
       0.12130912, 0.12896625, 0.13588574, 0.14830132, 0.15021299,
       0.16155324, 0.15787013, 0.17764155, 0.16623377, 0.15584416,
       0.16645714, 0.16919484, 0.17402597, 0.178026  , 0.17495062,
       0.16396881, 0.16949613, 0.17934547, 0.18779741, 0.17715843])]
[0.16927791446834417]

[array([0.01246754, 0.        , 0.00764156, 0.01714287, 0.0607844 ,
       0.05355843, 0.06139221, 0.05272728, 0.0727117 , 0.0761091 ,
       0.08682596, 0.0943896 , 0.08825454, 0.07413508, 0.0733039 ,
       0.08609869, 0.08051948, 0.09974024, 0.09516887, 0.12727273,
       0.12018702, 0.11641037, 0.1081195 , 0.12337662, 0.13402599,
       0.13574544, 0.14640004, 0.14378702, 0.16011432, 0.14345973,
       0.12130912, 0.12896625, 0.13588574, 0.14830132, 0.15021299,
       0.16155324, 0.15787013, 0.17764155, 0.16623377, 0.15584416,
       0.16645714, 0.16919484, 0.17402597, 0.178026  , 0.17495062,
       0.16396881, 0.16949613, 0.17934547, 0.18779741, 0.17715843]), 
array([0.        , 0.00764156, 0.01714287, 0.0607844 , 0.05355843,
       0.06139221, 0.05272728, 0.0727117 , 0.0761091 , 0.08682596,
       0.0943896 , 0.08825454, 0.07413508, 0.0733039 , 0.08609869,
       0.08051948, 0.09974024, 0.09516887, 0.12727273, 0.12018702,
       0.11641037, 0.1081195 , 0.12337662, 0.13402599, 0.13574544,
       0.14640004, 0.14378702, 0.16011432, 0.14345973, 0.12130912,
       0.12896625, 0.13588574, 0.14830132, 0.15021299, 0.16155324,
       0.15787013, 0.17764155, 0.16623377, 0.15584416, 0.16645714,
       0.16919484, 0.17402597, 0.178026  , 0.17495062, 0.16396881,
       0.16949613, 0.17934547, 0.18779741, 0.17715843, 0.16927791])]
[0.16927791446834417, 0.15038444221793834]

For testing set:

Python3




X_test = []
y_test = []
for i in range(50, len(scaled_test)):
    X_test.append(scaled_test[i-50:i, 0])
    y_test.append(scaled_test[i, 0])


Step 8: In this step, the data is converted into a format that is suitable for input to an RNN. np.reshape(X_train, (X_train.shape[0], X_train.shape[1], 1)) transforms the X_train array, which was originally a 2-dimensional array of shape (samples, features), into a 3-dimensional array of shape (samples, time steps, features), where time steps denotes the number of time steps in the input sequence and features denotes the number of features in the input data. Size 1 is an additional dimension that serves as an indication that each time step only has a single feature.

The y_train array is transformed from a 1-dimensional array of shape (samples) into a 2-dimensional array of shape (samples, 1) by np.reshape(y_train, (y_train.shape[0], 1)), where each row represents the output value at a certain time step. 

For training set:

Python3




# The data is converted to Numpy array
X_train, y_train = np.array(X_train), np.array(y_train)
 
#Reshaping
X_train = np.reshape(X_train, (X_train.shape[0], X_train.shape[1],1))
y_train = np.reshape(y_train, (y_train.shape[0],1))
print("X_train :",X_train.shape,"y_train :",y_train.shape)


Output:

X_train : (555, 50, 1) y_train : (555, 1)

For testing set:

Python3




# The data is converted to numpy array
X_test, y_test = np.array(X_test), np.array(y_test)
 
#Reshaping
X_test = np.reshape(X_test, (X_test.shape[0], X_test.shape[1],1))
y_test = np.reshape(y_test, (y_test.shape[0],1))
print("X_test :",X_test.shape,"y_test :",y_test.shape)


Output:

X_test : (101, 50, 1) y_test : (101, 1)

Step 9: Three RNN models are created in this step. The libraries needed for the model is imported.

Python3




# importing libraries
from keras.models import Sequential
from keras.layers import LSTM
from keras.layers import Dense
from keras.layers import SimpleRNN
from keras.layers import Dropout
from keras.layers import GRU, Bidirectional
from keras.optimizers import SGD
from sklearn import metrics
from sklearn.metrics import mean_squared_error


SimpleRNN Model: 

Using the Keras API, this code creates a recurrent neural network (RNN) with four layers of basic RNNs and a dense output layer. It makes use of the tanh hyperbolic tangent activation function. To avoid overfitting, a dropout layer with a rate of 0.2 is introduced. It employs the optimizer as Adam, mean squared error as the loss function, and accuracy as the evaluation metric while compiling. With a batch size of 2, it fits the model to the training data for 20 epochs. The number of parameters in each layer and the overall number of parameters in the model are listed in a summary of the model architecture.

Python3




# initializing the RNN
regressor = Sequential()
 
# adding RNN layers and dropout regularization
regressor.add(SimpleRNN(units = 50,
                        activation = "tanh",
                        return_sequences = True,
                        input_shape = (X_train.shape[1],1)))
regressor.add(Dropout(0.2))
 
regressor.add(SimpleRNN(units = 50,
                        activation = "tanh",
                        return_sequences = True))
 
regressor.add(SimpleRNN(units = 50,
                        activation = "tanh",
                        return_sequences = True))
 
regressor.add( SimpleRNN(units = 50))
 
# adding the output layer
regressor.add(Dense(units = 1,activation='sigmoid'))
 
# compiling RNN
regressor.compile(optimizer = SGD(learning_rate=0.01,
                                  decay=1e-6,
                                  momentum=0.9,
                                  nesterov=True),
                  loss = "mean_squared_error")
 
# fitting the model
regressor.fit(X_train, y_train, epochs = 20, batch_size = 2)
regressor.summary()


Output:

Epoch 1/20
278/278 [==============================] - 13s 39ms/step - loss: 0.0187
Epoch 2/20
278/278 [==============================] - 11s 39ms/step - loss: 0.0035
Epoch 3/20
278/278 [==============================] - 11s 39ms/step - loss: 0.0031
Epoch 4/20
278/278 [==============================] - 12s 42ms/step - loss: 0.0028
Epoch 5/20
278/278 [==============================] - 11s 39ms/step - loss: 0.0027
Epoch 6/20
278/278 [==============================] - 10s 36ms/step - loss: 0.0023
Epoch 7/20
278/278 [==============================] - 11s 39ms/step - loss: 0.0026
Epoch 8/20
278/278 [==============================] - 11s 39ms/step - loss: 0.0023
Epoch 9/20
278/278 [==============================] - 11s 39ms/step - loss: 0.0021
Epoch 10/20
278/278 [==============================] - 11s 40ms/step - loss: 0.0021
Epoch 11/20
278/278 [==============================] - 11s 39ms/step - loss: 0.0019
Epoch 12/20
278/278 [==============================] - 11s 39ms/step - loss: 0.0019
Epoch 13/20
278/278 [==============================] - 11s 39ms/step - loss: 0.0022
Epoch 14/20
278/278 [==============================] - 11s 39ms/step - loss: 0.0019
Epoch 15/20
278/278 [==============================] - 11s 39ms/step - loss: 0.0019
Epoch 16/20
278/278 [==============================] - 11s 39ms/step - loss: 0.0018
Epoch 17/20
278/278 [==============================] - 10s 36ms/step - loss: 0.0019
Epoch 18/20
278/278 [==============================] - 11s 39ms/step - loss: 0.0017
Epoch 19/20
278/278 [==============================] - 11s 39ms/step - loss: 0.0016
Epoch 20/20
278/278 [==============================] - 11s 39ms/step - loss: 0.0016
Model: "sequential_6"
_________________________________________________________________
 Layer (type)                Output Shape              Param #   
=================================================================
 simple_rnn_24 (SimpleRNN)   (None, 50, 50)            2600      
                                                                 
 dropout_6 (Dropout)         (None, 50, 50)            0         
                                                                 
 simple_rnn_25 (SimpleRNN)   (None, 50, 50)            5050      
                                                                 
 simple_rnn_26 (SimpleRNN)   (None, 50, 50)            5050      
                                                                 
 simple_rnn_27 (SimpleRNN)   (None, 50)                5050      
                                                                 
 dense_6 (Dense)             (None, 1)                 51        
                                                                 
=================================================================
Total params: 17,801
Trainable params: 17,801
Non-trainable params: 0
_________________________________________________________________

LSTM RNN Model:  

This code creates a LSTM Model with three layers and a dense output layer. It employs the optimizer as Adam, mean squared error as the loss function, and accuracy as the evaluation metric while compiling. With a batch size of 1, it fits the model to the training data for 10 epochs. The number of parameters in each layer and the overall number of parameters in the model are listed in a summary of the model architecture.

Python3




#Initialising the model
regressorLSTM = Sequential()
 
#Adding LSTM layers
regressorLSTM.add(LSTM(50,
                       return_sequences = True,
                       input_shape = (X_train.shape[1],1)))
regressorLSTM.add(LSTM(50,
                       return_sequences = False))
regressorLSTM.add(Dense(25))
 
#Adding the output layer
regressorLSTM.add(Dense(1))
 
#Compiling the model
regressorLSTM.compile(optimizer = 'adam',
                      loss = 'mean_squared_error',
                      metrics = ["accuracy"])
 
#Fitting the model
regressorLSTM.fit(X_train,
                  y_train,
                  batch_size = 1,
                  epochs = 12)
regressorLSTM.summary()


Output:

Epoch 1/12
555/555 [==============================] - 18s 25ms/step - loss: 0.0050
Epoch 2/12
555/555 [==============================] - 14s 25ms/step - loss: 0.0024
Epoch 3/12
555/555 [==============================] - 14s 25ms/step - loss: 0.0018
Epoch 4/12
555/555 [==============================] - 14s 25ms/step - loss: 0.0017
Epoch 5/12
555/555 [==============================] - 14s 25ms/step - loss: 0.0013
Epoch 6/12
555/555 [==============================] - 14s 25ms/step - loss: 0.0013
Epoch 7/12
555/555 [==============================] - 14s 26ms/step - loss: 0.0010
Epoch 8/12
555/555 [==============================] - 14s 25ms/step - loss: 0.0010
Epoch 9/12
555/555 [==============================] - 14s 25ms/step - loss: 9.8315e-04
Epoch 10/12
555/555 [==============================] - 15s 26ms/step - loss: 0.0011
Epoch 11/12
555/555 [==============================] - 14s 25ms/step - loss: 0.0011
Epoch 12/12
555/555 [==============================] - 14s 24ms/step - loss: 9.1305e-04
Model: "sequential_15"
_________________________________________________________________
 Layer (type)                Output Shape              Param #   
=================================================================
 lstm_12 (LSTM)              (None, 50, 50)            10400     
                                                                 
 lstm_13 (LSTM)              (None, 50)                20200     
                                                                 
 dense_19 (Dense)            (None, 25)                1275      
                                                                 
 dense_20 (Dense)            (None, 1)                 26        
                                                                 
=================================================================
Total params: 31,901
Trainable params: 31,901
Non-trainable params: 0
_________________________________________________________________

GRU RNN Model: 

This code defines a recurrent neural network (RNN) model using the GRU (Gated Recurrent Unit) layer in Keras. It consists of four stacked GRU layers followed by a single output layer. It makes use of the ‘tanh’ hyperbolic tangent activation function. To avoid overfitting, a dropout layer with a rate of 0.2 is introduced. It employs the optimizer as Stochastic Gradient Descent (SGD) with a learning rate of 0.01, the decay rate of 1e-7, the momentum of 0.9, and Nesterov is set to False. The mean squared error is the loss function, and accuracy is the evaluation metric while compiling. With a batch size of 2, it fits the model to the training data for 20 epochs. The number of parameters in each layer and the overall number of parameters in the model are listed in a summary of the model architecture.

Python3




#Initialising the model
regressorGRU = Sequential()
 
# GRU layers with Dropout regularisation
regressorGRU.add(GRU(units=50,
                     return_sequences=True,
                     input_shape=(X_train.shape[1],1),
                     activation='tanh'))
regressorGRU.add(Dropout(0.2))
 
regressorGRU.add(GRU(units=50,
                     return_sequences=True,
                     activation='tanh'))
 
regressorGRU.add(GRU(units=50,
                     return_sequences=True,
                     activation='tanh'))
 
regressorGRU.add(GRU(units=50,
                     activation='tanh'))
 
# The output layer
regressorGRU.add(Dense(units=1,
                       activation='relu'))
# Compiling the RNN
regressorGRU.compile(optimizer=SGD(learning_rate=0.01,
                                   decay=1e-7,
                                   momentum=0.9,
                                   nesterov=False),
                     loss='mean_squared_error')
 
# Fitting the data
regressorGRU.fit(X_train,y_train,epochs=20,batch_size=1)
regressorGRU.summary()


Output:

Epoch 1/20
555/555 [==============================] - 32s 46ms/step - loss: 0.0155
Epoch 2/20
555/555 [==============================] - 26s 46ms/step - loss: 0.0027
Epoch 3/20
555/555 [==============================] - 26s 46ms/step - loss: 0.0030
Epoch 4/20
555/555 [==============================] - 26s 46ms/step - loss: 0.0026
Epoch 5/20
555/555 [==============================] - 26s 47ms/step - loss: 0.0022
Epoch 6/20
555/555 [==============================] - 26s 48ms/step - loss: 0.0025
Epoch 7/20
555/555 [==============================] - 26s 46ms/step - loss: 0.0021
Epoch 8/20
555/555 [==============================] - 26s 46ms/step - loss: 0.0021
Epoch 9/20
555/555 [==============================] - 25s 46ms/step - loss: 0.0021
Epoch 10/20
555/555 [==============================] - 27s 49ms/step - loss: 0.0021
Epoch 11/20
555/555 [==============================] - 27s 49ms/step - loss: 0.0022
Epoch 12/20
555/555 [==============================] - 27s 48ms/step - loss: 0.0022
Epoch 13/20
555/555 [==============================] - 28s 50ms/step - loss: 0.0022
Epoch 14/20
555/555 [==============================] - 28s 50ms/step - loss: 0.0021
Epoch 15/20
555/555 [==============================] - 26s 48ms/step - loss: 0.0018
Epoch 16/20
555/555 [==============================] - 27s 48ms/step - loss: 0.0021
Epoch 17/20
555/555 [==============================] - 27s 49ms/step - loss: 0.0021
Epoch 18/20
555/555 [==============================] - 26s 47ms/step - loss: 0.0019
Epoch 19/20
555/555 [==============================] - 27s 48ms/step - loss: 0.0019
Epoch 20/20
555/555 [==============================] - 27s 48ms/step - loss: 0.0018
Model: "sequential_17"
_________________________________________________________________
 Layer (type)                Output Shape              Param #   
=================================================================
 gru_4 (GRU)                 (None, 50, 50)            7950      
                                                                 
 dropout_8 (Dropout)         (None, 50, 50)            0         
                                                                 
 gru_5 (GRU)                 (None, 50, 50)            15300     
                                                                 
 gru_6 (GRU)                 (None, 50, 50)            15300     
                                                                 
 gru_7 (GRU)                 (None, 50)                15300     
                                                                 
 dense_22 (Dense)            (None, 1)                 51        
                                                                 
=================================================================
Total params: 53,901
Trainable params: 53,901
Non-trainable params: 0
_________________________________________________________________

Step 10: The X_test data is then used to make predictions from all three models.

Python3




# predictions with X_test data
y_RNN = regressor.predict(X_test)
y_LSTM = regressorLSTM.predict(X_test)
y_GRU = regressorGRU.predict(X_test)


Step 11:  The predicted values are transformed back from the normalized state to their original scale using the inverse_transform() function.

Python3




# scaling back from 0-1 to original
y_RNN_O = scaler.inverse_transform(y_RNN)
y_LSTM_O = scaler.inverse_transform(y_LSTM)
y_GRU_O = scaler.inverse_transform(y_GRU)


Step 12: Visualize the predicted prices using matplotlib.

Python3




fig, axs = plt.subplots(3,figsize =(18,12),sharex=True, sharey=True)
fig.suptitle('Model Predictions')
 
#Plot for RNN predictions
axs[0].plot(train_data.index[150:], train_data.Open[150:], label = "train_data", color = "b")
axs[0].plot(test_data.index, test_data.Open, label = "test_data", color = "g")
axs[0].plot(test_data.index[50:], y_RNN_O, label = "y_RNN", color = "brown")
axs[0].legend()
axs[0].title.set_text("Basic RNN")
 
#Plot for LSTM predictions
axs[1].plot(train_data.index[150:], train_data.Open[150:], label = "train_data", color = "b")
axs[1].plot(test_data.index, test_data.Open, label = "test_data", color = "g")
axs[1].plot(test_data.index[50:], y_LSTM_O, label = "y_LSTM", color = "orange")
axs[1].legend()
axs[1].title.set_text("LSTM")
 
#Plot for GRU predictions
axs[2].plot(train_data.index[150:], train_data.Open[150:], label = "train_data", color = "b")
axs[2].plot(test_data.index, test_data.Open, label = "test_data", color = "g")
axs[2].plot(test_data.index[50:], y_GRU_O, label = "y_GRU", color = "red")
axs[2].legend()
axs[2].title.set_text("GRU")
 
plt.xlabel("Days")
plt.ylabel("Open price")
 
plt.show()


Output:

TimeseriesForecasting - Geeksforgeeks

Time Series Forecasting



Like Article
Suggest improvement
Share your thoughts in the comments

Similar Reads