朴素Bayes

基本思想

朴素贝叶斯法是基于贝叶斯定理与特征条件独立假设的分类方法。算法的基础是概率问题,分类原理是通过某对象的先验概率,利用贝叶斯公式计算出其后验概率,即该对象属于某一类的概率,选择具有最大后验概率的类作为该对象所属的类。朴素贝叶斯假设是约束性很强的假设,假设特征条件独立,但朴素贝叶斯算法简单,快速,具有较小的出错率。

算法流程

假设某个体有n项特征(Feature),分别为F1、F2、...、Fn。现有m个类别(Category),分别为C1、C2、...、Cm。贝叶斯分类器就是计算出概率最大的那个分类,也就是求下面这个算式的最大值:

P(C|F1F2...Fn) 
  = P(F1F2...Fn|C)P(C) / P(F1F2...Fn)

由于 P(F1F2...Fn) 对于所有的类别都是相同的,可以省略,问题就变成了求

P(F1F2...Fn|C)P(C)

的最大值。
朴素贝叶斯分类器则是更进一步,假设所有特征都彼此独立,因此

P(F1F2...Fn|C)P(C) 
  = P(F1|C)P(F2|C) ... P(Fn|C)P(C)

贝叶斯定理之所以有用,是因为我们在生活中经常遇到这种情况:我们可以很容易直接得出P(A|B),P(B|A)则很难直接得出,但我们更关心P(B|A),贝叶斯定理就为我们打通从P(A|B)获得P(B|A)的道路。



总结

Bayes模型假设属性之间相互独立,这个假设在实际应用中往往是不成立的,这给Bayes模型的正确分类带来了一定影响。在属性个数比较多或者属性之间相关性较大时,Bayes模型的分类效率比不上决策树模型。而在属性相关性较小时,Bayes模型的性能良好。

Bayes分类算法实现数字识别




/******************************************************************
*   函数名称:BayesErzhishuju()
*   函数类型:int 
*   函数功能:基于二值数据的Bayes分类器 ,返回手写数字的类别
******************************************************************/
int Classification::BayesErzhishuju()
{
	double Pw[10];//先验概率P(wj)=Nj/N
	double P[10][25];//Pj(wi)wi:wi类,j:第j个特征
	double PXw[10];//类条件概率P(X|wj)
	double PwX[10];//后验概率P(wj|X)

	int i,j;

	//求先验概率
	int n[10];//各类样品数
	int N=0;//样品总数
	for(i=0;i<10;i++)
	{
		//各数字类别样品数
		n[i]=pattern[i].number;
		N+=n[i];//样品总数
	}
	for(i=0;i<10;i++)
		Pw[i]=(double)n[i]/(double)N;//先验概率
	
	//求类条件概率
	for(i=0;i<10;i++)
	{
		for(j=0;j<25;j++)
		{
			int numof1=0;//二值数据中1的个数
			for(int k=0;k<pattern[i].number;k++)
				numof1+=pattern[i].feature[k][j]>0.1?1:0;
			P[i][j]=(double)(numof1+1)/(double)(n[i]+2);
		}
	}

	for(i=0;i<10;i++)
	{
		double p=1;
		for(int j=0;j<25;j++)
		{
			p*=(testsample[j]>0.1)?P[i][j]:1-P[i][j];
		}
		PXw[i]=p;
	}

	//求后验概率
	double PX=0.0,maxP=0.0;
	int number;
	for(i=0;i<10;i++)
	{
		PX+=Pw[i]*PXw[i];
	}

	for(i=0;i<10;i++)
	{
		PwX[i]=Pw[i]*PXw[i]/PX;
		if(PwX[i]>maxP)
		{
			maxP=PwX[i];
			number=i;
		}
	}
	return number;
}

/******************************************************************
*   函数名称:BayesLeasterror()
*   函数类型:int 
*   函数功能:最小错误概率的Bayes分类器 ,返回手写数字的类别
******************************************************************/
int Classification::BayesLeasterror()
{
	double X[25];//待测样品
	double Xmeans[25];//样品的均值
	double S[25][25];//协方差矩阵
	double S_[25][25];//S的逆矩阵
	double Pw;//先验概率
	double hx[10];//判别函数
	
	int i,j,k,n;
	
	for(n=0;n<10;n++)//循环类别0~9
	{
		int num=pattern[n].number;//样品个数
		//求样品平均值
		for(i=0;i<25;i++)
			Xmeans[i]=0.0;
		for(k=0;k<num;k++)
		{
			for(i=0;i<25;i++)
				Xmeans[i]+=pattern[n].feature[k][i]>0.10?1.0:0.0;
		}
		for(i=0;i<25;i++)
			Xmeans[i]/=(double)num;
		//求协方差矩阵
		double mode[200][25];
		for(i=0;i<num;i++)
			for(j=0;j<25;j++)
				mode[i][j]=pattern[n].feature[i][j]>0.10?1.0:0.0;
		for(i=0;i<25;i++)
		for(j=0;j<25;j++)
		{
			double s=0.0;
			for(k=0;k<num;k++)
				s=s+(mode[k][i]-Xmeans[i])*(mode[k][j]-Xmeans[j]);
			s=s/(double)(num-1);
			S[i][j]=s;
		}
		//求先验概率
		int total=0;
		for(i=0;i<10;i++)
			total+=pattern[i].number;
		Pw=(double)num/(double)total;
		//求S的逆矩阵
		for(i=0;i<25;i++)
			for(j=0;j<25;j++)
				S_[i][j]=S[i][j];

		double(*p)[25]=S_;

		brinv(*p,25);//S的逆矩阵
		//求S的行列式
		double (*pp)[25]=S;
		double DetS;
		DetS=bsdet(*pp,25);//S的行列式
		//求判别函数
		for(i=0;i<25;i++)
			X[i]=testsample[i]>0.10?1.0:0.0;
		for(i=0;i<25;i++)
			X[i]-=Xmeans[i];
		double t[25];
		for(i=0;i<25;i++)
			t[i]=0;
		brmul(X,S_,25,t);
		double t1=brmul(t,X,25);
		double t2=log(Pw);
		double t3=log(DetS+1);
		hx[n]=-t1/2+t2-t3/2;
	}

	double maxval=hx[0];
	int number=0;
	//判别函数的最大值
	for(n=1;n<10;n++)
	{
		if(hx[n]>maxval)
		{
			maxval=hx[n];
			number=n;
		}
	}
	return number;
}

/******************************************************************
*   函数名称:BayesLeastRisk(double loss[10][10])
*   函数类型:double*
*   参数说明:double loss[10][10]:损失
*   函数功能:最小风险的Bayes分类器 ,返回各类的风险值
******************************************************************/
double* Classification::BayesLeastRisk(double loss[10][10])
{
	double X[25];//待测样品
	double Xmeans[25];//样品的均值
	double S[25][25];//协方差矩阵S
	double S_[25][25];//S的逆矩阵
	double P[10];//后验概率
	double Pw;//先验概率
	double hx[10];//判别函数
	
	int i,j,k,n;
	
	for(n=0;n<10;n++)//
	{
		int num=pattern[n].number;//样品个数
		//求样品均值
		for(i=0;i<25;i++)
			Xmeans[i]=0.0;
		for(k=0;k<num;k++)
		{
			for(i=0;i<25;i++)
				Xmeans[i]+=pattern[n].feature[k][i]>0.2?1.0:0.0;
		}
		for(i=0;i<25;i++)
			Xmeans[i]/=(double)num;
		//求协方差矩阵
		double mode[200][25];
		for(i=0;i<num;i++)
			for(j=0;j<25;j++)
				mode[i][j]=pattern[n].feature[i][j]>0.2?1.0:0.0;
		for(i=0;i<25;i++)
		for(j=0;j<25;j++)
		{
			double s=0.0;
			for(k=0;k<num;k++)
				s=s+(mode[k][i]-Xmeans[i])*(mode[k][j]-Xmeans[j]);
			s=s/(double)(num-1);
			S[i][j]=s;
		}
		//求先验概率
		int total=0;//样品总数
		for(i=0;i<10;i++)
			total+=pattern[i].number;
		Pw=(double)num/(double)total;
		//求S的逆矩阵
		for(i=0;i<25;i++)
			for(j=0;j<25;j++)
				S_[i][j]=S[i][j];

		double(*p)[25]=S_;

		brinv(*p,25);//S的逆矩阵
		//求S的行列式
		double (*pp)[25]=S;
		double DetS;
		DetS=bsdet(*pp,25);//S的行列式
		//求判别函数
		for(i=0;i<25;i++)
			X[i]=testsample[i]>0.2?1.0:0.0;
		for(i=0;i<25;i++)
			X[i]-=Xmeans[i];
		double t[25];
		for(i=0;i<25;i++)
			t[i]=0;
		brmul(X,S_,25,t);
		double t1=brmul(t,X,25);
		double t2=log(Pw);
		double t3=log(DetS+1);
		P[n]=-t1/2+t2-t3/2;
	}
	
	for(n=0;n<10;n++)
	{
		double t=0.0;
		for(i=0;i<10;i++)
			t+=loss[n][i]*P[i];
		hx[n]=t;
	}
	
	return (double*)hx;
}


下面是朴素bayes做男女名字分类的例子
部分女性名字
Abagael
Abagail
Abbe
Abbey
Abbi
Abbie
Abby
Abigael
部分男性名字
Aamir
Aaron
Abbey
Abbie
Abbot
Abbott
Abby
Abdel

目标是给一个名字如Bob然后输出是男的名字还是女的名字。

#coding: utf-8
import math
import random
from os.path import*
def get_features(data, letters="abcdefghijklmnopqrstuvwxyz"):
        """Based on NLTK's names_demo_features."""
        name = data.lower()
        features = {}
        for letter in letters:
            features["startswith(%s)" % letter] = name[0] == letter
            features["endswith(%s)" % letter] = name[-1] == letter
            features["has(%s)" % letter] = letter in name
        features["length(%d)"%len(name)] = len(name)
        return features

#x=[a1,a2,...,am]为一个待分类项,而每个a为x的一个特征属性
#类别集合{y1,...,yn}

class NaiveBayes:
    """A naïve Bayes classifier."""
    def __init__(self):
        self.wordPro = {}
        self.dicCount = {}
        self.wordCount = {}
        self.attri = set()
        self.priorScore = {}
 
    #计算每个label的数目
    def getdicCount(self, label):
        if label in self.dicCount.keys():
            self.dicCount[label] = self.dicCount[label] + 1
        else:
            self.dicCount[label] = 1
            
    #统计(yi,aj)数目
    def calculateWordPro(self, label, feature):
        
        for f in feature.items():
            #pro = {[label, word], num}
            if (label, f) in self.attri:
                self.wordPro[(label, f)] = self.wordPro[(label, f)] + 1
            else:
                self.attri.add((label, f))
                self.wordPro[(label, f)] = 1
            
            if label in self.wordCount:
                self.wordCount[label] = self.wordCount[label] + 1
            else:
                self.wordCount[label] = 1
            
    #计算p(aj|yi)
    def getWordPro(self):
        for key in self.wordPro.keys():
            self.wordPro[key] = float(self.wordPro[key])/(self.wordCount[key[0]])
    #得到男、女名字的先验概率p(yi),这里取log
    def getPrior(self):
        for key in self.dicCount.keys():
            self.priorScore[key] = math.log(self.dicCount[key]/float(self.totalNum))

    def train(self, instances):
        #instance为元组,(label,name)
        self.totalNum = 0
        for instance in instances:
            self.totalNum =self.totalNum + 1        
            label = instance[0]
            self.getdicCount(label)                 
            feature = get_features(instance[1])           
            self.calculateWordPro(label, feature)        
        self.getPrior()                                
        self.getWordPro()                               
        self.sumValue = sum(self.wordCount.itervalues())     
        self.smooth = math.log((float(1))/self.sumValue)     
        print self.wordCount

    def classify(self, instance): 
        result = []
        #计算p(x|yi)*p(yi)
        for key in self.dicCount.keys():
            tempScore = self.priorScore[key]
            features=get_features(instance[1])
            for f in features.items():
                if (key, f) in self.attri:
                    tempScore = tempScore +math.log(self.wordPro[key, f]) 
                else:
                    tempScore = tempScore + self.smooth        #Laplace校准
            result.append((tempScore, key))
        #取最大作为分类类别
        return max(result)[1]

    def classify1(self, name): 
        result = []
        #计算p(x|yi)*p(yi)
        for key in self.dicCount.keys():
            tempScore = self.priorScore[key]
            features=get_features(name)
            for f in features.items():
                if (key, f) in self.attri:
                    tempScore = tempScore +math.log(self.wordPro[key, f]) 
                else:
                    tempScore = tempScore + self.smooth        #Laplace校准
            result.append((tempScore, key))
        #取最大作为分类类别
        return max(result)[1]

def accuracy(classifier, test):
    correct = [classifier.classify(x) == x[0] for x in test]
    return float(sum(correct)) / len(correct)


def load_data(namelist,datafile="names/*.txt"):
        label = splitext(basename(datafile))[0]
        with open(datafile, "r") as file:
            for line in file:
                data = line.strip()
                namelist.append((label,data))

def test_names_nltk(namelist):
        """Classify names using NLTK features"""
        print "\ntest_names_nltk"
        load_data(namelist,"names/male.txt")
        load_data(namelist,"names/female.txt")
        random.seed(hash("names"))
        random.shuffle(namelist)
        train, test = namelist[:6000], namelist[6000:]
        classifier = NaiveBayes()
        classifier.train(train)
        print accuracy(classifier, test)
        s=''
        while s!='q':
              s=raw_input("input a name \n")
              print classifier.classify1(s)
if __name__=="__main__":
   namelist=[]
   test_names_nltk(namelist)

训练完成你可以试着输入几个名字试试看,准确率还可以

参考:http://www.cnblogs.com/leoo2sk/archive/2010/09/17/naive-bayesian-classifier.html

朴素贝叶斯理论推导与三种常见模型 - wepon的专栏 - 博客频道 - youkuaiyun.com


贝叶斯学习及共轭先验




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值