-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathlayers_builder.py
144 lines (110 loc) · 4.8 KB
/
layers_builder.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
import sys
import numpy as np
import utils
# Layer class is a map function which vinculates the *m* units from the layer (l) and link each one to
# the *n* units from the layer (l+1)
# the Weight matrix is composed by:
# + rows -> units from Layer(l+1)
# + columns -> units from Layer (l)
#
class Layer:
batch_size = None
def __init__(self, n_units_current, n_units_next, bias, layer_id):
self.layer_id = layer_id
self.n_units_current = n_units_current
self.n_units_next = n_units_next
self.bias = bias
# Summation vector
self.z = self.initialize_vector((self.n_units_current, Layer.batch_size))
# Activation vector
# Inialize the vector and then set the activation function
self.a = self.initialize_vector((self.n_units_current, Layer.batch_size))
self.set_activation()
# Weight matrix that connect units from current layer to next layer
self.W = self.initialize_weights()
# Delta-error vector
self.d = self.initialize_vector((self.bias + self.n_units_current, Layer.batch_size))
# Gradient error vector
self.g = self.initialize_vector(self.W.shape)
# Gradient approximation vector
self.ga = self.initialize_vector(self.g.shape)
def initialize_weights(self):
# case there's none next layer is the output layer, also there's no Weight matrix
if self.n_units_next is None:
return np.array([])
else:
weights = np.random.randn(self.n_units_next * (self.bias + self.n_units_current))
weights = weights.reshape(self.n_units_next, self.bias + self.n_units_current)
return weights
def initialize_vector(self, n_dimensions):
return np.random.normal(size=n_dimensions)
def set_activation(self):
self.a = utils.fun_sigmoid(self.z)
if self.bias:
self.add_activation_bias()
def add_activation_bias(self):
if len(self.a.shape) == 1:
self.a = np.vstack((1, self.a))
else:
self.a = np.vstack((np.ones(self.a.shape[1]), self.a))
def get_derivative_of_activation(self):
return utils.fun_sigmoid_derivative(self.a)
def update_weights(self, r):
self.W += -(r * self.g)
def check_gradient_computation(self, atol):
return np.allclose(self.g, self.ga, atol=atol)
def print_layer(self):
print("W:\n {} \n".format(self.W))
print("z: {}".format(self.z))
print("a: {}".format(self.a))
print("d: {}".format(self.d))
print("g: {}".format(self.g))
# The output layer is an exception case of the Layer class
# No summation vector
class LayerInput(Layer):
def __init__(self, n_units_current, n_units_next=None, bias=True, layer_id=0):
Layer.__init__(self, n_units_current, n_units_next, bias, layer_id)
self.z = []
# The hidden layer must have a link between the current units to next units
class LayerHidden(Layer):
def __init__(self, n_units_current, n_units_next, bias=True, layer_id=None):
Layer.__init__(self, n_units_current, n_units_next, bias, layer_id)
# The layer output is an exception case of the Layer class
# No bias, and no Weight matrix
class LayerOutput(Layer):
def __init__(self, n_units_current, layer_id):
Layer.__init__(self, n_units_current, n_units_next=None, bias=False, layer_id=None)
self.g = []
self.ga = []
class ObjLinearRegression(LayerOutput):
def __init__(self, n_units_current, layer_id):
LayerOutput.__init__(self, n_units_current, layer_id)
self.objective = 'linear-reg'
def set_activation(self):
self.a = self.z
def get_derivative_of_activation(self):
return np.ones(shape=self.a.shape)
class ObjLogisticRegression(LayerOutput):
def __init__(self, n_units_current, layer_id):
LayerOutput.__init__(self, n_units_current, layer_id)
self.objective = 'logistic-reg'
def net_constructer(layers_dim, batch_size, objective):
if len(layers_dim) < 2:
sys.exit("Neural Net must have at least 2 layers")
Layer.batch_size = batch_size
net = []
# First stage: create input and hidden layers
for i in np.arange(len(layers_dim) - 1, dtype=int):
if i == 0:
new_layer = LayerInput(layers_dim[i], layers_dim[i + 1], bias=True)
net.append(new_layer)
else:
new_layer = LayerHidden(layers_dim[i], layers_dim[i + 1], bias=True, layer_id=i)
net.append(new_layer)
# Second stage: create output layer
if objective == 'linear-reg':
new_layer = ObjLinearRegression(layers_dim[-1], layer_id=len(layers_dim))
elif objective == 'logistic-reg':
new_layer = ObjLogisticRegression(layers_dim[-1], layer_id=len(layers_dim))
net.append(new_layer)
return net