机器学习之(1)——学习朴素贝叶斯-三种模型理论+python代码编程实例

本文来源于:

博客:http://blog.csdn.net/u012162613/article/details/48323777
http://blog.csdn.net/zhihaoma/article/details/51052064

 感谢作者的分享,非常感谢

 

概率论相关数学公式 
条件概率:这里写图片描述 
相互独立事件:这里写图片描述 
贝叶斯定理:这里写图片描述

  1. 朴素贝叶斯分类器 
    众所周知,朴素贝叶斯是一种简单但是非常强大的线性分类器。它在垃圾邮件分类,疾病诊断中都取得了很大的成功。它只所以称为朴素,是因为它假设特征之间是相互独立的,但是在现实生活中,这种假设基本上是不成立的。那么即使是在假设不成立的条件下,它依然表现的很好,尤其是在小规模样本的情况下。但是,如果每个特征之间有很强的关联性和非线性的分类问题会导致朴素贝叶斯模型有很差的分类效果。 
    朴素贝叶斯指的两方面: 
    –|朴素:各个特征之间相互独立 
    –|贝叶斯:基于贝叶斯定理 
    证明过程 
    1.设事件A1,B1,B2,在B1、B2同时发生的前提下,A发生的概率为这里写图片描述 
    2.设B1,B2相互独立,则上式可以变换:这里写图片描述 
    3.于是得到如下公式: 
    这里写图片描述 
    4.推广: 
    假设有F1,F2,F3,..Fn个特征,即n维特征 
    有C1,C2,C3,…Cm个类别,m维类别,得到模型如下: 
    这里写图片描述 
    C可以分别取每个类别,C1,C2等等,P(F1 | C1)即代表在C1所属类别下F1特征出现的条件概率。最后求出每个类别出现的概率,取最大值即是预测值最有可能出现的类别。 
    由于分母项是常数,对每一项都相同,因此在求值的过程中我们常将其忽略。
  2. 举个例子 
    根据症状和职业来判定其最可能得什么病。 
    这里写图片描述 
    现在有一个人,是一个打喷嚏的建筑工人,请问患上感冒的概率有多大? 
    P(感冒|打喷嚏,建筑工人)=P(打喷嚏|感冒)P(建筑工人|感冒)*P(感冒)/P(打喷嚏,建筑工人)=2/3 1/3 * 3/6 /(3/6 * 2/6)=0.667 
    同理可以求得患其他病的原因,求其最大值即可得到最可能患的病,其中分母是可以不用求得,这里我也求了一下。 
    注意: 在我们求条件概率时可能会出现 某一特征出现可能性为0的情况,这样概率就是0了,因此我们需要做平滑处理。 
    –|拉普拉斯平滑 
    在求先验概率:这里写图片描述 
    其中K为类别的个数,lambda为算子,当lambda取1时即为拉普拉斯平滑,当取值在[0,1]时为Lidstone平滑。 
    求后验概率(即条件概率): 
    设M维特征的第j维有L个取值,则某维特征的某个取值ajl,在给定某分类ck下的条件概率为: 
    这里写图片描述 
    其中Li为第i维特征最大取值

  3. 代码实现:

这里附上GitHub网址可以下载:
https://github.com/hangdj/native_bayes/blob/master/native_bayes_1.py
# coding:utf-8
import numpy
def loadDataSet():  # 构建一个简单的文本集,以及标签信息 1 表示侮辱性文档,0表示正常文档
        postingList = [['my', 'dog', 'has', 'flea', 'problem', 'help', 'please'],
                       ['maybe', 'not', 'take', 'him', 'to', 'dog', 'park', 'stupid'],
                       ['my', 'dalmation', 'is', 'so', 'cute', 'I', 'love', 'him'],
                       ['stop', 'posting', 'ate', 'my', 'steak', 'how', 'to', 'stop', 'him'],
                       ['mr', 'licks', 'ate', 'my', 'steak', 'how', 'to', 'stop', 'him'],
                       ['quit', 'buying', 'worthless', 'dog', 'food', 'stupid']]
        classVec = [0, 1, 0, 1, 0, 1]
        return postingList, classVec

def createVocabList(dataSet):  # 统计词汇,创建词典
        vocabSet = set([])
        for document in dataSet:
            vocabSet = vocabSet | set(document)
        #print (vocabSet)
        return list(vocabSet)

def setOfWord2Vec(vocabList, inputSet):  # 创建词频统计向量,此方法为词集模型,出现赋值1,or0
        returnVec = [] # 返回的文本向量与词典大小保持一致
        for article in inputSet:
            tmp = [0] * len(vocabList)
            for word in article:
                if word in vocabList:
                    tmp[vocabList.index(word)] = 1  # 当前文档有这个词条,则根据词典位置获取其位置并赋值为1
                else:
                    print ("the word :%s is not in my vocabulary" % word)
            returnVec.append(tmp)
        print(returnVec)
        return returnVec

def bagOfWord2Vec(vocabList, inputSet):  # 词袋模型,统计概率的
        returnVec = []
        for article in inputSet:
            tmp=[0]*len(vocabList)
            for word in article:
                if word in vocabList:
                    tmp[vocabList.index(word)] += 1  # 当前文档有这个词条,则根据词典位置获取其位置并赋值为1
                else:
                    print ("the word :%s is not in my vocabulary" % word)
            returnVec.append(tmp)
        print (returnVec)
        return returnVec
def trianNB(trainMatrix,trainCategory):#训练生成朴素贝叶斯模型
    '''
    :param trainMatrix: 训练数组
    :param trainCategory: 训练标签
    :return:
    '''
    numTrainDoc=len(trainMatrix)#总共文档数量
    numWords=len(trainMatrix[0])#单词数量
    pAbusive=sum(trainCategory)/numTrainDoc#统计侮辱性文档总个数,然后除以总文档个数
    p0Num=numpy.ones(numWords)
    p1Num=numpy.ones(numWords)
    p0Denom=2.0
    p1Denom=2.0
    for i in range(numTrainDoc):
        if trainCategory[i]==1:#如果是侮辱性文档
            p1Num+=trainMatrix[i]#把属于同一类的文本向量相加,实质是统计某个词条在该文本类中出现的频率
            p1Denom+=sum(trainMatrix[i])#去重
            print(p1Denom,"p1Denom")
        else:
            p0Num+=trainMatrix[i]
            p0Denom+=sum(trainMatrix[i])
            print(p0Denom,"p0Denom")
    p1Vec=numpy.log(p1Num/p1Denom)#统计词典中所有词条在侮辱性文档中出现的概率
    p0Vec=numpy.log(p0Num/p0Denom)#统计词典中所有词条在正常性文档中出现的概率
    return pAbusive,p1Vec,p0Vec
def classifyNB(vec2classify,p0Vec,p1Vec,pClass1):# 参数1是测试文档向量,参数2和参数3是词条在各个
                                                    #类别中出现的概率,参数4是P(C1)
    p1=numpy.sum(vec2classify*p1Vec)+numpy.log(pClass1)
    p0=numpy.sum(vec2classify*p0Vec)+numpy.log(1.0-pClass1)
    if p1>p0:
        return 1
    else:
        return 0
if __name__=='__main__':
    test=[['mr', 'licks', 'ate', 'my', 'steak', 'how', 'food', 's']]
    #test=[['maybe', 'not', 'take', 'him', 'to', 'dog', 'park', 'stupid']]
    postingList, classVec=loadDataSet()#文档,标签
    vocabList=createVocabList(postingList)#词典
    returnVec=bagOfWord2Vec(vocabList,postingList)#文本向量
    pAbusive, p1Vec, p0Vec=trianNB(returnVec,classVec)#侮辱性文档比例,侮辱文档概率,正常文档概率
    print (pAbusive, p1Vec, p0Vec)
    testVec=bagOfWord2Vec(vocabList,test)
    pclass=classifyNB(testVec,p0Vec,p1Vec,pAbusive)
    print (pclass)
'''
{1: {0: {1: 0.25, 2: 0.3333333333333333, 3: 0.4166666666666667}, 1: {4: 0.16666666666666666, 5: 0.4166666666666667, 6: 0.4166666666666667}},
 -1: {0: {1: 0.4444444444444444, 2: 0.3333333333333333, 3: 0.2222222222222222}, 1: {4: 0.4444444444444444, 5: 0.3333333333333333, 6: 0.2222222222222222}}} conditional_prob
'''

5.朴素贝叶斯三种模型 
多项式模型:特征:单词,值:k类单词出现频次 
在多项分布朴素贝叶斯模型中,特征向量 X 的特征 通常为 离散型变量,并且假定所有特征的取值是符合多项分布的,可用于文本分类。 
在多项式模型中, 设某文档d=(t1,t2,…,tk),tk是该文档中出现过的单词,允许重复,则 
**先验概率**P(c)= 类c下单词总数/整个训练样本的单词总数 
**类条件概率**P(tk|c)=(类c下单词tk在各个文档中出现过的次数之和+1)/(类c下单词总数+|V|) 
V是训练样本的单词表(即抽取单词,单词出现多次,只算一个),|V|则表示训练样本包含多少种单词。 
P(tk|c)可以看作是单词tk在证明d属于类c上提供了多大的证据,而P(c)则可以认为是类别c在整体上占多大比例(有多大可能性)。 
举例: 
这里写图片描述 
这里写图片描述

# coding:utf-8
import numpy as np
#以单词为计量单位
class MultinomialNB(object):
    '''
    :parameter
        alpha :平滑参数
            -->0 不平滑
            -->0-1 Lidstone 平滑
            -->1 Laplace 平滑
        fit_prior:boolean
            是否学习类先验概率。
      如果fasle,则会使用统一的优先级。
        class_prior:array-like, size (n_classes,) 数组格式 大小 类别个数
            这些类的先验概率,如果指定的话,先验概率将不会根据数据计算
    Attributes
    fit(X,y):特征和标签 都是数组
    predict(X:
    '''
    def __init__(self,alpha=1.0,fit_prior=True,class_prior=None):
        self.alpha=alpha
        self.fit_prior=fit_prior
        self.class_prior=class_prior
        self.classes=None
        self.conditional_prob=None
    def _calculate_feature_prob(self,feature):#计算条件概率
        values=np.unique(feature)
        #print(values,'sddd')
        total_num=float(len(feature))
        value_prob={}
        for v in values:#有平滑效果
            value_prob[v]=((np.sum(np.equal(feature,v))+self.alpha)/(total_num+len(values)*self.alpha))
        return value_prob
    def fit(self,X,y):
        self.classes=np.unique(y)
        #计算先验概率
        if self.class_prior==None:#不指定先验概率
            class_num=len(self.classes)#类别个数
            if not self.fit_prior:#是否自学先验概率,false则统一指定
                self.class_prior=[1.0/class_num for _ in range(class_num)]#统一优先级
            else:
                self.class_prior=[]
                sample_num=float(len(y))
                for c in self.classes:
                    c_num=np.sum(np.equal(y,c))
                    self.class_prior.append((c_num+self.alpha)/(sample_num+class_num*self.alpha))#根据标签出现个数计算优先级
                #print(self.class_prior,"class_prior")
        #计算条件概率
        self.conditional_prob={}#like { c0:{ x0:{ value0:0.2, value1:0.8 }, x1:{}, c1:{...} }
        for c in self.classes:
            self.conditional_prob[c]={}
            #print(len(X[0]),"X[0]")
            for i in range(len(X[0])):#特征总数2
                #print(X[np.equal(y,c)][:,1],"y==c")
                feature=X[np.equal(y,c)][:,i]#这里加个逗号才是遍历所有行!!!
                #print(feature,i,"feature")
                self.conditional_prob[c][i]=self._calculate_feature_prob(feature)
        print(self.conditional_prob,"conditional_prob")
        return self

    #给了 单词概率{value0:0.2,value1:0.1,value3:0.3,.. } 和目标值 给出目标值的概率
    def _get_xj_prob(self, values_prob,target_value):
         return values_prob.get(target_value)
    #依据(class_prior,conditional_prob)预测一个简单地样本
    def _predict_single_sample(self,x):
        label=-1
        max_posterior_prob=0
        #对每一个类别,计算其后验概率:class_prior*conditional_prob
        for c_index in range(len(self.classes)):
            current_class_prior=self.class_prior[c_index]#类别优先级,类别多的优先级大
            current_conditional_prob=1.0#条件概率
            feature_prob=self.conditional_prob[self.classes[c_index]]#类别1的条件概率
            #print(feature_prob.keys(),"feature_prob")
            j=0
            for feature_i in feature_prob.keys():#每一个特征下的概率
                current_conditional_prob*=self._get_xj_prob(feature_prob[feature_i],x[j])
                j+=1

            #比较后验概率,更新max_posterior_prob ,label
            print( current_class_prior*current_conditional_prob,self.classes[c_index],"后验概率")
            if current_class_prior*current_conditional_prob>max_posterior_prob:#取最大的后验概率
                max_posterior_prob=current_class_prior*current_conditional_prob
                label=self.classes[c_index]

        return label
    def predict(self,X):
        if X.ndim==1:
            return self._predict_single_sample(X)
        else:
            labels=[]
            for i in range(X.shape(0)):
                label=self._predict_single_sample(X[i])
                labels.append(label)
            return labels
if __name__=='__main__':
    X = np.array([
                          [1,1,1,1,1,2,2,2,2,2,3,3,3,3,3],
                          [4,5,5,4,4,4,5,5,6,6,6,5,5,6,6]
                 ])
    X = X.T
    y = np.array(        [-1,-1,1,1,-1,-1,-1,1,1,1,1,1,1,1,-1])
    nb = MultinomialNB(alpha=1.0,fit_prior=True)
    nb.fit(X,y)
    print ("[2,5]-->",nb.predict(np.array([2,5])))#输出1 0.08169 0.04575
    print ("[2,4]-->",nb.predict(np.array([2,4])))#输出-1 0.0327 0.0610

伯努利模型:特征:文本,值:k类文本出现频次 
在伯努利朴素贝叶斯模型中,每个特征的取值是布尔型,或以0和1表示,所以伯努利模型中,每个特征值为0或者1。 
计算方式: 
* P(c) *= 类c下文件总数/整个训练样本的文件总数 
P(tk|c)=(类c下包含单词tk的文件数+1)/(类c的文档总数+2) 
上面两个模型区别: 
–|二者的计算粒度不一样,多项式模型以单词为粒度,伯努利模型以文件为粒度,因此二者的先验概率和类条件概率的计算方法都不同。 
–|计算后验概率时,对于一个文档d,多项式模型中,只有在d中出现过的单词,才会参与后验概率计算,伯努利模型中,没有在d中出现,但是在全局单词表中出现的单词,也会参与计算,不过是作为“反方”参与的。 
高斯模型:特点:只有它适用于连续变量预测(如身高预测) 
在高斯朴素贝叶斯模型中,特征向量 X 的特征 通常为 连续型变量,并且假定所有特征的取值是符合高斯分布的,即: 
这里写图片描述 
需要求每一维特征的均值和方差。 
举例: 
这里写图片描述 
这里的困难在于,由于身高、体重、脚掌都是连续变量,不能采用离散变量的方法计算概率。而且由于样本太少,所以也无法分成区间计算。怎么办? 
这时,可以假设男性和女性的身高、体重、脚掌都是正态分布,通过样本计算出均值和方差,也就是得到正态分布的密度函数。有了密度函数,就可以把值代入,算出某一点的密度函数的值。 
比如,男性的身高是均值5.855、方差0.035的正态分布。所以,男性的身高为6英尺的概率的相对值等于1.5789(大于1并没有关系,因为这里是密度函数的值,只用来反映各个值的相对可能性)。

 P(身高=6|男) x P(体重=130|男) x P(脚掌=8|男) x P(男) 
    = 6.1984 x e-9
  P(身高=6|女) x P(体重=130|女) x P(脚掌=8|女) x P(女) 
    = 5.3778 x e-4
# coding:utf-8
#当特征是连续变量的时候,运用多项式模型就会导致很多P(xi|yk)==0(不做平滑的情况下),
# 此时即使做平滑,所得到的条件概率也难以描述真实情况。所以处理连续的特征变量,应该采用高斯模型。

#高斯模型假设每一维特征都服从高斯分布,需要计算每一维的均值和方差
from self_multinomalNB import MultinomialNB
import numpy as np
#继承Multinomial并重载相应的方法
class GaussianNB(MultinomialNB):
    def _calculate_feature_prob(self,feature):#计算平均值和方差
        mu=np.mean(feature)
        sigma=np.std(feature)
        return (mu,sigma)
    #计算高斯分布的概率密度
    def _prob_gaussian(self,mu,sigma,x):
        return (1.0/(sigma*np.sqrt(2*np.pi)))*np.exp(-(x-mu)**2/(2*sigma**2))
    def _get_xj_prob(self, mu_sigma,target_value):
        return self._prob_gaussian(mu_sigma[0],mu_sigma[1],target_value)
if __name__=='__main__':
    X = np.array([
                          [1,1,1,1,1,2,2,2,2,2,3,3,3,3,3],
                          [4,5,5,4,4,4,5,5,6,6,6,5,5,6,6]
                 ])
    X = X.T
    y = np.array(        [-1,-1,1,1,-1,-1,-1,1,1,1,1,1,1,1,-1])
    nb = GaussianNB(alpha=1.0,fit_prior=True)
    nb.fit(X,y)
    print ("[2,5]-->",nb.predict(np.array([2,5])))#输出1 0.08169 0.04575
    print ("[2,4]-->",nb.predict(np.array([2,4])))#输出-1 0.0327 0.0610

本文

我的GitHub上有全部代码,自己实现的包括调用API的:大家可以下载分享:
https://github.com/hangdj/native_bayes

 

  • 1
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值