OpenCV SVM 总是预测更高的 class 标签
OpenCV SVM always predicts higher class label
我正在使用 OpenCV SVM 实现来二进制预测图像特征的重要性。因此,我正在根据正面和负面图像特征对其进行训练,并在 {0,1} 中寻找 class化。
我遇到的问题是,在训练之后,SVM 总是用 higher/greater class 标签预测 class。我可以更改训练数据集的标签,但这个问题仍然存在。我仔细检查了生成的标签和训练 cv::Mat 矩阵,没有发现任何问题。
下面是我的 SVM class 和附带的 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=1
和 gamma=1/d
(或 1/mean ||x||^2
)因为这些是大多数 SVM 实现中的默认值。
虽然这些对于构建有效模型至关重要。特别是,如果您的 C
值太低(1
可能是,取决于数据的许多特征),那么 SVM 会简单地构建一个简单的模型 总是预测 类.
你应该做什么?您应该检查 C
和 gamma
的多个值。这些参数的含义是什么?
C
(您的 1
)是错误分类的权重 - C
越大,SVM 将更加努力地准确学习训练数据,可能以过度拟合为代价。
gamma
(您的默认值)是 RBF 核的 2 倍方差的倒数。换句话说 - 伽马越大,高斯越小,因此 - 你的方法在几何意义上更 "local" 。同样 - 大伽玛可以帮助您最大限度地减少训练误差(偏差),但会导致更高的测试误差(方差)。
正确选择方差偏差之间的权衡是机器学习技术的关键要素。在 RBF SVM 的情况下——你可以通过上面的方法来控制它。和他们一起玩,检查训练集错误和测试集错误,看看发生了什么。如果你的训练集误差很大——增加 C and/or gamma。一旦你的训练集误差没问题,看看测试集——如果它太大——尝试减少值等等。它通常通过一些内部交叉验证和参数的网格搜索以自动方式完成。
查看有关模型选择和超参数优化的资料。
此外你固定迭代次数
params.term_crit = cv::TermCriteria(CV_TERMCRIT_ITER, 100, 1e-6);
而对于 SVM,您永远不应该这样做。让它收敛(或者至少放 100,000 之类的东西),在仅仅 100 步之后,SVM 可能甚至没有接近收敛(因此导致了微不足道的模型)。
我正在使用 OpenCV SVM 实现来二进制预测图像特征的重要性。因此,我正在根据正面和负面图像特征对其进行训练,并在 {0,1} 中寻找 class化。
我遇到的问题是,在训练之后,SVM 总是用 higher/greater class 标签预测 class。我可以更改训练数据集的标签,但这个问题仍然存在。我仔细检查了生成的标签和训练 cv::Mat 矩阵,没有发现任何问题。
下面是我的 SVM class 和附带的 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=1
和 gamma=1/d
(或 1/mean ||x||^2
)因为这些是大多数 SVM 实现中的默认值。
虽然这些对于构建有效模型至关重要。特别是,如果您的 C
值太低(1
可能是,取决于数据的许多特征),那么 SVM 会简单地构建一个简单的模型 总是预测 类.
你应该做什么?您应该检查 C
和 gamma
的多个值。这些参数的含义是什么?
C
(您的1
)是错误分类的权重 -C
越大,SVM 将更加努力地准确学习训练数据,可能以过度拟合为代价。gamma
(您的默认值)是 RBF 核的 2 倍方差的倒数。换句话说 - 伽马越大,高斯越小,因此 - 你的方法在几何意义上更 "local" 。同样 - 大伽玛可以帮助您最大限度地减少训练误差(偏差),但会导致更高的测试误差(方差)。
正确选择方差偏差之间的权衡是机器学习技术的关键要素。在 RBF SVM 的情况下——你可以通过上面的方法来控制它。和他们一起玩,检查训练集错误和测试集错误,看看发生了什么。如果你的训练集误差很大——增加 C and/or gamma。一旦你的训练集误差没问题,看看测试集——如果它太大——尝试减少值等等。它通常通过一些内部交叉验证和参数的网格搜索以自动方式完成。
查看有关模型选择和超参数优化的资料。
此外你固定迭代次数
params.term_crit = cv::TermCriteria(CV_TERMCRIT_ITER, 100, 1e-6);
而对于 SVM,您永远不应该这样做。让它收敛(或者至少放 100,000 之类的东西),在仅仅 100 步之后,SVM 可能甚至没有接近收敛(因此导致了微不足道的模型)。