我目前正在开发一个部分依赖于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
没有。
我试图:
trainData
和labels
矩阵的内容:一切正常CvParamGrid
函数中使用train_auto
结构,并通过将参数step
设置为0将一些参数设置为固定值:无更改params.term_crit
中的train_auto
并增加OpenCV优化问题解决方案的迭代次数:无更改你有任何建议,任何线索来解释我的代码的这种奇怪的行为?提前谢谢你!
_
[编辑]微小的变化(修正了一些语法错误,增加了一些额外的解释)。