Skip to content

Commit d909e4b

Browse files
authored
Create mlp.py
1 parent 5013ab8 commit d909e4b

File tree

1 file changed

+135
-0
lines changed

1 file changed

+135
-0
lines changed

Day-27-MLP/mlp.py

Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
"""
2+
Multi-Layer Preceptron
3+
"""
4+
import torch
5+
from sklearn.datasets import load_digits
6+
from sklearn.model_selection import train_test_split
7+
8+
class Sigmoid:
9+
def __call__(self, X):
10+
return 1 / (1 + torch.exp(-X))
11+
12+
def gradient(self, X):
13+
return self.__call__(X) * (1 - self.__call__(X))
14+
15+
class Softmax:
16+
def __call__(self, X):
17+
e_x = torch.exp(X - torch.max(X, dim=-1, keepdim=True).values)
18+
return e_x / torch.sum(e_x, dim=1, keepdim=True)
19+
20+
def gradient(self, X):
21+
p = self.__call__(X)
22+
return p * (1 - p)
23+
24+
def accuracy_score(y, p):
25+
accuracy = torch.sum(y == p, dim=0) / len(y)
26+
return accuracy
27+
28+
def to_categorical(X, n_col=None):
29+
if not n_col:
30+
n_col = torch.amax(X) + 1
31+
32+
one_hot = torch.zeros((X.shape[0], n_col))
33+
one_hot[torch.arange(X.shape[0]), X] = 1
34+
return one_hot
35+
36+
def normalization(X):
37+
"""
38+
:param X: Input tensor
39+
:return: Normalized input using l2 norm.
40+
"""
41+
l2 = torch.norm(X, p=2, dim=-1)
42+
l2[l2 == 0] = 1
43+
return X / l2.unsqueeze(1)
44+
45+
class CrossEntropy:
46+
def __init__(self):
47+
pass
48+
def loss(self, y, p):
49+
p = torch.clip(p, 1e-15, 1-1e-15)
50+
return - y * torch.log(p) - (1 -y) * torch.log(1 - p)
51+
52+
def accuracy_score(self, y, p):
53+
return accuracy_score(torch.argmax(y, dim=1), torch.argmax(p, dim=1))
54+
55+
def gradient(self, y, p):
56+
p = torch.clip(p, 1e-15, 1 - 1e-15)
57+
return - (y / p) + (1 - y) / (1 -p)
58+
59+
class MultiLayerPerceptron:
60+
def __init__(self, n_hidden, n_iterations=1000, learning_rate=0.001):
61+
self.n_hidden = n_hidden
62+
self.n_iterations = n_iterations
63+
self.learning_rate = learning_rate
64+
self.hidden_activation = Sigmoid()
65+
self.output_activation = Softmax()
66+
self.loss = CrossEntropy()
67+
68+
def initalize_weight(self, X, y):
69+
n_samples, n_features = X.shape
70+
_, n_outputs = y.shape
71+
limit = 1 / torch.sqrt(torch.scalar_tensor(n_features))
72+
self.W = torch.DoubleTensor(n_features, self.n_hidden).uniform_(-limit, limit)
73+
74+
self.W0 = torch.zeros((1, self.n_hidden))
75+
limit = 1 / torch.sqrt(torch.scalar_tensor(self.n_hidden))
76+
self.V = torch.DoubleTensor(self.n_hidden, n_outputs).uniform_(-limit, limit)
77+
self.V0 = torch.zeros((1, n_outputs))
78+
79+
def fit(self, X, y):
80+
self.initalize_weight(X, y)
81+
for i in range(self.n_iterations):
82+
hidden_input = torch.mm(X, self.W) + self.W0
83+
hidden_output = self.hidden_activation(hidden_input)
84+
85+
output_layer_input = torch.mm(hidden_output, self.V) + self.V0
86+
y_pred = self.output_activation(output_layer_input)
87+
88+
grad_wrt_first_output = self.loss.gradient(y, y_pred) * self.output_activation.gradient(output_layer_input)
89+
grad_v = torch.mm(hidden_output.T, grad_wrt_first_output)
90+
grad_v0 = torch.sum(grad_wrt_first_output, dim=0, keepdim=True)
91+
92+
grad_wrt_first_hidden = torch.mm(grad_wrt_first_output, self.V.T) * self.hidden_activation.gradient(hidden_input)
93+
grad_w = torch.mm(X.T, grad_wrt_first_hidden)
94+
grad_w0 = torch.sum(grad_wrt_first_hidden, dim=0, keepdim=True)
95+
96+
# Update weights (by gradient descent)
97+
# Move against the gradient to minimize loss
98+
self.V -= self.learning_rate * grad_v
99+
self.V0 -= self.learning_rate * grad_v0
100+
self.W -= self.learning_rate * grad_w
101+
self.W0 -= self.learning_rate * grad_w0
102+
103+
# Use the trained model to predict labels of X
104+
105+
def predict(self, X):
106+
# Forward pass:
107+
hidden_input = torch.mm(X,self.W) + self.W0
108+
hidden_output = self.hidden_activation(hidden_input)
109+
output_layer_input = torch.mm(hidden_output, self.V) + self.V0
110+
y_pred = self.output_activation(output_layer_input)
111+
return y_pred
112+
113+
114+
if __name__ == '__main__':
115+
data = load_digits()
116+
X = normalization(torch.tensor(data.data, dtype=torch.double))
117+
y = torch.tensor(data.target)
118+
119+
# Convert the nominal y values to binary
120+
y = to_categorical(y)
121+
122+
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.4, random_state=1)
123+
# MLP
124+
clf = MultiLayerPerceptron(n_hidden=16,
125+
n_iterations=1000,
126+
learning_rate=0.01)
127+
128+
clf.fit(X_train, y_train)
129+
y_pred = torch.argmax(clf.predict(X_test), dim=1)
130+
y_test = torch.argmax(y_test, dim=1)
131+
132+
accuracy = accuracy_score(y_test, y_pred)
133+
print("Accuracy:", accuracy)
134+
135+

0 commit comments

Comments
 (0)