================ by Jawad Haider
- RNN Exercises - Solutions
- Perform standard imports, load and plot the dataset
- Prepare the data
- 1. Divide the data into train and test sets
- 2. Normalize the training set
- 3. Prepare data for LSTM
- 4. Define the model
- 5. Define loss and optimization functions
- 10. Inverse transform the predicted values
- BONUS EXERCISE: Plot the result
- Great job!
RNN Exercises - Solutions¶
For these exercises we’re using data from the Federal Reserve Economic Database (FRED) concerning Electricity and Gas Utilities Production from January 1992 to January 2019 (325 records).
Data source: https://fred.stlouisfed.org/series/IPG2211A2N
In the exercises below you’ll be asked to do the following: * Perform standard imports, load & plot the dataset (code provided) * Prepare data for an LSTM model * Define the LSTM model, loss and optimization functions * Train the model * Evaluate the model on test data * OPTIONAL: Plot the results
otherwise you will end up writing over the example output!
Perform standard imports, load and plot the dataset¶
Run the cells below to load the libraries needed for this exercise and the Energy Production dataset, and to plot the data.
# RUN THIS CELL
import torch
import torch.nn as nn
from sklearn.preprocessing import MinMaxScaler
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
%matplotlib inline
from pandas.plotting import register_matplotlib_converters
register_matplotlib_converters()
df = pd.read_csv('../Data/TimeSeriesData/Energy_Production.csv',index_col=0,parse_dates=True)
df.dropna(inplace=True)
print(len(df))
df.head()
325
IPG2211A2N | |
---|---|
DATE | |
1992-01-01 | 85.5560 |
1992-02-01 | 80.4178 |
1992-03-01 | 74.7390 |
1992-04-01 | 69.8367 |
1992-05-01 | 67.3781 |
# RUN THIS CELL
plt.figure(figsize=(12,4))
plt.title('Industrial Production Index for Electricity and Gas Utilities')
plt.ylabel('Index 2012=100, Not Seasonally Adjusted')
plt.grid(True)
plt.autoscale(axis='x',tight=True)
plt.plot(df['IPG2211A2N'])
plt.show()
Prepare the data¶
For the first set of exercises we’ll * divide the data into train and test sets * normalize the training set * prepare windowed seq/label tuples for an LSTM model
1. Divide the data into train and test sets¶
Working with a window_size of 12, divide the dataset into a sequence of 313 training records (including the window), and a test set of 12 records.
# CODE HERE
y = df['IPG2211A2N'].values.astype(float)
test_size =
window_size =
train_set =
test_set =
# Run the code below to check your results:
print(f'Train: {len(train_set)}')
print(f'Test: {len(test_set)}')
# DON'T WRITE HERE
y = df['IPG2211A2N'].values.astype(float)
test_size = 12
window_size = 12
train_set = y[:-test_size]
test_set = y[-test_size:]
print(f'Train: {len(train_set)}')
print(f'Test: {len(test_set)}')
Train: 313
Test: 12
2. Normalize the training set¶
Feature scale the training set to fit within the range [-1,1].
# Run the code below to check your results:
print(f'First item, original: {train_set[0]}')
print(f'First item, scaled: {train_norm[0]}')
# DON'T WRITE HERE
scaler = MinMaxScaler(feature_range=(-1, 1))
train_norm = scaler.fit_transform(train_set.reshape(-1, 1))
print(f'First item, original: {train_set[0]}')
print(f'First item, scaled: {train_norm[0]}')
First item, original: 85.556
First item, scaled: [-0.4091274]
3. Prepare data for LSTM¶
Prepare the list of windowed sequence/label tuples to be fed into an LSTM model.
# RUN THIS CELL
train_norm = torch.FloatTensor(train_norm).view(-1)
def input_data(seq,ws):
out = []
L = len(seq)
for i in range(L-ws):
window = seq[i:i+ws]
label = seq[i+ws:i+ws+1]
out.append((window,label))
return out
# Run the code below to check your results:
print(f'Train_data: {len(train_data)}') # should equal 301
# DON'T WRITE HERE
train_data = input_data(train_norm,window_size)
print(f'Train_data: {len(train_data)}')
Train_data: 301
4. Define the model¶
Design a model that has a (1,64) LSTM layer and a (64,1) fully-connected linear layer. Be sure to initialize \(h_0\) and \(c_0\), and return only the last predicted value.
# DON'T WRITE HERE
class LSTMnetwork(nn.Module):
def __init__(self,input_size=1,hidden_size=64,output_size=1):
super().__init__()
self.hidden_size = hidden_size
# Add an LSTM layer:
self.lstm = nn.LSTM(input_size,hidden_size)
# Add a fully-connected layer:
self.linear = nn.Linear(hidden_size,output_size)
# Initialize h0 and c0:
self.hidden = (torch.zeros(1,1,self.hidden_size),
torch.zeros(1,1,self.hidden_size))
def forward(self,seq):
lstm_out, self.hidden = self.lstm(
seq.view(len(seq),1,-1), self.hidden)
pred = self.linear(lstm_out.view(len(seq),-1))
return pred[-1]
torch.manual_seed(101)
model = LSTMnetwork()
model
LSTMnetwork(
(lstm): LSTM(1, 64)
(linear): Linear(in_features=64, out_features=1, bias=True)
)
5. Define loss and optimization functions¶
Define a loss function called “criterion” and an optimizer called
“optimizer”.
You can use any functions you want, although we used
MSELoss and Adam (learning rate of 0.001) respectively.
# DON'T WRITE HERE
criterion = nn.MSELoss()
optimizer = torch.optim.Adam(model.parameters(), lr=0.001)
6. Train the model¶
Don’t worry about tracking loss values, displaying results, or
validating the test set. Just train the model through 50 epochs. We’ll
evaluate the trained model in the next step.
OPTIONAL: print
something after each epoch to indicate training progress.
# DON'T WRITE HERE
epochs = 50
for i in range(epochs):
for seq, y_train in train_data:
# reset the parameters and hidden states
optimizer.zero_grad()
model.hidden = (torch.zeros(1,1,model.hidden_size),
torch.zeros(1,1,model.hidden_size))
# apply the model
y_pred = model(seq)
# update parameters
loss = criterion(y_pred, y_train)
loss.backward()
optimizer.step()
# OPTIONAL print statement
print(f'{i+1} of {epochs} epochs completed')
1 of 50 epochs completed
2 of 50 epochs completed
3 of 50 epochs completed
4 of 50 epochs completed
5 of 50 epochs completed
6 of 50 epochs completed
7 of 50 epochs completed
8 of 50 epochs completed
9 of 50 epochs completed
10 of 50 epochs completed
11 of 50 epochs completed
12 of 50 epochs completed
13 of 50 epochs completed
14 of 50 epochs completed
15 of 50 epochs completed
16 of 50 epochs completed
17 of 50 epochs completed
18 of 50 epochs completed
19 of 50 epochs completed
20 of 50 epochs completed
21 of 50 epochs completed
22 of 50 epochs completed
23 of 50 epochs completed
24 of 50 epochs completed
25 of 50 epochs completed
26 of 50 epochs completed
27 of 50 epochs completed
28 of 50 epochs completed
29 of 50 epochs completed
30 of 50 epochs completed
31 of 50 epochs completed
32 of 50 epochs completed
33 of 50 epochs completed
34 of 50 epochs completed
35 of 50 epochs completed
36 of 50 epochs completed
37 of 50 epochs completed
38 of 50 epochs completed
39 of 50 epochs completed
40 of 50 epochs completed
41 of 50 epochs completed
42 of 50 epochs completed
43 of 50 epochs completed
44 of 50 epochs completed
45 of 50 epochs completed
46 of 50 epochs completed
47 of 50 epochs completed
48 of 50 epochs completed
49 of 50 epochs completed
50 of 50 epochs completed
9. Evaluate the model using the test set¶
Be sure to re-initialize the hidden parameters \(h_0\) and \(c_0\) before running the model.
# DON'T WRITE HERE
future = 12
preds = train_norm[-window_size:].tolist()
model.eval()
for i in range(future):
seq = torch.FloatTensor(preds[-window_size:])
with torch.no_grad():
model.hidden = (torch.zeros(1,1,model.hidden_size),
torch.zeros(1,1,model.hidden_size))
preds.append(model(seq).item())
preds[window_size:]
[0.25382155179977417,
-0.0027704648673534393,
-0.343053936958313,
-0.21152164041996002,
0.23945370316505432,
0.4895053505897522,
0.24688751995563507,
-0.08669154345989227,
-0.25793153047561646,
0.022461334243416786,
0.5438402891159058,
0.6108715534210205]
10. Inverse transform the predicted values¶
Rescale the predicted values up to the original test set range.
# DON'T WRITE HERE
true_predictions = scaler.inverse_transform(np.array(preds[window_size:]).reshape(-1, 1))
true_predictions
array([[105.95129313],
[ 98.05736803],
[ 87.58871716],
[ 91.63524249],
[105.50927345],
[113.20198736],
[105.73797111],
[ 95.47557801],
[ 90.20746543],
[ 98.83361172],
[114.87357457],
[116.93575791]])
BONUS EXERCISE: Plot the result¶
Plot the true_predictions values together with the original data. Remember to create a range of datetime values for the predicted data.
# DON'T WRITE HERE
x = np.arange('2018-02-01', '2019-02-01', dtype='datetime64[M]').astype('datetime64[D]')
plt.figure(figsize=(12,4))
plt.title('Industrial Production Index for Electricity and Gas Utilities')
plt.ylabel('Index 2012=100, Not Seasonally Adjusted')
plt.grid(True)
plt.autoscale(axis='x',tight=True)
plt.plot(df['IPG2211A2N'])
plt.plot(x,true_predictions)
plt.show()
# DON'T WRITE HERE
fig = plt.figure(figsize=(12,4))
plt.title('Industrial Production Index for Electricity and Gas Utilities')
plt.ylabel('Index 2012=100, Not Seasonally Adjusted')
plt.grid(True)
plt.autoscale(axis='x',tight=True)
fig.autofmt_xdate()
plt.plot(df['IPG2211A2N']['2017-01-01':])
plt.plot(x,true_predictions)
plt.show()