使用自定义的径向基函数核函数作为sklearn中SVC的方法比使用内置方法更快。

4
我注意到了一个非常特别但潜在非常有用的现象,在使用Scikit-Learn的SVC实现时。使用内置的rbf核函数比将自定义的rbf函数传递给SVC()的方式要慢得多,但可能更有效。
目前为止,我所看到和理解的唯一区别在于,在内置的rbf情况下,不是Scikit-Learn而是libsvm计算核函数。将专用的核函数作为超参数传递给SVC()会导致在Scikit-Learn中计算核函数,而不是在libsvm中。结果相同,但后一种情况只需要少部分的计算时间。
以下是一个例子,您可以复制此行为。 我已经创建了一个玩具数据集,模拟我目前正在处理的数据。顺便说一句,我还在处理大约一千个样本但高维度(~50000个特征)的数据,这导致了几乎相同的行为。
import numpy as np
from time import time
from sklearn.svm import SVC
from sklearn.datasets import make_classification
from sklearn.metrics.pairwise import rbf_kernel
from sklearn.metrics import accuracy_score

# create toy data
n_features = 1000
n_samples = 10000
n_informative = 10
X, y = make_classification(n_samples, n_features, n_informative=n_informative)
gamma = 1 / n_features

内置的RBF

首先,让我们使用内置的“rbf”核拟合SVC。这可能是人们通常运行SVC的方式。

# fit SVC with built-in rbf kernel
svc_built_in = SVC(kernel='rbf', gamma=gamma)
np.random.seed(13)
t1 = time()
svc_built_in.fit(X, y)
acc = accuracy_score(y, svc_built_in.predict(X))
print("Fitting SVC with built-in kernel took {:.1f} seconds".format(time()-t1))
print("Accuracy: {}".format(acc))

自定义RBF函数

其次,让我们做同样的事情,只是传递sklearn的rbf核函数,它应该完全相同。

# fit SVC with custom rbf kernel
svc_custom = SVC(kernel=rbf_kernel, gamma=gamma)
np.random.seed(13)
t1 = time()
svc_custom.fit(X, y)
acc = accuracy_score(y, svc_custom.predict(X))
print("Fitting SVC with a custom kernel took {:.1f} seconds".format(time()-t1))
print("Accuracy: {}".format(acc))

结果

这将产生以下结果。

Fitting SVC with built-in kernel took 58.6 seconds
Accuracy: 0.9846
Fitting SVC with a custom kernel took 3.2 seconds
Accuracy: 0.9846

我的问题

  1. 有没有人知道为什么传递内核函数比使用libsvm的内核计算要快得多?
  2. 对于我特定的用例(通常是大型数据集和长时间计算),由于计算时间显著减少,因此第二种方法非常有用,可以运行更多的超参数设置。有没有不这样做的原因?

1
这个问题最好作为一个 bug 报告提交到 sklearn 中,这样会更有意义。 - Marat
1
有道理...这是我在sklearn的Github页面上创建的问题 https://github.com/scikit-learn/scikit-learn/issues/21410 - Nils W.
1个回答

1
我在sklearn的bug报告(https://github.com/scikit-learn/scikit-learn/issues/21410)上收到了一些关于这个问题的好答案,所以我想在这里分享这些知识。
显然,在sklearn中计算核函数(而不是libsvm)时使用的是numpy。然而,numpy会自动使用您机器上所有可用的线程来加速核函数的计算。由于我在一台有32个线程的机器上运行此分析,因此我看到了明显的性能提升。不确定numpy更快的其他原因(更快或更智能的内存访问之类的),但我绝对可以确认并行化的发生。
因此,我的理解是,如果您正在一个较大的数据集上运行SVC,并且可以利用您机器上的多个线程,那么将核函数本身传递给SVC实例而不仅仅是一个字符串指示符可能是值得的。所有标准的核函数都已经在metrics.pairwise中实现(https://scikit-learn.org/stable/modules/metrics.html)。

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