贝叶斯分类器解读

来源:互联网 发布:淘宝买家v3会员是几钻 编辑:程序博客网 时间:2024/05/22 15:34

贝叶斯分类器主要是根据全概率公式、条件概率公式和统计信息而得出。

第一:统计信息得到结果概率;

第二:统计信息计算得到条件概率;

第三:根据统计信息、概率和条件概率计算判别概率。


在计算概率的过程中,可能会用到极大似然估计。朴素贝叶斯是在属性独立条件下进行计算的。

openCV中与贝叶斯分类器相关的API函数有以下几个:

(1)CvNormalBayesClassifier::CvNormalBayesClassifier();

该函数为默认构造函数;

(2)CvNormalBayesClassifier::CvNormalBayesClassifier(const Mat& trainData, const Mat& response, const Mat& varIdx=Mat(),const Mat& sampleIdx=Mat());

该函数实际是在默认构造函数内部调用train()函数进行分类器训练;

(3)bool CvNormalBayesClassifier::train(const Mat& trainData, const Mat& response, const Mat& varIdx=Mat(),const Mat& sampleIdx=Mat());

该函数进行贝叶斯分类器的训练,输入向量必须为行向量;变量response必须为整数,但其在初始化时类型可被设置为CV_32FC1;

所有特征向量必须完整,不允许训练样本集的某一个向量存在数据缺失;

(4)float CvNormalBayesClassifier::predict(const Mat& samples, Mat* result=0);

该函数根据用户输入的测试样本的特征向量,返回其所属的类别;注意,如果输入是很多个测试样本的特征向量组成的矩阵,返回值为result矩阵;

 

[cpp] view plaincopy
  1. //openCV中贝叶斯分类器的API函数用法举例  
  2. //运行环境:winXP + VS2008 + openCV2.3.0  
  3. #include "stdafx.h"  
  4. #include "opencv.hpp"  
  5. #include "iostream"  
  6. using namespace cv;  
  7. using namespace std;  
  8.   
  9. //10个样本特征向量维数为12的训练样本集,第一列为该样本的类别标签  
  10. double inputArr[10][13] =   
  11. {  
  12.     1,0.708333,1,1,-0.320755,-0.105023,-1,1,-0.419847,-1,-0.225806,0,1,   
  13.     -1,0.583333,-1,0.333333,-0.603774,1,-1,1,0.358779,-1,-0.483871,0,-1,  
  14.     1,0.166667,1,-0.333333,-0.433962,-0.383562,-1,-1,0.0687023,-1,-0.903226,-1,-1,  
  15.     -1,0.458333,1,1,-0.358491,-0.374429,-1,-1,-0.480916,1,-0.935484,0,-0.333333,  
  16.     -1,0.875,-1,-0.333333,-0.509434,-0.347032,-1,1,-0.236641,1,-0.935484,-1,-0.333333,  
  17.     -1,0.5,1,1,-0.509434,-0.767123,-1,-1,0.0534351,-1,-0.870968,-1,-1,  
  18.     1,0.125,1,0.333333,-0.320755,-0.406393,1,1,0.0839695,1,-0.806452,0,-0.333333,  
  19.     1,0.25,1,1,-0.698113,-0.484018,-1,1,0.0839695,1,-0.612903,0,-0.333333,  
  20.     1,0.291667,1,1,-0.132075,-0.237443,-1,1,0.51145,-1,-0.612903,0,0.333333,  
  21.     1,0.416667,-1,1,0.0566038,0.283105,-1,1,0.267176,-1,0.290323,0,1  
  22. };  
  23.   
  24. //一个测试样本的特征向量  
  25. double testArr[]=  
  26. {  
  27.     0.25,1,1,-0.226415,-0.506849,-1,-1,0.374046,-1,-0.83871,0,-1  
  28. };  
  29.   
  30.   
  31. int _tmain(int argc, _TCHAR* argv[])  
  32. {  
  33.     Mat trainData(10, 12, CV_32FC1);//构建训练样本的特征向量  
  34.     for (int i=0; i<10; i++)  
  35.     {  
  36.         for (int j=0; j<12; j++)  
  37.         {  
  38.             trainData.at<float>(i, j) = inputArr[i][j+1];  
  39.         }  
  40.     }  
  41.   
  42.     Mat trainResponse(10, 1, CV_32FC1);//构建训练样本的类别标签  
  43.     for (int i=0; i<10; i++)  
  44.     {  
  45.         trainResponse.at<float>(i, 0) = inputArr[i][0];  
  46.     }  
  47.   
  48.     CvNormalBayesClassifier nbc;  
  49.     bool trainFlag = nbc.train(trainData, trainResponse);//进行贝叶斯分类器训练  
  50.     if (trainFlag)  
  51.     {  
  52.         cout<<"train over..."<<endl;  
  53.         nbc.save("c:/normalBayes.txt");  
  54.     }  
  55.     else  
  56.     {  
  57.         cout<<"train error..."<<endl;  
  58.         system("pause");  
  59.         exit(-1);  
  60.     }  
  61.       
  62.   
  63.     CvNormalBayesClassifier testNbc;  
  64.     testNbc.load("c:/normalBayes.txt");  
  65.   
  66.     Mat testSample(1, 12, CV_32FC1);//构建测试样本  
  67.     for (int i=0; i<12; i++)  
  68.     {  
  69.         testSample.at<float>(0, i) = testArr[i];  
  70.     }  
  71.   
  72.     float flag = testNbc.predict(testSample);//进行测试  
  73.     cout<<"flag = "<<flag<<endl;  
  74.   
  75.     system("pause");  
  76.     return 0;  
  77. }  

贝叶斯定理由英国数学家托马斯.贝叶斯(Thomas Baves)在1763提出,因此得名贝叶斯定理。贝叶斯定理也称贝叶斯推理,是关于随机事件的条件概率的一则定理。

对于两个事件A和B,事件A发生则B也发生的概率记为P(B|A),事件B发生则A也发生的概率记为P(A|B),这样如果A发生B也必然发生或者B发生A也必然发生,则有P(B|A)=P(A|B)=1,这种情况是一种确定性推理。

更多的情况下,概率推理是不确定性推理,AB之间是一种不确定性概率关系,例如条件A发生时B会发生的概率计算公式为:


这种情况在现实中一般是容易推导计算出来的,或者根据先验知识是可以获取到的,例如如下两种情况:


1.条件A:一个人感冒了;条件B:这个人会发烧

2.条件A:一个人是女生;条件B:这个人会留长发


根据先验知识,条件B发生的概率是可以根据条件A推理得出的。在另一种更普遍的情况下,我们更加关心的是如果条件B发生,那么条件A发生的概率是多少?


1.条件B:一个人发烧了;条件A:这个人感冒了

2.条件B:一个人留长发;条件A:这个人是女生


这个问题称为“逆概率推理”,即已知A发生时B发生的概率,那么如果B已经发生,A发生的概率P(A|B)=?


贝叶斯公式


设有一个样本空间S,划分为B1,B2,....Bc一共c 类,或者成为c个事件,A为引发S中各个事件发生的事件,贝叶斯公式定义为:



其中P(Bi|A)称为后验概率,表示时间A发生后,各不相容的条件Bi发生的概率,它是在A的结果出现之后才能计算的,所以称为后验概率。

P(A|Bj)称为类条件概率,表示在各条件Bi存在时,事件A发生的概率。

P(Bj)称为先验概率,表示各不相容的条件Bi出现的概率,它与结果A是否出现无关,仅表示根据先验知识或者主观推断,认为总体上各个条件出现的可能性之间的差别。

贝叶斯是一位牧师,据不可靠消息,贝叶斯提出贝叶斯公式的目的是为了从统计理论上证明上帝的存在,尝试以一个科学的东西证明一个非科学的东西的科学性,这不科学。

朴素贝叶斯分类器(Naive Baves classifier)的“朴素”(Naive)之处在于,其假设了各个特征之间是独立的,简化了贝叶斯各个特征之间的相互关联,更易于推广应用。


以下代码是使用Opencv中正态贝叶斯Normal BavesClassifier 类建立的一个简单的病情诊断功能。假定以下是根据警院历来可靠数据获取的体温、咳嗽、流涕三个症状的严重程度判定的三类病情,分别是冷感冒、肺炎、热感冒。朴素贝叶斯根据这10组数据进行训练,然后输入一位病人的数据,自动进行诊断,以下是训练数据:




Opencv代码实现:


#include "opencv2/core/core.hpp"#include "opencv2/highgui/highgui.hpp"#include "opencv2/imgproc/imgproc.hpp"#include "opencv2/ml/ml.hpp"#include <iostream>using namespace cv;using namespace std;int main( int argc, char** argv ){   float trainingData[10][3] = { {34, 1, 1}, {35, 2, 2}, {36, 3,3},{37,8,4},{38,9,5},{39,10,6},{40,7,7},{41,4,8},{42,5,9},{43,6,10}};Mat trainingDataMat(10, 3, CV_32FC1, trainingData); float responses[10] = {1,1,1,2,2,2,3,3,3};Mat responsesMat(10, 1, CV_32FC1, responses);NormalBayesClassifier nbc;nbc.train(trainingDataMat, responsesMat); float myData[3] = {40, 8, 10}; //病人发烧、咳嗽、流涕数据Mat myDataMat(1, 3, CV_32FC1, myData);float r = nbc.predict( myDataMat );int result=r;string output;switch(result){case 1:output="冷感冒";break;case 2:output="肺炎";break;case 3:output="热感冒";break;default:output="健康";break;}cout<<endl<<"该病人诊断为:  "<<output<<endl<<endl;system("pause");return 0;}



输入病人的数据为:发烧-40、咳嗽-8、流涕-10,诊断结果为:



opencv3.0版本中,实现正态贝叶斯分类器(Normal Bayes Classifier)分类实例

复制代码
#include "stdafx.h"#include "opencv2/opencv.hpp"using namespace cv;using namespace cv::ml;int main(int, char**){    int width = 512, height = 512;    Mat image = Mat::zeros(height, width, CV_8UC3);  //创建窗口可视化    // 设置训练数据    int labels[10] = { 1, -1, 1, 1,-1,1,-1,1,-1,-1 };    Mat labelsMat(10, 1, CV_32SC1, labels);    float trainingData[10][2] = { { 501, 150 }, { 255, 10 }, { 501, 255 }, { 10, 501 }, { 25, 80 },    { 150, 300 }, { 77, 200 } , { 300, 300 } , { 45, 250 } , { 200, 200 } };    Mat trainingDataMat(10, 2, CV_32FC1, trainingData);    // 创建贝叶斯分类器    Ptr<NormalBayesClassifier> model=NormalBayesClassifier::create();        // 设置训练数据    Ptr<TrainData> tData =TrainData::create(trainingDataMat, ROW_SAMPLE, labelsMat);    //训练分类器    model->train(tData);    Vec3b green(0, 255, 0), blue(255, 0, 0);    // Show the decision regions given by the SVM    for (int i = 0; i < image.rows; ++i)    for (int j = 0; j < image.cols; ++j)    {        Mat sampleMat = (Mat_<float>(1, 2) << j, i);  //生成测试数据        float response = model->predict(sampleMat);  //进行预测,返回1或-1        if (response == 1)            image.at<Vec3b>(i, j) = green;        else if (response == -1)            image.at<Vec3b>(i, j) = blue;    }    // 显示训练数据    int thickness = -1;    int lineType = 8;    Scalar c1 = Scalar::all(0); //标记为1的显示成黑点    Scalar c2 = Scalar::all(255); //标记成-1的显示成白点    //绘图时,先宽后高,对应先列后行    for (int i = 0; i < labelsMat.rows; i++)    {        const float* v = trainingDataMat.ptr<float>(i); //取出每行的头指针        Point pt = Point((int)v[0], (int)v[1]);        if (labels[i] == 1)            circle(image, pt, 5, c1, thickness, lineType);         else            circle(image, pt, 5, c2, thickness, lineType);            }    imshow("normal Bayessian classifier Simple Example", image); // show it to the user    waitKey(0);}
复制代码

如果将数据换成是图片的像素值 ,则可实现图片的分类。

设训练数据集这里写图片描述这里写图片描述独立同分布产生。朴素贝叶斯法通过训练数据集学习联合概率密度函数这里写图片描述。具体地学习学习先验概率分布这里写图片描述和条件概率分布这里写图片描述,于是根据贝叶斯公式这里写图片描述就可以得到联合概率密度函数这里写图片描述了。从上面的描述中知道贝叶斯分类器要计算条件概率和先验概率来估计联合概率分布,属于产生式模型(Generative models )。产生式模型可以根据联合分布生成条件分布,比如,可以让机器生成不同场景、物体并让物体交互(感觉用到游戏中就可以避免单调的游戏背景了)。而在对数据建模完成后,就可以对数据分类了,根据公式

-这里写图片描述(1)

确定类别,而因为 
这里写图片描述,因此贝叶斯分类器选取联合概率最大的类别作为样本的类别。

opencv2.4.9中的贝叶斯

opencv2.4.9中实现的贝叶斯分类器是正态贝叶斯分类器(opencv中文版中也称它为朴素贝叶斯分类器),实现过程在CvNormalBayesClassifier类中。机器学习中文参考手册中有以下描述: 
Normal Bayes 这个简单的分类器模型是建立在每一个类别的特征向量服从正态分布的基础上的(尽管,不必是独立的),因此,整个分布函数被假设为一个高斯分布,每一类别一组系数。当给定了训练数据,算法将会估计每一个类别的向量均值和方差矩阵,然后根据这些进行预测。 
简言之,正态贝叶斯分类器假设条件概率分布函数这里写图片描述服从正态分布,分布函数是高斯函数,通过计算样本的均值和方差,从而来计算各个类别的概率。高斯分布函数数学表达式如下: 
这里写图片描述 
它简单是因为它假设所有的特征之间相互独立,而这在现实中很少见(如,找到一只眼睛常常意味着另一只眼睛在附近)。 正态贝叶斯分类器不能处理回归问题,但是它能有效地处理多类问题,而不仅仅是两类问题。这个分类器是当前快速发展的贝叶斯网络(也叫概率图模型)的最简单情况。

代码分析

关于参数的较详细的说明可以参考:http://blog.csdn.net/godenlove007/article/details/8913007。 
从数学的角度来解释下代码,则可以知道代码所做的事情就是计算如下等式。 
train函数计算:

这里写图片描述
predict函数计算: 
这里写图片描述
至于为什么会跟公式(1)有些出入,请参考《opencv2.4.9中贝叶斯分类器理解(二)》中的推导.

使用方法

这里仍然使用预测蘑菇是否有毒的测试数据,opencv中给出的例子是mushroom.cpp用的是决策树分类器,数据文件是agaricus-lepiota.data。 
贝叶斯分类器的使用代码如下:

int main(int argc, char** argv){    CvMat *data = 0, *missing = 0, *responses = 0;    const char* base_path = argc >= 2 ? argv[1] : "C:/opencv2.4.9/sources/samples/c/agaricus-lepiota.data";    if (!mushroom_read_database(base_path, &data, &missing, &responses))    {        printf("\nUnable to load the training database\n\n");        return -1;    }    Mat X = Mat(data, true);    X.convertTo(X, CV_32FC1);    Mat Y = Mat(responses, true);    Y.convertTo(Y, CV_32FC1);    cvReleaseMat(&data);    cvReleaseMat(&missing);//missing is not used in this program.    cvReleaseMat(&responses);    int SAMPLES = X.rows;    float SPLIT = 0.8f;    Mat X_train = X(Range(0, (int)(SAMPLES*SPLIT)), Range::all());    Mat Y_train = Y(Range(0, (int)(SAMPLES*SPLIT)), Range::all());    Mat X_test = X(Range((int)(SAMPLES*SPLIT), SAMPLES), Range::all());    Mat Y_test = Y(Range((int)(SAMPLES*SPLIT), SAMPLES), Range::all());    CvNormalBayesClassifier *bayes = new CvNormalBayesClassifier;    bayes->train(X_train, Y_train, Mat(), Mat(), false);    // perform classifier testing and report results    Mat result(Y_test.rows, Y_test.cols, CV_32F);    bayes->predict(X_test, &result);    int err = 0;    for (int i = 0; i < X_test.rows; i++){        int trueRes = *(Y_test.data + i);        float predictRes = *(result.data + i);        if (fabs(trueRes - predictRes) >= FLT_EPSILON){            err++;        }    }    cout <<"error number="<< err << " " <<"precision="<< 100-err*1.f / Y_test.rows * 100 << "%\n";    system("pause");    return 0;}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45

我得到的结果是: 
error number=3 precision=99.8154%

参考:连接

阅读全文
0 0