(在R中)为什么使用用户定义的线性内核的ksvm的结果与使用"vanilladot"的ksvm的结果不同?

use*_*765 5 r machine-learning svm

我想在R中使用Ksvm的用户自定义内核函数.所以,我尝试制作一个vanilladot内核并与"killalab"中的"vanilladot"进行比较.

我按如下方式编写内核.

#
###vanilla kernel with class "kernel"
#
kfunction.k <- function(){
   k <- function (x,y){crossprod(x,y)}
   class(k) <- "kernel"
   k}
l<-0.1 ; C<-1/(2*l)

###use kfunction.k
tmp<-ksvm(x,factor(y),scaled=FALSE, type = "C-svc", kernel=kfunction.k(), C = C)
alpha(tmp)[[1]]
ind<-alphaindex(tmp)[[1]]
x.s<-x[ind,] ; y.s<-y[ind]
w.class.k<-t(alpha(tmp)[[1]]*y.s)%*%x.s
w.class.k
Run Code Online (Sandbox Code Playgroud)

我认为这个操作的结果是以下的结果.但它不是.

#
###use "vanilladot"
#
l<-0.1 ; C<-1/(2*l)
tmp1<-ksvm(x,factor(y),scaled=FALSE, type = "C-svc", kernel="vanilladot", C = C)
alpha(tmp1)[[1]]
ind1<-alphaindex(tmp1)[[1]]
x.s<-x[ind1,] ; y.s<-y[ind1]
w.tmp1<-t(alpha(tmp1)[[1]]*y.s)%*%x.s
w.tmp1
Run Code Online (Sandbox Code Playgroud)

我想也许这个问题与内核类有关.当类设置为"kernel"时,会出现此问题.但是当class设置为"vanillakernel"时,使用用户定义内核的ksvm的结果与使用Kernlab中内置的"vanilladot"的ksvm的结果相同.

#
###vanilla kernel with class "vanillakernel"
#
kfunction.v.k <- function(){
   k <- function (x,y){crossprod(x,y)}
   class(k) <- "vanillakernel"  
   k}
# The only difference between kfunction.k and kfunction.v.k is "class(k)".
l<-0.1 ; C<-1/(2*l)

###use kfunction.v.k
tmp<-ksvm(x,factor(y),scaled=FALSE, type = "C-svc", kernel=kfunction.v.k(), C = C)
alpha(tmp)[[1]]
ind<-alphaindex(tmp)[[1]]
x.s<-x[ind,] ; y.s<-y[ind]
w.class.v.k<-t(alpha(tmp)[[1]]*y.s)%*%x.s
w.class.v.k
Run Code Online (Sandbox Code Playgroud)

当将类设置为"kernel"时,我不明白为什么结果与"vanilladot"不同.

我的手术有错误吗?

lej*_*lot 3

首先,这似乎是一个非常好的问题!

现在进入正题。在源代码中,ksvm我们可以找到使用用户定义内核和内置内核之间的界限:

 if (type(ret) == "spoc-svc") {
            if (!is.null(class.weights)) 
                weightedC <- class.weights[weightlabels] * rep(C, 
                  nclass(ret))
            else weightedC <- rep(C, nclass(ret))
            yd <- sort(y, method = "quick", index.return = TRUE)
            xd <- matrix(x[yd$ix, ], nrow = dim(x)[1])
            count <- 0
            if (ktype == 4) 
                K <- kernelMatrix(kernel, x)
            resv <- .Call("tron_optim", as.double(t(xd)), as.integer(nrow(xd)), 
                as.integer(ncol(xd)), as.double(rep(yd$x - 1, 
                  2)), as.double(K), as.integer(if (sparse) xd@ia else 0), 
                as.integer(if (sparse) xd@ja else 0), as.integer(sparse), 
                as.integer(nclass(ret)), as.integer(count), as.integer(ktype), 
                as.integer(7), as.double(C), as.double(epsilon), 
                as.double(sigma), as.integer(degree), as.double(offset), 
                as.double(C), as.double(2), as.integer(0), as.double(0), 
                as.integer(0), as.double(weightedC), as.double(cache), 
                as.double(tol), as.integer(10), as.integer(shrinking), 
                PACKAGE = "kernlab")
            reind <- sort(yd$ix, method = "quick", index.return = TRUE)$ix
            alpha(ret) <- t(matrix(resv[-(nclass(ret) * nrow(xd) + 
                1)], nclass(ret)))[reind, , drop = FALSE]
            coef(ret) <- lapply(1:nclass(ret), function(x) alpha(ret)[, 
                x][alpha(ret)[, x] != 0])
            names(coef(ret)) <- lev(ret)
            alphaindex(ret) <- lapply(sort(unique(y)), function(x)
which(alpha(ret)[, 
                x] != 0))
            xmatrix(ret) <- x
            obj(ret) <- resv[(nclass(ret) * nrow(xd) + 1)]
            names(alphaindex(ret)) <- lev(ret)
            svindex <- which(rowSums(alpha(ret) != 0) != 0)
            b(ret) <- 0
            param(ret)$C <- C
        }
Run Code Online (Sandbox Code Playgroud)

重要的部分有两件事,首先,如果我们提供ksvm自己的内核,那么ktype=4(而对于vanillakernel, ktype=0),它会进行两个更改:

  • 如果是用户定义的内核,则计算内核矩阵而不是实际使用内核
  • tron_optim例程使用有关内核的信息运行

现在,在 中svm.cpp我们可以找到tron例程,并且在tron_run(从 调用tron_optim)中,该LINEAR内核有一个单独的优化例程

if (param->kernel_type == LINEAR)
    {
     /* lots of code here */
     while (Cpj < Cp)
       {
       totaliter += s.Solve(l, prob->x, minus_ones, y, alpha, w, 
                            Cpj, Cnj, param->eps, sii, param->shrinking, 
                            param->qpsize);
     /* lots of code here */
       }
     totaliter += s.Solve(l, prob->x, minus_ones, y, alpha, w, Cp, Cn,
                          param->eps, sii, param->shrinking, param->qpsize);
     delete[] w;
    }
else
{    
    Solver_B s;
    s.Solve(l, BSVC_Q(*prob,*param,y), minus_ones, y, alpha, Cp, Cn, 
    param->eps, sii, param->shrinking, param->qpsize);
}
Run Code Online (Sandbox Code Playgroud)

正如您所看到的,线性情况以更复杂、更详细的方式处理。有一个内部优化循环多次调用求解器。这需要对此处执行的实际优化进行真正深入的分析,但在这一步,可以通过以下方式回答您的问题:

  • 您的操作没有错误
  • kernlab 的 svm有一个单独的例程,用于使用线性内核训练 SVM,该例程基于传递给代码的内核类型,将“kernel”更改为“vanillakernel”使其认为ksvm 它实际上正在使用 vanillakernel,因此执行了此单独的优化常规
  • 事实上,这似乎并不是一个错误,因为线性 SVM 实际上在高效优化技术方面与内核化版本有很大不同。需要处理的启发式问题和数值问题的数量确实很大。因此,需要一些近似值,并且可能会导致不同的结果。虽然对于丰富的特征空间(如 RBF 内核引起的特征空间)来说,这并不重要,但对于简单的线性内核来说,这种简化可能会导致显着的输出变化。