SVM简介
知乎上的一个回答我认为是史上最NB最形象的SVM含义解释,想看介绍戳这里(里面的第一个回答),再看看百科就能知道个大概了。
开发环境
Windows10 + VS2013 + Qt580 + OpenCV300
主要代码
利用opencv-SVM算法和Mnist数据集封装成一个单例模式的数字识别检测器。
DigitsDetector.h
#ifndef DIGITSDETECTOR
#define DIGITSDETECTOR
#include <opencv2/opencv.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/core/core.hpp>
#include <opencv2/features2d/features2d.hpp>
#include <opencv2/flann/flann.hpp>
#include <opencv.hpp>
#include <opencv2/ml/ml.hpp>
#include <iostream>
#include <string>
#include <fstream>
using namespace std;
using namespace cv;
using namespace cv::ml;
class DigitsDetector
{
public:
static DigitsDetector *I();
public:
Mat ReadMnistImage(const string pathName);
Mat ReadMnistLabel(const string pathName);
void CreateSVM();
bool Train(Mat data, Mat label);
float Predict(Mat testdata);
protected:
int CvtToLittleEndian(int i);
private:
DigitsDetector();
~DigitsDetector();
static DigitsDetector *inst;
Ptr<SVM> svm;
};
#endif // !DIGITSDETECTOR
DigitsDetector.cpp
#include "DigitsDetector.h"
DigitsDetector *DigitsDetector::inst = new DigitsDetector();
DigitsDetector::DigitsDetector(){
}
DigitsDetector::~DigitsDetector(){
if (inst != NULL){
delete inst;
inst = NULL;
}
}
DigitsDetector *DigitsDetector::I(){
if (inst == NULL) inst = new DigitsDetector();
return inst;
}
Mat DigitsDetector::ReadMnistImage(const string pathName){
int magicNumber = 0;
int imageNumber = 0;
int rows = 0;
int cols = 0;
Mat dataMat;
ifstream file(pathName, ios::binary);
if (file.is_open() == true){
file.read((char*)&magicNumber, sizeof(magicNumber));
file.read((char*)&imageNumber, sizeof(imageNumber));
file.read((char*)&rows, sizeof(rows));
file.read((char*)&cols, sizeof(cols));
magicNumber = CvtToLittleEndian(magicNumber);
imageNumber = CvtToLittleEndian(imageNumber);
rows = CvtToLittleEndian(rows);
cols = CvtToLittleEndian(cols);
// 每张数字图像为一个一维向量,构成imageNumber * (rows * cols)的矩阵
dataMat = Mat::zeros(imageNumber, rows * cols, CV_32FC1);
for (int i = 0; i < imageNumber / 1; i++){
for (int j = 0; j < rows * cols; j++){
unsigned char temp = 0;
file.read((char*)&temp, sizeof(temp));
float value = float((temp + 0.0) / 255.0);
dataMat.at<float>(i, j) = value;
}
}
}
file.close();
return dataMat;
}
Mat DigitsDetector::ReadMnistLabel(const string pathName){
int magicNumber;
int labelNumber;
Mat labelMat;
ifstream file(pathName, ios::binary);
if (file.is_open() == true){
file.read((char*)&magicNumber, sizeof(magicNumber));
file.read((char*)&labelNumber, sizeof(labelNumber));
magicNumber = CvtToLittleEndian(magicNumber);
labelNumber = CvtToLittleEndian(labelNumber);
labelMat = Mat::zeros(labelNumber, 1, CV_32SC1);
for (int i = 0; i < labelNumber / 1; i++){
unsigned char temp = 0;
file.read((char*)&temp, sizeof(temp));
labelMat.at<unsigned int>(i, 0) = (unsigned int)temp;
}
}
file.close();
return labelMat;
}
int DigitsDetector::CvtToLittleEndian(int i){
unsigned char c1, c2, c3, c4;
c1 =i & 255;
c2 = (i >> 8) & 255;
c3 = (i >> 16) & 255;
c4 = (i >> 24) & 255;
return ((int)c1 << 24) + ((int)c2 << 16) + ((int)c3 << 8) + c4;
}
void DigitsDetector::CreateSVM(){
svm = SVM::create();
svm->setType(SVM::C_SVC);
svm->setKernel(SVM::RBF);
svm->setGamma(0.01);
svm->setC(10.0);
svm->setTermCriteria(TermCriteria(CV_TERMCRIT_EPS, 1000, FLT_EPSILON));
}
bool DigitsDetector::Train(Mat data, Mat label){
return svm->train(data, ROW_SAMPLE, label);
}
float DigitsDetector::Predict(Mat testdata){
return svm->predict(testdata);
}
实验效果
训练Mnist训练集过程
训练过程时间有些长,需要耐心等待。我的测试环境为:windows10+i7-6700+8GRAM,debug版本的训练时间大约10分钟,release版本大约5分钟,训练过程如下图:
测试Mnist测试集的过程和结果
测试10000张Mnist的测试集,也需要一小段时间,测试结果的准确率为98.33%,如下图。
测试现场手写数字
在画板上利用鼠标手写测试数字,点击“开始识别当前数字”识别,实验效果如下图:
总结
训练过程比较耗时,但是识别率比较高(相比于KNN好不少,KNN实现可以参考上一篇文章《KNN实现手写数字识别》)。和KNN的区别可以应用知乎上的一句神总结:svm, 就像是在河北和北京之间有一条边界线,如果一个人居住在北京一侧就预测为北京人,在河北一侧,就预测为河北人。但是住在河北的北京人和住在北京的河北人就会被误判。knn,就是物以类聚,人以群分。如果你的朋友里大部分是北京人,就预测你也是北京人。
附件
源代码工程戳这里(注:release里面的可执行程序可以直接运行)。
更多参考
SVM百度百科:https://baike.baidu.com/item/svm/4385807?fr=aladdin
SVM笔试题:https://blog.csdn.net/szlcw1/article/details/52259668
SVM最牛逼的解释:https://www.zhihu.com/question/21094489
SVM车牌识别:https://blog.csdn.net/u010429424/article/details/75335212
支持向量机通俗导论(理解SVM的三层境界):https://blog.csdn.net/v_july_v/article/details/7624837
opencv3-svm参数详解:http://livezingy.com/svm-in-opencv3-1/