朴素贝叶斯算法

来源:互联网 发布:日本好听的歌 知乎 编辑:程序博客网 时间:2024/06/06 20:36

1、从贝叶斯到朴素贝叶斯

  贝叶斯公式如下:

                        

    通过先验概率求后验概率 

    P(A)被称为先验概率,是已经给出的或者通过现有数据统计可以求出的,对A出现概率的一个大胆估计。P(B|A)/P(B)可以理解为一个实验,即满足某种现实状况,是对这个贝叶斯估计的一个修正因子。P(A|B)被称为后验概率,即满足某种事实条件的概率。


    对应到机器学习中,B即为特征X,A为需要判断的目标类别Y,将其展开就是如下公式:


    右侧的第一项先验概率容易通过对原式数据统计得到,但是条件概率却不容易得到,如果每一个特征的取值个数为sj个,Y的取值有K个,那么参数的个数为,这样会导致参数急剧增多,在实际中不可行,例如现在有20个特征,每个特征取值有10中可能,10^20,如果真要训练这么个模型,那要爆炸了,更何况工程应用中,高维度数据很普遍。因此,在原来的基础上做了一个比较强的假设,即条件特征独立假设----在Y的某一个取值情况下,特征的各维度概率互相对立。如下:


    因为现实生活中的数据,无法保证特征之间是完成独立的,因此,朴素贝叶斯会丢失部分信息,即特征间的相关性,朴素贝叶斯是一个低方差的模型。对于高维数据,因为条件独立假设会丢失大量信息,导致准确率欠缺,因此朴素贝叶斯不适合应用于高维数据集。但在小量数据集上,朴素贝叶斯通常表现出的性能不错。


    朴素贝叶斯算法通过求在给定特征条件下分类的后验概率最大的类作为预测的结果,如下所示:


又因为分母位置对于所有的y取值相同,所以为了方便运算,可以省略简化如下。


在实际应用中,乘法的运算比较消耗计算资源,同时小数连乘,容易发生下溢出,一般会采取取负对数:


在训练数据时保存先验概率和条件概率的负对数,在预测时通过求累加和求解,这样计算速度更快。


2、模型 的训练

    通过上述的模型可以看出,模型的训练需要求出两类概率,一类为分类的先验概率,一类为特征的条件概率。

    先验概率的通过极大似然估计求得,如下:

                                

    条件概率的公式如下:

   

    这种方法存在一个问题,因为数据集只是一个样本,所以无法保证每一个特征的每一个取值都会出现,这时就会出现概率为0的情况,影响后验概率的计算。为了解决这个问题,采用贝叶斯估计,即给每一个特征的每一个取值出现次数预设一个比较小的数,避免概率为0。公式如下所示:


    当λ=1,这时称为拉普拉斯光滑,也可以对先验概率进行同样的处理。


3、代码实现:

#_*_encoding:utf-8_*_import numpy as npclass NB:    def __init__(self,lam=1):        self.lam=lam    def fit(self,trainX,trainY):        self.m,self.n=np.shape(trainX)        self.calPreRatio(trainY)        self.calConditionProb(trainX,trainY)    def calPreRatio(self,trainY):            #计算先验概率        self.YCount={}        for i in trainY:            self.YCount[i]=self.YCount.get(i,0)+1        k=len(self.YCount)        self.YP={}        for key in self.YCount.keys():            self.YP[key]=-np.log( (self.YCount.get(key)+self.lam)*1.0/(self.m+self.lam*k))    def calConditionProb(self,trainX,trainY):   #计算条件概率        self.CP={}        for ck in self.YP.keys():            self.CP[ck]=self.calOneClassP(trainX,trainY,ck)            print ck,self.CP[ck]    def calOneClassP(self,trainX,trainY,ck):  #某一个类别各个特征的条件概率        ckDict={}        for i in range(self.n):            ckDict[i]=self.calOneCP(trainX[:,i],trainY,ck)        return ckDict    def calOneCP(self,trainX,trainY,ck):    #某一个类别的某一个特征各取值对应的条件概率        dict={}        for i in range(self.m):            if trainY[i]==ck:                dict[trainX[i]]=dict.get(trainX[i],0)+1        l=len(dict)        if l==1:                        #这里采用的数据每个特征取值为0、1 只是一个特殊处理            if dict.keys()[0]==0:                dict[1]=0            else:                dict[0]=0        for key in dict.keys():            dict[key]=-np.log((dict.get(key)+self.lam)*1.0/(self.YCount.get(ck)+self.lam*l))        return dict    def predict(self,testX):           #预测端口        results=[]        for i in range(np.shape(testX)[0]):            results.append(self.calBP(testX[i]))        return results    def calBP(self,feats):             #计算测试样本的类别        min=np.inf        result=0        for ck in self.YP.keys():            sum=self.YP.get(ck)            for i in range(self.n):                sum+=self.CP.get(ck).get(i).get(feats[i])            if sum<min:                min=sum                result=ck        return resultdef loadDataSet(fileName):    file=open(fileName)    trainX=[]    trainY=[]    for line in file.readlines():        currLine=line.strip().split('\t')        feats=[]        for feat in currLine[:-1]:            feats.append(feat)        trainX.append(feats)        trainY.append(currLine[-1])    return trainX,trainYdef img2vector(filename):    returnVect = np.zeros((1,1024))    fr = open(filename)    for i in range(32):        lineStr = fr.readline()        for j in range(32):            returnVect[0,32*i+j] = int(lineStr[j])    return returnVectdef loadDigits(dirName):    from os import listdir    hwLabels=[]    trainingFileList=listdir(dirName)    m=len(trainingFileList)    trainingMat=np.zeros((m,1024))    for i in range(m):        fileNameStr=trainingFileList[i]        fileStr=fileNameStr.split('.')[0]        classNumber=int(fileStr.split('_')[0])        hwLabels.append(classNumber)        trainingMat[i,:]=img2vector("%s/%s"%(dirName,fileNameStr))    return trainingMat,hwLabelstrainX,trainY=loadDigits('trainingDigits') nb=NB(lam=1)nb.fit(trainX,trainY)n=len(trainY)sum=0results=nb.predict(trainX)for i in range(n):    print trainY[i],results[i],'\n'    if trainY[i]==results[i]:        sum+=1print sum*1.0/n


还不错,准确率为:0.936918304033

测试数据集的下载链接为:http://download.csdn.net/detail/u013732444/9830443


4、总结

    朴素贝叶斯算法原理简单,计算速度也快,在小规模数据集上表现效果很好,适合多分类问题。缺点:要求标称型数据,需要对数据进行预处理。



1 0