我想在高维二进制数据(文本)的情况下实现一个简单的SVM分类器,我认为简单的线性SVM是最好的。自己实现它的原因基本上就是我想学习它是如何工作的,所以使用库不是我想要的。
问题在于,大多数教程都可以解决一个可以解决为“二次问题”的方程式,但它们从未显示出实际的算法!那么你能指出一个我可以学习的非常简单的实现,或者(更好)指向一直到实现细节的教程吗?
非常感谢!
答案 0 :(得分:12)
顺序最小优化(SMO)方法的一些伪代码可以在John C. Platt的论文中找到: Fast Training of Support Vector Machines using Sequential Minimal Optimization 。还有一个SMO算法的Java实现,它是为研究和教育目的而开发的(SVM-JAVA)。
解决QP优化问题的其他常用方法包括:
但请注意,需要一些数学知识才能理解这些事情(拉格朗日乘数,Karush-Kuhn-Tucker条件等)。
答案 1 :(得分:9)
您是否对使用内核感兴趣?没有内核,解决这些优化问题的最佳方法是通过各种形式的随机梯度下降。 http://ttic.uchicago.edu/~shai/papers/ShalevSiSr07.pdf中描述了一个好的版本,它有一个明确的算法。
显式算法不适用于内核但可以修改;但是,在代码和运行时复杂性方面,它会更复杂。
答案 2 :(得分:1)
在libsvm上查看liblinear和非线性SVM
答案 3 :(得分:0)
以下论文“Pegasos:Primal Estimated sub-GrAdient SOlver for SVM”第11页顶部描述了Pegasos算法也适用于内核。它可以从http://ttic.uchicago.edu/~nati/Publications/PegasosMPB.pdf下载
它似乎是坐标下降和次梯度下降的混合体。此外,算法的第6行是错误的。在谓词中,y_i_t的第二次出现应该替换为y_j。
答案 4 :(得分:0)
我想在有关原始普拉特作品的答案上添加一些补充。 Stanford Lecture Notes中有一个简化的版本,但是所有公式的推导都应该在其他地方找到(例如this random notes I found on the Internet)。
如果可以脱离原始实现,我可以为您提出我自己的以下SMO算法变体。
class SVM:
def __init__(self, kernel='linear', C=10000.0, max_iter=100000, degree=3, gamma=1):
self.kernel = {'poly':lambda x,y: np.dot(x, y.T)**degree,
'rbf':lambda x,y:np.exp(-gamma*np.sum((y-x[:,np.newaxis])**2,axis=-1)),
'linear':lambda x,y: np.dot(x, y.T)}[kernel]
self.C = C
self.max_iter = max_iter
def restrict_to_square(self, t, v0, u):
t = (np.clip(v0 + t*u, 0, self.C) - v0)[1]/u[1]
return (np.clip(v0 + t*u, 0, self.C) - v0)[0]/u[0]
def fit(self, X, y):
self.X = X.copy()
self.y = y * 2 - 1
self.lambdas = np.zeros_like(self.y, dtype=float)
self.K = self.kernel(self.X, self.X) * self.y[:,np.newaxis] * self.y
for _ in range(self.max_iter):
for idxM in range(len(self.lambdas)):
idxL = np.random.randint(0, len(self.lambdas))
Q = self.K[[[idxM, idxM], [idxL, idxL]], [[idxM, idxL], [idxM, idxL]]]
v0 = self.lambdas[[idxM, idxL]]
k0 = 1 - np.sum(self.lambdas * self.K[[idxM, idxL]], axis=1)
u = np.array([-self.y[idxL], self.y[idxM]])
t_max = np.dot(k0, u) / (np.dot(np.dot(Q, u), u) + 1E-15)
self.lambdas[[idxM, idxL]] = v0 + u * self.restrict_to_square(t_max, v0, u)
idx, = np.nonzero(self.lambdas > 1E-15)
self.b = np.sum((1.0-np.sum(self.K[idx]*self.lambdas, axis=1))*self.y[idx])/len(idx)
def decision_function(self, X):
return np.sum(self.kernel(X, self.X) * self.y * self.lambdas, axis=1) + self.b
在简单的情况下,它的功能不如sklearn.svm.SVC有价值,下面显示了比较(我在GitHub上发布了生成这些图像的代码)
我使用了完全不同的方法来得出公式,您可能需要检查my preprint on ResearchGate以获得详细信息。