前言
在研究分类问题时,可能会遇到需要分类器返回样本属于每一类的概率,而不是直接输出该样本的类别的情况。因为之前一直使用opencv库进行开发,所以也想在opencv的ml模块寻找是否有对应功能的SVM分类器,无果。最后发现最新的LIBSVM库提供类似功能的函数。于是,借助LIBSVM,实现了样本的概率估计。过程如下:
过程
- 下载最新的LIBSVM,官网http://www.csie.ntu.edu.tw/~cjlin/libsvm/。使用之前,建议阅读README。
- 打开cmd,进入libsvm -> windows。
- 训练之前,建议采用svm-train进行归一化。语法如下:
svm-scale -l lower -u upper data_filename > scale_data_filename
示例如下:
svm-scale -l -1 -u 1 -s range train > train.scale
这里需要注意svm_type,kernel_type,probability_estimates这几个参数。为了得到概率模型,需要将probability_estimates置1。例子如下:
svm-train -s 0 -b 1 data_file
注:为了方便,可将上述过程保存为一个批处理文件。
4. 训练完成后,得到model文件。然后在cpp文件中调用。主要分为载入模型,构造节点,调用概率估计函数。代码如下:
svm_node* charac = new svm_node[featureNum+1];
if (joint.cols != featureNum)
{
cout << "single joint characters calculate error" << endl;
return -1;
}
//特征向量归一化
//normalize(joint, joint, 0, 1, NORM_MINMAX, -1);
for (int i = 0; i < joint.rows; i++)
{
float* rowData = joint.ptr<float>(i);
for (int j = 0; j < joint.cols; j++)
{
charac[j].index = j + 1;
charac[j].value = rowData[j];
}
}
charac[featureNum].index = -1;
double* prob_estimates = new double[labelNum];
int* label = new int[labelNum];
//svm_get_labels(svmModel[classffierIndex], label);
label = svmModel[classffierIndex]->label;
if (svm_check_probability_model(svmModel[classffierIndex]) == 1)
svm_predict_probability(svmModel[classffierIndex], charac, prob_estimates);
double pj = prob_estimates[label[id]];
delete[] charac;
delete[] prob_estimates;
return pj;
注:注意模型中的类别顺序和训练时的类别顺序的差异性,如果相同则不需要改,否则需要按照模型的类别顺序来输出概率。
总结
在调试过程中,发现SVM概率模型的准确率比直接输出类别的模型的准确率低。就我自己的样本而言,前者的准确率为70%左右,而后者的准确率为100%。目前还不清楚原因。