【问题标题】:Help using predict() for kernlab's SVM in R?帮助在 R 中对 kernlab 的 SVM 使用 predict()?
【发布时间】:2010-12-17 17:17:13
【问题描述】:

我正在尝试使用 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 想要什么,我就可以确保我的测试数据能够以理智/合理/数学上合理的方式符合该格式。)

【问题讨论】:

    标签: r svm kernlab


    【解决方案1】:

    如果您考虑支持向量机如何“使用”内核矩阵,您会发现您无法以您尝试的方式真正做到这一点(正如您所见 :-)

    当我第一次使用 kernlab + 内核矩阵时,我实际上有点挣扎......巧合的是,它也适用于图形内核!

    无论如何,让我们首先意识到,由于 SVM 不知道如何计算您的核函数,它需要在您的新(测试)示例和它挑选的示例之间已经计算出这些值作为支持向量训练步骤。

    因此,您需要一起计算所有示例的内核矩阵。您稍后将通过在适当时从内核矩阵中删除行+列来训练一些并测试其他一些。让我用代码告诉你。

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

    library(kernlab)
    example(ksvm)
    

    您需要按几 (2) 次 return 才能绘制绘图,然后让示例完成,但现在您的工作区中应该有一个名为 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 中的代码以了解它如何使用支持向量索引,您就会明白为什么要这样做。或许可以用另一种方式做到这一点,但我没有看到任何关于 predicting 使用内核矩阵的文档/示例,所以我在这里“艰难地”做这件事。
    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 &lt;- K[holdout, -holdout] 有什么意义 - 你不是要删除与测试集对应的列吗?

    是的。简短的回答是,如果你想predict 使用核矩阵,你必须通过support vectors 提供维度为rows 的矩阵。对于矩阵的每一行(您要预测的新示例),列中的值只是在该示例和支持向量之间评估的内核矩阵的值。

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

    所以,首先执行testK &lt;- K[holdout, -holdout] 会给我一个testK 矩阵,其中包含我想要预测的示例行,并且列来自模型训练时使用的相同示例(维度)。

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

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

    有点防御性的编码,保证在执行索引操作后,返回的对象与被索引的对象是同一类型的。

    在 R 中,如果您仅索引 2D(或更高(?))对象的一个​​维度,则返回一个更低维度的对象。我不想将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.frames 等也会发生同样的情况。

    【讨论】:

    • 好的,我运行了你写的代码,它工作正常!但是我在理解它的作用时遇到了一些麻烦。您能否帮助我理解:K[-noldout,-holdout] 是什么意思? (“-”是什么意思?) as.kernelMatrix 的参数是什么 - 尤其是 [,SVindex(m),drop=F] 参数(这很奇怪,因为看起来整个括号都是矩阵索引K 的?)
    • 由于篇幅较长,我在原帖中回复了您的评论——阅读下半部分。如果您有更多问题,您可以随时来 r-help 列表提出更多问题。最后,如果我的回答确实回答了您的问题,请不要忘记将其标记为 ;-)
    • 老兄。你太摇滚了。谢谢!
    • @AP13:不要误会我的意思,但如果你对 SVM 的了解有限,那么在“盲目”使用它们之前,你应该做好功课以更好地了解它们的工作原理。话虽如此:(1)内核函数是您示例特征的函数,并且与它们的标签无关,因此在计算内核矩阵时不需要y; (2) 这是可能的,但您可能必须自己编写解决方案。内核矩阵为您提供每对示例(行,列)的内核值。您需要创建一个尊重这一点的“更新”内核矩阵。
    • @wrahool 我正在更新我的答案以解决您的评论 - 这个评论框太长了。
    【解决方案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)提供了一个通用的评估和测试框架,并包含几个小插曲和一个JSS paper

    【讨论】:

    • 对 - 我也在阅读 ?predict 。在此示例中,您将内核函数 ("rbfdot") 和训练数据 ("genetrain") 传递到 ksvm()。在我的例子中,我的输入是一个内核矩阵,所以 ksvm() 从来没有一个“数据”参数,所以我的训练数据的结构和我的测试数据的结构之间没有明确的映射。
    • 如果您很好奇,我正在尝试将图形内核实现到 R 中 - 因此,我不是对矢量数据进行分类,而是在查看图形数据。因此,我的核函数查看两个图之间等效的随机游走的数量,以确定它们的“距离”
    【解决方案3】:

    Steve Lianoglou 是对的。

    在 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")
    

    kernels 是输入内核矩阵。 trainidx 和 testidx 是训练和测试的 id。

    【讨论】:

      【解决方案4】:

      根据解决方案的元素自己构建标签。使用这种替代预测器方法,它采用 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
      

      【讨论】:

        猜你喜欢
        • 2012-03-03
        • 2015-06-09
        • 2016-12-16
        • 2012-08-18
        • 2012-11-02
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2015-02-15
        相关资源
        最近更新 更多