-
Notifications
You must be signed in to change notification settings - Fork 0
/
Perceptron.py
executable file
·180 lines (152 loc) · 7.09 KB
/
Perceptron.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
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
import numpy as np
import OptimizedTools as op
# Some inspiration is taken from:
# https://github.com/yihui-he/kernel-perceptron/blob/master/kerpercep.py
class Perceptron:
dim = None
sigma = None
kernel = None
def __init__(self, dataset_path, train_x, train_y,
_kernel_=1, epochs=10, _dim_=2, _sigma_=5):
"""
Loads or creates all the necessary files like the Gram Matrix and sets
all necessary variables in the class. It also looks for a file containing
pre-trained data like "alpha"s and "b"s.
:param dataset_path: String
path were the already prepared dataset is situated
:param train_x: array_like
a train set in the form of an ndarray containing all test elements
:param train_y: array_like
an ndarray containing binary classes, it can take values 1 or -1
it has the same number of rows as train_x
:param _kernel_: int
1: Linear, 2: Polynomial, 3: Gaussian
:param epochs: int
maximum number of epochs
:param _dim_: int
:param _sigma_: float
"""
self.epochs = epochs
self.train_x = np.ascontiguousarray(train_x, dtype=np.float32)
self.train_y = np.ascontiguousarray(train_y, dtype=np.float32)
self.gram_mat = None
self.R = None
self.dataset_path = dataset_path
Perceptron.dim = _dim_
Perceptron.sigma = _sigma_
Perceptron.kernel = _kernel_
try:
sv = np.loadtxt(str(self.dataset_path) + "/alpha_and_b_"
+ str(Perceptron.kernel) + ".csv", delimiter=',')
self.alpha = sv[:-1]
self.b = sv[-1]
except:
print("Alphas an b yet to compute")
gram_mat_filename = str(self.dataset_path) + "/gram_mat_" + str(Perceptron.kernel) + "_" + \
str(Perceptron.dim) + "_" + str(Perceptron.sigma) + ".npy"
try:
print("Trying to load the Gram Matrix...")
self.gram_mat = np.load(gram_mat_filename)
print("Gram Matrix loaded successfully")
except:
print("Gram Matrix doesn't exist...")
self.gram_mat = np.ascontiguousarray(Perceptron.calculate_and_save_gram_mat(self.train_x,
gram_mat_filename),
dtype=np.float32)
print("Gram Matrix created successfully")
def predict_set(self, test_x, test_y):
"""
Predicts a test_set and returns the accuracy
The computation is delegated to OptimizedTools
:param test_x: array_like
a test set in the form of an ndarray containing all test elements
:param test_y: array_like
an ndarray containing binary classes, it can take values 1 or -1
:return: ndarray
an ndarray containing predicted values that can be 1 or -1
"""
return op.predict_set(self.train_x,
self.train_y,
np.ascontiguousarray(test_x, dtype=np.float32),
np.ascontiguousarray(test_y, dtype=np.float32),
alpha=self.alpha,
b=self.b,
kernel=Perceptron.kernel,
dim=Perceptron.dim,
sigma=Perceptron.sigma)
def predict_element(self, an_element):
"""
Classifies a given element
:param an_element: ndarray
:return: int
1 or -1
"""
# the classes of the dataset are converted to numerical values
# thus here we are retrieving corresponding names
types = np.loadtxt(str(self.dataset_path) + "/types.csv", delimiter=',', dtype=str)
types = {types[0, 0]: types[0, 1], types[1, 0]: types[1, 1]}
summation = 0
for j in range(len(self.train_y)):
if Perceptron.kernel == 1:
summation += (self.alpha[j] * self.train_y[j] *
np.dot(self.train_x[j], an_element) + self.b)
elif Perceptron.kernel == 2:
summation += (self.alpha[j] * self.train_y[j] *
(np.dot(self.train_x[j], an_element) + 1) ** Perceptron.dim + self.b)
else:
summation += (self.alpha[j] * self.train_y[j] *
np.exp(-(np.linalg.norm(self.train_x[j] - an_element) ** 2)
/ (2.0 * (Perceptron.sigma ** 2))) + self.b)
if summation > 0:
activation = 1
else:
activation = -1
print("The given element is: ", types[str(activation)], " AKA: ", activation)
return activation
def fit(self):
"""
Fits according to the dual perceptron example in :
"Nello Cristianini, John Shawe-Taylor - An Introduction to
Support Vector Machines and Other Kernel-based Learning Methods, 1999"
The computation is delegated to OptimizedTools
Finally the corresponding parameters that are crucial in the dual form
of the hyperplane are saved in a .csv file
:return: None
"""
self.alpha, self.b = op.fit(self.train_x,
self.train_y,
np.ascontiguousarray(self.gram_mat, dtype=np.float32),
self.epochs)
np.savetxt(str(self.dataset_path) + "/alpha_and_b_" + str(Perceptron.kernel)
+ ".csv", X=np.append(self.alpha, self.b), delimiter=',')
return self.alpha, self.b
def set_epochs(self, epochs):
self.epochs = epochs
def set_kernel(self, kernel):
self.kernel = kernel
@staticmethod
def accuracy(actual_labels, predicted_labels):
"""
Calculates accuracy percentage
"""
correct = 0
for i in range(len(actual_labels)):
if actual_labels[i] == predicted_labels[i]:
correct += 1
return correct / float(len(actual_labels)) * 100.0
@staticmethod
def calculate_and_save_gram_mat(train_set, file_name):
"""
This is a wrapper method to allow np.save which is not supported in Numba yet
:param train_set: array_like
a train set in the form of an ndarray containing all test elements
:param file_name: String
desired file name of the gram mat
:return: ndarray
"""
gram_mat = op.calculate_gram_mat(np.ascontiguousarray(train_set, dtype=np.float32),
Perceptron.kernel,
Perceptron.dim,
Perceptron.sigma)
np.save(file=file_name, arr=gram_mat)
return gram_mat