-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathEnergyefficientDNNlinearreg.py
More file actions
280 lines (205 loc) · 12.3 KB
/
EnergyefficientDNNlinearreg.py
File metadata and controls
280 lines (205 loc) · 12.3 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
#It is a good practice to import all the modules required for training the model.
import torch
import torchvision
import torch.nn as nn
import pandas as pd
import matplotlib.pyplot as plt
import torch.nn.functional as F
from torchvision.datasets.utils import download_url
from torch.utils.data import DataLoader, TensorDataset, random_split
# Step 1: Downloading and Exploring the Dataset
DATASET_URL = "https://archive.ics.uci.edu/ml/machine-learning-databases/00242/ENB2012_data.xlsx" #The url of the dataset is given here
DATA_FILENAME = "Energyefficiency.csv" #the name of the saved downloaded file.
download_url(DATASET_URL, root='.',filename=DATA_FILENAME) #Don't forget to add the directory(any name where the downloaded file is saved) at the root parameter.
# To read the data from the excel file(.xlsx means excel),we use pandas dataframe where dataframe is like row by column which can be visualized below.
dataframe = pd.read_excel(DATA_FILENAME)
dataframe.head(5) #It is used to get the first 5 rows of the dataframe.
num_rows = len(dataframe)
print(num_rows)
num_cols = sum(1 for i in dataframe.columns)
print(num_cols)
input_cols = [i for i in dataframe.columns if(i!='Y1' and i!='Y2')] #the column titles of the input variables
print(len(input_cols))
output_cols = ['Y1','Y2'] #the column titles of output/target variable(s)
print(len(output_cols))
# Visualizing the distribution of target values in a graph.
plt.plot(dataframe['Y1'],'r')
plt.plot(dataframe['Y2'],'b')
plt.legend(['heating load','cooling load'])
plt.show()
# Step 2: Prepare the dataset for training
# We need to convert the data from the Pandas dataframe into a PyTorch tensors for training. To do this, the first step is to convert it numpy arrays. If you've filled out `input_cols`, `categorial_cols` and `output_cols` correctly, this following function will perform the conversion to numpy arrays.
import numpy as np
def dataframe_to_arrays(dataframe):
# Make a copy of the original dataframe as we may need the original one if anything goes wrong which is a good practice.
dataframe1 = dataframe.copy(deep=True)
# Extract input & outupts as numpy arrays of datatype float32 as the model expects the data to be float instead of double.
inputs_array = dataframe1[input_cols].to_numpy().astype(np.float32)
targets_array = dataframe1[output_cols].to_numpy().astype(np.float32)
return inputs_array, targets_array
inputs_array, targets_array = dataframe_to_arrays(dataframe)
inputs_array, targets_array
# We should convert the numpy arrays into tensor as the model expects tensors.
inputs = torch.from_numpy(inputs_array)
targets = torch.from_numpy(targets_array)
#let's confirm that the datatype is float
inputs.dtype, targets.dtype
# Let's now create the dataset with inputs and targets combined to create batches at next step
dataset = TensorDataset(inputs, targets)
# It is necessary to split the data into train and validation in order to evaluate the model on untouched data while training the model.
torch.manual_seed(16)#this will make the random same everytime we run this notebook which will help us evaluate a particular value as we do below .
val_percent = 0.1 # between 0.1 and 0.2 is good.
val_size = int(num_rows * val_percent)
train_size = num_rows - val_size
train_ds, val_ds = random_split(dataset,[train_size,val_size]) # Use the random_split function to split dataset into 2 parts of the desired length.
#Usually we take batch_size as a power of 2(i.e,16,32,64,...) as it improves the speed of the traing convergence(i.e,loss is reduced to minimum soon)
batch_size = 128
train_loader = DataLoader(train_ds, batch_size, shuffle=True, num_workers=4, pin_memory=True) #pin_memory enables the data to be loaded into pinmemory which speedups the GPU loading action.
val_loader = DataLoader(val_ds, batch_size*2, num_workers=4, pin_memory=True) # num_works are used when the dataset is large to make the loading fat as it is done parallely by 4 systems.
#Let's have a look at the first batch.
for xb, yb in train_loader:
print("inputs:", xb)
print("targets:", yb)
break
# Step 3: Create a Linear Regression Model
# Our model itself is a fairly straightforward linear regression (we'll build more complex models in the next assignment).
input_size = len(input_cols)
output_size = len(output_cols)
class EfficiencyModel(nn.Module):
def __init__(self):
super().__init__()
#input layer
self.linear1=nn.Linear(input_size,128) # we have to pass the number of inputs(number of input columns) and the number of hidden units
#hidden layers
self.linear2=nn.Linear(128,64) #previous layer hidden units count and the current layers hidden units count
self.linear3=nn.Linear(64,32) # make sure that the previous layer hidden units count and the current layer hidden units count are equal.
self.linear4=nn.Linear(32,16)
self.linear6=nn.Linear(16,8)
#output layer
self.linear5=nn.Linear(8,output_size) # here we need to pass previous layers hidden units count and number of outputs(number of output columns) as we mentioned above.
def forward(self, xb):
# Flatten images into vectors
out = xb.view(xb.size(0), -1) #As the model expects the features to flattened(i.e, all the features are stacked vertically),view()will do this for us where -1 makes the model to rest features as 2nd dimension.
# Apply layers & activation functions
out= self.linear1(out) #It is an inbuilt-method for linear regression i.e z= w*x +b where w is the weight and b is the bias
out=F.relu(out) #As we can know from the previous post that linear relation can't make a good prediction , now we are using a non-linear activation function called rectified linear units (Relu)
out= self.linear2(out)
out=F.relu(out)
out= self.linear3(out)
out=F.relu(out)
out= self.linear4(out)
out=F.relu(out)
out= self.linear6(out)
out=F.relu(out)
out=self.linear5(out) # here we used 5 layers including 3 hidden layers.
return out
def training_step(self, batch):
inputs, targets = batch
# Generate predictions
out = self(inputs) #which returns the predicted values
# Calcuate loss
loss = F.l1_loss(out,targets) #L1 loss(i.e ((1/num_rows)*(predicted-target)^2)^1/2) which is used to calculate the loss. Although there are lot loss functions, l1_loss will be more suitable for linear reg.
return loss
def validation_step(self, batch):
inputs, targets = batch
# Generate predictions
out = self(inputs)
# Calculate loss
loss = F.l1_loss(out,targets) # l1_loss is calculated for validation set
return {'val_loss': loss.detach()}
#detach() is used to remove this from computational graph as it may confuse the backprop since it is the loss of validation set to clear the memory whic will make training fast.
def validation_epoch_end(self, outputs):
batch_losses = [x['val_loss'] for x in outputs]
epoch_loss = torch.stack(batch_losses).mean() # Combining losses for all the batches to compute a single loss value
return {'val_loss': epoch_loss.item()}
def epoch_end(self, epoch, result, num_epochs):
# Print result every 20th epoch where epoch represents a complete traversal through the training examples.
if (epoch+1) % 2 == 0 or epoch == num_epochs-1:
print("Epoch [{}], train_loss: {:.4f}, val_loss: {:.4f}".format(epoch+1, result['loss'],result['val_loss']))
torch.cuda.is_available() #This gives whether the gpu is available or not in form of boolean(true or false)
def get_default_device():
"""To pick GPU if available, else CPU so that the program won't crash if gpu not available"""
if torch.cuda.is_available():
return torch.device('cuda')
else:
return torch.device('cpu')
device=get_default_device()
print(device)
def to_device(data, device):
"""Move tensor(s) to chosen device as everything should be present in the respective device """
if isinstance(data, (list,tuple)): # isinstance checks whether the input is of the desired type like it is list or tuple and will go next only if the condition is passed.
return [to_device(x, device) for x in data]
return data.to(device, non_blocking=True) #thus the data is moved according to the device selected
class DeviceDataLoader():
"""Wrap a dataloader to move data to a device"""
def __init__(self, dl, device):
self.dl = dl
self.device = device
def __iter__(self):
"""Yield a batch of data after moving it to device .Don't worry if you dont't get it,just do as instructed"""
for b in self.dl:
yield to_device(b, self.device)
def __len__(self):
"""Number of batches"""
return len(self.dl)
#The above class will perform the functions when the instance is called.
#Let's now create the instances for the below data.
train_loader = DeviceDataLoader(train_loader, device)
val_loader = DeviceDataLoader(val_loader, device)
model = to_device(EfficiencyModel(),device) #Initializing the model and also moving the model to the device
#once the model is initialized ,all the w's(weights) and b's(biases) are initialized at random which are then updated by calculating the derivative of the loss fuction with respect to both w and b respectively.
#This is how the model is made to learn the relations between inputs and targets.
list(model.parameters())
# Step 4: Train the model to fit the data
# To train our model, we'll use the same `fit` function explained in the lecture. That's the benefit of defining a generic training loop - you can use it for any problem.
def evaluate(model, val_loader):
#To evaluate the validation set
outputs = [model.validation_step(batch) for batch in val_loader]
return model.validation_epoch_end(outputs)
def fit(epochs, lr, model, train_loader, val_loader, opt_func=torch.optim.SGD):
history = []
optimizer = opt_func(model.parameters(), lr) #optimizer which is used update the parameters. Stochastic Gradient Descent is mostly used for linear reg. problems.
for epoch in range(epochs):
# Training Phase
losses=[]
for batch in train_loader:
loss = model.training_step(batch)
losses.append(loss) #the training losses are stored for every batch for future visualizations.
loss.backward() # Backpropagation(i.e, derivative(slope) calculation) is made by the model.
optimizer.step() #It updates the weights and biases with the derivatives and learning rate(i.e, w=w-lr*dw)
optimizer.zero_grad()
# Validation phase
result = evaluate(model, val_loader)
result['loss']= torch.stack(losses).mean() #The mean of training losses is stored for each epoch.
model.epoch_end(epoch, result, epochs)
history.append(result) #appending the results for visualization purposes.
return history
result = evaluate(model,val_loader) # Use the the evaluate function to evaluate the model.
print(result)
epochs = 150 #number of epochs to run the model.
lr = 0.5e-3 #learning rate
model = to_device(EfficiencyModel(),device)
model.load_state_dict(torch.load("savedmodel/1")) #To load the model and predict
model.eval()
#If you want run comment the above 2 lines and uncomment the below line
#history = fit(epochs, lr, model, train_loader, val_loader)
val_loss = evaluate(model,val_loader)
print(val_loss)
#Let's visualize the validation loss for the epochs
plt.plot([i['loss'] for i in history],'r')
plt.plot([i['val_loss'] for i in history],'b')
plt.xlabel("epochs")
plt.ylabel("losses")
plt.legend(['loss','val_loss'])
plt.show()
def predict_single(input, target, model):
predictions = model(input.unsqueeze(0))
prediction = predictions[0].detach() #detach() is done to remove it from the computational graphs i.e, its dervatives are no longer calculated.
print("Input:", input)
print("Target:", target)
print("Prediction:", prediction)
#Let's check the predictions of the model by passing a value from the validation set(you can also try by passing a tensor of your wish as torch.tensor([...]))
print(val_ds[4])
val=DeviceDataLoader(val_ds[4],device) #we have to move the data to the device to avoid errors.
input, target = val
predict_single(input, target, model)