ShamanChingChong commited on
Commit
027680f
·
1 Parent(s): c7cf7db
Files changed (1) hide show
  1. MLP.py +111 -0
MLP.py ADDED
@@ -0,0 +1,111 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import numpy as np
2
+ from abc import ABC, abstractmethod
3
+
4
+ class Layer(ABC):
5
+ def __init__(self, input_size, output_size):
6
+ self.input = None
7
+ self.output = None
8
+ self.input_size = input_size
9
+ self.output_size = output_size
10
+ self.weights = np.random.randn(input_size, output_size) * np.sqrt(2. / input_size)
11
+ self.bias = np.zeros((1, output_size))
12
+
13
+ @staticmethod
14
+ def activate(z, activation: str):
15
+ if activation == 'sigmoid':
16
+ return 1 / (1 + np.exp(-z))
17
+ elif activation == 'relu':
18
+ return np.maximum(0, z)
19
+ else:
20
+ print("Undefined activation type")
21
+ return None
22
+
23
+ @staticmethod
24
+ def derivative(activation: str, z):
25
+ if activation == 'sigmoid':
26
+ return z * (1 - z)
27
+ elif activation == 'relu':
28
+ return np.where(z > 0, 1, 0)
29
+ else:
30
+ print("Undefined activation type")
31
+ return None
32
+
33
+ @abstractmethod
34
+ def feedforward(self, x_train):
35
+ pass
36
+
37
+ @abstractmethod
38
+ def backpropagation(self, x_train, y_train, learning_rate):
39
+ pass
40
+
41
+ class Dense(Layer):
42
+ def __init__(self, input_size, output_size, activation: str):
43
+ super().__init__(input_size, output_size)
44
+ self.activation = activation
45
+
46
+ def feedforward(self, input):
47
+ self.input = input
48
+ z = np.dot(self.input, self.weights) + self.bias
49
+ self.output = self.activate(z, self.activation)
50
+ return self.output
51
+
52
+ def backpropagation(self, error, learning_rate):
53
+ d = self.derivative(self.activation, self.output)
54
+ db = np.sum(error * d, axis=0, keepdims=True)
55
+ dW = np.dot(self.input.T, error * d)
56
+
57
+ input_error = np.dot(error * d, self.weights.T)
58
+
59
+ self.weights -= learning_rate * dW
60
+ self.bias -= learning_rate * db
61
+
62
+ return input_error
63
+
64
+ class MLP:
65
+ def __init__(self):
66
+ self.layers = []
67
+
68
+ @staticmethod
69
+ def loss_MSE(y_train, yhat):
70
+ loss = np.mean(np.square(yhat - y_train))
71
+ return loss
72
+
73
+ @staticmethod
74
+ def loss_cross(y_train, yhat):
75
+ epsilon = 1e-9 # avoid log(0)
76
+ loss = -np.sum(y_train * np.log(yhat + epsilon)) / y_train.shape[0]
77
+ return loss
78
+
79
+ def addlayer(self, layer: Dense):
80
+ self.layers.append(layer)
81
+
82
+ def predict(self, input):
83
+ for layer in self.layers:
84
+ input = layer.feedforward(input)
85
+ return input
86
+
87
+ def fit(self, x_train, y_train, learning_rate=0.01, batch_size=8, epochs=10, loss_type: str = 'MSE'):
88
+ num_samples = x_train.shape[0]
89
+ for epoch in range(epochs):
90
+ indices = np.arange(num_samples)
91
+ np.random.shuffle(indices)
92
+ x_train = x_train[indices]
93
+ y_train = y_train[indices]
94
+ loss = 0
95
+
96
+ for i in range(0, num_samples, batch_size):
97
+ x_batch = x_train[i: i + batch_size]
98
+ y_batch = y_train[i: i + batch_size]
99
+
100
+ output = self.predict(input=x_batch)
101
+ error = output - y_batch
102
+
103
+ if loss_type == 'MSE':
104
+ loss += self.loss_MSE(y_batch, output)
105
+ else:
106
+ loss += self.loss_cross(y_batch, output)
107
+
108
+ for layer in reversed(self.layers):
109
+ error = layer.backpropagation(error, learning_rate)
110
+
111
+ print(f'Epoch {epoch + 1}/{epochs}, Loss: {loss / (num_samples // batch_size)}')