最近用Matlab libsvm实现了一个二分类的项目,需要将代码迁移到VS,参考了网上的一些博客后,成功实现了完美迁移,以下捋一捋迁移的经验。
1.SVM原理 https://blog.csdn.net/c406495762/article/details/78072313
2.Matlab使用libsvm需要先进行配置,因为需要进行参数优化,故需要下载台湾大学林智仁(Lin Chih-Jen)教授github上的libsvm包,链接https://github.com/faruto/Libsvm-FarutoUltimate-Version
Matlab libsvm配置博文 https://my.oschina.net/u/4476995/blog/3191329
之后便可使用Matlab libsvm了
% 参数c和参数g寻优
[bestacc,bestc,bestg] = SVMcgForClass(train_label,train,-10,10,-10,10,2,0.2,0.2,0.5);
% 使用寻优得到的参数进行训练
cmd = ['-c ',num2str(bestc),' -g ',num2str(bestg)];
model=svmtrain(train_label,train,cmd);
% 预测
[predict_label, accuracy, dec_values]=svmpredict(test_label,test,model);
SVMcgForClass使用的是网格参数寻优,(-10,10,-10,10,0.2,0.2)表示参数c的网格变化范围为(2^-10, 2^-9.8,...,2^9.8, 2^10),参数 g的网格变化范围同c一样,2表示寻优时按照2折交叉验证,最后一个0.5参数为matlab作图时使用,坐标范围70:0.5:100
svmtrain中带入bestc和bestg进行训练。
svmpredict预测得到预测标签predict_label, 准确度accuracy, dec_value个人人为是预测的每类的得分(虽然有些奇怪)
如果需要savemodel或者loadmodel,参考博文 https://blog.csdn.net/weixin_42296976/article/details/86103729?depth_1-utm_source=distribute.pc_relevant.none-task&utm_source=distribute.pc_relevant.none-task
% savemodel('modelname.model', model)
% model = loadmodel('modelname.model', N);
3.VS上使用SVM, 除了手撕SVM源码以外,就是调用Opencv内的ml.cpp,注意此时命名空间为using namespace cv::ml
Ptr<SVM> svm = SVM::create();
// trainDataMat 格式为CV_32F, trainLabelMat 格式为CV_32S
// ROW_SAMPLE表示对于输入矩阵按行训练
Ptr<TrainData> trainDataSet = TrainData::create(trainDataMat, ROW_SAMPLE, trainLabelMat);
void svm_train(Ptr<SVM>& svm, Ptr<TrainData>& trainDataSet)
{
svm->setType(SVM::Types::C_SVC); // C类支持向量分类机
svm->setKernel(SVM::KernelTypes::RBF); // RBF核函数
svm->setC(0.757858283255200);
svm->setGamma(0.00170029406893774);
svm->setTermCriteria(TermCriteria(TermCriteria::MAX_ITER, 5000, 1e-5)); // 迭代训练过程的中止条件
// 对于需要优化的参数, 需要设置网格
ParamGrid Cgrid = SVM::getDefaultGrid(SVM::ParamTypes::C);
ParamGrid gammaGrid = SVM::getDefaultGrid(SVM::ParamTypes::GAMMA);
ParamGrid pGrid = SVM::getDefaultGrid(SVM::ParamTypes::P);
ParamGrid nuGrid = SVM::getDefaultGrid(SVM::ParamTypes::NU);
ParamGrid coeffGrid = SVM::getDefaultGrid(SVM::ParamTypes::COEF);
ParamGrid degreeGrid = SVM::getDefaultGrid(SVM::ParamTypes::DEGREE);
// 对于不需要优化的参数, 步长设置为0即可
pGrid.logStep = 0.0;
nuGrid.logStep = 0.0;
coeffGrid.logStep = 0.0;
degreeGrid.logStep = 0.0;
svm->trainAuto(trainDataSet, 2, Cgrid, gammaGrid, pGrid, nuGrid, coeffGrid, degreeGrid, true);
//svm->train(trainDataSet);
}
二分类需要的参数有 SVM类型 C_SVC, 核函数RBF较优,同时需要对它们设置初值。训练终止条件为迭代5000次或者误差小于1e-5。对于需要优化的参数,需要设置网格,对于不需要优化的参数,步长设置为0。这里网格为minval:step:maxval
需要参数优化时,使用trainAuto函数进行循环,2表示2折交叉验证,末尾参数设置为true时表示如果是二分类,将创建更多的类平衡交叉验证子集。
经过使用trainAuto进行参数c和参数g优化后,对比发现训练得到的结果不如Matlab SVMcgForClass 网格寻优得到的结果,所以我直接将Matlab寻优得到参数c和参数g写在setC()和setGamma()里面,使用trian函数进行训练,得到的结果于Matlab得到的结果一致。
预测函数大有意思,可解释为如下:
/*
svm->predict(InputArray samples, OutputArray results = noArray(), int flags = 0)
如果只有samples一个参数, 此时只能对测试矩阵的每行依次预测, 返回值为标签分类的一个float值
如果还有results参数, 此时可对整个测试矩阵进行预测, 不会返回值, 结果会保存在Mat result中, 为标签分类的值
如果还有flags()参数, Mat result中保存的结果为预测的得分, 而不是标签, 类似于matlab中的dec_values
*/
float val = svm->predict(testDataMat.row(i));
svm->predict(testDataMat, result);
svm->predict(testDataMat, Score, StatModel::Flags::RAW_OUTPUT);
保存模型函数和加载模型函数就没有多大意思了,如下:
// 注意保存模型与加载模型的写法不同
Ptr<SVM> svm = SVM::create();
svm->save("modelName.xml");
svm = SVM::load("modelName.xml");
完工~