5

我想在 R 中为 Ksvm 使用用户定义的内核函数。所以,我尝试制作一个 vanilladot 内核并与“kernlab”中内置的“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

我认为此操作的结果与以下操作相同。然而它没有。

#
###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

我认为这个问题可能与内核类有关。当类设置为“内核”时,就会出现这个问题。然而,当 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

我不明白为什么将类设置为“内核”时结果与“vanilladot”不同。

我的操作有错误吗?

4

1 回答 1

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
        }

重要的部分是两件事,首先,如果我们提供ksvm我们自己的内核,那么ktype=4(while for vanillakernel, ktype=0) 所以它做了两个改变:

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

现在,在svm.cpp我们可以找到tron例程中,并且在tron_run(调用 from 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);
}

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

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