-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathmulti_layer_percep.py
More file actions
337 lines (221 loc) · 7.32 KB
/
Copy pathmulti_layer_percep.py
File metadata and controls
337 lines (221 loc) · 7.32 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
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
import numpy as np
import matplotlib.pyplot as plt
class Perceptron():
"""
Functions to call after initializing the object are:
set_params -> to set parameters of the network
set_activation -> to choose the activation function
set_cost_function -> to choose how the cost function is calculated
train -> to train the network
predict -> after the training to get the predicted value
If the label is not encoded then you can use the one_hot_encoder
for one hot encoding
"""
def __init__(self,x,y):
xo = np.ones(shape = (x.shape[0],1))
self.x = np.hstack((xo,x))
self.y = y
self.m = x.shape[0]
self.n = x.shape[1]
def set_params(self, n_nodes, n_hlayers, n_classes):
"""
Setting parameters for Neural network model
n_nodes is the number of nodes in each hidden layer in a form of an iterable
n_hlayer gives number of hidden layer
n_classes is the number of output nodes or the number of classes to classify
"""
self.n_nodes = list(n_nodes)
self.n_nodes.insert(0,self.n)
self.n_nodes.insert(-1,n_classes)
self.n_hlayers = n_hlayers
self.n_classes = n_classes
if len(self.n_nodes)!=self.n_hlayers+2:
print("Number nodes and layers mismatch")
raise ValueError
def init_weights(self):
"""
Initializing weights to random values.(small values)
"""
self.theta = []
for i in range(len(self.n_nodes)-1):
a = np.random.normal(size = (self.n_nodes[i+1],self.n_nodes[i]+1))
self.theta.append(a)
def sigmoid(self,z):
return 1/(1+np.exp(-z))
def grad_sigmoid(self,z):
return self.sigmoid(z)*(1-self.sigmod(z))
def tanh(self,z):
return np.tanh(z)
def grad_tanh(self,z):
return 1-np.tanh(z)**2
def relu(self,z):
return np.maximum(0,z)
def grad_relu(self,z):
return z>0
def set_activation(self,act):
"""
choosing the activation functions
0 -> sigmoid
1 ->tanh
2->relu
"""
self.act = act
def activate(self,z):
"""
Runs the chosen activation function
"""
if self.act == 0:
self.sigmoid(z)
elif self.act == 1:
self.tanh(z)
elif self.act == 2:
self.relu(z)
else:
raise ValueError
def activate_grad(self,z):
"""
Gradient of the chosen activation function
"""
if self.act == 0:
self.grad_sigmoid(z)
elif self.act == 1:
self.grad_tanh(z)
elif self.act ==2:
self.grad_relu(z)
else:
raise ValueError
def feedforward(self, inp = None):
"""
Feed forward through the network
Return the output and also the input of each layer
"""
if inp == None:
a = self.bx
else:
a = inp
zlist = []
for i in range(self.n_hlayers):
z = np.dot(a,np.transpose(self.theta[i])) #Getting input from previous layer and summation
zlist.append(z)
a = self.activate(z) #passing the summation value through the activation function
ao = np.ones(shape = (a.shape[0],1))
a = np.hstack((ao,a)) #adding activation value of bias unit(1)
z = np.dot(a,np.transpose(self.theta[-1]))
return z,zlist
def sigmoid_cross_entropy(self):
z,_ = self.feedforward()
a = self.sigmoid(z)
t_sum = sum([np.sum(t[:,1:]**2) for t in self.theta]) #Regularisation term
self.cost = (-1/self.batch_size)*np.sum(self.by*np.log(a) + (1-self.by)*np.log(1 - a)) + (self.lam/(2*self.batch_size))*t_sum
def sigmoid_squared_error(self):
z,_ = self.feedforward()
a = self.sigmoid(z)
t_sum = sum([np.sum(t[:,1:]**2) for t in self.theta]) #Regularisation term
self.cost = (1/(2*self.batch_size))*np.sum(self.by-a)**2 + (self.lam/(2*self.batch_size))*t_sum
def softmax_cross_entropy(self):
z,_ = self.feedforward()
a = np.exp(z)
a = a/np.sum(a,axis = 1)
t_sum = sum([np.sum(t[:,1:]**2) for t in self.theta]) #Regularisation term
self.cost = (-1/self.batch_size)*np.sum(self.by*np.log(a) + (1-self.by)*np.log(1 - a)) + (self.lam/(2*self.batch_size))*t_sum
def set_cost_function(self,cf):
"""
Setting which cost function to use
0-> sigmoid cross entropy
1-> sigmoid squared error
2-> softmax cross entropy
"""
self.cf = cf
def cost_function(self):
"""
Running the chosen cost function
"""
if self.cf == 0:
self.sigmoid_cross_entropy()
elif self.cf == 1:
self.sigmoid_squared_error()
elif self.cf == 2:
self.softmax_cross_entropy()
else:
raise ValueError
def cost_grad(self,z):
"""
Chosing how to calculate error at output layer depending
cost function used
"""
if self.cf == 0:
a = self.sigmoid(z)
return a - self.by
elif self.cf ==1:
a = self.sigmoid(z)
return (a-self.by) * a * (1-a)
elif self.cf ==2:
a = np.exp(z)
a = a/np.sum(a,axis = 1)
return a-self.by
else:
raise ValueError
def backprop(self):
"""
Backpropogation function for finiding the gradients.
"""
d = []
z,zlist = self.feedforward()
d.append(self.cost_grad(z)) #error at output(depends on the cost_function used)
self.theta_grad = [np.zeros(shape = t.shape) for t in self.theta] #initializing the gradients
for i in range(n_hlayers):
a_grad = self.activate_grad(zlist[::-1][i])
d.append(np.dot(d[i],self.theta[::-1][i])[:,1:]*a_grad)
d.reverse()
self.theta_grad[0] = self.theta_grad[0] + np.dot(np.transpose(d[0]),self.bx) #gradient calculation of input weights(inp-next layer weights)
for i in range(1,len(self.theta_grad)):
a = self.activate(zlist[i-1])
ao = np.ones(shape = (a.shape[0],1))
a = np.hstack(ao,a)
self.theta_grad[i] = self.theta_grad[i] + np.dot(np.transpose(d[i]),a) #gradient calculation of all the other weights
#Regularisation
for i in range(len(self.theta_grad)):
self.theta_grad[i][:,1:]+=(self.lam*self.theta[:,1:])
self.theta_grad = [(1/self.batch_size)*tg for tg in self.theta_grad]
def train(self, alpha = 0.01, batch_size = 100,epochs = 10,lam = 0):
"""
Traines the network
Split to batches
Mini-Batch gradient descent
alpha is the learning rate
epochs is the number of iterations of the entire dataset
lam is the regularization factor by default it is zero
"""
self.lam = lam
self.batch_size = batch_size
self.init_weights()
j = 1
i=0
while j<=epochs:
while i+self.batch_size<self.m:
self.bx = self.x[i:i+self.batch_size,:] #Splitting up batches
self.by = self.y[i:i+self.batch_size,:]
print("Cost at current state: ",self.cost_function())
self.backprop()
for i in range(len(self.theta)):
self.theta[i] = self.theta[i] - (alpha*self.theta_grad[i]) #updating weights
i+=self.batch_size
j+=1
print("Trained!!")
def predict(self,inp):
"""
Run this function to get predictions
Return the index of the class predicted
"""
z,_ = self.feedforward(inp)
i = np.argmax(z) #finiding index of max value
return i
def one_hot_encoder(self):
"""
One hot encoding of labels
Given y value is from 1 to n (no of classes)
"""
ny = np.zeros(shape = (self.m,self.n_classes))
for i in range(m):
ny[i,self.y[i]-1] = 1 #The n-1 th index will be 1 for each example [where n is the class]
self.y = ny