训练自定义 SVM 以在 OpenCV 中与 HOGDescriptor 一起使用

Posted

技术标签:

【中文标题】训练自定义 SVM 以在 OpenCV 中与 HOGDescriptor 一起使用【英文标题】:Training custom SVM to use with HOGDescriptor in OpenCV 【发布时间】:2013-02-26 16:37:39 【问题描述】:

我正在尝试训练我自己的检测器以与 OpenCV::HOGDescriptor 一起使用,但我无法让现有的 HOGDescriptor 与我新训练的 SVM 一起工作。

我计算了正负训练图像的 HOG 特征,标记它们并使用 CvSVM 训练 SVM。我使用的参数是:

    CvSVMParams params;
    params.svm_type =CvSVM::EPS_SVR;
    params.kernel_type = CvSVM::LINEAR;
    params.C = 0.01;
    params.p = 0.5;

然后我计算支持向量的原始形式,这样我只得到一个向量而不是多个向量,并使用 HOGDescriptor.setSVMDetector(vector); 设置计算的支持向量;

This is Primal Form

当我使用 CvSVM.predict() 时,我能够使用 SVM 正确分类对象,但 HOGDescriptor.detect() 或 detectMultiScale() 总是返回很多正匹配并且不能给出准确的预测。

CvSVM.predict() 使用原始支持向量进行分类,因此我计算原始形式的方式可能有问题。

有没有人训练过自己的探测器,可以为我指明正确的方向?

【问题讨论】:

似乎从 libsvm 继承了一个错误,其中标签的顺序被弄乱了。 predict 函数知道顺序并查找哪个标签是哪个标签,因此可以正常工作。我通过将我的 +ve 标签设置为小于我的 -ve 的数字解决了这个问题,即 pos = 1,neg = 2。否则你可能必须通过乘以 -1 来反转你的模型。 (这就是为什么接受的答案有奇怪的负号)。我选择不这样做,以防他们修复错误(如果您使用预测,它不是真正的错误,但他们可以改变它) 【参考方案1】:

我写了一个 CvSVM 的子类来在线性 svm 训练后提取原始形式。正样本标记为 1,负样本标记为 -1。奇怪的是,为了从 HogDescriptor 获得正确的结果,我必须在 alpha 前面加上负号并保持 rho 的符号不变。

线性支持向量机.h

#ifndef LINEAR_SVM_H_
#define LINEAR_SVM_H_
#include <opencv2/core/core.hpp>
#include <opencv2/ml/ml.hpp>

class LinearSVM: public CvSVM 
public:
  void getSupportVector(std::vector<float>& support_vector) const;
;  

#endif /* LINEAR_SVM_H_ */

LinearSVM.cc

#include "linear_svm.h"    
void LinearSVM::getSupportVector(std::vector<float>& support_vector) const 

    int sv_count = get_support_vector_count();
    const CvSVMDecisionFunc* df = decision_func;
    const double* alphas = df[0].alpha;
    double rho = df[0].rho;
    int var_count = get_var_count();
    support_vector.resize(var_count, 0);
    for (unsigned int r = 0; r < (unsigned)sv_count; r++) 
      float myalpha = alphas[r];
      const float* v = get_support_vector(r);
      for (int j = 0; j < var_count; j++,v++) 
        support_vector[j] += (-myalpha) * (*v);
      
    
    support_vector.push_back(rho);

【讨论】:

非常好的解决方案,只是一个小问题@DXM:这种到原始形式的转换只适用于Liner SVM,当我使用RBF内核时呢? opencv中使用RBF内核的源码我没有仔细研究过。但我的猜测是,一旦你将内核应用于你的特性,它应该与线性 svm 相​​同。 恕我直言,这可能非常慢。 Rbf 内核计算特征和所有 svm 支持向量之间的欧几里得距离。看一下 predict 方法代码。我很好奇您考虑实施它的方式... 什么是'decision_func'?它是从哪里来的? decision_func 继承自 CvSVM。【参考方案2】:

我也在为同样的问题而苦苦挣扎。搜索论坛我发现,无法使用 CvSVM 训练检测器(我不知道原因)。我使用 LIBSVM 来训练检测器。以下是为 HOGDescriptor.setSVMDetector(w) 提取检测器的代码:有关数据详细信息,请参阅 LIBSVM 文档/标题。我用 C++ 完成了所有的训练,将 LIBSVM 训练数据从 CV 填充到 LIBSVM;下面的代码提取了 cv::HOGDescriptor 所需的检测器向量。 w参数为std::vector<float> w

    const double * const *sv_coef = model.sv_coef;
const svm_node * const *SV = model.SV;
int l = model.l;
model.label;

const svm_node* p_tmp = SV[0];
int len = 0;
while( p_tmp->index != -1 )

    len++;
    p_tmp++;

w.resize( len+1 );

for( int i=0; i<l; i++)

    double svcoef = sv_coef[0][i];
    const svm_node* p = SV[i];
    while( p->index != -1 )
    
        w[p->index-1] += float(svcoef * p->value);
        p++;
    

w[len] = float(-model.rho[0]);

希望这会有所帮助...

【讨论】:

你试过这个代码吗?似乎即使在虚假图像中也能检测到 +ve。【参考方案3】:

根据我在 Dalal 关于 HOG 检测器的论文中读到的内容,他建议要消除误报,我们需要重新训练我们的模型。重新训练是通过应用初步模型(您的模型会产生大量误报)完成的,然后检测所有负样本图像中的对象。所有返回的矩形肯定会误报。

然后,将所有这些误报添加到您的负样本图像(负数据集)中,再次进行训练。正如论文中所建议的那样,生成的模型将返回更少的误报。

不幸的是,我尝试过(重新训练),但生成的模型无法识别任何东西,即使是正图像样本也是如此。但我认为值得一试,因为这是发明者关于 HOG 检测器的论文中提出的建议

【讨论】:

以上是关于训练自定义 SVM 以在 OpenCV 中与 HOGDescriptor 一起使用的主要内容,如果未能解决你的问题,请参考以下文章

opencv进阶-自定义对象检测

OpenCV支持向量机(SVM)介绍

Opencv 3 SVM 训练

Opencv 3 SVM 训练

SVM 训练 C++ OpenCV

在 OpencV 中训练 SVM 分类器?