在Python中加速支持向量机(SVM)运行

58

使用以下Python代码实现SVM:

from sklearn import datasets
from sklearn.multiclass import OneVsRestClassifier
from sklearn.svm import SVC
iris = datasets.load_iris()
X, y = iris.data, iris.target
clf = OneVsRestClassifier(SVC(kernel='linear', probability=True, class_weight='auto'))
clf.fit(X, y)
proba = clf.predict_proba(X)

但这需要大量的时间。

实际数据维度:

train-set (1422392,29)
test-set (233081,29)

我应该如何加速它(使用并行或其他方式)?请帮帮我。 我已经尝试过PCA和降采样。

我有6个类。 编辑: 找到http://scikit-learn.org/stable/modules/generated/sklearn.linear_model.SGDClassifier.html 但我需要概率估计,SVM似乎没有这个功能。

编辑:

from sklearn import datasets
from sklearn.multiclass import OneVsRestClassifier
from sklearn.svm import SVC,LinearSVC
from sklearn.linear_model import SGDClassifier
import joblib
import numpy as np
from sklearn import grid_search
import multiprocessing
import numpy as np
import math

def new_func(a):                              #converts array(x) elements to (1/(1 + e(-x)))
    a=1/(1 + math.exp(-a))
    return a

if __name__ == '__main__':
    iris = datasets.load_iris()
    cores=multiprocessing.cpu_count()-2
    X, y = iris.data, iris.target                       #loading dataset

    C_range = 10.0 ** np.arange(-4, 4);                  #c value range 
    param_grid = dict(estimator__C=C_range.tolist())              

    svr = OneVsRestClassifier(LinearSVC(class_weight='auto'),n_jobs=cores) ################LinearSVC Code faster        
    #svr = OneVsRestClassifier(SVC(kernel='linear', probability=True,  ##################SVC code slow
    #   class_weight='auto'),n_jobs=cores)

    clf = grid_search.GridSearchCV(svr, param_grid,n_jobs=cores,verbose=2)  #grid search
    clf.fit(X, y)                                                   #training svm model                                     

    decisions=clf.decision_function(X)                             #outputs decision functions
    #prob=clf.predict_proba(X)                                     #only for SVC outputs probablilites
    print decisions[:5,:]
    vecfunc = np.vectorize(new_func)
    prob=vecfunc(decisions)                                        #converts deicision to (1/(1 + e(-x)))
    print prob[:5,:]

编辑2: 用户3914041的答案给出了非常糟糕的概率估计。


1
量化“大量时间”。你用什么来分析你的代码? - user559633
2
OneVsRestClassifier带有并行选项,但请注意它可能会消耗大量资源,因为拟合每个模型需要显著的时间。尝试根据文档此处设置n_jobs参数。 - rabbit
2
尝试使用Continuum的MKL优化,参见https://store.continuum.io/cshop/mkl-optimizations/。他们提供30天免费试用,售价为99美元。我不是销售代表,但我使用他们的Anaconda Python发行版并且很喜欢它——在Spark Summit培训中推荐了它。顺便说一句,Spark支持SVM,并且在即使是小型Spark集群上运行它也可以大大提高性能,请参见https://spark.apache.org/docs/1.1.0/mllib-linear-methods.html#linear-support-vector-machines-svms。 - user4322779
1
支持向量机(SVM)在CPU上运行速度非常慢,需要大量时间。为了加快速度,您需要对主成分分析(PCA)数据进行白化处理,或者尝试寻找可以在GPU上运行的库。 - pbu
1
这并不是真正的并行梯度,而是在并行中拟合6个单独的OneVsRest模型,因此它不能并行化更多。如果您打算因为概率估计而继续使用Python和sklearn.SVC,那么我认为您最好的选择可能是对其进行降采样、PCA处理,并使用6个作业的OneVsRest。 - rabbit
显示剩余15条评论
5个回答

134

如果您想尽可能地坚持使用SVC并在完整数据集上进行训练,则可以使用在数据子集上训练的SVC集合来减少每个分类器的记录数量(这显然对复杂度有二次影响)。 Scikit支持使用BaggingClassifier包装器实现此目的。与单个分类器相比,这应该为您提供类似(或更好)的准确性,并且训练时间要少得多。还可以使用n_jobs参数将单个分类器的训练设置为并行运行。

或者,我还建议考虑使用随机森林分类器-它原生支持多类别分类,速度快,并且在适当设置min_samples_leaf时给出相当不错的概率估计。

我在鸢尾花数据集上进行了一次快速测试,将其放大100倍,使用10个SVC集合进行了测试,每个集合都是在数据的10%上进行训练。与单个分类器相比,它要快十多倍。以下是我在笔记本电脑上得到的数字:

单个SVC:45秒

组合SVC:3秒

随机森林分类器:0.5秒

请参见下面的代码,这是我用来生成数字的代码:

import time
import numpy as np
from sklearn.ensemble import BaggingClassifier, RandomForestClassifier
from sklearn import datasets
from sklearn.multiclass import OneVsRestClassifier
from sklearn.svm import SVC

iris = datasets.load_iris()
X, y = iris.data, iris.target

X = np.repeat(X, 100, axis=0)
y = np.repeat(y, 100, axis=0)
start = time.time()
clf = OneVsRestClassifier(SVC(kernel='linear', probability=True, class_weight='auto'))
clf.fit(X, y)
end = time.time()
print "Single SVC", end - start, clf.score(X,y)
proba = clf.predict_proba(X)

n_estimators = 10
start = time.time()
clf = OneVsRestClassifier(BaggingClassifier(SVC(kernel='linear', probability=True, class_weight='auto'), max_samples=1.0 / n_estimators, n_estimators=n_estimators))
clf.fit(X, y)
end = time.time()
print "Bagging SVC", end - start, clf.score(X,y)
proba = clf.predict_proba(X)

start = time.time()
clf = RandomForestClassifier(min_samples_leaf=20)
clf.fit(X, y)
end = time.time()
print "Random Forest", end - start, clf.score(X,y)
proba = clf.predict_proba(X)

如果您想确保在BaggingClassifier中每条记录仅用于一次训练,可以将bootstrap参数设置为False。


1
非常感谢您的精彩回答!我之前并不知道这些。除了速度,准确性也是我的主要关注点。如果可能的话,您能否进行比较?我并不局限于使用SVC,如果您愿意,请推荐其他好的方法。 - Abhishek Bhatia
此外,您还可以查看 sklearn.ensemble.AdaBoostClassifier 以与随机森林或决策树一起使用。 - jchook
1
如果您想要一个线性核,可以使用 sklearn.svm.LinearSVC,它基本上与 sklearn.svm.SVC 相同,但是使用的库比 sklearn.svm.SVC 更快。 - fdelia
2
这是一个很好的方法!:在没有BaggingClassifier的情况下运行时,我得到了类似的F1分数结果;但是在使用BaggingClassifier时,它只花费了31分钟8秒,而不是4天3小时27分钟。 - kaleemsagard
第一次训练SVM时,我尝试将n_jobs增加到超过1,但我意识到它根本不运行。只有当n_jobs = 1时才会运行。你知道可能是什么原因吗? - fatbringer
显示剩余2条评论

23

SVM分类器不容易进行扩展。从文档中了解到,sklearn.svm.SVC的复杂度如下:

  

拟合时间复杂度随着样本数的增加而超过二次方,这使得它难以处理超过几千个样本的数据集。

在scikit-learn中,您可以使用svm.linearSVC进行更好的扩展。显然,它能够处理您的数据。

或者您可以选择另一个分类器。如果您想要概率估计,我建议使用逻辑回归。逻辑回归还具有不需要概率校准即可输出“正确”概率的优点。

编辑:

我不知道linearSVC的复杂性,最后我在用户指南中找到了信息:

  

还要注意,在线性情况下,liblinear实现中LinearSVC使用的算法比其基于libsvm的SVC对应物更高效,并且可以几乎线性地扩展到数百万个样本和/或特征。

要从linearSVC中获取概率,请查看此链接。它距离我上面链接的概率校准指南仅有几个链接,并包含一种估算概率的方法。即:

    prob_pos = clf.decision_function(X_test)
    prob_pos = (prob_pos - prob_pos.min()) / (prob_pos.max() - prob_pos.min())

注意,如果没有校准(调整),估计结果可能会很差,如链接所示。


谢谢回复!关于缩放,@NBartley之前已经提到过了。我尝试过逻辑回归,但准确率较低。 - Abhishek Bhatia
1
谢谢回复!但是linearSVC没有输出概率估计的选项。 - Abhishek Bhatia
1
你说得对。一个可能的解决方法是使用decision_function属性,就像我在概率校准链接中介绍LinearSVC一样。但是您肯定需要进行概率校准才能使其有意义。 - ldirer
2
如果您有具体问题,请随时提问,但对于概念,我无法比我在帖子中提供的链接做得更好。 - ldirer
非常感谢!predict_proba()的实现是否与您所提到的类似? - Abhishek Bhatia
显示剩余9条评论

9

7

在高赞答案中简要提到了它,这是代码:最快的方法是通过 n_jobs参数 来实现:替换该行

clf = OneVsRestClassifier(SVC(kernel='linear', probability=True, class_weight='auto'))

使用

clf = OneVsRestClassifier(SVC(kernel='linear', probability=True, class_weight='auto'), n_jobs=-1)

这将利用您计算机上所有可用的CPU,同时仍执行与以前相同的计算。


你会将 n_jobs 参数传递给 OVR 分类器 还是 Bagging 分类器 - artemis
顶层,在这种情况下是OvR - serv-inc
对于我的SVM,只有当n_jobs = 1时才能进行训练。对于其他数字,它似乎就卡在那里了。这是因为我正在使用图像作为输入数据吗? - fatbringer

3

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接