Skip to content

Commit f57b71f

Browse files
committed
Update SVM
1 parent f3f274e commit f57b71f

File tree

5 files changed

+64
-67
lines changed

5 files changed

+64
-67
lines changed

e_SVM/KP.py

+14-22
Original file line numberDiff line numberDiff line change
@@ -38,27 +38,19 @@ def _fit(self, sample_weight, lr):
3838
class GDKP(GDKernelBase):
3939
GDKPTiming = Timing()
4040

41-
def __init__(self, **kwargs):
42-
super(GDKP, self).__init__(**kwargs)
43-
self._fit_args, self._fit_args_names = [1e-3], ["tol"]
44-
45-
@GDKPTiming.timeit(level=1, prefix="[Core] ")
46-
def _loss(self, y, y_pred, sample_weight):
47-
return np.sum(
48-
np.maximum(0, 1 - y * y_pred) * sample_weight
49-
)
50-
5141
@GDKPTiming.timeit(level=1, prefix="[Core] ")
5242
def _get_grads(self, x_batch, y_batch, y_pred, sample_weight_batch, *args):
53-
err = -y_batch * (x_batch.dot(self._alpha) + self._b)
54-
if np.max(err) < 0:
55-
return [None, None]
56-
mask = err >= 0
57-
delta = -y_batch[mask]
58-
self._model_grads = [
59-
np.sum(delta[..., None] * x_batch[mask], axis=0),
60-
np.sum(delta)
61-
]
43+
err = -y_batch * (x_batch.dot(self._alpha) + self._b) * sample_weight_batch
44+
mask = err >= 0 # type: np.ndarray
45+
if not np.any(mask):
46+
self._model_grads = [None, None]
47+
else:
48+
delta = -y_batch[mask] * sample_weight_batch[mask]
49+
self._model_grads = [
50+
np.sum(delta[..., None] * x_batch[mask], axis=0),
51+
np.sum(delta)
52+
]
53+
return np.sum(err[mask])
6254

6355
if __name__ == '__main__':
6456
# xs, ys = DataUtil.gen_two_clusters(center=5, dis=1, scale=2, one_hot=False)
@@ -67,17 +59,17 @@ def _get_grads(self, x_batch, y_batch, y_pred, sample_weight_batch, *args):
6759
ys[ys == 0] = -1
6860

6961
animation_params = {
70-
"show": False, "mp4": False, "period": 500,
62+
"show": False, "mp4": False, "period": 50,
7163
"dense": 400, "draw_background": True
7264
}
7365

7466
kp = KP(animation_params=animation_params)
75-
kp.fit(xs, ys, p=12, epoch=10 ** 4)
67+
kp.fit(xs, ys, kernel="poly", p=12, epoch=200)
7668
kp.evaluate(xs, ys)
7769
kp.visualize2d(xs, ys, dense=400)
7870

7971
kp = GDKP(animation_params=animation_params)
80-
kp.fit(xs, ys, p=12, epoch=10 ** 4)
72+
kp.fit(xs, ys, kernel="poly", p=12, epoch=10000)
8173
kp.evaluate(xs, ys)
8274
kp.visualize2d(xs, ys, dense=400)
8375

e_SVM/LinearSVM.py

+12-17
Original file line numberDiff line numberDiff line change
@@ -30,24 +30,20 @@ def __init__(self, **kwargs):
3030
self._params["tol"] = kwargs.get("tol", 1e-3)
3131
self._params["optimizer"] = kwargs.get("optimizer", "Adam")
3232

33-
@LinearSVMTiming.timeit(level=1, prefix="[Core] ")
34-
def _loss(self, y, y_pred, c):
35-
return np.sum(
36-
np.maximum(0, 1 - y * y_pred)
37-
) + c * np.linalg.norm(self._w)
38-
3933
@LinearSVMTiming.timeit(level=1, prefix="[Core] ")
4034
def _get_grads(self, x_batch, y_batch, y_pred, sample_weight_batch, *args):
4135
c = args[0]
4236
err = (1 - y_pred * y_batch) * sample_weight_batch
4337
mask = err > 0 # type: np.ndarray
4438
if not np.any(mask):
45-
return [None, None]
46-
delta = -c * y_batch[mask] * sample_weight_batch[mask]
47-
self._model_grads = [
48-
np.sum(delta[..., None] * x_batch[mask], axis=0),
49-
np.sum(delta)
50-
]
39+
self._model_grads = [None, None]
40+
else:
41+
delta = -c * y_batch[mask] * sample_weight_batch[mask]
42+
self._model_grads = [
43+
np.sum(delta[..., None] * x_batch[mask], axis=0),
44+
np.sum(delta)
45+
]
46+
return np.sum(err[mask]) + c * np.linalg.norm(self._w)
5147

5248
@LinearSVMTiming.timeit(level=1, prefix="[API] ")
5349
def fit(self, x, y, sample_weight=None, c=None, lr=None, optimizer=None,
@@ -79,15 +75,14 @@ def fit(self, x, y, sample_weight=None, c=None, lr=None, optimizer=None,
7975
self._optimizer = OptFactory().get_optimizer_by_name(
8076
optimizer, self._model_parameters, lr, epoch
8177
)
82-
loss_function = lambda _y, _y_pred: self._loss(_y, _y_pred, c)
8378

84-
bar = ProgressBar(max_value=epoch, name="TorchLinearSVM")
79+
bar = ProgressBar(max_value=epoch, name="LinearSVM")
8580
ims = []
8681
train_repeat = self._get_train_repeat(x, batch_size)
8782
for i in range(epoch):
8883
self._optimizer.update()
89-
l = self.batch_training(
90-
x, y, batch_size, train_repeat, loss_function, sample_weight, c
84+
l = self._batch_training(
85+
x, y, batch_size, train_repeat, sample_weight, c
9186
)
9287
if l < tol:
9388
bar.terminate()
@@ -152,7 +147,7 @@ def fit(self, x, y, c=None, lr=None, batch_size=None, epoch=None, tol=None,
152147
ims = []
153148
train_repeat = self._get_train_repeat(x, batch_size)
154149
for i in range(epoch):
155-
l = self.batch_training(x, y_2d, batch_size, train_repeat, loss, train_step)
150+
l = self._batch_training(x, y_2d, batch_size, train_repeat, loss, train_step)
156151
if l < tol:
157152
bar.terminate()
158153
break

e_SVM/README.md

+11-5
Original file line numberDiff line numberDiff line change
@@ -26,14 +26,20 @@ Implemented `Tensorflow` & `PyTorch` backend for `LinearSVM` & `SVM`
2626
![TorchLinearSVM on Two Clusters](https://cdn.rawgit.com/carefree0910/Resources/cbd5675e/Backgrounds/TorchLinearSVM.gif)
2727

2828
### Kernel Perceptron
29-
![Kernel Perceptron on Spiral](https://cdn.rawgit.com/carefree0910/Resources/d269faeb/Lines/KP.gif)
3029

31-
![Kernel Perceptron on Spiral](https://cdn.rawgit.com/carefree0910/Resources/d269faeb/Backgrounds/KP.gif)
30+
#### GD
31+
![Kernel Perceptron on Spiral](https://cdn.rawgit.com/carefree0910/Resources/14dfc108/Backgrounds/GDKP.gif)
32+
33+
#### SMO
34+
![Kernel Perceptron on Spiral](https://cdn.rawgit.com/carefree0910/Resources/14dfc108/Backgrounds/KP.gif)
3235

3336
### SVM
34-
![SVM on Spiral](https://cdn.rawgit.com/carefree0910/Resources/d269faeb/Lines/SVM.gif)
3537

36-
![SVM on Spiral](https://cdn.rawgit.com/carefree0910/Resources/d269faeb/Backgrounds/SVM.gif)
38+
#### GD
39+
![SVM on Spiral](https://cdn.rawgit.com/carefree0910/Resources/14dfc108/Backgrounds/GDSVM.gif)
40+
41+
#### SMO
42+
![SVM on Spiral](https://cdn.rawgit.com/carefree0910/Resources/14dfc108/Backgrounds/SVM.gif)
3743

3844
## Example
3945
```python
@@ -43,7 +49,7 @@ from e_SVM.SVM import SVM
4349
x, y = DataUtil.gen_spiral(20, 4, 2, 2, one_hot=False)
4450
y[y == 0] = -1 # Get spiral dataset, Notice that y should be 1 or -1
4551

46-
svm = SVM()
52+
svm = SVM() # Build SVM with SMO algorithm
4753
svm.fit(x, y, kernel="poly", p=12) # Train SVM (kernel: poly, degree: 12)
4854
svm.evaluate(x, y) # Print out accuracy
4955
svm.visualize2d(x, y, padding=0.1, dense=400, emphasize=svm["alpha"] > 0)

e_SVM/SVM.py

+11-19
Original file line numberDiff line numberDiff line change
@@ -108,27 +108,19 @@ def _fit(self, sample_weight, tol):
108108
class GDSVM(GDKernelBase):
109109
GDSVMTiming = Timing()
110110

111-
def __init__(self, **kwargs):
112-
super(GDSVM, self).__init__(**kwargs)
113-
self._fit_args, self._fit_args_names = [1e-3], ["tol"]
114-
115-
@GDSVMTiming.timeit(level=1, prefix="[Core] ")
116-
def _loss(self, y, y_pred, sample_weight):
117-
return np.sum(
118-
np.maximum(0, 1 - y * y_pred) * sample_weight
119-
) + 0.5 * (y_pred - self._b).dot(self._alpha)
120-
121111
@GDSVMTiming.timeit(level=1, prefix="[Core] ")
122112
def _get_grads(self, x_batch, y_batch, y_pred, sample_weight_batch, *args):
123113
err = -y_batch * (x_batch.dot(self._alpha) + self._b)
124-
if np.max(err) < 0:
125-
return [None, None]
126114
mask = err >= 0
127-
delta = -y_batch[mask]
128-
self._model_grads = [
129-
np.sum(delta[..., None] * x_batch[mask], axis=0),
130-
np.sum(delta)
131-
]
115+
if np.max(err) < 0:
116+
self._model_grads = [None, None]
117+
else:
118+
delta = -y_batch[mask] * sample_weight_batch[mask]
119+
self._model_grads = [
120+
np.sum(delta[..., None] * x_batch[mask], axis=0),
121+
np.sum(delta)
122+
]
123+
return np.sum(err[mask]) + 0.5 * (y_pred - self._b).dot(self._alpha)
132124

133125

134126
class TFSVM(TFKernelBase):
@@ -165,7 +157,7 @@ def _prepare(self, sample_weight, **kwargs):
165157
def _fit(self, sample_weight, tol):
166158
if self._train_repeat is None:
167159
self._train_repeat = self._get_train_repeat(self._x, self._batch_size)
168-
l = self.batch_training(
160+
l = self._batch_training(
169161
self._gram, self._y, self._batch_size, self._train_repeat,
170162
self._loss, self._train_step
171163
)
@@ -212,7 +204,7 @@ def _prepare(self, sample_weight, **kwargs):
212204
def _fit(self, sample_weight, tol):
213205
if self._train_repeat is None:
214206
self._train_repeat = self._get_train_repeat(self._x, self._batch_size)
215-
l = self.batch_training(
207+
l = self._batch_training(
216208
self._gram, self._y, self._batch_size, self._train_repeat,
217209
self._loss_function
218210
)

e_SVM/TestSVM.py

+16-4
Original file line numberDiff line numberDiff line change
@@ -14,17 +14,17 @@ def main():
1414
y[y == 0] = -1
1515

1616
animation_params = {
17-
"show": False, "period": 50, "mp4": False,
18-
"dense": 400, "draw_background": False
17+
"show": False, "mp4": False, "period": 50,
18+
"dense": 400, "draw_background": True
1919
}
2020

2121
svm = SVM(animation_params=animation_params)
22-
svm.fit(x, y, kernel="poly", p=12)
22+
svm.fit(x, y, kernel="poly", p=12, epoch=600)
2323
svm.evaluate(x, y)
2424
svm.visualize2d(x, y, padding=0.1, dense=400, emphasize=svm["alpha"] > 0)
2525

2626
svm = GDSVM(animation_params=animation_params)
27-
svm.fit(x, y, kernel="poly", p=12)
27+
svm.fit(x, y, kernel="poly", p=12, epoch=10000)
2828
svm.evaluate(x, y)
2929
svm.visualize2d(x, y, padding=0.1, dense=400, emphasize=svm["alpha"] > 0)
3030

@@ -90,6 +90,18 @@ def main():
9090
plt.plot(range(len(logs)), logs)
9191
plt.show()
9292

93+
svm = GDSVM()
94+
logs = [log[0] for log in svm.fit(
95+
x_train, y_train, metrics=["acc"], x_test=x_test, y_test=y_test
96+
)]
97+
svm.evaluate(x_train, y_train)
98+
svm.evaluate(x_test, y_test)
99+
100+
plt.figure()
101+
plt.title(svm.title)
102+
plt.plot(range(len(logs)), logs)
103+
plt.show()
104+
93105
svm.show_timing_log()
94106

95107
if __name__ == '__main__':

0 commit comments

Comments
 (0)