-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathsnacks_solver.py
73 lines (59 loc) · 2.24 KB
/
snacks_solver.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
import numpy as np
import cvxopt
import torch
def gaussian(x, z, sigma=0.1):
return np.exp(-np.linalg.norm(x - z, axis=1) ** 2 / (2 * (sigma ** 2)))
class Snacks:
def __init__(self, kernel=gaussian, C=1):
self.kernel = kernel
self.C = C
def fit(self, X, y):
self.y = y
self.X = X
self.check_data(X, y)
m, n = self.X.shape
# Calculate Kernel
self.K = np.zeros((m, m))
for i in range(m):
self.K[i, :] = self.kernel(self.X[i, np.newaxis], self.X)
# Solve with cvxopt final QP needs to be reformulated
# to match the input form for cvxopt.solvers.qp
P = cvxopt.matrix(np.outer(self.y, self.y) * self.K)
q = cvxopt.matrix(-np.ones((m, 1)))
G = cvxopt.matrix(np.vstack((np.eye(m) * -1, np.eye(m))))
h = cvxopt.matrix(np.hstack((np.zeros(m), np.ones(m) * self.C)))
A = cvxopt.matrix(self.y, (1, m), "d")
b = cvxopt.matrix(np.zeros(1))
cvxopt.solvers.options["show_progress"] = False
sol = cvxopt.solvers.qp(P, q, G, h, A, b)
self.alphas = np.array(sol["x"])
def predict(self, X):
y_predict = np.zeros((X.shape[0]))
sv = self.get_parameters(self.alphas)
for i in range(X.shape[0]):
y_predict[i] = np.sum(
self.alphas[sv]
* self.y[sv, np.newaxis]
* self.kernel(X[i], self.X[sv])[:, np.newaxis]
)
return (np.sign(y_predict + self.b) * 0.5 + 0.5).astype(int)
def check_data(self, X, y):
if type(X) == torch.Tensor:
self.X = X.detach().cpu().numpy()
else:
self.X = X
if type(y) == torch.Tensor:
self.y = y.detach().cpu().numpy()
else:
self.y = y
if np.min(self.y) == 0:
self.y = 2*(self.y) - 1
def get_parameters(self, alphas):
threshold = 1e-5
sv = ((alphas > threshold) * (alphas < self.C)).flatten()
self.w = np.dot(self.X[sv].T, alphas[sv] * self.y[sv, np.newaxis])
self.b = np.mean(
self.y[sv, np.newaxis]
- self.alphas[sv] * self.y[sv, np.newaxis] * self.K[sv, sv][:, np.newaxis]
)
return sv