OpenCV 3.1.0:保存并加载经过训练的SVM

时间:2016-05-09 06:11:10

标签: c++ svm opencv3.1

目前我正在尝试训练不同的SVM来识别不同的情绪。因此,例如,为了认识快乐的情感,我用快乐的人的图像训练SVM作为积极和图像,其中人们表达其他情绪,如愤怒,恐惧,厌恶,......作为消极。图像存储在我已在训练部分和测试部分中分区的数据库中。

当我训练SVM时,我立即使用它们来测试数据库测试图像的准确性,这很好。但我也保存了训练有素的SVM,因为我想在另一个程序中使用它们,并且不想在每次启动另一个程序时重新训练它们。

因此我在其他程序中加载了SVM,但结果非常糟糕。准确率几乎为零。因此,我试图在训练计划中保存和即时加载SVM,但此处的准确度现在几乎为零。

在搜索了一段时间后,我发现如果我已经加载了SVM并且我打印了SVM类型,内核类型和支持向量,它们与SVM .xml文件中的相同。所以我认为问题在于预测没有以正确的方式执行。我也不知道我是否保存了我的SVM并以适当的方式加载它们。

此刻我尝试寻找解决方案但没有任何成功。我尝试过的一些链接是:

Train SVM and save it with OpenCV 3.0

How to load previously stored svm classifier?

Load Trained SVM – Emgu CV

opencv 3 (C++) auto trained SVM loading issue

我用来训练SVM并立即测试它们而不再加载它们的代码是:

trainData = ml::TrainData::create(training_mat, ROW_SAMPLE, label_mat);
svm = SVM::create();
svm->setType(SVM::C_SVC);
svm->setKernel(SVM::RBF);
svm->trainAuto(trainData);
svm->save(svmSaveNames[i]);

// Test SVMs
data_file.open(filenameLabelsTestingImages[i]);
data_file << "Number\n";
startTest = stopTest;
stopTest  = startTest + emotionCountersTesting[i];
int numberRightClassified = 0;
int numberClassified = 0;

for (int j = 0; j < numberOfTestImg; j++)
{
    cv::Mat testing_one_image_mat(1, numberOfFeatures, CV_32F);
    for (int k = 0; k < numberOfFeatures; k++)
    {
        testing_one_image_mat.at<float>(0, k) = testing_mat.at<float>(j, k);
    }

    int value_svm = svmNew->predict(testing_one_image_mat);

    if (value_svm == 1)
    {   
        if (j >= startTest && j < stopTest)
        {
            numberRightClassified++;
        }
        numberClassified++;
    }
    data_file << value_svm << endl;
}
data_file.close();

所以这个工作正常,直到我更改代码首先保存SVM然后再次加载它们进行预测,如下所示

trainData = ml::TrainData::create(training_mat, ROW_SAMPLE, label_mat);
svm = SVM::create();
svm->setType(SVM::C_SVC);
svm->setKernel(SVM::RBF);
svm->trainAuto(trainData);
svm->save(svmSaveNames[i]);

Ptr<SVM> svmNew = SVM::create();
svmNew = SVM::load<SVM>(svmSaveNames[i]);
//cout << "The type is " << svmNew->getType() << endl;
//cout << "The kernel type is " << svmNew->getKernelType() << endl;
//cout << "The support vectors are " << svmNew->getSupportVectors() << endl;

// Test SVMs
data_file.open(filenameLabelsTestingImages[i]);
data_file << "Number\n";
startTest = stopTest;
stopTest  = startTest + emotionCountersTesting[i];
int numberRightClassified = 0;
int numberClassified = 0;

for (int j = 0; j < numberOfTestImg; j++)
{
    cv::Mat testing_one_image_mat(1, numberOfFeatures, CV_32F);
    for (int k = 0; k < numberOfFeatures; k++)
    {
        testing_one_image_mat.at<float>(0, k) = testing_mat.at<float>(j, k);
    }

    //int value_svm = svm -> predict(testing_one_image_mat);
    int value_svm = svmNew->predict(testing_one_image_mat);

    if (value_svm == 1)
    {   
        if (j >= startTest && j < stopTest)
        {
            numberRightClassified++;
        }
        numberClassified++;
    }
    data_file << value_svm << endl;
}
data_file.close();

数组svmSaveNames包含用于保存不同SVM的名称的字符串,如svm_anger.xml,svm_contempt.xml,...

我使用变量data_file为每个测试的SVM创建一个.txt文件。因此,首先我训练和测试SVM以识别例如情绪愤怒,并且在测试该SVM时我使用所有测试图像。因此,所有这些图像的预测(1 =正/ -1 =负)被写入文本文件。

参数startTest和stopTest用于验证正图像(预测值是否为1)是否在需要被识别为正图像的范围内。在数据库的测试图中,我按照情感对所有图像进行了排序,因此首先是愤怒然后蔑视,......

2D矩阵testing_mat包含来自SVM的所有测试图像的数据,用于预测情绪。

所以我的问题是,在我加载了SVM之后,他们没有给我正确的预测。

1 个答案:

答案 0 :(得分:0)

经过一段时间的搜索,我发现如果我使用线性内核就没有问题了。那么我就可以保存并加载SVM并且预测是正确的。所以我开始寻找它适用于线性内核而不适用于其他内核的原因。

答案是,根据Github上的问题#5054,OpenCV 3.1中存在一个错误。我尝试了提出的解决方案,但它仍然无法正常工作。最终我下载了OpenCV 2.4,现在一切正常。