【机器学习】C++版本libSVM的使用和非线性核函数分类代码

本文介绍了一个使用libSVM解决圆形区域分类问题的例子。通过详细解释libSVM的配置参数及其实现过程,展示了如何利用RBF核函数进行非线性分类。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

  libSVM是台湾大学林智仁(Lin Chih-Jen)教授等开发的一个简单易用、快速有效的SVM模式识别与回归的第三方库。该库无需额外的第三方库

支持,只需要纯粹的C++编译运行环境,可以横跨Windows\Linux\Unix等平台。
  libSVM是开源C++库,可从libSVM下载安装包。解压缩之后的文件夹,提供了跨平台的多个

MAKEFILE文件和源代码;一个Windows文件夹,里面是已经可以使用的程序和库文件。在Windows下,打开VS的命令行窗口,进入libSVM目录,输入:
  nmake -f Makefile.win clean all
  nmake -f Makefile.win
  nmake -f Makefile.win lib
  

  即可生成libsvm.lib和libsvm.dll文件。svm.h头文件在根目录下。将它们加入工程项目即可使用。libSVM使用主要需要构造两个结构:svm_param和svm_problem。

struct svm_parameter
    {
        int svm_type;
        int kernel_type;
        int degree; /* for poly */
        double gamma;   /* for poly/rbf/sigmoid */
        double coef0;   /* for poly/sigmoid */
        /* these are for training only */
        double cache_size; /* in MB */
        double eps; /* stopping criteria */
        double C;   /* for C_SVC, EPSILON_SVR, and NU_SVR */
        int nr_weight;      /* for C_SVC */
        int *weight_label;  /* for C_SVC */
        double* weight;     /* for C_SVC */
        double nu;  /* for NU_SVC, ONE_CLASS, and NU_SVR */
        double p;   /* for EPSILON_SVR */
        int shrinking;  /* use the shrinking heuristics */
        int probability; /* do probability estimates */
    };

1、svm_type是SVM类型,包含C_SVC(传统SVM分类), NU_SVC(nu-SVM分类,和C_SVC差别不大), ONE_CLASS(单分类SVM,用于找到一个最小的边界包围一类数据), EPSILON_SVR(见SVR分类), NU_SVR
  2、kernel_type表示用到的SVM核函数,有linear/poly/rbf/sigmoid,即线性(不加核)、多项式核、径向基核、sigmoid核。
  3、degree,多项式核的参数;gamma核函数共同的参数;coef0多项式、sigmoid核的参数。具体可参考核方法。
  4、cache_size,缓冲大小,自己设置。
  5、eps,求解svm的方法是个迭代过程,eps是迭代收敛的终止条件。
  6、C,SVM方法的惩罚参数。
  7、nr_weight、weight_label、weight表示样本的权重。nr_weight是样本个数,weight_label是权重参数的数组,weight是缩放因子数组。如果样本都是平等的,则设置nr_weight=0
  8、nu是 NU_SVC, ONE_CLASS, and NU_SVR的参数。
  9、p是SVR回归中的松弛因子。
  10、shrinking表示是否使用启发式算法,取值为1和0
  11、probability表示是否计算概率信息,取值为1和0

struct svm_problem
    {
        int l;
        double *y;
        struct svm_node **x;
    };

l是样本个数,y是样本分类数组,即标准答案;x是样本特征矩阵,用一个二维数组表示;svm_node*数组用于表示一个样本特征向量。

struct svm_node
    {
        int index;
        double value;
    };
传统的SVM分类

构建好svm_parameter和svm_problem,可以使用函数:struct svm_model *svm_train(const struct svm_problem *prob,const struct svm_parameter *param);进行训练,返回的svm_model是训练结果,是一个黑匣子,用户不必理会。svm_model可以用svm_save_model函数存储,具体见libsvm的README文档。
  使用训练好的结果svm_model,需要调用函数double svm_predict(const struct svm_model *model, const struct svm_node *x);x是需要预测的样本的特征数组,返回的double表示分类结果。
  下面来看一个球形区域聚类的例子!这个例子在[-100,100]x[-100,100]的平面中随机生成点,点到中心的距离小于80的话就是正样本点,否则就是负样本点。可以看出,正样本是一个圆形区域,圆外是负样本,圆内是正样本,是一个非线性分类问题,必须得用核函数。最为通用的和函数是RBF径向基核,我使用这个核函数。下面我写的网上广为流传的一个核函数聚类问题的解决代码。

#include <iostream>
#include <svm.h>
#include <ctime>
#include <stdlib.h>

int main()
{
    //build svm_parameter
    svm_parameter param;
    param.svm_type = C_SVC;
    param.kernel_type = RBF;
    //param.degree = 3;
    param.gamma = 0.5;
    //param.coef0 = 0;
    //param.nu = 0.5;
    param.cache_size = 100;
    param.C = 0.5;
    param.eps = 1e-3;
    //param.p = 0.1;
    param.shrinking = 0;//默认为1,当样本量很大,设为0
    param.probability = 0;
    param.nr_weight = 0;
    param.weight_label = NULL;
    param.weight = NULL;

    // build problem
    svm_problem prob;
    int dataSize = 10000;
    prob.l = dataSize;
    prob.y = new double[prob.l];
    prob.x = new svm_node *[prob.l];
    //build data
    srand(time(0));
    double radius2 = 80.0 * 80.0;
    int posNum=0, negNum = 0;
    for(int i = 0; i < dataSize; ++i){
        prob.x[i] = new svm_node[3];
        prob.x[i][0].index = 1;
        prob.x[i][0].value = double(rand()%200 - 100);
        prob.x[i][1].index = 2;
        prob.x[i][1].value = double(rand()%200 - 100);
        prob.x[i][2].index = -1;
        prob.x[i][2].value = 0;
        prob.y[i] = (prob.x[i][0].value * prob.x[i][0].value 
            + prob.x[i][1].value * prob.x[i][1].value) > radius2 ? -1.0 : 1.0;
        if (prob.y[i] > 0)
        {
            ++posNum;
        }else ++negNum;
        if(i%500 == 0) std::cout << "x: "<<prob.x[i][0].value 
            << "  y: " << prob.x[i][1].value << " preclass : " << prob.y[i] << std::endl;
    }
    std::cout << "pos: " << posNum << "  neg: " << negNum << std::endl;
    //check
    const char* err = svm_check_parameter(&prob, &param);
    if(NULL != err){
        std::cout << errno << std::endl;
        return -1;
    }
    //build model and train
    svm_model *model = svm_train(&prob, &param);
    //predict
    for(int i = 0; i < 20; ++i){
        int n = std::max(0, rand() % dataSize - 1);
        std:: cout << "x : " <<prob.x[i][0].value << "  y: " << prob.x[i][1].value
        << " class : " << svm_predict(model, prob.x[i]) << " true class " << prob.y[i] << std::endl;
    }
    //svm_node x[3];
    //x[0].index = 1;
    //x[1].index = 2;
    //x[2].index = -1;
    //std::cout << "input x : " << std::endl;
    //std::cin >> x[0].value;
    //std::cout << "input y : " << std::endl;
    //std::cin >> x[1].value;
    //x[0].value = 0;
    //x[1].value = 0;
    //x[2].value = -1;

    //double d = svm_predict(model, x);
    //std::cout << "class is : " << d << std::endl;

    svm_free_and_destroy_model(&model);
    for (int i = 0; i < dataSize; ++i){
        delete[] prob.x[i];
    }
    delete[] prob.x;
    delete[] prob.y;

    return 0;
}

输出为:
x: -74 y: 5 preclass : 1
x: 19 y: 11 preclass : 1
x: 1 y: 37 preclass : 1
x: -85 y: -21 preclass : -1
x: -36 y: 37 preclass : 1
x: -20 y: -53 preclass : 1
x: -34 y: -13 preclass : 1
x: -64 y: 34 preclass : 1
x: -36 y: -75 preclass : -1
x: 25 y: 57 preclass : 1
x: 65 y: -51 preclass : -1
x: -34 y: 68 preclass : 1
x: -57 y: 45 preclass : 1
x: -30 y: -62 preclass : 1
x: -41 y: 1 preclass : 1
x: -61 y: -13 preclass : 1
x: -48 y: -85 preclass : -1
x: -84 y: -71 preclass : -1
x: 35 y: -98 preclass : -1
x: -63 y: 40 preclass : 1
pos: 5066 neg: 4934
………..*
optimization finished, #iter = 11797
nu = 0.793672
obj = -2218.051986, rho = -0.010793
nSV = 9327, nBSV = 5399
Total nSV = 9327
x : -74 y: 5 class : 1 true class 1
x : 71 y: 78 class : -1 true class -1
x : -16 y: -67 class : 1 true class 1
x : -72 y: -36 class : -1 true class -1
x : -16 y: 89 class : -1 true class -1
x : 78 y: 61 class : -1 true class -1
x : 12 y: 83 class : -1 true class -1
x : 3 y: -68 class : 1 true class 1
x : 73 y: -53 class : -1 true class -1
x : 60 y: -13 class : 1 true class 1
x : 52 y: -3 class : 1 true class 1
x : 30 y: 21 class : 1 true class 1
x : 87 y: 41 class : -1 true class -1
x : -99 y: -60 class : -1 true class -1
x : -22 y: 37 class : 1 true class 1
x : -1 y: 10 class : 1 true class 1
x : -4 y: -29 class : 1 true class 1
x : 26 y: -71 class : 1 true class 1
x : 9 y: -5 class : 1 true class 1
x : -43 y: -63 class : 1 true class 1

  可以看出,我们用RBF SVM解决了圆形区域的分类问题,虽然有点过拟合,但是分类效果还是不错的。

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值