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

この問題はおそらくカーネルクラスに関連していると思います。クラスが「カーネル」に設定されている場合、この問題が発生します。ただし、クラスが「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

クラスを「kernel」に設定すると、結果が「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
        }

重要な部分は 2 つあります。まず、ksvm独自のカーネルを提供する場合、 ktype=4(while for vanillakernel, ktype=0) 2 つの変更が加えられます。

  • ユーザー定義のカーネルの場合、実際にカーネルを使用する代わりにカーネル行列が計算されます
  • tron_optimルーチンはカーネルに関する情報で実行されます

ここで、 でルーチンsvm.cppを見つけることができ、 ( から呼び出された) で、そのカーネルには別の最適化ルーチンがあります。trontron_runtron_optimLINEAR

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」に変更すると、実際に vanillakernelで動作していると考えられ、この別の最適化を実行しましたルーティーンksvm
  • 効率的な最適化手法に関して、線形 SVM は実際にはカーネル化されたバージョンとは大きく異なるため、実際にはバグのようには見えません。対処しなければならないヒューリスティックおよび数値の問題の量は非常に大きいです。その結果、いくつかの概算が必要になり、異なる結果になる可能性があります。豊富な機能空間 (RBF カーネルによって誘導されるものなど) の場合はそれほど重要ではありませんが、単純なカーネルが線形のものを並べる場合、この単純化によって出力が大幅に変化する可能性があります。
于 2013-09-17T08:51:41.330 に答える