手动实施支持向量机

时间:2016-09-12 12:04:36

标签: python r svm

我想知道是否有任何文章在R或Python中手动实现SVM(支持向量机)。

想要使用内置函数或包。 ?

在特征空间和线性可分离方面,示例可能非常简单。

我只是想通过整个过程来增强我的理解。

2 个答案:

答案 0 :(得分:1)

这个问题的答案相当广泛,因为有几种可能的算法可以训练SVM。另外像LibSVM(Python和R都可用)这样的包是开源的,所以你可以自由检查里面的代码。

在下文中,我将考虑J.Pratt的顺序最小优化(SMO)算法,该算法在LibSVM中实现。手动实现解决SVM优化问题的算法相当繁琐但是,如果这是您使用SVM的第一种方法,我建议使用以下(尽管是简化的)SMO算法版本

http://cs229.stanford.edu/materials/smo.pdf

本讲座来自Andrew Ng教授(斯坦福大学),他展示了SMO算法的简化版本。我不知道你在SVM中的理论背景是什么,但我们只是说主要区别在于拉格朗日乘数对( alpha_i alpha_j )是随机选择的,而在最初的SMO算法中,涉及到更难的启发式算法 换句话说,这个算法并不能保证收敛到全局最优(如果用适当的算法训练,这对于手头的数据集在SVM中总是如此),但这可以为你提供一个很好的介绍背后的优化问题支持向量机。

然而,本文没有在R或Python中显示任何代码,但伪代码相当简单且易于实现(在Matlab或Python中约有100行代码)。另请注意,此训练算法返回 alpha (拉格朗日乘数向量)和 b (拦截)。您可能希望有一些其他参数,例如正确的支持向量,但从向量 alpha 开始,这样的数量相当容易计算。

首先,让我们假设你有一个LearningSet,其中有多少行,就像有模式一样多的列,有多少列都有功能,让我们假设你有LearningLabels这是一个矢量与元素一样多,因为有模式,这个矢量(顾名思义)包含LearningSet中模式的正确标签。
还记得 alpha 包含的元素与模式一样多。

为了评估支持向量索引,您可以检查 alpha 中的元素 i 是否大于或等于0:如果alpha[i]>0则< em> i 来自LearningSet的模式是支持向量。同样,LearningLabels中的 i -th元素是相关标签。

最后,您可能想要评估向量 w ,即自由参数向量。鉴于 alpha 已知,您可以应用以下公式

w = ((alpha.*LearningLabels)'*LearningSet)'

其中alphaLearningLabels是列向量,LearningSet是如上所述的矩阵。在上面的公式中,.*是元素乘积,而'是转置算子。

答案 1 :(得分:0)

我想对先前的答案做些补充。 如果您对线性代数有信心,可以使用SMO算法的另一种派生方式,并基于该公式进行非常简单的实现。 基本上,它包含约30行Python代码

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的价值不高,如下所示 enter image description here

有关公式的更详细说明,您可能需要参考this ResearchGate preprint。 图像生成代码可在GitHub上找到。