-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathSimpleSVM.py
More file actions
133 lines (110 loc) · 3.61 KB
/
SimpleSVM.py
File metadata and controls
133 lines (110 loc) · 3.61 KB
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
# -*- coding: utf-8 -*-
"""
Created on Mon Jul 11 20:40:56 2016
@author: josiahw
"""
import numpy
import numpy.linalg
def polyKernel(a,b,pwr):
return numpy.dot(a,b)**pwr #numpy.dot(a,a) - numpy.dot(b,b) # -1 #
def rbfKernel(a,b,gamma):
return numpy.exp(-gamma * numpy.linalg.norm(a - b))
class SimpleSVM:
w = None
a = None
b = None
C = None
sv = None
kernel = None
kargs = ()
tolerance = None
verbose = True
def __init__(self,
C,
tolerance = 0.001,
kernel = numpy.dot,
kargs = ()
):
"""
The parameters are:
- C: SVC cost
- tolerance: gradient descent solution accuracy
- kernel: the kernel function do use as k(a, b, *kargs)
- kargs: extra parameters for the kernel
"""
self.C = C
self.kernel = kernel
self.tolerance = tolerance
self.kargs = kargs
def fit(self, X, y):
"""
Fit to data X with labels y.
"""
"""
Construct the Q matrix for solving
"""
ysigned = y * 2 - 1
Q = numpy.zeros((len(data),len(data)))
for i in xrange(len(data)):
for j in xrange(i,len(data)):
Qval = ysigned[i] * ysigned[j]
Qval *= self.kernel(*(
(data[i,:], data[j,:])
+ self.kargs
))
Q[i,j] = Q[j,i] = Qval
"""
Solve for a and w simultaneously by coordinate descent.
This means no quadratic solver is needed!
The support vectors correspond to non-zero values in a.
"""
self.w = numpy.zeros(X.shape[1])
self.a = numpy.zeros(X.shape[0])
delta = 10000000000.0
while delta > self.tolerance:
delta = 0.
for i in xrange(len(data)):
g = numpy.dot(Q[i,:], self.a) - 1.0
adelta = self.a[i] - min(max(self.a[i] - g/Q[i,i], 0.0), self.C)
self.w += adelta * X[i,:]
delta += abs(adelta)
self.a[i] -= adelta
if self.verbose:
print "Descent step magnitude:", delta
#print Q #self.a
self.sv = X[self.a > 0.0, :]
self.a = (self.a * ysigned)[self.a > 0.0]
if self.verbose:
print "Number of support vectors:", len(self.a)
"""
Select support vectors and solve for b to get the final classifier
"""
self.b = self._predict(self.sv[0,:])[0]
if self.a[0] > 0:
self.b *= -1
if self.verbose:
print "Bias value:", self.b
def _predict(self, X):
if (len(X.shape) < 2):
X = X.reshape((1,-1))
clss = numpy.zeros(len(X))
for i in xrange(len(X)):
for j in xrange(len(self.sv)):
clss[i] += self.a[j] * self.kernel(* ((self.sv[j,:],X[i,:]) + self.kargs))
return clss
def predict(self, X):
"""
Predict classes for data X.
"""
return self._predict(X) > self.b
if __name__ == '__main__':
import sklearn.datasets
data = sklearn.datasets.load_digits(2).data
labels = sklearn.datasets.load_digits(2).target
C = 100.0
clss = SimpleSVM(C,0.001,rbfKernel,(0.5,))
clss.fit(data,labels)
t = clss.predict(data)
print "Error", numpy.sum((labels-t)**2) / float(len(data))
#print sum(a > 0)
#print w