SVM直观原理,以及LIBSVM的应用
SVM的原理
SVM的主要思想可以概括为两点:
(1) 它是针对线性可分情况进行分析,对于线性不可分的情况,通过使用非线性映射算法将低维输入空间线性不可分的样本转化为高维特征空间使其线性可分,从而 使得高维特征空间采用线性算法对样本的非线性特征进行线性分析成为可能;
(2) 它基于结构风险最小化理论之上在特征空间中建构最优分割超平面,使得学习器得到全局最优化,并且在整个样本空间的期望风险以某个概率满足一定上界。
SVM方法是通过一个非线性映射p,把样本空间映射到一个高维乃至无穷维的特征空间中(Hilbert空间),使得在原来的样本空间中非线性可分的问题转化为在特征空间中的线性可分的问题.简单地说,就是升
维和线性化.升维,就是把样本向高维空间做映射,一般情况下这会增加计算的复杂性,甚至会引起“维数灾难”,因而人们很少问津.但是作为分类、回归等问题来说,很可能在低维样本空间无法线性处理的样本集,在高维特征空间中却可以通过一个线性超平面实现线性划分(或回归).一般的升维都会带来计算的复杂化,SVM方法巧妙地解决了这个难题:应用核函数的展开定理,就不需要知道非线性映射的显式表达式;由于是在高维特征空间中建立线性学习机,所以与线性模型相比,不但几乎不增加计算的复杂性,而且在某种程度上避免了“维数灾难”.这一切要归功于核函数的展开和计算理论.
选择不同的核函数,可以生成不同的SVM,常用的核函数有以下4种:
(1)线性核函数K(x,y)=x·y;
(2)多项式核函数K(x,y)=[(x·y)+1]d;
(3)径向基函数K(x,y)=exp(-|x-y|^2/d^2)
(4)二层神经网络核函数K(x,y)=tanh(a(x·y)+b).
其中K就是核函数,起作用在于将低维度线性不可分样本向高纬线性可分样本作映射。
X‘ = K(w, X) + b; 将低维度的X映射到了高纬度上的X’
[引于:SVM百度百科]
LIBSVM的应用
LIBSVM是台湾大学林智仁(Lin Chih-Jen)副教授等开发设计的一个简单、易于使用和快速有效的SVM模式识别与回归的软件包,他不但提供了编译好的可在Windows系列系统的执行文件,还提供了源代码,方便改进、修改以及在其它操作系统上应用;该软件对SVM所涉及的参数调节相对比较少,提供了很多的默认参数,利用这些默认参数可以解决很多问题;并提供了交互检验(Cross Validation)的功能。
train: C++版本的加入svm.h 和 svm.cpp, 自己调用函数做svm train的时候,需要自己建一个svm_problem的结构体。
svm_problem{
svm_node**x;//样本集
double* y;// 每个样本集对应的类的标志
int l;// 数据集包含样本的数目
}
svm_node{
double value;//对应的值
int index;//维度标号
}
样本表示的方法有多种,可以自由选择,例如bag-of-words等常用的方法。
每个样本用svm_node* 表示,并且每个样本以 .index = -1作为结束标志(计算响亮点击时所需)。样本集是一个二维的列表。
按照这个格式建立起两个 svm_problem ,一个用来train 一个用来test。
随后需要配置svm的参数,包括核函数选择,最大迭代次数等,见ConstructParam()。
调用svm_train(&_prob, &_param)并用svm_save_model(const char* path, const svm_problem* svmprob) 保存;
predict:
对于一个新的输入构造好对应的svm向量形式,然后调用库函数svm_predict(svm_problem* _prob, svm_node* xnew)得到一个值。
此值是你的输入的trainning set中的所有的y值中的一个。
void DoCrossValidation()
{
int gBegin, gEnd, gStep, cBegin, cEnd, cStep;
int nrFold = 5;
gBegin = -15; gEnd = 3; gStep = 2;
cBegin = -5; cEnd = 15; cStep = 2;
double tempG, tempC, bestG, bestC;
double bestAccuracy = 0;
for (int i=gBegin; i<=gEnd; i+=gStep)
{
tempG = pow(2.0, 1.0*i);
g_param.gamma = tempG;
for (int j=cBegin; j<=cEnd; j+=cStep)
{
double *target = new double[g_prob.l];
int totalCorrect = 0;
tempC = pow(2.0, 1.0*j);
g_param.C = tempC;
svm_cross_validation(&g_prob, &g_param, nrFold, target);
for (int k=0; k<g_prob.l; k++)
{
if (target[k] == g_prob.y[k])
{
totalCorrect++;
}
}
if ((totalCorrect*1.0/g_prob.l) > bestAccuracy)
{
bestC = tempC;
bestG = tempG;
bestAccuracy = totalCorrect*1.0/g_prob.l;
}
delete []target;
}
}
g_param.C = bestC;
g_param.gamma = bestG;
printf("bestC : %lf ", bestC);
printf("bestG : %lf ", bestG);
printf("bestAccuracy : %lf\n", bestAccuracy);
}
int ConstructParam()
{
g_param.svm_type = C_SVC;
g_param.kernel_type = RBF;
g_param.degree = 3;
g_param.gamma = (double)1/30; // 1/k
g_param.coef0 = 0;
g_param.nu = 0.5;
g_param.cache_size = 100;
g_param.C = 1;
g_param.eps = 1e-3;
g_param.p = 0.1;
g_param.shrinking = 1;
g_param.probability = 0;
g_param.nr_weight = 0;
g_param.weight_label = NULL;
g_param.weight = NULL;
//do grid-search and cross-validation
printf("on the process of param selection...\n");
DoCrossValidation();
return 0;
}