title: OpenCV - Ensemble of exemplar SVMs
body: <p>I have been trying to implement an <a href="http://www.cs.cmu.edu/~tmalisie/projects/iccv11/" rel="nofollow">ensemble of exemplar SVM</a> 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. </p>
  
  <p>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. </p>
  
  <p>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. </p>
  
  <p>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.</p>
  
  <p>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]).</p>
  
  <p><strong>Training code</strong></p>
  
  <pre><code>int ensambles = trainingData.size();
  
  if(ensambles&gt;1)
  {
      //get params to normalise the data in [0-1]
      std::vector&lt;float&gt; mins(trainingData.size());
      std::vector&lt;float&gt; maxes(trainingData.size());
  
      for(int i=0; i&lt;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&gt;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&lt;trainingDataMat.rows; ++i)
              for(int j=0; j&lt;trainingDataMat.cols; ++j)
                  trainingDataMat.at&lt;float&gt;(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&lt;ensambles; ++i)
          {
              std::vector&lt;int&gt; labels(ensambles, -1);
              labels[i] = 1; //one positive only, and is the current sample
              cv::Mat labelsMat(ensambles, 1, CV_32SC1, &amp;labels[0]);
              cv::Ptr&lt;cv::ml::SVM&gt; this_svm = cv::ml::StatModel::train&lt;SVM&gt;(trainingDataMat, ROW_SAMPLE, labelsMat, svmparams);
              ensamble_svm.push_back(EnsambleSVMElement(this_svm));   
              Mat sv = ensamble_svm[i].svm-&gt;getSupportVectors();
              std::cout &lt;&lt; "SVM_" &lt;&lt; i &lt;&lt; " has " &lt;&lt; ensamble_svm[i].svm-&gt;getSupportVectors().rows &lt;&lt; " support vectors." &lt;&lt; std::endl;
          }
      }
      else
          std::cout &lt;&lt;"You passed empty feature vectors!" &lt;&lt; std::endl;
  }
  else
      std::cout &lt;&lt;"I need at least 2  SVMs to create an ensamble!" &lt;&lt; std::endl;
  </code></pre>
  
  <p>The cout always prints "SVM_i has 1 support vectors".</p>
  
  <p>For completeness, these are my SVM parameters:</p>
  
  <pre><code>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);
  </code></pre>
  
  <p>Varying C between 0.1 and 1.0 doesn't affect the results. Neither does setting up weights for the samples, as read <a href="http://answers.opencv.org/question/26818/svm-bias-on-weights-of-positives-and-negatives/" rel="nofollow">here</a>. Just for reference, this is how I am setting up the weights (big penalty for negatives):</p>
  
  <pre><code>cv::Mat1f class_weights(1,2); 
  class_weights(0,0) = 0.01; 
  class_weights(0,1) = 0.99; 
  params.classWeights = class_weights;
  </code></pre>
  
  <p>There is clearly something wrong eiether in my code, or in my formulation of the problem. Can anyone spot that?</p>
  
  <p>Thanks!</p>