上次写线性SVM喷SMO神经病结果立即遭到了现世报。。。
我用SMO写出的SVM始终不收敛。。。(我能怎么办我也很绝望(;′⌒`)
但是不要紧,我们先曲线救下国,上次线性核的时候我们说了,SVM说白了就是感知机的升级版,所以我们可以用SGD对它进行收敛
然而,SGD相比SMO你要自己设定学习率,这是个很麻烦的参数啊= =绝望。。。
核函数干嘛用的,做映射,就是向高维投影,感觉就像是在计算距离的时候做特征工程?(不知道你们能不能get到我的点,就是我们不再用单一的原本的特征去度量作为支持向量的样本和其他距离的样本了,而是通过这些原始特征的高维组合来度量他们的距离
啥?你问我为支持向量是啥,为什么要度量它们的距离= =
再被我瞎比比一通带偏之前,你们先看看书((●ˇ∀ˇ●)
SVM我们说过了本身是找最大间隔,也就是超平面,怎么找超平面,本质上其实只要找到邻近超平面的这些样本点就可以了,而这些样本点我们就称为支持向量,然后通过新样本与支持向量这些点的位置关系我们就可以确定新样本的标签了
咦?怎么和线性SVM的时候不一样,其实一样的,线性SVM的时候我们在原问题上求解,那些hige损失非0的样本其实就是支持向量
我们把它转换成对偶问题,训练样本所对应的α非0,即说明对结果有作用,即为支持向量
这里又引出一个问题,问什么要转化成对偶问题,实质上为了自然引出核函数,也就是度量支持向量与新样本向量之间的关系
当然,我看到知乎上有同学说到会影响求解的复杂度,原回答是这样的
并不一定要用拉格朗日对偶。
要注意用拉格朗日对偶并没有改变最优解,而是改变了算法复杂度:
在原问题下,求解算法的复杂度与样本维度(等于权值w的维度)有关;
而在对偶问题下,求解算法的复杂度与样本数量(等于拉格朗日算子a的数量)有关。
出处:https://www.zhihu.com/question/36694952
很有道理。。但SMO我写着出问题啊((;′⌒`)
我们通过拉格朗日展开求解对偶问题,问题的变量就成了α与b
SMO的想法很有意思,我们通过KKT条件找出一个违反条件的α1,再找一个会让其变化最大的α2,固定其他α,我们就可以一步步逼近最优解
但,考虑到作者目前写出BUG了,我们不这么干(一本正经
我们转换对偶问题的时候会得到w=Σαi*yi*xi,我们把它带回到原问题y = wx+b,但我们现在要用核函数来度量xi与新的x
就变成了y=Σαi*yi*K(xi,x),通过损失进行梯度下降
class SVM2:
def __init__(self):
self._alpha = self._b = self._x = None
def fit(self, x, y, batchsize=128, l2=0.001, lr=0.1, epoch=10000):
x, y = np.asarray(x, np.float32), np.asarray(y, np.float32)
self._alpha = np.zeros(len(x))
self._b = 0.
self._x = x*y.reshape(len(x), 1)
k_mat = self._kernel(self._x, x)
for _ in range(epoch):
err = .3-y * (self._alpha.dot(k_mat)+self._b)
err[err < 0] = 0
idx = np.argsort(err)[::-1]
err = err[idx]
batchsize = min(batchsize, len(y))
err = err[:batchsize]
if err[0] <= 0:
continue
mask = err > 0
idx = idx[mask]
delta = lr * y[idx]
self._alpha += np.mean(delta[..., None] * k_mat[idx], axis=0)
self._b += np.mean(delta)
def _kernel(self, y, x):
# return x.dot(y.T)
return np.exp(-10 * np.sum((x[..., None, :] - y) ** 2, axis=2))
def predict(self, x, raw=False):
x = np.asarray(x, np.float32)
k_mat = self._kernel(self._x, x)
y_pred = k_mat.dot(self._alpha) + self._b
if raw:
return y_pred
return np.sign(y_pred).astype(np.float32)
看看结果
线辣么弯。。。一看就是有故事的曲线。。。
收工