分享

贝叶斯分类器、算法部分源代码

 昵称12110172 2014-06-19

[转]贝叶斯分类器、算法部分源代码

转自星海流浪者&博客

在具有模式的完整统计知识条件下,按照贝叶斯决策理论进行设计的一种最优分类器。分类器是对每一个输入模式赋予一个类别名称的软件或硬件装置,而贝叶斯分类器是各种分类器中分类错误概率最小或者在预先给定代价的情况下平均风险最小的分类器。它的设计方法是一种最基本的统计分类方法。 
  最小错误概率贝叶斯分类器  。把代表模式的特征向量x分到c个类别(ω1,ω2,...,ωc)中某一类的最基本方法是计算在 x的条件下,该模式属于各类的概率,用符号P(ω1|x),P(ω2|x),...,P(ωc|x)表示。比较这些条件概率,最大数值所对应的类别ωi就是该模式所属的类。例如表示某个待查细胞的特征向量 x属于正常细胞类的概率是0.2,属于癌变细胞类的概率是0.8,就把它归类为癌变细胞。上述定义的条件概率也称为后验概率,在特征向量为一维的情况下,一般有图中的变化关系。当 x=x*时,P(ω1|x)=P(ω2|x)对于 xx*的区域,由于P(ω2|x)>P(ω1|x)因此xω2类,对于x<x*的区域,由于P(ω1|x)>P(ω2|x),xω1类,x*就相当于区域的分界点。图中的阴影面积就反映了这种方法的错误分类概率,对于以任何其他的 x值作为区域分界点的分类方法都对应一个更大的阴影面积,因此贝叶斯分类器是一种最小错误概率的分类器

图

  一般情况下,不能直接得到后验概率而是要通过贝叶斯公式

进行计算。式中的P(xωi)为在模式属于ωi类的条件下出现x的概率密度,称为x的类条件概率密度;P(ωi)为在所研究的识别问题中出现ωi类的概率,又称先验概率;P(x)是特征向量x的概率密度。分类器在比较后验概率时,对于确定的输入x,P(x)是常数,因此在实际应用中,通常不是直接用后验概率作为分类器的判决函数gi(x)(见线性判别函数)而采用下面两种形式:

对所有的c个类计算gi(x)(i=1,2,...,c)。与gi(x)中最大值相对应的类别就是x的所属类别。 
  最小风险贝叶斯分类器  由于客观事物的复杂性,分类器作出各种判决时的风险是不一样的。例如将癌细胞误判为正常细胞的风险就比将正常细胞误判为癌细胞的风险大。因此,在贝叶斯分类器中引入了风险的概念。在实际应用中根据具体情况决定各种风险的大小,通常用一组系数Cij来表示。Cij表示分类器将被识别样本分类为ωi,而该样本的真正类别为ωj时的风险。设计最小风险分类器的基本思想是用后验概率计算将 x分类为ωi的条件风险

比较各Ri(x)的大小,与最小值对应的类别是分类的结果。评价这种分类器的标准是平均风险,它的平均风险最小。在实际应用时,后验概率是难以获得的,根据模式类别的多少和Cij的取值方式,可设计出各种分类器,例如模式为两类时,判别函数为

如果选择C11C22为零,C12C21为1,它就是两类最小错误概率分类器。实际上,最小错误概率分类器是最小风险分类器的一种特殊情况。 
  设计贝叶斯分类器的关键是要知道样本特征 x的各种概率密度函数。条件概率密度函数为多元正态分布是研究得最多的分布。这是由于它的数学表达式易于分析,在实际应用中也是一种常见的分布形式。经常使用参数方法来设计正态分布的判别函数。 
  参考书目 
 福永圭之介著,陶笃纯译:《统计图形识别导论》,科学出版社,北京,1978。

/********************************源代码*******************************/

//贝叶斯分类器所需函数的声明:2006/11/13
#ifndef _BAYES_H
#define _BAYES_H
#include"matrix.h"
//正态分布的监督参数估计:最大似然估计
//此函数用于求样本的均值向量U
//参数 X 代表一类样本集,X 是一个 n x d 的矩阵
//代表 n 个 特征空间维数为 d 的样本
//每行代表一个样本
Matrix getU(const Matrix &X);

//正态分布的监督参数估计:最大似然估计
//此函数用于求样本的协方差矩阵E
//参数 X 代表一类样本集,X 是一个 n x d 的矩阵
//代表 n 个 特征空间维数为 d 的样本
//每行代表一个样本
//参数 U 是该样本的均值向量,可用上面的 getU() 函数求得
Matrix getE(const Matrix &X, const Matrix &U);

//多元正态概率型下的最小错误率贝叶斯判别函数
//U[i]   为每个类的均值向量
//E[i]   为每个类的协方差矩阵
//Pw[i] 为某类样本的先验概率
//X    为要判别的样本
//其返回值为样本类别的代号,范围是 1, 2, ..., c,c为类别数
int bayesFun(const Matrix U[], const Matrix E[], 
        const double Pw[],const Matrix &X, int c);

//贝叶斯分类器
//X[c] 为总体样本集,每个数组元素为一个类的样本集
//X 为要判别的样本
//c:类别数
void bayesDepart(const Matrix X[], const double Pw[], 
         const Matrix &x, int c, char* name[]);
   
//下面是此贝叶斯分类器需要给定的参数       
//这里只作声明,相应的定义在bayesapp.cpp中
extern int c;      //类别数
extern char* name[];   //类别名称
extern double Pw[];    //每类样本的先验概率
extern Matrix X[];   //总体样本集,数组中的每个矩阵代表一类样本集,
            //矩阵中的每一行代表此类样本集的一个样本
#endif

//贝叶斯分类器中相应函数的实现:2006/11/13
#include<iostream.h>
#include<math.h>
#include"bayes.h"
#include"matrix.h"
#include<stdlib.h>

//计算均值向量U
Matrix getU(const Matrix &X)
{
int d = X.getCol();
int n = X.getRow();
Matrix U(d, 1);        //U=(u1,u2,...,ud)^T,
                //用 d x 1 的矩阵表示
for(int k=0; k<n; k++)    //k 代表第 k 个样本
   for(int j=0; j<d; j++)   //j 代表样本的第 j 个特征值
    U.set(j, 0, U.get(j,0)+X.get(k,j));
    //这里要注意对矩阵元素的访问下标是从 0 开始的
return 1.0/n * U; //U 是所有样本的均值,故加和后除以样本总数
}

//计算协方差矩阵E
Matrix getE(const Matrix &X, const Matrix &U)
{
int d = X.getCol(); //X 的列数代表特征空间维数
int n = X.getRow(); //X 的每一行代表一个样本
Matrix E(d, d);         //E 是 d x d 维矩阵,d 为每个样本特征空间数
                 //故此处可以用 X 列数来初始化它
double *ar = new double[d];   //此数组用于从矩阵中提取行,创建新矩阵
                 //从而可以进行下面的矩阵运算
for(int k=0; k<n; k++){     //k 代表第 k 个样本
   for(int j=0; j<d; j++)
    ar[j] = X.get(k, j);    //将 X 的一行元素值放入数组 ar 中

   Matrix Xk(d, 1, ar);     //利用 ar 创建矩阵
   E = E + (Xk - U) * trv(Xk - U); //(X-U) * (X-U)^T
}
delete[] ar; //释放动态分配的数组空间
//E 是 n 个矩阵( (Xk - U)与其转置的乘积 )的算术平均
return 1.0/n * E;
}

//贝叶斯判别函数
int bayesFun(const Matrix U[], const Matrix E[],
        const double Pw[],const Matrix &X, int c)
{
double testPw = 0;
for(int w=0; w<c; w++){
   if(Pw[w] <= 0){
    cout << "Wrong Pw[i]\n"; exit(0); }
   testPw += Pw[w];
   if(det(E[w]) == 0){
    cout << "Check input, E[" << w << "]=0!\n";
    exit(0); }
}
if(testPw != 1){
   cout << "Wrong Pw[i]\n"; exit(0); }

//对于贝叶斯判别函数中的 -d/2 * ln(2*PI)项因与 i 无关故可去掉
//另外(X-U)(E^-1)(X-U)^T 的计算结果虽然是一个数,但在这里
//其表示为一个一行一列的矩阵,因此需要对其求行列式值后才能
//赋给左边的 double maxg
double maxg = -1.0/2 * det( trv(X-U[0]) * inv(E[0]) * (X-U[0]) )
         -1.0/2 * log(det(E[0])) + log(Pw[0]);
int code = 0; //用来返回类别代号

//求使得贝叶斯判别函数取得最大值类
for(int i=1; i<c; i++){
   double g =   -1.0/2 * det( trv(X-U[i]) * inv(E[i]) * (X-U[i]) )
         -1.0/2 * log(det(E[i])) + log(Pw[i]);
   if(g > maxg){
    maxg = g;
    code = i;
   }
}//for i

return code;
}//bayesFun()

//贝叶斯分类器
void bayesDepart(const Matrix X[], const double Pw[],
         const Matrix &x, int c, char* name[])
{
Matrix* U = new Matrix[c];
Matrix* E = new Matrix[c];

//先求出每个类的均值向量和协方差矩阵
for(int i=0; i<c; i++){
   U[i] = getU(X[i]);
   E[i] = getE(X[i], U[i]);
}
//调用贝叶斯判别函数,判断X所属类别,输出类别名称
cout << name[bayesFun(U,E,Pw,x,c)];
}

//贝叶斯分类器:2006/11/13
#include<iostream.h>
#include<conio.h>
#include"bayes.h"

//这个宏仅为了少写些代码
#define test(arx)                     \
{                                     \
for(int i=0; i<25; i++){            \
   Matrix x(d,1,arx[i]);             \
   bayesDepart(X, Pw, x, c, name);   \
   cout << ' ';                      \
   if((i+1)%5 == 0) cout << endl;    \
}                                   \
   cout << "--------------\n";       \
}                                     \

void main()
{
cout << "\n==============\n";
//样本集分为 2 个类别,特征空间维数为 2,每个类别 25 个样本
const int c = 2, d = 2, n = 25;
//类别名称分别为 w1 和 w2
char* name[c] = {"w1", "w2"};
//两个类别的先验概率,可以给予不同的值,观察结果变化
double   Pw[c] = { 0.5, 0.5 };
//数组 X[i]为 i 类别的样本集,X 为总样本集
Matrix* X = new Matrix[c];

//类别 w1 的样本数据,
//特征值 1 集中在 4|5附近,特征值 2 集中在 13 附近
double ar0[] = {
    1,11.45, 2,12.46, 3,13.78, 3,13.62, 4,13.84,
    4,11.26, 4,15.28, 4,17.29, 4,21.36, 5,15.46,
    5,13.63, 5,13.78, 5,12.29, 5,14.55, 5,21.73,
    5,14.12, 5,15.34, 6,13.66, 6,13.21, 6,14.02,
    7,12.16, 7,19.88, 7,10.21, 8,12.29, 9,15.52 };
//类别 w2 的样本数据
//特征值 1 集中在 7|8附近,特征值 2 集中在 19 附近  
double ar1[] = {
    1,19.76, 2,21.43, 3,18.79, 4,19.84, 5,18.75,
    5,21.45, 5,18.78, 6,19.72, 7,19.04, 7,21.27,
    7,20.01, 7,18.99, 7,19.67, 8,17.98, 8,14.26,
    8,18.99, 8,15.67, 8,22.01, 8,21.91, 8,19.03,
    9,18.97, 9,18.92, 9,19.34, 9,19.28, 9,17.63 };
//将两个类别的样本数组构造为矩阵  
Matrix X0(n,d,ar0);
Matrix X1(n,d,ar1);
//放入总样本集中便于下面的参数传递
X[0] = X0; 
X[1] = X1;

   //测试样本数据,其中前两个与原样本相同,第三个为随机输入
   //可以改变第三个样本中的数据值,观察结果变化
  
   double arx0[25][2] = {
    1,11.45, 2,12.46, 3,13.78, 3,13.62, 4,13.84,
    4,11.26, 4,15.28, 4,17.29, 4,21.36, 5,15.46,
    5,13.63, 5,13.78, 5,12.29, 5,14.55, 5,21.73,
    5,14.12, 5,15.34, 6,17.66, 6,13.21, 6,14.02,
    7,12.16, 7,19.88, 8,10.21, 9,12.29, 10,15.52 };
double arx1[25][2] = {
    1,19.76, 2,21.43, 3,18.79, 4,19.84, 5,18.75,
    5,21.45, 5,18.78, 6,19.72, 7,19.04, 7,21.27,
    7,20.01, 7,18.99, 7,19.67, 8,17.98, 8,14.26,
    8,18.99, 8,15.67, 8,22.01, 8,21.91, 8,19.03,
    9,18.97, 9,18.92, 10,19.34,10,19.28,11,17.63 };
double arx2[25][2] = {
    3,14.56, 5,12.35, 7,19.89, 8,25.37, 11,26.10,
    3,14.29, 6,29.28, 5,16.19, 5,21.19, 8,27.19,
    8,18.28, 7,17.15, 6,14.12, 6,14.13, 1,25.10,
    5,16.29, 6,19.80, 10,19.34, 8,15.09,6,17.20,
    6,12.20, 8,19.21, 2,19.29, 5,16.10, 7,18.23 };
test(arx0);
test(arx1);
test(arx2);

//试着输入与(4|5,13)接近的样本,看结果是否为 w1
//试着输入与(7|8,19)接近的样本,看结果是否为 w2
//改变先验概率,看结果是否发生改变
double testAr[2];
char ch = ' ';
while(ch != 'q'){
   if(ch == 'p'){
    cout << "Please enter Pw[1] and Pw[2]:\n";
    cin   >> Pw[0] >> Pw[1];
   }
   cout << "Please enter what you want test :\n";
   cin   >> testAr[0] >> testAr[1];

   Matrix x(d,1,testAr);
   bayesDepart(X, Pw, x, c, name);

   cout << "\nPress any key to continue\n";
   cout << "q to exit\n";
   cout << "p to change Pw[i]\n";
   ch = getch();
}
}

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多