-
Notifications
You must be signed in to change notification settings - Fork 0
/
firstNN.py
271 lines (211 loc) · 9.52 KB
/
firstNN.py
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
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import pdb
import sys
class DataCleanser(object):
def __init__(self, train_features, train_targets, val_features,
val_targets, test_features, test_targets, test_data, data):
self.train_features = train_features
self.train_targets = train_targets
self.val_features = val_features
self.val_targets = val_targets
self.test_features = test_features
self.test_targets = test_targets
self.test_data = test_data
self.data = data
# Load data from csv returns as Pd.Frame
def extract(self, data_path):
rides = pd.read_csv(data_path)
return rides
def transform(self, rides):
# Convert Categorical variables into binary
# dummy/indicator variables in Pd.Frame
dummy_fields = ['season', 'weathersit', 'mnth', 'hr', 'weekday']
for each in dummy_fields:
dummies = pd.get_dummies(
rides[each], prefix=each, drop_first=False)
rides = pd.concat([rides, dummies], axis=1)
# purge non-required fields from data
fields_to_drop = ['instant', 'dteday', 'season', 'weathersit',
'weekday', 'atemp', 'mnth', 'workingday', 'hr']
self.data = rides.drop(fields_to_drop, axis=1)
# Scale continuous variables in data such that mean = 0, std. deviation = 1
# Also returns scaled features dict for use with plotting results
def scale_feat(self):
quant_features = ['casual', 'registered',
'cnt', 'temp', 'hum', 'windspeed']
# Store scalings in a dictionary so we can convert back later
scaled_features = {}
for each in quant_features:
mean, std = self.data[each].mean(), self.data[each].std()
scaled_features[each] = [mean, std]
self.data.loc[:, each] = (self.data[each] - mean) / std
return scaled_features
# Load up the data for use with training the model
def load(self):
# Save the last 21 days
data = self.data
test_data = data[-21 * 24:]
data = data[:-21 * 24]
# Separate the data into features and targets
target_fields = ['cnt', 'casual', 'registered']
features, targets = data.drop(target_fields, axis=1), data[
target_fields]
test_features, test_targets = test_data.drop(
target_fields, axis=1), test_data[target_fields]
# Hold out the last 60 days of the remaining data as a validation set
train_features, train_targets = features[:-60 * 24], targets[:-60 * 24]
val_features, val_targets = features[-60 * 24:], targets[-60 * 24:]
return DataCleanser(train_features, train_targets, val_features,
val_targets, test_features, test_targets,
test_data, data)
class NeuralNetwork(object):
def __init__(self, input_nodes, hidden_nodes, output_nodes, learning_rate):
# Set number of nodes in input, hidden and output layers.
self.input_nodes = input_nodes
self.hidden_nodes = hidden_nodes
self.output_nodes = output_nodes
# Initialize weights
self.weights_input_to_hidden = \
np.random.normal(0.0, self.hidden_nodes**-0.5,
(self.hidden_nodes, self.input_nodes))
self.weights_hidden_to_output = \
np.random.normal(0.0, self.output_nodes**-0.5,
(self.output_nodes, self.hidden_nodes))
self.lr = learning_rate
# Activation function is the sigmoid function
def activation_function(self, x):
return 1 / (1 + np.exp(-x))
# Trains the model
def train(self, inputs_list, targets_list):
# Convert inputs list to 2d array
inputs = np.array(inputs_list, ndmin=2).T
targets = np.array(targets_list, ndmin=2).T
# Forward pass
hidden_inputs = np.dot(self.weights_input_to_hidden, inputs)
hidden_outputs = self.activation_function(hidden_inputs)
# Signals into final output layer
final_inputs = np.dot(self.weights_hidden_to_output, hidden_outputs)
final_outputs = final_inputs
# Backward pass
# Output layer error is the difference between desired target and
# actual output.
output_errors = np.subtract(targets, final_outputs)
# errors propagated to the hidden layer
hidden_errors = np.dot(self.weights_hidden_to_output.T, output_errors)
# hidden layer gradients
hidden_grad = hidden_outputs * (1 - hidden_outputs)
# update hidden-to-output weights with gradient descent step
self.weights_hidden_to_output += self.lr * \
np.dot(output_errors, hidden_outputs.T)
# And update input to hidden weights
self.weights_input_to_hidden += self.lr * \
np.dot((hidden_grad * hidden_errors), inputs.T)
# Runs model we have trained
def run(self, inputs_list):
# Run a forward pass through the network
inputs = np.array(inputs_list, ndmin=2).T
# Hidden layer
hidden_inputs = np.dot(self.weights_input_to_hidden, inputs)
hidden_outputs = self.activation_function(hidden_inputs)
# Output layer
final_inputs = np.dot(self.weights_hidden_to_output, hidden_outputs)
final_outputs = final_inputs
return final_outputs
# Calc mean standard error
# Total error of network squared for normalization &
# Ease of use with derivatives
def MSE(y, Y):
return np.mean((y - Y)**2)
# Set the hyperparameters here
def set_hyper(hy_param):
# define hyper params and read into dict
# start low and increase til error loss flattens out
epochs = 1500
# .005 to .02 works best
learning_rate = 0.01
# roughly half way between # of inputs and # of output units
hidden_nodes = 25
output_nodes = 1
hy_param = {'epochs': epochs, 'learning_rate': learning_rate,
'hidden_nodes': hidden_nodes, 'output_nodes': output_nodes}
return hy_param
# Display results of the model
def plot_display(losses, rides, network, scaled_features, dc):
# Set up training loss vs validation loss graph
plt.plot(losses['train'], label='Training loss')
plt.plot(losses['validation'], label='Validation loss')
plt.legend()
plt.ylim(ymax=0.5)
fig, ax = plt.subplots(figsize=(8, 4))
# Set up prediction vs actual values graph
mean, std = scaled_features['cnt']
predictions = network.run(dc.test_features) * std + mean
ax.plot(predictions[0], label='Prediction')
ax.plot((dc.test_targets['cnt'] * std + mean).values, label='Data')
ax.set_xlim(right=len(predictions))
ax.legend()
# Set date ranges for x axis
dates = pd.to_datetime(rides.ix[dc.test_data.index]['dteday'])
dates = dates.apply(lambda d: d.strftime('%b %d'))
ax.set_xticks(np.arange(len(dates))[12::24])
# graph the graph! Woo!
plt.show()
def run_train(dc, hy, network):
losses = {'train': [], 'validation': []}
for e in range(hy['epochs']):
# Go through a random batch of 128 records from the training data set
# pdb.set_trace()
batch = np.random.choice(dc.train_features.index, size=128)
for record, target in zip(dc.train_features.ix[batch].values,
dc.train_targets.ix[batch]['cnt']):
network.train(record, target)
# Printing out the training progress
train_loss = MSE(network.run(dc.train_features),
dc.train_targets['cnt'].values)
val_loss = MSE(network.run(dc.val_features),
dc.val_targets['cnt'].values)
sys.stdout.write("\rProgress: " + str(100 * e / float(hy['epochs']))[
:4] + "%...Training loss: " + str(train_loss)[:5] +
" ... Validation loss: " + str(val_loss)[:5])
losses['train'].append(train_loss)
losses['validation'].append(val_loss)
# losses = summation of errors made against given data set
return losses
def main():
# Initilize some stuffs
data_path = 'Bike-Sharing-Dataset/hour.csv'
# Hyper parameter dict
hy_param = {}
# Get hyper values
hy_param = set_hyper(hy_param)
# Dummy variable used to send a bunch of blank data frames to
# Data Cleansing function.
# TODO: This seems stupid , pack all blank df into a dict and pass around
df = pd.DataFrame()
# Call data cleansing function, send objects to be filled with glorious
# data
dc = DataCleanser(df, df, df, df, df, df, df, df)
# Extract data from input into pd.Frame
rides = dc.extract(data_path)
# Perform necessary transforms on data for processing
dc.transform(rides)
# Scale / normalize features (0 mean, 1 std dev)
scaled_features = dc.scale_feat()
# Split data into relevant training / validation / predidiction sets
dc = dc.load()
# Define number of input nodes based on training_features
num_inputs = dc.train_features.shape[1]
# Triumphantly initilize our neural net!
network = NeuralNetwork(num_inputs, hy_param['hidden_nodes'],
hy_param['output_nodes'],
hy_param['learning_rate'])
# Run it and output our loss as dict
loss = run_train(dc, hy_param, network)
# Render beautiful graphs of
# Training loss vs validation loss and,
# Predictions Vs. Outcomes
plot_display(loss, rides, network, scaled_features, dc)
if __name__ == "__main__":
main()