ShamanChingChong commited on
Commit
f13d03e
·
2 Parent(s): a43ef418cc5050
Files changed (1) hide show
  1. CNN.py +203 -0
CNN.py ADDED
@@ -0,0 +1,203 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import numpy as np
2
+ from abc import ABC, abstractmethod
3
+
4
+
5
+ class Layer(ABC):
6
+ def __init__(self):
7
+ self.input = None
8
+ self.output = None
9
+
10
+ @abstractmethod
11
+ def forward(self, x_train):
12
+ pass
13
+
14
+ @abstractmethod
15
+ def backward(self, e, eta=0.01):
16
+ pass
17
+
18
+
19
+ class Conv2D(Layer):
20
+ def __init__(self, num_filter, filter_size, rgb: bool = True, stride=(1,1), pad: str = 'valid'):
21
+ super().__init__()
22
+ self.num_filter = num_filter
23
+ self.filter_size = filter_size
24
+ self.stride = stride
25
+ self.pad = pad
26
+ self.input = None
27
+ self.padding = None
28
+ if rgb:
29
+ self.filter = np.random.randn(num_filter, 3, filter_size, filter_size)
30
+ else:
31
+ self.filter = np.random.randn(num_filter, 1, filter_size, filter_size)
32
+ self.bias = np.random.randn(num_filter)
33
+
34
+ def forward(self, x_train):
35
+ self.input = x_train
36
+ num_samples, h, w, c = self.input.shape
37
+ pad_h, pad_w = (0, 0)
38
+ if self.pad == 'same':
39
+ pad_h = ((h - 1)(self.stride[0] - 1) + self.filter[0] - 1)//2
40
+ pad_w = ((w - 1)(self.stride[1] - 1) + self.filter[1] - 1)//2
41
+ self.padding = (pad_h, pad_w)
42
+ self.input = np.pad(x_train, pad_width=((0, 0,), (pad_h, pad_h), (pad_w, pad_w), (0, 0)),mode='constant')
43
+ out_h = (h - self.filter_size + 1) // self.stride + 1
44
+ out_w = (w - self.filter_size + 1) // self.stride + 1
45
+ self.output = np.zeros((num_samples, self.num_filter, out_h, out_w))
46
+ for h in range(out_h):
47
+ for w in range(out_w):
48
+ h_s = h * self.stride
49
+ h_e = h_s + self.filter_size
50
+ w_s = w * self.stride
51
+ w_e = w_s + self.filter_size
52
+ self.output[:, :, h, w] = np.sum(self.input[:, :, h_s:h_e, w_s:w_e] * self.filter + self.bias)
53
+ return self.output
54
+
55
+ def backward(self, e, eta=0.01):
56
+ num_samples, channel, h, w = self.input.shape
57
+ input_error = np.zeros(self.input.shape)
58
+ filter_error = np.zeros(self.filter.shape)
59
+ # get error per stride
60
+ for i in range((h - self.filter_size) // self.stride + 1):
61
+ for j in range((w - self.filter_size) // self.stride + 1):
62
+ h_s = i * self.stride
63
+ w_s = j * self.stride
64
+ region = self.input[:, :, h_s:h_s+self.filter_size, w_s:w_s+self.filter_size]
65
+ for k in range(self.num_filter): # take loop if the image is rgb
66
+ filter_error += np.sum(region * e[:, k, i, j][:, None, None, None], axis=0)
67
+ for n in range(num_samples):
68
+ input_error[n, :, h_s:h_s + self.filter_size, w_s:w_s + self.filter_size] += np.sum(self.filter * e[n, :, i, j][:, None, None, None], axis=0)
69
+ self.filter -= eta * filter_error
70
+ if self.padding != (0, 0):
71
+ input_error = input_error[:, :, self.padding[0]: -self.padding[0], self.padding[1]:-self.padding[1]]
72
+ return input_error
73
+
74
+
75
+ class MaxPooling2D(Layer):
76
+ def __init__(self, pool_size=2, stride=2):
77
+ super().__init__()
78
+ self.pool_size = pool_size
79
+ self.stride = stride
80
+ self.max_indices = None
81
+
82
+ def forward(self, x_train):
83
+ batch, channel, h, w = x_train.shape
84
+ self.input = x_train
85
+ out_h = (h-self.pool_size)//self.stride + 1
86
+ out_w = (h-self.pool_size)//self.stride + 1
87
+ self.output = np.zeros((batch, channel, out_h, out_w))
88
+ self.max_indices = np.zeros_like(self.output, dtype=int)
89
+ for i in range(out_h):
90
+ for j in range(out_w):
91
+ h_s = i*self.stride
92
+ h_e = h_s+self.pool_size
93
+ w_s = j*self.stride
94
+ w_e = w_s+self.pool_size
95
+ region = input[:, :, h_s:h_e, w_s:w_e]
96
+ self.output[:, :, i, j] = np.max(region, axis=(2, 3))
97
+ self.max_indices[:, :, i, j] = np.argmax(region.reshape(batch, channel, -1), axis=1)
98
+ return self.output
99
+
100
+ def backward(self, e, eta=0.01):
101
+ input_error = np.zeros_like(self.input)
102
+ out_height, out_width = self.output.shape[2], self.output.shape[3]
103
+
104
+ for i in range(out_height):
105
+ for j in range(out_width):
106
+ h_s = i * self.stride
107
+ w_s = j * self.stride
108
+ region = self.input[:, :, h_s:h_s + self.pool_size, w_s:w_s + self.pool_size]
109
+ region_reshaped = region.reshape(region.shape[0], region.shape[1], -1)
110
+ for k in range(region_reshaped.shape[0]):
111
+ for l in range(region_reshaped.shape[1]):
112
+ max_index = self.max_indices[k, l, i, j]
113
+ input_error[k, l, h_s:h_s + self.pool_size, w_s:w_s + self.pool_size].reshape(-1)[max_index] = e[k, l, i, j]
114
+ return input_error
115
+
116
+ class Dense(Layer):
117
+ def __init__(self, input_size, output_size, activation=None):
118
+ super().__init__()
119
+ self.weights = np.random.randn(input_size, output_size) * 0.01
120
+ self.biases = np.zeros((1, output_size))
121
+ self.activation = activation
122
+
123
+ @staticmethod
124
+ def sigmoid(x):
125
+ return 1/(1 + np.exp(-x))
126
+
127
+ @staticmethod
128
+ def relu(x):
129
+ return np.maximum(0, x)
130
+
131
+ @staticmethod
132
+ def sigmoid_dev(x):
133
+ return x*(1-x)
134
+
135
+ @staticmethod
136
+ def relu_dev(x):
137
+ return np.where(x>1, 1, 0)
138
+
139
+ def forward(self, x_train):
140
+ self.input = x_train
141
+ z = np.dot(x_train, self.weights) + self.biases
142
+ if self.activation == 'sigmoid':
143
+ self.output = self.sigmoid(z)
144
+ elif self.activation == 'relu':
145
+ self.output = self.relu(z)
146
+ return self.output
147
+
148
+ def backward(self, e, eta=0.01):
149
+ activation_derivative = 0
150
+ if self.activation == 'relu':
151
+ activation_derivative = self.relu_dev(self.output)
152
+ else:
153
+ activation_derivative = self.sigmoid_dev(self.output)
154
+ input_error = np.dot(e * activation_derivative, self.weights.T)
155
+ weights_error = np.dot(self.input.T, e * activation_derivative)
156
+
157
+ # Update parameters
158
+ self.weights -= eta * weights_error
159
+ self.biases -= eta * np.sum(e * activation_derivative, axis=0, keepdims=True)
160
+ return input_error
161
+
162
+ class Flatten(Layer):
163
+ def __init__(self):
164
+ super().__init__()
165
+
166
+ def forward(self, x_train):
167
+ self.input = x_train
168
+ flatten = x_train.reshape((x_train.shape[0], -1))
169
+ return flatten
170
+
171
+ def backward(self, e, eta=0.01):
172
+ return e.reshape(self.input.shape)
173
+
174
+
175
+ class CNN:
176
+ def __init__(self):
177
+ self.layers = []
178
+
179
+ def append(self, layer):
180
+ self.layers.append(layer)
181
+
182
+ def predict(self, input):
183
+ for layer in self.layers:
184
+ input = layer.forward(input)
185
+ return input
186
+
187
+ def fit(self, x_train, y_train, batch_size=36, epochs=10):
188
+
189
+ num = x_train.shape[0]
190
+
191
+ for i in range(epochs):
192
+ indices = np.arange(num)
193
+ np.random.shuffle(indices)
194
+ x = x_train[indices]
195
+ y = y_train[indices]
196
+
197
+ for i in range(0, num, batch_size):
198
+ x_batch = x[i:i+batch_size]
199
+ y_batch = y[i:i+batch_size]
200
+ output = self.predict(x_batch)
201
+ error = output - y_batch
202
+ for layer in reversed(self.layers):
203
+ error = layer.backward(error, 0.01)