当Matlab对数据进行正确分类时,OpenCV的SVM会输出较高的错误分类率

时间:2015-12-09 17:10:10

标签: c++ matlab opencv svm

我目前正在开发一个部分依赖于OpenCV支持向量机(SVM)的项目。

在这个项目中,我考虑了454个图像的火车数据库,平均分为两类:模糊或尖锐。根据一系列改编自学术文献的描述符,我的目的是确定输入图像是否模糊。

为数据库的所有图像计算这些描述符。然后,我使用OpenCV的train_auto函数训练带有获得数据的SVM以预测图像的模糊性。此外,为了检查分类的相关性,我在列车数据库上执行交叉验证算法:80%的图像被随机拾取以训练SVM,而剩余的20%用于测试它。同样,火车和测试图像同样分为模糊和非模糊图像。

现在,这个SVM没有经过正确的训练。实际上,相同的标签归因于所有图像,导致错误分类率为50%。我现在正试图找到并解释这种现象的原因,更令人惊讶的是,如果我在Matlab中使用具有相同数据集的fitcsvm函数,则此错误分类比率降至10.05%。因此,先验,数据可以实际上由SVM分开:但是,由于某种原因,OpenCV无法找到正确的参数来调整和训练SVM。

我的实施如下:

int main() {
    // Reads descriptors
    // A [0 .. 226] : examples of descriptors for images of class 0 (blurred)
    // A [227 .. 453] : examples of descriptors for images of class 1 (sharp)
    Mat A = readData("data_train_16.txt");
    // Shuffles the indices
    std::vector<int> t;
    t.reserve(227);
    for (int i = 0 ; i < 227 ; i++) t.push_back(i);
    FisherYatesShuffle(t);
    // Selects 80 % of the indices to train a SVM
    int n = 0.8 * 227;
    trainData = Mat(2 * n, 16, CV_32F);
    for (int i = 0 ; i < n ; i++) {
        A.row(t[i]).copyTo(trainData.row(i));
        A.row(227 + t[i]).copyTo(trainData.row(i + n));
    }
    // So we have :
    // trainData [0 .. n - 1] : examples of descriptors for images of class 0
    // trainData [n .. 2 * n] : examples of descriptors for images of class 1
    labels = Mat(2 * n, 1, CV_32S, Scalar::all(0));
    labels.rowRange(0, n).setTo(0);
    labels.rowRange(n, 2 * n).setTo(1);
    // Trains the SVM
    CvSVM svm;
    svm.train_auto(trainData, labels, Mat(), Mat(), CvSVMParams());
    // Tests the SVM on the remaining 20% images
    int ok_0 = 0, ok_1 = 0;
    int terms = t.size() - n;
    for (int i = n ; i < t.size() ; i++) {
        Mat D;
        int answer;
        // Tests if a blurred image is marked as 'blurred' by the SVM
        A.row(t[i]).copyTo(D);
        answer = int(svm.predict(D));
        if (answer == 0) ok_0++;
        // Tests if a sharp image is marked as 'sharp' by the SVM
        A.row(227 + t[i]).copyTo(D);
        answer = int(svm.predict(D));
        if (answer == 1) ok_1++;
    }
    // Outputs some info
    CvSVMParams params = svm.get_params();
    std::cout << "SVM type : " << params.svm_type << ", kernel : " << params.kernel_type << std::endl;
    std::cout << "C : " << params.C << ", nu : " << params.nu << ", deg : " << params.degree << ", gamma : " << params.gamma << ", coef0 : " << params.coef0 << std::endl;
    std::cout << "Class 0 : " << ok_0 << "/" << terms << " (" << 100.0f * float(ok_0) / float(terms) << " %), ";
    std::cout << "Class 1 : " << ok_1 << "/" << terms << " (" << 100.0f * float(ok_1) / float(terms) << " %)" << std::endl;
    std::cout << "Total : " << 100.0f * float(ok_0 + ok_1) / float(2 * terms) << " %" << std::endl;
    return 0;
}

输出:

SVM type : 100 [i.e. C_SVM], kernel : 2 [i.e. RBF]
C : 2.5, nu : 0, deg : 0, gamma : 0.50625, coef0 : 0
Class 0 : 0/46 (0 %), Class 1 : 46/46 (100 %)

这样的比率让我认为,要标记为0,一个点应该非常接近已标记为0的现有点。因此,我怀疑当前的情况,而不是well-trained SVM like this。与that one类似。这两个图表示使用相同数据集训练的2D SVM(大约,我只是在不同时间输出它们)。蓝点表示0级的点,红点,1级的点。在第二个图中,您可以看到,由于SVM内核的参数不好,我们会遇到以下情况:即使0级点(即蓝点)落在许多其他蓝点附近,让我们说在图的左下角,该点也会被错误分类,因为它会落入红色区域,对应于第1类。在第一个图中不是这种情况,其中边距是正确计算的。

我认为这是同样的问题。并且它没有告诉我为什么Matlab的fitcsvm成功地对数据进行分类,而OpenCV的train_auto没有。

我试图:

  • 检查trainDatalabels矩阵的内容:一切正常
  • 规范化每个变量的数据:无变化
  • CvParamGrid函数中使用train_auto结构,并通过将参数step设置为0将一些参数设置为固定值:无更改
  • 更改params.term_crit中的train_auto并增加OpenCV优化问题解决方案的迭代次数:无更改
  • 降低数据的维数并仅使用2个组件:算法的错误分类率约为16或17%(我使用Matlab获得了类似的值)。

你有任何建议,任何线索来解释我的代码的这种奇怪的行为?提前谢谢你!

_

[编辑]微小的变化(修正了一些语法错误,增加了一些额外的解释)。

0 个答案:

没有答案