Fabrizio
Fabrizio

Reputation: 31

OpenCV - Ensemble of exemplar SVMs

I have been trying to implement an ensemble of exemplar SVM using OpenCV. The general idea beyond it is that, given K exemplars for training (e.g., images of cars), one can train K SVMs, where each SVM is trained using one positive sample and K-1 negatives. At testing time then, the ensemble will fire K times, with the highest score being the best prediction.

To implement this, I am using OpenCV SVM implementation (using current GIT master - 3.0 at time of writing), and the C++ interface. As features, I am using HOGs, with the response being ~7000 in size. Hence each image has a 7000D feature vector.

The problem I am facing is that the various SVM do not train properly. Actually, they do not train at all! The training in fact executes extremely fast, and always returns 1 support vector per SVM, with alpha=1.0. I am not sure if this is due to the fact that I have a single positive versus many (>900) negatives, or if it's simply a bug in my code. However, after looking at it several times, I cannot spot any obvious mistakes.

This is how I set-up my problem (assuming we got the HOG responses for the entire dataset and put them in a std::vector > trainingData ). Please note that EnsambleSVMElement is a Struct holding the SVM plus a bunch of other info.

In brief: I set-up a training matrix, where each row contains the HOG response for a particular sample. I then start training each SVM separately. For each training iteration, I create a label vector, where each entry is set to -1 (negative sample), except the entry associate to the current SVM I am training which is set to 1 (so if I am training entry 100, the only positive label will be at labels[100]).

Training code

int ensambles = trainingData.size();

if(ensambles>1)
{
    //get params to normalise the data in [0-1]
    std::vector<float> mins(trainingData.size());
    std::vector<float> maxes(trainingData.size());

    for(int i=0; i<trainingData.size(); ++i)
    {
        mins[i] =   *std::min_element(trainingData[i].begin(), trainingData[i].end());
        maxes[i] =  *std::max_element(trainingData[i].begin(), trainingData[i].end());
    }

    float min_val = *std::min_element(mins.begin(), mins.end());
    float max_val = *std::min_element(maxes.begin(), maxes.end());
    int featurevector_size = trainingData[0].size();

    if(featurevector_size>0)
    {
        //set-up training data. i-th row contains HOG response for sample i
        cv::Mat trainingDataMat(ensambles, featurevector_size, CV_32FC1); 
        for(int i=0; i<trainingDataMat.rows; ++i)
            for(int j=0; j<trainingDataMat.cols; ++j)
                trainingDataMat.at<float>(i, j) = (trainingData.at(i).at(j)-min_val)/(max_val-min_val); //make sure data are normalised in [0-1] - libSVM constraint

        for(int i=0; i<ensambles; ++i)
        {
            std::vector<int> labels(ensambles, -1);
            labels[i] = 1; //one positive only, and is the current sample
            cv::Mat labelsMat(ensambles, 1, CV_32SC1, &labels[0]);
            cv::Ptr<cv::ml::SVM> this_svm = cv::ml::StatModel::train<SVM>(trainingDataMat, ROW_SAMPLE, labelsMat, svmparams);
            ensamble_svm.push_back(EnsambleSVMElement(this_svm));   
            Mat sv = ensamble_svm[i].svm->getSupportVectors();
            std::cout << "SVM_" << i << " has " << ensamble_svm[i].svm->getSupportVectors().rows << " support vectors." << std::endl;
        }
    }
    else
        std::cout <<"You passed empty feature vectors!" << std::endl;
}
else
    std::cout <<"I need at least 2  SVMs to create an ensamble!" << std::endl;

The cout always prints "SVM_i has 1 support vectors".

For completeness, these are my SVM parameters:

cv::ml::SVM::Params params;
params.svmType    = cv::ml::SVM::C_SVC;
params.C           = 0.1;
params.kernelType = cv::ml::SVM::LINEAR;
params.termCrit   = cv::TermCriteria(cv::TermCriteria::MAX_ITER, (int)1e4, 1e-6);

Varying C between 0.1 and 1.0 doesn't affect the results. Neither does setting up weights for the samples, as read here. Just for reference, this is how I am setting up the weights (big penalty for negatives):

cv::Mat1f class_weights(1,2); 
class_weights(0,0) = 0.01; 
class_weights(0,1) = 0.99; 
params.classWeights = class_weights;

There is clearly something wrong eiether in my code, or in my formulation of the problem. Can anyone spot that?

Thanks!

Upvotes: 3

Views: 1474

Answers (2)

Janana
Janana

Reputation: 1

I think you have a small error here: the second line, I think it should be

*std::max_element(...), not *std::min_element(...)

 float min_val = *std::min_element(mins.begin(), mins.end());
 float max_val = *std::min_element(maxes.begin(), maxes.end());

Upvotes: 0

Raul Andrew
Raul Andrew

Reputation: 33

have you made any progress? My guess is that your C parameter is too low, try bigger values (10, 100, 1000). Another important aspect is that in the Exemplar SVM framework the training phase is not this simple. The author alternates between a training step and a hard negative mining step, in order to make the training phase more effective. If you need more details than reported in the Exemplar-SVM article, you can look at Malisiewicz phd thesis: http://people.csail.mit.edu/tomasz/papers/malisiewicz_thesis.pdf

Upvotes: 3

Related Questions