如何在 Matlab 中训练模型,将其保存到磁盘并加载到 C++ 程序中?

Posted

技术标签:

【中文标题】如何在 Matlab 中训练模型,将其保存到磁盘并加载到 C++ 程序中?【英文标题】:How to train in Matlab a model, save it to disk, and load in C++ program? 【发布时间】:2013-02-14 11:50:16 【问题描述】:

我使用的是 libsvm 3.16 版。我在 Matlab 中进行了一些培训,并创建了一个模型。现在我想将此模型保存到磁盘并在我的 C++ 程序中加载此模型。到目前为止,我找到了以下替代方案:

    这个答案解释了来自 C++ 的how to save a model,它基于this 网站。不完全是我需要的,但可以适应。 (这需要开发时间)。 我可以在 Matlab 中找到最佳的训练参数(内核、C),并在 C++ 中重新训练所有内容。 (每次更改参数时都需要使用 C++ 进行培训。它不可扩展)。

因此,这两个选项都不令人满意,

有人有想法吗?

【问题讨论】:

您未能描述您找到的替代方案为何不令人满意。对您来说显而易见的事情对我们来说并不明显。 @ÖöTiib,我更新了我的答案。感谢您的关注 除非你能找到一个接受 Matlab 保存的模型的 C++ 包,否则你必须编写代码将 Matlab 模型从 Matlab 形式转换为 C++ 形式。为什么不直接在 c++ 中运行模型,它应该比 matlab 更快。 @slayton,主要是因为我认为这不会是一次性的过程,而是在Matlab中改进的迭代过程,然后在C++中再次运行,...有点烦跨度> @Andrey 为什么不将模型的结构字段提取为单独的二维数组并将它们写入文本文件。然后移植到 C++。或者在 C++ 中使用 libsvm,不确定是否可用。它确实在 C# 中可用 【参考方案1】:

选项1实际上是相当合理的。如果您通过 matlab 将模型保存为 libsvm 的 C 格式,那么使用 libsvm 提供的函数可以直接在 C/C++ 中处理模型。尝试在 C++ 中处理 matlab 格式的数据可能会更加困难。

“svm-predict.c”(位于 libsvm 包的根目录)中的 main 函数可能具有您需要的大部分功能:

if((model=svm_load_model(argv[i+1]))==0)

    fprintf(stderr,"can't open model file %s\n",argv[i+1]);
    exit(1);

要使用模型预测标签,例如x,您可以运行

int predict_label = svm_predict(model,x);

其中最棘手的部分是将您的数据转换为 libsvm 格式(除非您的数据是 libsvm 文本文件格式,在这种情况下,您可以使用“svm-predict.c”中的predict 函数)。

libsvm 向量x 是一个struct svm_node 数组,它表示一个稀疏数据数组。每个 svm_node 都有一个索引和一个值,并且向量必须以设置为 -1 的索引终止。例如,要对向量[0,1,0,5] 进行编码,您可以执行以下操作:

struct svm_node *x = (struct svm_node *) malloc(3*sizeof(struct svm_node));
x[0].index=2; //NOTE: libsvm indices start at 1
x[0].value=1.0;
x[1].index=4;
x[1].value=5.0;
x[2].index=-1;

对于分类器 (C_SVC) 以外的 SVM 类型,请查看“svm-predict.c”中的predict 函数。

【讨论】:

【参考方案2】:

我的解决方案是用 C++ 重新训练,因为我找不到直接保存模型的好方法。这是我的代码。您需要对其进行调整并进行一些清理。最大的改变是不要像我一样对svm_parameter 值进行硬编码。您还必须将FilePath 替换为std::string。我在 SO 中复制、粘贴和进行小的编辑,所以格式不会完美:

这样使用:

    auto targetsPath = FilePath("targets.txt");
    auto observationsPath = FilePath("observations.txt");

    auto targetsMat = MatlabMatrixFileReader::Read(targetsPath, ',');
    auto observationsMat = MatlabMatrixFileReader::Read(observationsPath, ',');
    auto v = MiscVector::ConvertVecOfVecToVec(targetsMat);
    auto model = SupportVectorRegressionModel observationsMat, v ;

    std::vector<double> observation  // 32 feature observation
        0.883575729725847,0.919446119013878,0.95359403450317,
        0.968233630936732,0.91891307107125,0.887897763183844,
        0.937588566544751,0.920582702918882,0.888864454119387,
        0.890066735260163,0.87911085669864,0.903745573664995,
        0.861069296586979,0.838606194934074,0.856376230548304,
        0.863011311537075,0.807688936997926,0.740434984165146,
        0.738498042748759,0.736410940165691,0.697228384912424,
        0.608527698289016,0.632994967880269,0.66935784966765,
        0.647761430696238,0.745961037635717,0.560761134660957,
        0.545498063585615,0.590854855113663,0.486827902942118,
        0.187128866890822,- 0.0746523069562551
     ;

    double prediction = model.Predict(observation);

miscvector.h

    static vector<double> ConvertVecOfVecToVec(const vector<vector<double>> &mat)
    
        vector<double> targetsVec;
        targetsVec.reserve(mat.size());
        for (size_t i = 0; i < mat.size(); i++)
        
            targetsVec.push_back(mat[i][0]);
        
        return targetsVec;
    

libsvmtargetobjectconvertor.h

#pragma once

#include "machinelearning.h"

struct svm_node;

class LibSvmTargetObservationConvertor

public:
    svm_node ** LibSvmTargetObservationConvertor::ConvertObservations(const vector<MlObservation> &observations, size_t numFeatures) const

    svm_node **svmObservations = (svm_node **)malloc(sizeof(svm_node *) * observations.size());
    for (size_t rowI = 0; rowI < observations.size(); rowI++)
    
        svm_node *row = (svm_node *)malloc(sizeof(svm_node) * numFeatures);
        for (size_t colI = 0; colI < numFeatures; colI++)
        
            row[colI].index = colI;
            row[colI].value = observations[rowI][colI];
        
        row[numFeatures].index = -1; // apparently needed
        svmObservations[rowI] = row;
    
    return svmObservations;


svm_node* LibSvmTargetObservationConvertor::ConvertMatToSvmNode(const MlObservation &observation) const

    size_t numFeatures = observation.size();
    svm_node *obsNode = (svm_node *)malloc(sizeof(svm_node) * numFeatures);
    for (size_t rowI = 0; rowI < numFeatures; rowI++)
    
        obsNode[rowI].index = rowI;
        obsNode[rowI].value = observation[rowI];
    
    obsNode[numFeatures].index = -1; // apparently needed
    return obsNode;

;

machinelearning.h

#pragma once

#include <vector>
using std::vector;

using MlObservation = vector<double>;
using MlTarget = double;

//machinelearningmodel.h
#pragma once

#include <vector>
#include "machinelearning.h"
class MachineLearningModel

public:
    virtual ~MachineLearningModel() 
    virtual double Predict(const MlObservation &observation) const = 0;
;

matlabmatrixfilereader.h

#pragma once

#include <vector>
using std::vector;

class FilePath;
// Matrix created with command:
// dlmwrite('my_matrix.txt', somematrix, 'delimiter', ',', 'precision', 15);
// In these files, each row is a matrix row. Commas separate elements on a row.
// There is no space at the end of a row. There is a blank line at the bottom of the file.
// File format:
// 0.4,0.7,0.8
// 0.9,0.3,0.5
// etc.
static class MatlabMatrixFileReader

public:
    static vector<vector<double>> Read(const FilePath &asciiFilePath, char delimiter)


    vector<vector<double>> values;
    vector<double> valueline;
    std::ifstream fin(asciiFilePath.Path());
    string item, line;
    while (getline(fin, line))
    
        std::istringstream in(line);

        while (getline(in, item, delimiter))
        
            valueline.push_back(atof(item.c_str()));
                   
        values.push_back(valueline);
        valueline.clear();
    
    fin.close();
    return values;


;

supportvectorregressionmodel.h

#pragma once

#include <vector>
using std::vector;
#include "machinelearningmodel.h"

#include "svm.h" // libsvm

class FilePath;

class SupportVectorRegressionModel : public MachineLearningModel

public:
    SupportVectorRegressionModel::~SupportVectorRegressionModel()

    svm_free_model_content(model_);
    svm_destroy_param(&param_);
    svm_free_and_destroy_model(&model_);


SupportVectorRegressionModel::SupportVectorRegressionModel(const vector<MlObservation>& observations, const vector<MlTarget>& targets)

    // assumes all observations have same number of features
    size_t numFeatures = observations[0].size();

    //setup targets
    //auto v = ConvertVecOfVecToVec(targetsMat);
    double *targetsPtr = const_cast<double *>(&targets[0]); // why aren't the targets const?

    LibSvmTargetObservationConvertor conv;
    svm_node **observationsPtr = conv.ConvertObservations(observations, numFeatures);

    // setup observations
    //svm_node **observations = BuildObservations(observationsMat, numFeatures);

    // setup problem
    svm_problem problem;
    problem.l = targets.size();
    problem.y = targetsPtr;
    problem.x = observationsPtr;

    // specific to out training sets
    // TODO:    This is hard coded. 
    //          Bust out these values for use in constructor
    param_.C = 0.4;                 // cost
    param_.svm_type = 4;            // SVR
    param_.kernel_type = 2;         // radial
    param_.nu = 0.6;                // SVR nu
                                    // These values are the defaults used in the Matlab version
                                    // as found in svm_model_matlab.c
    param_.gamma = 1.0 / (double)numFeatures;
    param_.coef0 = 0;
    param_.cache_size = 100;        // in MB
    param_.shrinking = 1;
    param_.probability = 0;
    param_.degree = 3;
    param_.eps = 1e-3;
    param_.p = 0.1;
    param_.shrinking = 1;
    param_.probability = 0;
    param_.nr_weight = 0;
    param_.weight_label = NULL;
    param_.weight = NULL;

    // suppress command line output
    svm_set_print_string_function([](auto c) );

    model_ = svm_train(&problem, &param_);


double SupportVectorRegressionModel::Predict(const vector<double>& observation) const

    LibSvmTargetObservationConvertor conv;
    svm_node *obsNode = conv.ConvertMatToSvmNode(observation);
    double prediction = svm_predict(model_, obsNode);
    return prediction;


SupportVectorRegressionModel::SupportVectorRegressionModel(const FilePath & modelFile)

    model_ = svm_load_model(modelFile.Path().c_str());

private:
    svm_model *model_;
    svm_parameter param_;
;

【讨论】:

以上是关于如何在 Matlab 中训练模型,将其保存到磁盘并加载到 C++ 程序中?的主要内容,如果未能解决你的问题,请参考以下文章

使用 Huggingface Transformers 从磁盘加载预训练模型

如何在matlab里保存以及打开变量

如何保存大型 sklearn RandomForestRegressor 模型进行推理

如何在matlab中将句柄对象层次结构保存到磁盘

在matlab中保存和读取巨大的结构?

如何将 NSValue 保存到磁盘?