OpenCV機器學習——樸素貝葉斯NBC

OpenCV中集成了多種機器學習演算法供我們方便使用,如果我們要訓練數據進行分類,不用自己寫分類器,只需要調用相應的庫和類即可輕鬆實現。本文重點不在於介紹機器學習原理及數學推導,著重介紹OpenCV中的機器學習相關函數,並且用十分簡單的訓練數據作為例子實現分類。

對於OpenCV的機器學習分類器,大多換湯不換藥,構造方法和實現方法很類似,基本遵循原始數據—訓練分類器—進行分類的步驟,某些演算法可能有特殊的初始化參數,需要額外設置

簡單說一下今天的主角——樸素貝葉斯分類器。樸素貝葉斯分類器英文為Na?ve Bayesian Classifier,簡稱NBC,從名稱就可以看出來,這個分類器與概率學有很緊密的關聯。NBC在訓練時是使用全概率公式和貝葉斯公式為原理,分類時是建立若干高斯分布並使用最優化分類。樸素貝葉斯正如其名,「樸素」是其重要的特點:原理簡單易理解,無多餘參數設置

在實現任何分類器之前,都需要訓練數據。插句題外話,訓練數據的好壞是一個分類器成功與否的決定性條件,數據選取永遠凌駕於分類器演算法選取之上,如果訓練數據選取得當,無論使用任何演算法都會得到不錯的效果,反之如果訓練數據選取不當,分類演算法是無法彌補的。在此我們使用簡單的二維數據作為訓練數據,其標號分別為1和-1,我們用圖像來直觀的表示:

t//設定800*800的二維坐標平面區域ntint width = 800, height = 800;ntMat I = Mat::zeros(height, width, CV_8UC3);nnt//訓練數據集,前10個標記為1,後10個標記為-1ntfloat trainingData[20][2] =nt{ { 100, 100 }, { 200, 100 }, { 400, 100 }, { 200, 200 }, { 500, 200 },nt{ 100, 300 }, { 300, 300 }, { 400, 300 }, { 100, 400 }, { 200, 500 },nt{ 600, 600 }, { 700, 300 }, { 700, 300 }, { 400, 500 }, { 600, 500 },nt{ 200, 700 }, { 300, 600 }, { 500, 600 }, { 600, 300 }, { 400, 700 } };nt//訓練數據集存入矩陣ntMat trainingDataMat(20, 2, CV_32FC1, trainingData);nnt//訓練數據標記,前10個標記為1,後10個標記為-1ntfloat labels[20] =nt{ 1.0, 1.0, 1.0, 1.0, 1.0,nt1.0, 1.0, 1.0, 1.0, 1.0,nt-1.0, -1.0, -1.0, -1.0, -1.0,nt-1.0, -1.0, -1.0, -1.0, -1.0 };nt//訓練數據標記存入矩陣ntMat labelsMat(20, 1, CV_32FC1, labels);nnt//將訓練數據用不同顏色畫出:1為綠色,-1為藍色ntfor (int i = 0; i < 20; i++)nt{nttif (labels[i] == 1.0)ntttcircle(I, Point(trainingData[i][0], trainingData[i][1]), 2, Scalar(255, 0, 0), 2);nttelse ntttcircle(I, Point(trainingData[i][0], trainingData[i][1]), 2, Scalar(0, 255, 0), 2);nt}ntimshow("dataset", I);n

注意訓練數據集矩陣類型一定是CV_32FC1型,長寬分別為數據個數和維度(20個訓練數據,2維);訓練數據標記矩陣是一維向量,也建議使用CV_32FC1型,還可用CV_32SC1型,長度為數據個數,要和訓練數據一一對應(如例子中前10個數據標記為1,後10個數據標記為-1)

接下來是NBC訓練了,由於類初始化需要CvMat*數據類型,建議初始化一個空類,需要什麼參數用函數添加,具體如下:

CvNormalBayesClassifier NBC;nNBC.train(trainingDataMat, labelsMat, Mat(), Mat());n

NBC.train即為訓練函數,其參數為

bool train( const cv::Mat& trainData, const cv::Mat& responses,nconst cv::Mat& varIdx = cv::Mat(), const cv::Mat& sampleIdx=cv::Mat(),nbool update=false );n

const cv::Mat& trainData:訓練數據集,前文設定20*2的Mat trainingDataMat,再次提醒格式一定是CV_32FC1

const cv::Mat& responses:響應數據,即前文的訓練數據標記,20*1的向量Mat labelsMat,格式最好也是CV_32FC1

const cv::Mat& varIdx=cv::Mat(), constncv::Mat& sampleIdx=cv::Mat():兩個參數表示感興趣的特徵和樣本,如沒有感興趣對象則設為空矩陣Mat()即可

運行完train後,樣本訓練過程結束,可用NBC.predict()函數進行分類:

float predict( const cv::Mat& samples, CV_OUT cv::Mat* results=0 )n

函數作用:判斷sample的類別

參數const Mat& sample:待分類向量,文中訓練數據是二維數據,因此待分類向量應是1*2的Mat矩陣,數據類型應為float型(CV_32F)

參數bool returnDFVal=false:判斷是否為二分類器,通常情況下不用設定,默認false即可

返回值:const Mat& sample的分類結果,文中返回值應為前文設定的訓練數據標記種類1或-1

簡單例子:

float temp[2] = { i, j };nMat sampleMat(1, 2, CV_32F, temp);nfloat response = SVM.predict(sampleMat);n

NBC函數大體如此,完整代碼及注釋:

#include <iostream>n#include <opencv.hpp>nusing namespace std;nusing namespace cv;nnvoid main()n{nt//設定800*800的二維坐標平面區域ntint width = 800, height = 800;ntMat I = Mat::zeros(height, width, CV_8UC3);nnt//訓練數據集,前10個標記為1,後10個標記為-1ntfloat trainingData[20][2] =nt{ { 100, 100 }, { 200, 100 }, { 400, 100 }, { 200, 200 }, { 500, 200 },nt{ 100, 300 }, { 300, 300 }, { 400, 300 }, { 100, 400 }, { 200, 500 },nt{ 600, 600 }, { 700, 300 }, { 700, 300 }, { 400, 500 }, { 600, 500 },nt{ 200, 700 }, { 300, 600 }, { 500, 600 }, { 600, 300 }, { 400, 700 } };nt//訓練數據集存入矩陣ntMat trainingDataMat(20, 2, CV_32FC1, trainingData);nnt//訓練數據標記,前10個標記為1,後10個標記為-1ntfloat labels[20] =nt{ 1.0, 1.0, 1.0, 1.0, 1.0,nt1.0, 1.0, 1.0, 1.0, 1.0,nt-1.0, -1.0, -1.0, -1.0, -1.0,nt-1.0, -1.0, -1.0, -1.0, -1.0 };nt//訓練數據標記存入矩陣ntMat labelsMat(20, 1, CV_32FC1, labels);nnt//將訓練數據用不同顏色畫出:1為綠色,-1為藍色ntfor (int i = 0; i < 20; i++)nt{nttif (labels[i] == 1.0)ntttcircle(I, Point(trainingData[i][0], trainingData[i][1]), 2, Scalar(255, 0, 0), 2);nttelsentttcircle(I, Point(trainingData[i][0], trainingData[i][1]), 2, Scalar(0, 255, 0), 2);nt}ntimshow("dataset", I);nnt//NBC訓練ntCvNormalBayesClassifier NBC;ntNBC.train(trainingDataMat, labelsMat, Mat(), Mat());nnt//NBC分類結果顯示:1區域為綠色,-1區域為藍色ntfor (int i = 0; i < I.rows; ++i)ntfor (int j = 0; j < I.cols; ++j)nt{nttfloat temp[2] = { i, j };nttMat sampleMat(1, 2, CV_32FC1, temp);nttfloat response = NBC.predict(sampleMat);nnttif (response == 1)ntttI.at<Vec3b>(j, i) = Vec3b(255, 0, 0);nttelse if (response == -1)ntttI.at<Vec3b>(j, i) = Vec3b(0, 255, 0);nt}ntfor (int i = 0; i < 20; i++)nt{nttif (labels[i] == 1.0)ntttcircle(I, Point(trainingData[i][0], trainingData[i][1]), 2, Scalar(255, 255, 255), 2);nttelsentttcircle(I, Point(trainingData[i][0], trainingData[i][1]), 2, Scalar(0, 0, 0), 2);nt}nntimshow("result", I);ntwaitKey();n}n

分類結果:

當然以上只是簡單的分類器基本介紹及一個十分簡單的分類訓練的例子,並不能概述機器學習全部過程,在分類訓練之後還需要validation set和testing set評價分類器好壞,當然這屬於機器學習理論知識,本文主要介紹簡單的機器學習分類器訓練演算法函數,在此就不對機器學習原理多做介紹了

推薦閱讀:

元旦贈書 | 18本紙質書:OpenCV、Python和機器學習,總有一本適合你
OpenCV 與 OpenGL 的關係是什麼?
用OpenCV人臉檢測,出現這個錯誤,大神賜教?

TAG:OpenCV | 计算机技术 | 机器学习 |