Obtaining weights in CvSVM, the SVM implementation of OpenCV
After dealing with it I have been able to obtain the weights. For obtaining the weights one has to obtain first the support vectors and then add them multiplied by the alpha values.
// get the svm weights by multiplying the support vectors by the alpha values
int numSupportVectors = SVM.get_support_vector_count();
const float *supportVector;
const CvSVMDecisionFunc *dec = SVM.decision_func;
svmWeights = (float *) calloc((numOfFeatures+1),sizeof(float));
for (int i = 0; i < numSupportVectors; ++i)
{
float alpha = *(dec[0].alpha + i);
supportVector = SVM.get_support_vector(i);
for(int j=0;j<numOfFeatures;j++)
*(svmWeights + j) += alpha * *(supportVector+j);
}
*(svmWeights + numOfFeatures) = - dec[0].rho; //Be careful with the sign of the bias!
The only trick here is that the instance variable float *decision_function
is protected on the opencv framework, so I had to change it in order to access it.
How to calculate confidence score from CvSVM
just call CvSVM::predict with the returnDFVal param set to true, and it will return distances instead of class-labels
getting primal form from CvSVM trained file
Answer with a test harness. I put in new answer as it would add allot of clutter to the original answer, possibly making it a bit confusing.
//dummy features
std:: vector<float>
dummyDerReaderForOneDer(const vector<float> &pattern)
{
int i = std::rand() % pattern.size();
int j = std::rand() % pattern.size();
vector<float> patternPulNoise(pattern);
std::random_shuffle(patternPulNoise.begin()+std::min(i,j),patternPulNoise.begin()+std::max(i,j));
return patternPulNoise;
};
//extend CvSVM to get access to weights
class mySVM : public CvSVM
{
public:
vector<float>
getWeightVector(const int descriptorSize);
};
//get the weights
vector<float>
mySVM::getWeightVector(const int descriptorSize)
{
vector<float> svmWeightsVec(descriptorSize+1);
int numSupportVectors = get_support_vector_count();
//this is protected, but can access due to inheritance rules
const CvSVMDecisionFunc *dec = CvSVM::decision_func;
const float *supportVector;
float* svmWeight = &svmWeightsVec[0];
for (int i = 0; i < numSupportVectors; ++i)
{
float alpha = *(dec[0].alpha + i);
supportVector = get_support_vector(i);
for(int j=0;j<descriptorSize;j++)
{
*(svmWeight + j) += alpha * *(supportVector+j);
}
}
*(svmWeight + descriptorSize) = - dec[0].rho;
return svmWeightsVec;
}
// main harness entry point for detector test
int main (int argc, const char * argv[])
{
//dummy variables for example
int posFiles = 10;
int negFiles = 10;
int dims = 1000;
int randomFactor = 4;
//setup some dummy data
vector<float> dummyPosPattern;
dummyPosPattern.assign(int(dims/randomFactor),1.f);
dummyPosPattern.resize(dims );
random_shuffle(dummyPosPattern.begin(),dummyPosPattern.end());
vector<float> dummyNegPattern;
dummyNegPattern.assign(int(dims/randomFactor),1.f);
dummyNegPattern.resize(dims );
random_shuffle(dummyNegPattern.begin(),dummyNegPattern.end());
// the labels and lables mat
float posLabel = 1.f;
float negLabel = 2.f;
cv::Mat cSvmLabels;
//the data mat
cv::Mat cSvmTrainingData;
//dummy linear svm parmas
SVMParams cSvmParams;
cSvmParams.svm_type = cv::SVM::C_SVC;
cSvmParams.C = 0.0100;
cSvmParams.kernel_type = cv::SVM::LINEAR;
cSvmParams.term_crit = cv::TermCriteria(CV_TERMCRIT_ITER+CV_TERMCRIT_EPS, 1000000, FLT_EPSILON);
cout << "creating training data. please wait" << endl;
int i;
for(i=0;i<posFiles;i++)
{
//your feature for one box from file
vector<float> d = dummyDerReaderForOneDer(dummyPosPattern);
//push back a new mat made from the vectors data, with copy data flag on
//this shows the format of the mat for a single example, (1 (row) X dims(col) ), as training mat has each **row** as an example;
//the push_back works like vector add adds each example to the bottom of the matrix
cSvmTrainingData.push_back(cv::Mat(1,dims,CV_32FC1,d.data(),true));
//push back a pos label to the labels mat
cSvmLabels.push_back(posLabel);
}
//do same with neg files;
for(i=0;i<negFiles;i++)
{
float a = rand();
vector<float> d = dummyDerReaderForOneDer(dummyNegPattern);
cSvmTrainingData.push_back(cv::Mat(1,dims,CV_32FC1,d.data(),true));
cSvmLabels.push_back(negLabel);
}
//have a look
cv::Mat viz;
cSvmTrainingData.convertTo(viz,CV_8UC3);
viz = viz*255;
cv::imshow("svmData", viz);
cv::waitKey(10);
cout << "press any key to continue" << endl;
getchar();
viz.release();
//create the svm;
cout << "training, please wait" << endl;
mySVM svm;
svm.train(cSvmTrainingData,cSvmLabels,cv::Mat(),cv::Mat(),cSvmParams);
cout << "get weights" << endl;
vector<float> svmWeights = svm.getWeightVector(dims);
for(i=0; i<dims+1; i++)
{
cout << svmWeights[i] << ", ";
if(i==dims)
{
cout << endl << "bias: " << svmWeights[i] << endl;
}
}
cout << "press any key to continue" << endl;
getchar();
cout << "testing, please wait" << endl;
//test the svm with a large amount of new unseen fake one at a time
int totExamples = 10;
int k;
for(i=0;i<totExamples; i++)
{
cout << endl << endl;
vector<float> dPos = dummyDerReaderForOneDer(dummyPosPattern);
cv::Mat dMatPos(1,dims,CV_32FC1,dPos.data(),true);
float predScoreFromDual = svm.predict(dMatPos,true);
float predScoreBFromPrimal = svmWeights[dims];
for( k = 0; k <= dims - 4; k += 4 )
predScoreBFromPrimal += dPos[k]*svmWeights[k] + dPos[k+1]*svmWeights[k+1] +
dPos[k+2]*svmWeights[k+2] + dPos[k+3]*svmWeights[k+3];
for( ; k < dims; k++ )
predScoreBFromPrimal += dPos[k]*svmWeights[k];
cout << "Dual Score:\t" << predScoreFromDual << "\tPrimal Score:\t" << predScoreBFromPrimal << endl;
}
cout << "press any key to continue" << endl;
getchar();
return(0);
}
Can SVM solution change after shuffling the inputs?
I am not familiar with the OpenCV implementation. But do this: run several trials on exactly the same data set -- no shuffling, same order, same data points. See if the SVM changes. Obviously, in theory, it shouldn't. But it could be that there is some small randomization step somewhere in the implementation that produces different outputs for the same input.
Edit: As Chris A. asks, do the feature vectors correspond to their proper labels after shuffling? If not, that would obviously destroy your results.
How to find Mat file consisting of hog features
this sample should contain the cases that we have discussed above in the comments.
//dummy readers
std:: vector<float>
dummyDerReaderForOneDer(const vector<float> &pattern)
{
int i = std::rand() % pattern.size();
int j = std::rand() % pattern.size();
vector<float> patternPulNoise(pattern);
std::random_shuffle(patternPulNoise.begin()+std::min(i,j),patternPulNoise.begin()+std::max(i,j));
return patternPulNoise;
};
std:: vector<float>
dummyDerReaderForManyDers(const vector<float> &posPattern,
const vector<float> &negPattern,
vector<float> labels,
float posLabel, float negLabel)
{
int i,j;
vector<float> allPatternsInOneVector;
for(i=0;i< labels.size(); i++)
{
vector<float> patternPulNoise;
if(labels[i]==posLabel)
{
patternPulNoise = dummyDerReaderForOneDer(posPattern);
}
if(labels[i]==negLabel)
{
patternPulNoise = dummyDerReaderForOneDer(negPattern);
}
for(j=0;j< patternPulNoise.size(); j++)
{
allPatternsInOneVector.push_back(patternPulNoise[j]);
}
}
return allPatternsInOneVector;
}
// main harness entry point for detector test
int main (int argc, const char * argv[])
{
//dummy variables for example
int posFiles = 128;
int negFiles = 128;
int dims = 3780;
//setup some dummy data
vector<float> dummyPosPattern;
dummyPosPattern.assign(1000,1.f);
dummyPosPattern.resize(dims );
random_shuffle(dummyPosPattern.begin(),dummyPosPattern.end());
vector<float> dummyNegPattern;
dummyNegPattern.assign(1000,1.f);
dummyNegPattern.resize(dims );
random_shuffle(dummyNegPattern.begin(),dummyNegPattern.end());
// the labels and lables mat
float posLabel = 1.f;
float negLabel = 2.f;
cv::Mat cSvmLabels;
//the data mat
cv::Mat cSvmTrainingData;
//dummy linear svm parmas
SVMParams cSvmParams;
cSvmParams.svm_type = cv::SVM::C_SVC;
cSvmParams.C = 0.0100;
cSvmParams.kernel_type = cv::SVM::LINEAR;
cSvmParams.term_crit = cv::TermCriteria(CV_TERMCRIT_ITER+CV_TERMCRIT_EPS, 1000000, FLT_EPSILON);
cout << "creating training data. please wait" << endl;
int i;
for(i=0;i<posFiles;i++)
{
//your feature for one box from file
vector<float> d = dummyDerReaderForOneDer(dummyPosPattern);
//push back a new mat made from the vectors data, with copy data flag on
//this shows the format of the mat for a single example, (1 (row) X dims(col) ), as training mat has each **row** as an example;
//the push_back works like vector add adds each example to the bottom of the matrix
cSvmTrainingData.push_back(cv::Mat(1,dims,CV_32FC1,d.data(),true));
//push back a pos label to the labels mat
cSvmLabels.push_back(posLabel);
}
//do same with neg files;
for(i=0;i<negFiles;i++)
{
float a = rand();
vector<float> d = dummyDerReaderForOneDer(dummyNegPattern);
cSvmTrainingData.push_back(cv::Mat(1,dims,CV_32FC1,d.data(),true));
cSvmLabels.push_back(negLabel);
}
//have a look
cv::Mat viz;
cSvmTrainingData.convertTo(viz,CV_8UC3);
viz = viz*255;
cv::imshow("svmData", viz);
cv::waitKey(10);
cout << "press any key to continue" << endl;
getchar();
viz.release();
//create the svm;
cout << "training, please wait" << endl;
CvSVM svm;
svm.train(cSvmTrainingData,cSvmLabels,cv::Mat(),cv::Mat(),cSvmParams);
cout << "testing, please wait" << endl;
//test the svm with a large amount of new unseen fake one at a time
int totExamples = 10000;
float TP(0.f), FP(0.f), FN(0.f), TN(0.f);
for(i=0;i<totExamples; i++)
{
vector<float> dPos = dummyDerReaderForOneDer(dummyPosPattern);
cv::Mat dMatPos(1,dims,CV_32FC1,dPos.data(),true);
float predLabelPos = svm.predict(dMatPos);
if(predLabelPos == posLabel) TP++;
else(FN++);
vector<float> dNeg = dummyDerReaderForOneDer(dummyNegPattern);
cv::Mat dMatNeg(1,dims,CV_32FC1,dNeg.data(),true);
float predLabelNeg = svm.predict(dMatNeg);
if(predLabelNeg == negLabel) TN++;
else(FP++);
if(i%1000==0)
cout << "testing " << i << "of " << totExamples << endl;
}
//http://en.wikipedia.org/wiki/Precision_and_recall
float sensitivity = TP / (TP + FN);
float specificity = TN / (TN + FP);
float precision = TP / (TP + FP);
cout << "sensitivity: " << sensitivity << endl;
cout << "specificity: " << specificity << endl;
cout << "precision: " << precision << endl;
cout << "press any key to continue" << endl;
getchar();
cout << "creating test data, please wait" << endl;
//test the svm with a large amount of new unseen fake data all at one time
TP=FP=FN=TN = 0.f;
vector<float> testLabels;
for(i=0;i<int(totExamples/2);i++)
{
testLabels.push_back(posLabel);
}
for(i;i<totExamples;i++)
{
testLabels.push_back(negLabel);
}
vector<float> allExamples = dummyDerReaderForManyDers(dummyPosPattern,dummyNegPattern,testLabels,posLabel,negLabel);
//on big mat
cv::Mat allExamplesMat(1,allExamples.size(),CV_32FC1,allExamples.data(),true);
//need reshape to one example per row
allExamplesMat = allExamplesMat.reshape(0,totExamples);
//have a look
allExamplesMat.convertTo(viz,CV_8UC3);
viz = viz*255;
cv::imshow("testData", viz);
cv::waitKey(10);
cout << "press any key to continue" << endl;
getchar();
viz.release();
//test them all at once ( uses intel tbb :)
cout << "predict all at once, please wait" << endl;
cv::Mat predLabels_mat;
svm.predict(allExamplesMat,predLabels_mat);
//evaluate
for(i=0;i<testLabels.size();i++)
{
float testLabel = testLabels.at(i);
float predLabel = predLabels_mat.at<float>(i);
if(testLabel==predLabel)
{
if(testLabel==posLabel) TP++;
else TN++;
}
else
{
if(testLabel==posLabel) FP++;
else FN++;
}
}
//http://en.wikipedia.org/wiki/Precision_and_recall
sensitivity = TP / (TP + FN);
specificity = TN / (TN + FP);
precision = TP / (TP + FP);
cout << "sensitivity: " << sensitivity << endl;
cout << "specificity: " << specificity << endl;
cout << "precision: " << precision << endl;
cout << "press any key to continue" << endl;
getchar();
return(0);
}
Feeding HOG into SVM: the HOG has 9 bins, but the SVM takes in a 1D matrix
The SVM always takes in a single row of data per feature vector. The dimensionality of the feature vector is thus the length of the row. If you're dealing with 2D data, then there are 2 items per feature vector. Example of 2D data is on this webpage:
http://www.csie.ntu.edu.tw/~cjlin/libsvm/
code of an equivalent demo in OpenCV http://sites.google.com/site/btabibian/labbook/svmusingopencv
The point is that even though you're thinking of the histogram as 2D with 9-bin cells, the feature vector is in fact the flattened version of this. So it's correct to flatten it out into a long feature vector. The result for me was a feature vector of length 2304 (16x16x9) and I get 100% prediction accuracy on a small test set (i.e. it's probably slightly less than 100% but it's working exceptionally well).
The reason this works is that the SVM is working on a system of weights per item of the feature vector. So it doesn't have anything to do with the problem's dimension, the hyperplane is always in the same dimension as the feature vector. Another way of looking at it is to forget about the hyperplane and just view it as a bunch of weights for each item in the feature vector. In this case, it needs one weighting for every item, then it multiplies each item by its weighting and outputs the result.
Related Topics
Keep Bluetooth Sound When Initializing Avaudiosession
Fbsession.Activesession.Isopen Returns No Even Though the User Logged-In
Remove Next/Previous Buttons (Inputaccessoryview) for Custom Keyboard in iOS8 Webview
How to Update Data in Tableview Without the Delay Using Cloudkit When Creating New Records
Native Zlib Inflate/Deflate for Swift3 on iOS
Swift - the Data Couldn't Be Read Because It Isn't in the Correct Format
Firebase Pod Install - Pod 'Firebase/Database' - Required a Higher Minimum Deployment Target
iOS - Linking Framework Storyboard to Viewcontroller for Use in Main Project
Facebook Registration:The Operation Couldn't Be Completed (Com.Facebook.Sdk Error 2)
How to Fix Status Bar Overlap Issue in iOS 7
Warning in Custom Map Annotations Iphone
Make Swift Assume Degrees for Trigonometry Calculations
How to Properly Structure Firebase Database to Allow Easy Reading and Removal
Apple Push Notification in Background Issue
#If Canimport() Does Not Find Frameworks with Cocoapods