Cainvas

Energy Consumption Prediction

Credit: AITS Cainvas Community

Photo by Alex Pirenis, Konstantinos Pappas on Dribbble

Energy Consumption Models are needed for Energy Conservation and they serve as the basic building blocks of Smart Buildings and Smart grid Systems.With the help of Deep Learning we can predict the energy consumption and deliver only that much energy which is needed and hence contriute towards energy conservation.

Import necessary libraries

In [1]:
import os
import numpy as np
import pandas as pd 

import matplotlib.pyplot as plt  
import seaborn as sns  
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import MinMaxScaler
from sklearn import metrics 
from sklearn.metrics import mean_squared_error,r2_score

## for Deep-learing:
import keras
from keras.layers import Dense
from keras.models import Sequential
from keras.utils import to_categorical
from keras.optimizers import SGD 
from keras.callbacks import EarlyStopping
from keras.utils import np_utils
import itertools
from keras.layers import LSTM
from keras.layers.convolutional import Conv1D
from keras.layers.convolutional import MaxPooling1D
from keras.layers import Dropout

Data importing, pre-processing and analysis

In [2]:
 df = pd.read_csv('https://cainvas-static.s3.amazonaws.com/media/user_data/cainvas-admin/household_power_consumption.txt',
                    sep=';', parse_dates={'dt' : ['Date', 'Time']}, infer_datetime_format=True, 
                    low_memory=False, na_values=['nan','?'], index_col='dt')
In [3]:
df.head()
Out[3]:
Global_active_power Global_reactive_power Voltage Global_intensity Sub_metering_1 Sub_metering_2 Sub_metering_3
dt
2006-12-16 17:24:00 4.216 0.418 234.84 18.4 0.0 1.0 17.0
2006-12-16 17:25:00 5.360 0.436 233.63 23.0 0.0 1.0 16.0
2006-12-16 17:26:00 5.374 0.498 233.29 23.0 0.0 2.0 17.0
2006-12-16 17:27:00 5.388 0.502 233.74 23.0 0.0 1.0 17.0
2006-12-16 17:28:00 3.666 0.528 235.68 15.8 0.0 1.0 17.0

Dealing with Missing values

In [4]:
droping_list_all=[]
for j in range(0,7):
    if not df.iloc[:, j].notnull().all():
        droping_list_all.append(j)        
In [5]:
for j in range(0,7):        
        df.iloc[:,j]=df.iloc[:,j].fillna(df.iloc[:,j].mean())
In [6]:
df.isnull().sum()
Out[6]:
Global_active_power      0
Global_reactive_power    0
Voltage                  0
Global_intensity         0
Sub_metering_1           0
Sub_metering_2           0
Sub_metering_3           0
dtype: int64

Mean and std of Global_active_power

In [7]:
df.Global_active_power.resample('D').sum().plot(title='Global_active_power resampled over day for sum') 

plt.tight_layout()
plt.show()   

df.Global_active_power.resample('D').mean().plot(title='Global_active_power resampled over day for mean', color='red') 
plt.tight_layout()
plt.show()

Mean and std of 'Global_intensity'

In [8]:
r = df.Global_intensity.resample('D').agg(['mean', 'std'])
r.plot(subplots = True, title='Global_intensity resampled over day')
plt.show()

Mean and std of Global_reactive_power

In [9]:
r2 = df.Global_reactive_power.resample('D').agg(['mean', 'std'])
r2.plot(subplots = True, title='Global_reactive_power resampled over day', color='red')
plt.show()

Resample the data

In [10]:
def series_to_supervised(data, n_in=1, n_out=1, dropnan=True):
	n_vars = 1 if type(data) is list else data.shape[1]
	dff = pd.DataFrame(data)
	cols, names = list(), list()
	# input sequence (t-n, ... t-1)
	for i in range(n_in, 0, -1):
		cols.append(dff.shift(i))
		names += [('var%d(t-%d)' % (j+1, i)) for j in range(n_vars)]
	# forecast sequence (t, t+1, ... t+n)
	for i in range(0, n_out):
		cols.append(dff.shift(-i))
		if i == 0:
			names += [('var%d(t)' % (j+1)) for j in range(n_vars)]
		else:
			names += [('var%d(t+%d)' % (j+1, i)) for j in range(n_vars)]
	# put it all together
	agg = pd.concat(cols, axis=1)
	agg.columns = names
	# drop rows with NaN values
	if dropnan:
		agg.dropna(inplace=True)
	return agg
 
In [11]:
df_resample = df.resample('h').mean() 
df_resample.shape
values = df_resample.values 


scaler = MinMaxScaler(feature_range=(0, 1))
scaled = scaler.fit_transform(values)
# frame as supervised learning
reframed = series_to_supervised(scaled, 1, 1)

# drop columns we don't want to predict
reframed.drop(reframed.columns[[8,9,10,11,12,13]], axis=1, inplace=True)
reframed.head()
Out[11]:
var1(t-1) var2(t-1) var3(t-1) var4(t-1) var5(t-1) var6(t-1) var7(t-1) var1(t)
1 0.636816 0.295738 0.337945 0.631157 0.0 0.011366 0.782418 0.545045
2 0.545045 0.103358 0.335501 0.541487 0.0 0.144652 0.782676 0.509006
3 0.509006 0.110073 0.283802 0.502152 0.0 0.030869 0.774169 0.488550
4 0.488550 0.096987 0.315987 0.481110 0.0 0.000000 0.778809 0.455597
5 0.455597 0.099010 0.434417 0.449904 0.0 0.008973 0.798917 0.322555

Test-Train Split

In [12]:
# split into train and test sets
values = reframed.values

n_train_time = 365*24
train = values[:n_train_time, :]
test = values[n_train_time:, :]
# split into input and outputs
train_X, train_y = train[:, :-1], train[:, -1]
test_X, test_y = test[:, :-1], test[:, -1]
# reshape input to be 3D [samples, timesteps, features]
train_X = train_X.reshape((train_X.shape[0], 1, train_X.shape[1]))
test_X = test_X.reshape((test_X.shape[0], 1, test_X.shape[1]))
print(train_X.shape, train_y.shape, test_X.shape, test_y.shape) 
# We reshaped the input into the 3D format as expected by LSTMs, namely [samples, timesteps, features].
(8760, 1, 7) (8760,) (25828, 1, 7) (25828,)

Model Architecture

In [13]:
model = Sequential()
model.add(LSTM(100, input_shape=(train_X.shape[1], train_X.shape[2])))
model.add(Dropout(0.2))

model.add(Dense(1))
In [14]:
model.compile(loss='mean_squared_error', optimizer='adam')
In [15]:
model.summary()
Model: "sequential"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
lstm (LSTM)                  (None, 100)               43200     
_________________________________________________________________
dropout (Dropout)            (None, 100)               0         
_________________________________________________________________
dense (Dense)                (None, 1)                 101       
=================================================================
Total params: 43,301
Trainable params: 43,301
Non-trainable params: 0
_________________________________________________________________

Model Training

In [16]:
history = model.fit(train_X, train_y, epochs=10, batch_size=70, validation_data=(test_X, test_y), verbose=2, shuffle=False)
Epoch 1/10
126/126 - 1s - loss: 0.0195 - val_loss: 0.0122
Epoch 2/10
126/126 - 1s - loss: 0.0127 - val_loss: 0.0108
Epoch 3/10
126/126 - 1s - loss: 0.0115 - val_loss: 0.0098
Epoch 4/10
126/126 - 1s - loss: 0.0109 - val_loss: 0.0094
Epoch 5/10
126/126 - 1s - loss: 0.0107 - val_loss: 0.0094
Epoch 6/10
126/126 - 1s - loss: 0.0106 - val_loss: 0.0093
Epoch 7/10
126/126 - 1s - loss: 0.0105 - val_loss: 0.0093
Epoch 8/10
126/126 - 1s - loss: 0.0105 - val_loss: 0.0093
Epoch 9/10
126/126 - 1s - loss: 0.0105 - val_loss: 0.0093
Epoch 10/10
126/126 - 1s - loss: 0.0105 - val_loss: 0.0092

Saving the trained Model

In [17]:
model.save("energy.h5")

Accessing the performance of the Model

In [18]:
# summarize history for loss
plt.plot(history.history['loss'])
plt.plot(history.history['val_loss'])
plt.title('model loss')
plt.ylabel('loss')
plt.xlabel('epoch')
plt.legend(['train', 'test'], loc='upper right')
plt.show()

# make a prediction
yhat = model.predict(test_X)
test_X = test_X.reshape((test_X.shape[0], 7))
# invert scaling for forecast
inv_yhat = np.concatenate((yhat, test_X[:, -6:]), axis=1)
inv_yhat = scaler.inverse_transform(inv_yhat)
inv_yhat = inv_yhat[:,0]
# invert scaling for actual
test_y = test_y.reshape((len(test_y), 1))
inv_y = np.concatenate((test_y, test_X[:, -6:]), axis=1)
inv_y = scaler.inverse_transform(inv_y)
inv_y = inv_y[:,0]
# calculate RMSE
rmse = np.sqrt(mean_squared_error(inv_y, inv_yhat))
print('Test RMSE: %.3f' % rmse)
Test RMSE: 0.616
In [19]:
aa=[x for x in range(200)]
plt.plot(aa, inv_y[:200], marker='.', label="actual")
plt.plot(aa, inv_yhat[:200], 'r', label="prediction")
plt.ylabel('Global_active_power', size=15)
plt.xlabel('Time step', size=15)
plt.legend(fontsize=15)
plt.show()

Compiling the model with DeepC compiler

In [20]:
!deepCC energy.h5
[INFO]
Reading [keras model] 'energy.h5'
[SUCCESS]
Saved 'energy.onnx'
[INFO]
Reading [onnx model] 'energy.onnx'
[INFO]
Model info:
  ir_vesion : 4
  doc       : 
[WARNING]
[ONNX]: lstm (LSTM) has 4 inputs, that aren't connected.
[WARNING]
[ONNX]: terminal (input/output) lstm_input's shape is less than 1. Changing it to 1.
[WARNING]
[ONNX]: terminal (input/output) dense's shape is less than 1. Changing it to 1.
WARN (GRAPH): found operator node with the same name (dense) as io node.
[INFO]
Running DNNC graph sanity check ...
ERROR (GRAPH): some of graph sequential's node lstm's
               outputs are not connected to other nodes in the graph.
[ERROR]
Failed. Please check your model. graph sequential
operator Transpose {
	input lstm_input
	output lstm_X
}
operator lstm {
	input lstm_X
	input lstm_W
	input lstm_R
	input lstm_B
	output lstm_Y
	output lstm_Y_h
	output lstm_Y_c
}
operator Squeeze {
	input lstm_Y_h
	output lstm_PartitionedCall_0
}
operator dense {
	input lstm_PartitionedCall_0
	input dense_kernel_0
	output dense0
}
operator Add {
	input dense0
	input dense_bias_0
	output dense
}
weight { float dense_kernel_0 [100,1] }
weight { float dense_bias_0 [1] }
weight { float lstm_W [1,400,7] }
weight { float lstm_R [1,400,100] }
weight { float lstm_B [1,800] }
input {float lstm_input[1,1,7]}
output {float dense[1,1]}


[INFO]
Writing C++ file 'energy_deepC/energy.cpp'
ERROR (TYPE INFER): cound not find all nodes for lstm,
WARN (CODEGEN): cound not find all nodes for lstm,
                an instance of LSTM.
                Please check model's sanity and try again.
[INFO]
deepSea model files are ready in 'energy_deepC/' 
[RUNNING COMMAND]
g++ -std=c++11 -O3 -fno-rtti -fno-exceptions -I. -I/opt/tljh/user/lib/python3.7/site-packages/deepC-0.13-py3.7-linux-x86_64.egg/deepC/include -isystem /opt/tljh/user/lib/python3.7/site-packages/deepC-0.13-py3.7-linux-x86_64.egg/deepC/packages/eigen-eigen-323c052e1731 energy_deepC/energy.cpp -o energy_deepC/energy.exe
[RUNNING COMMAND]
size "energy_deepC/energy.exe"
   text	   data	    bss	    dec	    hex	filename
 182616	 178064	    760	 361440	  583e0	energy_deepC/energy.exe
[SUCCESS]
Saved model as executable "energy_deepC/energy.exe"