实现线性二进制 SVM(支持向量机)

Posted

技术标签:

【中文标题】实现线性二进制 SVM(支持向量机)【英文标题】:Implementing a linear, binary SVM (support vector machine) 【发布时间】:2010-12-17 22:50:50 【问题描述】:

我想实现一个简单的 SVM 分类器,在高维二进制数据(文本)的情况下,我认为简单的线性 SVM 是最好的。自己实现的原因基本上是想了解它是如何工作的,所以使用库不是我想要的。

问题在于,大多数教程都提出了一个可以作为“二次问题”求解的方程,但它们从未展示过实际的算法!那么,您能否指出我可以学习的一个非常简单的实现,或者(更好)指向一个可以一直到实现细节的教程?

非常感谢!

【问题讨论】:

【参考方案1】:

顺序最小优化 (SMO) 方法的一些伪代码可以在 John C. Platt 的这篇论文中找到:Fast Training of Support Vector Machines using Sequential Minimal Optimization。还有一个 SMO 算法的 Java 实现,它是为研究和教育目的而开发的 (SVM-JAVA)。

解决QP优化问题的其他常用方法包括:

约束共轭梯度 内点法 活动集方法

但请注意,理解这些东西需要一些数学知识(拉格朗日乘数、Karush–Kuhn–Tucker 条件等)。

【讨论】:

我有数学背景,只是时间不多...谢谢你的回答!【参考方案2】:

您是否对使用内核感兴趣?如果没有内核,解决这类优化问题的最佳方法是通过各种形式的随机梯度下降。 http://ttic.uchicago.edu/~shai/papers/ShalevSiSr07.pdf 中描述了一个好的版本,它有一个明确的算法。

显式算法不适用于内核,但可以修改;但是,无论是在代码复杂度还是运行时复杂度方面,它都会更加复杂。

【讨论】:

不,目前我只对线性 SVM 感兴趣。感谢您的回答! 你知道一个简单的内核示例吗?我了解梯度下降,但内核更有趣。没有内核,它基本上是一个线性激活的感知器,不是吗?【参考方案3】:

在 libsvm 上查看 liblinear 和非线性 SVM

【讨论】:

您至少应该将链接添加到存储库。【参考方案4】:

以下论文“Pegasos: Primal Estimated sub-GrAdient SOlver for SVM”第 11 页顶部描述了 Pegasos 算法也适用于内核。它可以从 http://ttic.uchicago.edu/~nati/Publications/PegasosMPB.pdf 下载

它似乎是坐标下降和次梯度下降的混合体。此外,算法的第 6 行是错误的。在谓词中,y_i_t 的第二次出现应该替换为 y_j。

【讨论】:

【参考方案5】:

我想对关于普拉特原创作品的答案添加一点补充。 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 了解详细信息。

【讨论】:

以上是关于实现线性二进制 SVM(支持向量机)的主要内容,如果未能解决你的问题,请参考以下文章

支持向量机SVM及python实现

支持向量机(SVM):超平面及最大间隔化支持向量机的数学模型软间隔与硬间隔线性可分支持向量机线性支持向量机非线性支持向量机核函数核函数选择SMO算法SVM vs LR优缺点

11支持向量机SVM:线性可分支持向量机

支持向量机(SVM)常见问题

支持向量机(SVM)常见问题

支持向量机SVM(3)核函数、非线性支持向量机