如何在R中使用kernlab的SVM进行predict()函数的帮助?

7
我正在尝试使用kernlab R包进行支持向量机(SVM)操作。在我的简单示例中,我有两个训练数据A和B。
(A和B是类型为matrix的邻接矩阵图表)
因此,我编写了一个函数,它将A+B作为输入并生成一个核矩阵。
> km
         [,1]     [,2]
[1,] 14.33333 18.47368
[2,] 18.47368 38.96053

现在我使用kernlabksvm函数来生成我的预测模型。现在,我只是试图让这个东西正常工作 - 我不担心训练误差等问题。
因此,问题1:我是否正确生成了我的模型?合理吗?
# y are my classes. In this case, A is in class "1" and B is in class "-1"
> y
[1]  1 -1

> model2 =  ksvm(km, y, type="C-svc", kernel = "matrix");
> model2
Support Vector Machine object of class "ksvm" 

SV type: C-svc  (classification) 
 parameter : cost C = 1 

[1] " Kernel matrix used as input."

Number of Support Vectors : 2 

Objective Function Value : -0.1224 
Training error : 0 

到目前为止,一切都很顺利。我们创建了自定义的核矩阵,然后使用该矩阵创建了一个ksvm模型。我们的训练数据标记为“1”和“-1”。

现在来进行预测:

> A
     [,1] [,2] [,3]
[1,]    0    1    1
[2,]    1    0    1
[3,]    0    0    0

> predict(model2, A)
Error in as.matrix(Z) : object 'Z' not found

哎呀,没事。这有点意料之中。"预测"需要某种类型的向量,而不是矩阵。

那么让我们尝试一些东西:

> predict(model2, c(1))
Error in as.matrix(Z) : object 'Z' not found
> predict(model2, c(1,1))
Error in as.matrix(Z) : object 'Z' not found
> predict(model2, c(1,1,1))
Error in as.matrix(Z) : object 'Z' not found
> predict(model2, c(1,1,1,1))
Error in as.matrix(Z) : object 'Z' not found
> predict(model2, km)
Error in as.matrix(Z) : object 'Z' not found

一些上述测试是毫无意义的,但这正是我的观点:无论我做什么,我都不能让predict()查看我的数据并进行预测。标量不起作用,向量也不起作用。2x2矩阵不起作用,3x3矩阵也不起作用。
我在这里做错了什么?
(一旦我弄清楚ksvm想要什么,那么我就可以确保我的测试数据以一个合理/可行/数学上正确的方式符合该格式。)
4个回答

23

如果您思考支持向量机如何“使用”核矩阵,您会发现您试图以这种方式做不到这一点(正如您所见:-)

当我第一次使用kernlab +核矩阵时,我确实遇到了一些困难...巧合的是,它也适用于图形内核!

无论如何,首先要认识到SVM不知道如何计算您的核函数,因此在训练步骤中选择为支持向量的示例与新(测试)示例之间必须已经计算这些值。

因此,您需要同时计算所有示例的核矩阵。稍后,通过在适当的时候从核矩阵中删除行+列,对其中某些进行培训和测试。 让我用代码给您展示。

我们可以使用ksvm文档中的示例代码来加载一些数据到我们的工作区:

library(kernlab)
example(ksvm)

您需要多次按回车键(2次),以便让图绘制和示例完成,但现在您的工作空间中应该有一个名为K的内核矩阵。我们需要恢复它应该用于其标签的y向量(因为在示例中已被其他代码覆盖):

y <- matrix(c(rep(1,60),rep(-1,60)))

现在,选择一部分示例用于测试

holdout <- sample(1:ncol(K), 10)

从这里开始,我要:

  1. 从原始的K内核矩阵创建一个名为trainK的训练内核矩阵。
  2. 从我的训练集trainK创建一个SVM模型
  3. 使用从模型中找到的支持向量创建一个测试内核矩阵testK...这是个奇怪的部分。如果你查看kernlab中的代码,看看它如何使用支持向量索引,你就会明白为什么要这样做。可能有其他的方法来做这件事,但我没有看到任何有关使用内核矩阵进行预测的文档/示例,所以我在这里“走了条艰难的路”。
  4. 使用SVM对这些特征进行预测并报告准确度

以下是代码:

trainK <- as.kernelMatrix(K[-holdout,-holdout])  # 1
m <- ksvm(trainK, y[-holdout], kernel='matrix')  # 2
testK <- as.kernelMatrix(K[holdout, -holdout][,SVindex(m), drop=F]) # 3
preds <- predict(m, testK)  # 4
sum(sign(preds) == sign(y[holdout])) / length(holdout) # == 1 (perfect!)

这就差不多了。祝你好运!

以下是对下面评论的回复

K[-holdout,-holdout]是什么意思?("-"代表什么?)

假设你有一个向量x,你想从它中获取第1、3和5个元素,你可以这样做:

x.sub <- x[c(1,3,5)]

如果你想从x中检索除了1、3和5之外的所有元素,你可以这样做:

x.sub <- x[-c(1,3,5)]

因此,K[-holdout,-holdout] 返回 K 的除了我们想要保留的行之外的所有行和列。

你的as.kernelMatrix函数有哪些参数——特别是[,SVindex(m),drop=F]参数(这个参数看起来非常奇怪,因为整个括号似乎是K的矩阵索引?)

是的,我把两个命令合并为一个了:

testK <- as.kernelMatrix(K[holdout, -holdout][,SVindex(m), drop=F])

在模型训练完成后,您希望用测试样例的新核矩阵来评估它。 K[holdout,] 将仅提供与K中训练样本相对应的行,并提供K的所有列。

SVindex(m)会给出来自您原始训练矩阵的支持向量的索引 —— 请记住,这些行/列已经去除了holdout。所以为了使这些列索引正确(即引用正确的SV列),我必须先删除holdout列。

也许这样更清晰:

testK <- K[holdout, -holdout]
testK <- testK[,SVindex(m), drop=FALSE]

现在testK仅包含我们的测试示例和对应支持向量的列。testK[1,1]将返回第一个测试示例和第一个支持向量之间计算得出的核函数值,testK[1,2]将返回第一个测试示例和第二个支持向量之间计算得出的核函数值,以此类推。

更新(2014-01-30)以回答@wrahool的评论

我已经有一段时间没有尝试过这个了,所以关于kernlab::ksvm的细节有点生疏,但原则上这应该是正确的:-)……来吧:

testK <- K[holdout, -holdout]的目的是什么 - 你不是在删除与测试集相对应的列吗?

是的。简短的答案是,如果你想使用核矩阵进行预测,你必须提供一个维度为rows乘以support vectors的矩阵。对于矩阵的每一行(你要预测的新示例),列中的值仅是在该示例和支持向量之间计算得出的核矩阵的值。

调用SVindex(m)返回给定在原始训练数据维度中的支持向量的索引。

因此,首先执行testK <- K[holdout, -holdout]会给我一个testK矩阵,其中包含我要预测的示例的行,以及来自模型训练的相同示例(维数)的列。

我进一步通过SVindex(m)testK的列进行了子集选择,只给我与支持向量(现在)对应的列。如果我没有执行第一个[, -holdout]选择,则SVindex(m)返回的索引可能不对应正确的示例(除非你的所有N个测试示例都是矩阵的最后N个列)。

此外,drop = FALSE条件到底是做什么的?

这是一种防御性编程,以确保在操作索引后返回的对象与被索引对象的类型相同。

在R中,如果你只索引二维(或更高维度?)对象的一个维度,则会返回一个较低维度的对象。我不想将一个numeric向量传递到predict中,因为它需要一个matrix

例如:

x <- matrix(rnorm(50), nrow=10)

class(x)
[1] "matrix"

dim(x)
[1] 10  5

y <- x[, 1]

class(y)
[1] "numeric"

dim(y)
NULL

对于data.frame等数据框架,同样也会发生这种情况。


1
由于内容较长,我在原始帖子中回复了您的评论,请查看底部。如果您有更多问题,可以随时来r-help列表询问。最后,如果我的回答解决了您的问题,请不要忘记将其标记为解决方案;-) - Steve Lianoglou
@Steve Lianoglou 谢谢你的解决方案,它救了我的一天!我对 SVM 知之甚少,而 kernlab 有非常特定的方法... 但是我不清楚是否应该包括响应变量 y 来计算核矩阵?是否有任何方法可以在有新数据时“更新”核矩阵而无需重新计算它?再次感谢。 - nopeva
1
@AP13:不要误会我的意思,但如果你对SVM的知识有限,你应该先做好功课,更好地理解它们的工作原理,而不是盲目使用它们。话虽如此:(1)核函数是你的示例特征的函数,与它们的标签无关,因此在计算核矩阵时不需要y;(2)这是可能的,但你可能需要自己编写代码来解决问题。核矩阵为每对示例(行、列)提供了核值。你需要创建一个“更新”的核矩阵来尊重这一点。 - Steve Lianoglou
@Steve Lianoglou,感谢您的回答,我一定会在使用它们之前先学习理论。 - nopeva
1
@wrahool 我正在更新我的答案以回应你的评论——它太长了,无法适应这个评论框。 - Steve Lianoglou
显示剩余3条评论

2
首先,我没有经常使用kernlab。但是仅通过查看文档,我可以看到predict.ksvm()方法的工作示例。复制并粘贴示例代码,省略屏幕打印:
 ## example using the promotergene data set
 data(promotergene)

 ## create test and training set
 ind <- sample(1:dim(promotergene)[1],20)
 genetrain <- promotergene[-ind, ]
 genetest <- promotergene[ind, ]

 ## train a support vector machine
 gene <-  ksvm(Class~.,data=genetrain,kernel="rbfdot",\
               kpar=list(sigma=0.015),C=70,cross=4,prob.model=TRUE)

 ## predict gene type probabilities on the test set
 genetype <- predict(gene,genetest,type="probabilities")

这似乎相当保守:使用随机抽样生成一个训练集“genetrain”及其补集“genetest”,然后通过“ksvm”进行拟合,并调用一个“predict()”方法使用匹配格式中的新数据与该拟合。这是非常标准的。
你可能会发现Max Kuhn开发的caret包很有用,它提供了一个通用的评估和测试框架,适用于各种回归、分类和机器学习方法和软件包,包括kernlab,并且包含几个vignettes和一篇JSS论文

没错 - 我也在阅读“predict”。在这个例子中,你将一个核函数(“rbfdot”)和训练数据(“genetrain”)传递到ksvm()中。在我的情况下,我的输入是一个核矩阵,因此ksvm()从未有过“data”参数,因此我的训练数据的结构和测试数据的结构之间没有明确的映射关系。 - poundifdef
1
如果你好奇的话,我正在尝试将图内核实现到R中 - 因此不是对向量数据进行分类,而是查看图形数据。因此,我的内核函数查看两个图之间等效的随机游走数量来确定它们的“距离”。 - poundifdef

2

史蒂夫·利亚诺格鲁是正确的。

在kernlab中有点奇怪,当进行预测时需要输入每个测试示例和支持向量之间的核矩阵。你需要自己找到这个矩阵。

例如,一个测试矩阵[n x m],其中n是测试样本的数量,m是学习模型中支持向量的数量(按SVindex(model)的顺序排序)。

示例代码

trmat <- as.kernelMatrix(kernels[trainidx,trainidx])
tsmat <- as.kernelMatrix(kernels[testidx,trainidx])

#training
model = ksvm(x=trmat, y=trlabels, type = "C-svc", C = 1)

#testing
thistsmat = as.kernelMatrix(tsmat[,SVindex(model)])
tsprediction = predict(model, thistsmat, type = "decision")

内核是输入内核矩阵。trainidx和testidx是用于训练和测试的ID。

0

从解决方案的元素中自己构建标签。使用这种替代预测方法,该方法采用ksvm模型(m)和原始训练格式中的数据(d)。

predict.alt <- function(m, d){
  sign(d[, m@SVindex] %*% m@coef[[1]] - m@b)
}

K是用于训练的kernelMatrix。为了验证,如果在训练数据上运行predict.alt,您会注意到备用预测方法会在ksvm返回的拟合值旁边切换值。本机预测器的行为方式不符合预期:

aux <- data.frame(fit=kout@fitted, native=predict(kout, K), alt=predict.alt(m=kout, d=as.matrix(K))) 
sample_n(aux, 10)
    fit  native alt
1     0       0  -1
100   1       0   1
218   1       0   1
200   1       0   1
182   1       0   1
87    0       0  -1
183   1       0   1
174   1       0   1
94    1       0   1
165   1       0   1

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