代码改变世界

LIBSVM用法

2012-10-23 23:36  ggzwtj  阅读(3546)  评论(1编辑  收藏  举报

svm-train用来从样本数据中训练出用来判定的规则,其中的参数设置如下:

options:
-s svm_type : set type of SVM (default 0)
    0 -- C-SVC
    1 -- nu-SVC
    2 -- one-class SVM
    3 -- epsilon-SVR
    4 -- nu-SVR
-t kernel_type : set type of kernel function (default 2)
    0 -- linear: u'*v
    1 -- polynomial: (gamma*u'*v + coef0)^degree
    2 -- radial basis function: exp(-gamma*|u-v|^2)
    3 -- sigmoid: tanh(gamma*u'*v + coef0)
-d degree : set degree in kernel function (default 3)
-g gamma : set gamma in kernel function (default 1/num_features)
-r coef0 : set coef0 in kernel function (default 0)
-c cost : set the parameter C of C-SVC, epsilon-SVR, and nu-SVR (default 1)
-n nu : set the parameter nu of nu-SVC, one-class SVM, and nu-SVR (default 0.5)
-p epsilon : set the epsilon in loss function of epsilon-SVR (default 0.1)
-m cachesize : set cache memory size in MB (default 100)
-e epsilon : set tolerance of termination criterion (default 0.001)
-h shrinking: whether to use the shrinking heuristics, 0 or 1 (default 1)
-b probability_estimates: whether to train a SVC or SVR model for probability estimates, 0 or 1 (default 0)
-wi weight: set the parameter C of class i to weight*C, for C-SVC (default 1)

如果不知道各种参数之间的区别也不会玩的开心,首先来看-s参数的含义:

C-SVC对应的原问题是:

  

  它的对偶问题如下:

  

  对应的决策函数为:

  

nu-CVS对应的原问题是:

  

  它的对偶问题为:

  
  它的决策函数是:
  

one-class SVM原始问题是:

  

  对偶问题为:

  

  判定函数:

  

epsilon-SVR的原型为:

  

  它的对偶问题为:

  

nu-SVR原问题如下:

  

  对偶问题为:

  

使用软件包中的heart_scale文件用来测试,使用下面命令用来测试:

svm-train -s 0 -t 2 ../heart_scale result_module

输入文件的格式为:

+1 1:0.708333 2:1 3:1 4:-0.320755 5:-0.105023 6:-1 7:1 8:-0.419847 9:-1 10:-0.225806 12:1 13:-1
-1 1:0.583333 2:-1 3:0.333333 4:-0.603774 5:1 6:-1 7:1 8:0.358779 9:-1 10:-0.483871 12:-1 13:1
+1 1:0.166667 2:1 3:-0.333333 4:-0.433962 5:-0.383562 6:-1 7:-1 8:0.0687023 9:-1 10:-0.903226 11:-1 12:-1 13:1
-1 1:0.458333 2:1 3:1 4:-0.358491 5:-0.374429 6:-1 7:-1 8:-0.480916 9:1 10:-0.935484 12:-0.333333 13:1

……

在result中的输出如下:

svm_type c_svc               # SVM类型
kernel_type rbf                     # 核函数类型
gamma 0.0769231              # 核函数中的g
nr_class 2                   # 分类时的类别数
total_sv 132                       # 总共的支持向量个数
rho 0.424462                     # 决策函数中的常量b
label 1 -1                        # 类别标签
nr_sv 64 68                                                  # 各类别分别的支持向量的个数
SV                       # 支持向量的列表
1 1:0.166667 2:1 3:-0.333333 4:-0.433962 5:-0.383562 6:-1 7:-1 8:0.0687023 9:-1 10:-0.903226 11:-1 12:-1 13:1
0.5104832128985164 1:0.125 2:1 3:0.333333 4:-0.320755 5:-0.406393 6:1 7:1 8:0.0839695 9:1 10:-0.806452 12:-0.333333 13:0.5

……

其中的svm-toy就像一个玩具,比如。。。

从这个图中可以看出上面大于2的类的划分还是基于两个类的划分的,当然在下面的代码中也可以很明显地看到这一点。

而svm-predict则是用train出来的module来预测数据的,我们这里就用heart_scale建立的module来在heart_scale做检验,如下:

svm-predict.exe ../heart_scale ../result_module ../a

输出为:

Accuracy = 86.6667% (234/270) (classification)

也就是说准确度达到了86.6667。那这个predict是怎么利用Module来预测的,虽然知道预测函数了,但是貌似看到代码才算完,如下:

 1 struct svm_model
 2 {
 3     struct svm_parameter param;    /* parameter */
 4     int nr_class;                  /* number of classes, = 2 in regression/one class svm */
 5     int l;                         /* total #SV */
 6     struct svm_node **SV;          /* SVs (SV[l]) */
 7     double **sv_coef;              /* coefficients for SVs in decision functions (sv_coef[k-1][l]) */
 8     double *rho;                   /* constants in decision functions (rho[k*(k-1)/2]) */
 9     double *probA;                 /* pariwise probability information */
10     double *probB;
11 
12     /* for classification only */
13 
14     int *label;                   /* label of each class (label[k]) */
15     int *nSV                      /* number of SVs for each class (nSV[k]) */
16                                   /* nSV[0] + nSV[1] + ... + nSV[k-1] = l */
17     /* XXX */
18     int free_sv;                  /* 1 if svm_model is created by svm_load_model*/
19                                   /* 0 if svm_model is created by svm_train */
20 };

在具体的程序中大段的代码都是在解析文件,核心的代码如下(里面有注释):

double svm_predict_values(const svm_model *model, const svm_node *x, double* dec_values)
{
    int i;
    if(model->param.svm_type == ONE_CLASS ||
       model->param.svm_type == EPSILON_SVR ||
       model->param.svm_type == NU_SVR)
    {
        double *sv_coef = model->sv_coef[0];
        double sum = 0;
        for(i=0;i<model->l;i++)
            sum += sv_coef[i] * Kernel::k_function(x,model->SV[i],model->param);
        sum -= model->rho[0];
        *dec_values = sum;

        if(model->param.svm_type == ONE_CLASS)
            return (sum>0)?1:-1;
        else
            return sum;
    }
    else
    {
        int nr_class = model->nr_class;
        int l = model->l;
        
        double *kvalue = Malloc(double,l);

        // 依次计算Kernel
        for(i=0;i<l;i++)
            kvalue[i] = Kernel::k_function(x,model->SV[i],model->param);

        int *start = Malloc(int,nr_class);
        start[0] = 0;

        // 计算各个类的支持向量的开始的位置
        for(i=1;i<nr_class;i++)
            start[i] = start[i-1]+model->nSV[i-1];

        int *vote = Malloc(int,nr_class);
        for(i=0;i<nr_class;i++)
            vote[i] = 0;

        int p=0;
        for(i=0;i<nr_class;i++)
            for(int j=i+1;j<nr_class;j++)
            {
                double sum = 0;
                int si = start[i];
                int sj = start[j];
                int ci = model->nSV[i];
                int cj = model->nSV[j];
                
                int k;
                // 支持向量对应的参数
                double *coef1 = model->sv_coef[j-1];
                double *coef2 = model->sv_coef[i];

                // 计算出各个类之间的差距
                for(k=0;k<ci;k++)
                    sum += coef1[si+k] * kvalue[si+k];
                for(k=0;k<cj;k++)
                    sum += coef2[sj+k] * kvalue[sj+k];
                sum -= model->rho[p];
                dec_values[p] = sum;

                // 如果大于0的话选i,否则选j
                if(dec_values[p] > 0)
                    ++vote[i];
                else
                    ++vote[j];
                p++;
            }

        int vote_max_idx = 0;

        // 从中选出最大的一个类型
        for(i=1;i<nr_class;i++)
            if(vote[i] > vote[vote_max_idx])
                vote_max_idx = i;

        free(kvalue);
        free(start);
        free(vote);

        // 返回地vote_max_idx对应的label
        return model->label[vote_max_idx];
    }
}

---------------------------------------------

欢迎拍砖。