如何从model.mat转换为model.model?
一个看似小的东西,折腾了一个晚上才弄好
心路历程:
最开始认为libsvm没有提供直接从model.mat转化为mdoel.model的函数,网上几乎搜不到相关信息,打算自己动手写一个转换函数,参考matlab的example里面的explore.c,写的时候发现其他参数读取都没有问题,唯独model.totalSV怎么都读不正确,抓狂的是VS2012在此处还没法调试,一边想着解决方案,一边继续搜索能否直接将model.mat转化为model.model,终于在StackOverFlow上搜到了lib-svm-how-to-convert-mymodel-mat-to-mymodel-model,在http://ick.mech.kumamoto-u.ac.jp/kumon/ja/node/26 Japanese给出了一个代码,answer里面加以了改进。
将程序拷进来直接放到libsvm下用mex编译,mex svm_savemodel.c出现错误“Error using mex (line 206) Unable to complete successfully.”仔细检查了代码,发现没有什么问题,后来看到这个函数里面调用了定义在svm_save_model.c中的matlab_matrix_to_model()函数,联想到svmpredict和svmtrain的编译,再看make.m文件,找到了问题来源,将编译语句改为::mex CFLAGS="\$CFLAGS -std=c99" -largeArrayDims svm_savemodel.c ../svm.cpp svm_model_matlab.c 即可成功通过编译。
用heart_scale进行测试,没有问题,输出标准的model
然而,当用我所需要的model测试时,却出现错误:
“number of return field is not correct”
原因在于需要的model相比于标准的model结构体少了一个域,少了这个域并不会影响用模型进行测试,但是在这个函数里面却需要检查域的数量是否为11(svm_node结构体里面最后一项free_sv不考虑),再进一步看,该函数的核心函数是libsvm自带的函数svm_model_matlab.c,该函数实现了标准的model.model和model.mat之间的相互转换,注意,是“标准”的,要求model.mat结构体里面域必须是11个,贴代码如下:
#include <stdlib.h> #include <string.h> #include "../svm.h" #include "mex.h" #ifdef MX_API_VER #if MX_API_VER < 0x07030000 typedef int mwIndex; #endif #endif #define NUM_OF_RETURN_FIELD 11 #define Malloc(type,n) (type *)malloc((n)*sizeof(type)) static const char *field_names[] = { "Parameters", "nr_class", "totalSV", "rho", "Label", "sv_indices", "ProbA", "ProbB", "nSV", "sv_coef", "SVs" }; struct svm_model *matlab_matrix_to_model(const mxArray *matlab_struct, const char **msg) { int i, j, n, num_of_fields; double *ptr; int id = 0; struct svm_node *x_space; struct svm_model *model; mxArray **rhs; num_of_fields = mxGetNumberOfFields(matlab_struct); if(num_of_fields != NUM_OF_RETURN_FIELD) { *msg = "number of return field is not correct"; return NULL; } rhs = (mxArray **) mxMalloc(sizeof(mxArray *)*num_of_fields); for(i=0;i<num_of_fields;i++) rhs[i] = mxGetFieldByNumber(matlab_struct, 0, i); model = Malloc(struct svm_model, 1); model->rho = NULL; model->probA = NULL; model->probB = NULL; model->label = NULL; model->sv_indices = NULL; model->nSV = NULL; model->free_sv = 1; // XXX ptr = mxGetPr(rhs[id]); model->param.svm_type = (int)ptr[0]; model->param.kernel_type = (int)ptr[1]; model->param.degree = (int)ptr[2]; model->param.gamma = ptr[3]; model->param.coef0 = ptr[4]; id++; ptr = mxGetPr(rhs[id]); model->nr_class = (int)ptr[0]; id++; ptr = mxGetPr(rhs[id]); model->l = (int)ptr[0]; id++; // rho n = model->nr_class * (model->nr_class-1)/2; model->rho = (double*) malloc(n*sizeof(double)); ptr = mxGetPr(rhs[id]); for(i=0;i<n;i++) model->rho[i] = ptr[i]; id++; // label if(mxIsEmpty(rhs[id]) == 0) { model->label = (int*) malloc(model->nr_class*sizeof(int)); ptr = mxGetPr(rhs[id]); for(i=0;i<model->nr_class;i++) model->label[i] = (int)ptr[i]; } id++; // sv_indices if(mxIsEmpty(rhs[id]) == 0) { model->sv_indices = (int*) malloc(model->l*sizeof(int)); ptr = mxGetPr(rhs[id]); for(i=0;i<model->l;i++) model->sv_indices[i] = (int)ptr[i]; } id++; // probA if(mxIsEmpty(rhs[id]) == 0) { model->probA = (double*) malloc(n*sizeof(double)); ptr = mxGetPr(rhs[id]); for(i=0;i<n;i++) model->probA[i] = ptr[i]; } id++; // probB if(mxIsEmpty(rhs[id]) == 0) { model->probB = (double*) malloc(n*sizeof(double)); ptr = mxGetPr(rhs[id]); for(i=0;i<n;i++) model->probB[i] = ptr[i]; } id++; // nSV if(mxIsEmpty(rhs[id]) == 0) { model->nSV = (int*) malloc(model->nr_class*sizeof(int)); ptr = mxGetPr(rhs[id]); for(i=0;i<model->nr_class;i++) model->nSV[i] = (int)ptr[i]; } id++; // sv_coef ptr = mxGetPr(rhs[id]); model->sv_coef = (double**) malloc((model->nr_class-1)*sizeof(double)); for( i=0 ; i< model->nr_class -1 ; i++ ) model->sv_coef[i] = (double*) malloc((model->l)*sizeof(double)); for(i = 0; i < model->nr_class - 1; i++) for(j = 0; j < model->l; j++) model->sv_coef[i][j] = ptr[i*(model->l)+j]; id++; // SV { int sr, sc, elements; int num_samples; mwIndex *ir, *jc; mxArray *pprhs[1], *pplhs[1]; // transpose SV pprhs[0] = rhs[id]; if(mexCallMATLAB(1, pplhs, 1, pprhs, "transpose")) { svm_free_and_destroy_model(&model); *msg = "cannot transpose SV matrix"; return NULL; } rhs[id] = pplhs[0]; sr = (int)mxGetN(rhs[id]); sc = (int)mxGetM(rhs[id]); ptr = mxGetPr(rhs[id]); ir = mxGetIr(rhs[id]); jc = mxGetJc(rhs[id]); num_samples = (int)mxGetNzmax(rhs[id]); elements = num_samples + sr; model->SV = (struct svm_node **) malloc(sr * sizeof(struct svm_node *)); x_space = (struct svm_node *)malloc(elements * sizeof(struct svm_node)); // SV is in column for(i=0;i<sr;i++) { int low = (int)jc[i], high = (int)jc[i+1]; int x_index = 0; model->SV[i] = &x_space[low+i]; for(j=low;j<high;j++) { model->SV[i][x_index].index = (int)ir[j] + 1; model->SV[i][x_index].value = ptr[j]; x_index++; } model->SV[i][x_index].index = -1; } id++; } mxFree(rhs); return model; }
这样对于域并不完全的model,就完全没有办法。
于是我将此代码稍作修改,以适应我的需要,我的情况是没有sv_indice域,因此修改如下:
struct svm_model *matlab_matrix_to_model(const mxArray *matlab_struct, const char **msg) { int i, j, n, num_of_fields; double *ptr; int id = 0; struct svm_node *x_space; struct svm_model *model; mxArray **rhs; num_of_fields = mxGetNumberOfFields(matlab_struct); printf("%d\n",num_of_fields); //允许10个的也可以 if(num_of_fields != NUM_OF_RETURN_FIELD&&num_of_fields!=NUM_OF_RETURN_FIELD-1) { *msg = "number of return field is not correct"; return NULL; } rhs = (mxArray **) mxMalloc(sizeof(mxArray *)*num_of_fields); for(i=0;i<num_of_fields;i++) rhs[i] = mxGetFieldByNumber(matlab_struct, 0, i); model = Malloc(struct svm_model, 1); model->rho = NULL; model->probA = NULL; model->probB = NULL; model->label = NULL; model->sv_indices = NULL; model->nSV = NULL; model->free_sv = 1; // XXX ptr = mxGetPr(rhs[id]); model->param.svm_type = (int)ptr[0]; model->param.kernel_type = (int)ptr[1]; model->param.degree = (int)ptr[2]; model->param.gamma = ptr[3]; model->param.coef0 = ptr[4]; id++; ptr = mxGetPr(rhs[id]); model->nr_class = (int)ptr[0]; id++; ptr = mxGetPr(rhs[id]); model->l = (int)ptr[0]; id++; // rho n = model->nr_class * (model->nr_class-1)/2; model->rho = (double*) malloc(n*sizeof(double)); ptr = mxGetPr(rhs[id]); for(i=0;i<n;i++) model->rho[i] = ptr[i]; id++; // label if(mxIsEmpty(rhs[id]) == 0) { model->label = (int*) malloc(model->nr_class*sizeof(int)); ptr = mxGetPr(rhs[id]); for(i=0;i<model->nr_class;i++) model->label[i] = (int)ptr[i]; } id++; if(num_of_fields==NUM_OF_RETURN_FIELD) { // sv_indices if(mxIsEmpty(rhs[id]) == 0) { model->sv_indices = (int*) malloc(model->l*sizeof(int)); ptr = mxGetPr(rhs[id]); for(i=0;i<model->l;i++) model->sv_indices[i] = (int)ptr[i]; } id++; } // probA if(mxIsEmpty(rhs[id]) == 0) { model->probA = (double*) malloc(n*sizeof(double)); ptr = mxGetPr(rhs[id]); for(i=0;i<n;i++) model->probA[i] = ptr[i]; } id++; // probB if(mxIsEmpty(rhs[id]) == 0) { model->probB = (double*) malloc(n*sizeof(double)); ptr = mxGetPr(rhs[id]); for(i=0;i<n;i++) model->probB[i] = ptr[i]; } id++; // nSV if(mxIsEmpty(rhs[id]) == 0) { model->nSV = (int*) malloc(model->nr_class*sizeof(int)); ptr = mxGetPr(rhs[id]); for(i=0;i<model->nr_class;i++) model->nSV[i] = (int)ptr[i]; } id++; // sv_coef ptr = mxGetPr(rhs[id]); model->sv_coef = (double**) malloc((model->nr_class-1)*sizeof(double)); for( i=0 ; i< model->nr_class -1 ; i++ ) model->sv_coef[i] = (double*) malloc((model->l)*sizeof(double)); for(i = 0; i < model->nr_class - 1; i++) for(j = 0; j < model->l; j++) model->sv_coef[i][j] = ptr[i*(model->l)+j]; id++; // SV { int sr, sc, elements; int num_samples; mwIndex *ir, *jc; mxArray *pprhs[1], *pplhs[1]; // transpose SV pprhs[0] = rhs[id]; if(mexCallMATLAB(1, pplhs, 1, pprhs, "transpose")) { svm_free_and_destroy_model(&model); *msg = "cannot transpose SV matrix"; return NULL; } rhs[id] = pplhs[0]; sr = (int)mxGetN(rhs[id]); sc = (int)mxGetM(rhs[id]); ptr = mxGetPr(rhs[id]); ir = mxGetIr(rhs[id]); jc = mxGetJc(rhs[id]); num_samples = (int)mxGetNzmax(rhs[id]); elements = num_samples + sr; model->SV = (struct svm_node **) malloc(sr * sizeof(struct svm_node *)); x_space = (struct svm_node *)malloc(elements * sizeof(struct svm_node)); // SV is in column for(i=0;i<sr;i++) { int low = (int)jc[i], high = (int)jc[i+1]; int x_index = 0; model->SV[i] = &x_space[low+i]; for(j=low;j<high;j++) { model->SV[i][x_index].index = (int)ir[j] + 1; model->SV[i][x_index].value = ptr[j]; x_index++; } model->SV[i][x_index].index = -1; } id++; } mxFree(rhs); return model; }
如果你情况类似,缺乏哪个域可以用相应类似的方式,感兴趣的话可以将所有异常的情况都考虑了。急着去做下一步,暂时现弄一个项目适用的。
这样就可以有两种方案:
1.将修改后的代码用于我的程序之中,仍然读取mat文件,用修改后的函数进行转换。
2.对修改后的代码mex编译,在matlab上面转化.mat到.model
load('model.mat'); svm_savemodel(model,'model.model');
尝试第一种方案,结果在调用matlab转置函数mexCallMATLAB(1, pplhs, 1, pprhs, "transpose")的时候出错,原因没有细究,猜测是路劲配置的问题。
尝试第二种方案,成功!
其实相比第一种方案,第二种方案更加的方便和高效。
结果如图所示:
结论:
1.不同的情况适用性不同,libsvm的库仍有不完善的地方
2.善用mex编程可以方便地实现C++和matlab混合编程
Feb 21, 2024 02:07:32 AM
Thanks again for the article post.Thanks Again. Really Great