SVM實現手寫數字識別
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/