OpenCV-SVM总是预测更高级的标签

OpenCV SVM always predicts higher class label

本文关键字:高级的 标签 OpenCV-SVM      更新时间:2023-10-16

我使用OpenCV SVM实现来二进制预测图像特征的重要性。因此,我在正图像特征和负图像特征上训练它,并在{0,1}中寻找分类。

我遇到的问题是,在训练之后,SVM总是预测具有更高/更大类别标签的类别。我可以更改训练数据集的标签,但此问题仍然存在。我仔细检查了生成的标签和培训cv::Mat矩阵,没有发现任何问题。

下面是我的SVM类和附带的SVM参数

//Populate the SVM parameters
void SVM::setSVMParams()
{
    params.svm_type = cv::SVM::C_SVC;
    params.kernel_type = cv::SVM::RBF;
    params.term_crit = cv::TermCriteria(CV_TERMCRIT_ITER, 100, 1e-6);
    params_set = true;
}
//Train the SVM with the given data
void SVM::train(cv::Mat train_data, cv::Mat labels)
{
    //Set the SVM parameters if they haven't been already
    if (!params_set)
    {
        setSVMParams();
    }
    svm.train(train_data, labels, cv::Mat(), cv::Mat(), params);
}
//Based on training, predict the class of the given data
float SVM::predict(cv::Mat sample)
{
    return svm.predict(sample, false);
}

这里是负责生成训练数据和相应标签的功能

//Creates the appropriate training data and class labels for subsequent SVM training according to supplied D threshold
void Matchings::createSVMTrainingObjects(const float t_D, const float positive_label, const float negative_label, bool print_info)
{
    cv::Mat train_data_l((int)matchings_list.size(), 132, CV_32FC1);
    cv::Mat labels_l((int)matchings_list.size(), 1, CV_32FC1);
    int num_pos = 0;
    int num_neg = 0;
    for (int i = 0; i < matchings_list.size(); i++)
    {
        matching_d entry = matchings_list[i];
        //Important feature, label 1
        if (entry.D > t_D)
        {
            labels_l.at<float>(i) = positive_label;
            num_pos++;
        }
        //Unimportant feature, label -1
        else
        {
            labels_l.at<float>(i) = negative_label;
            num_neg++;
        }
        int j = 0;
        //Copy feature into current row of openCV matrix
        train_data_l.at<float>(i, j++) = entry.feature.x;
        train_data_l.at<float>(i, j++) = entry.feature.y;
        train_data_l.at<float>(i, j++) = entry.feature.scale;
        train_data_l.at<float>(i, j++) = entry.feature.angle;
        for (int k = 0; k < 128; k++)
        {
            train_data_l.at<float>(i, j + k) = entry.feature.vec[k];
        }
    }
    std::cout << "For training: #+ves=" << num_pos << ", #-ves=" << num_neg << std::endl;
    train_data = train_data_l;
    labels = labels_l;
}

最后,这里是一个函数,它实际调用SVM预测结果来保留重要的图像特征

matchingslist ASIFT::filterFeaturesWithSVM(matchingslist matchings, SVM& svm)
{
    matchingslist new_matchings;
    for (int i = 0; i < (int)matchings.size(); i++)
    {
        cv::Mat first = Utility::keypointToMat(matchings[i].first);
        cv::Mat second = Utility::keypointToMat(matchings[i].second);
        //If both features are of importance, retain them
        if (svm.predict(first) == 1.0f && svm.predict(second) == 1.0f)
        {
            new_matchings.push_back(matchings[i]);
        }
        else
        {
            std::cout << "Feature removed" << std::endl;
        }
    }
    return new_matchings;
}

该方法的一个主要问题是,在使用RBF时,您没有设置SVM的超功率计,因此可能是C=1gamma=1/d(或1/mean ||x||^2),因为它们是大多数SVM实现中的默认值。

而这些对于建立一个有效的模型至关重要。特别是,如果你的C值太低(1可能太低,取决于数据的许多特征),那么SVM只需构建一个平凡的模型,总是预测其中一个类

你该怎么办?您应该同时检查Cgamma的多个值。这些参数的含义是什么?

  • C(你的1)是错误分类的权重——C越大,SVM将更难准确地学习训练数据,可能会以过拟合为代价
  • gamma(默认值)是RBF核的2倍方差的倒数。换句话说,伽马越大,高斯越小,因此,你的方法在几何意义上更"局部"。再次强调,大伽马有助于最大限度地减少训练误差(偏差),但会导致更高的测试误差(方差)

正确选择方差偏差之间的折衷是机器学习技术的关键因素。在RBF SVM的情况下,你可以通过上面的方法来控制它。和他们一起玩,检查训练集错误和测试集错误,看看发生了什么。如果您的训练集错误很大,请增加C和/或gamma。一旦你的训练集错误很好,看看测试集-如果它太大-试着减少值等等。这通常是通过对参数的网格搜索进行一些内部交叉验证来自动完成的。

查看模型选择超参数优化的材料。

此外,您还修复了的迭代次数

params.term_crit = cv::TermCriteria(CV_TERMCRIT_ITER, 100, 1e-6);

而对于SVM,你永远不应该这样做。让它收敛(或者至少假设100000),在仅仅100步之后,SVM可能甚至没有接近收敛(因此导致了琐碎的模型)。