• 朴素贝叶斯


    朴素贝叶斯是一个概率模型,在数学上能用概率解释的模型一般被认为是好模型。

    朴素贝叶斯常用于文本分类。

    先介绍几个基础概念。

    1. 概率

    设x为符合某种特征的样本,H为某个假设,比如假设x属于类别c,那分类就是求这个假设发生的概率,即P(H|x)的大小。

    P(H|X)是后验概率,或者说在条件X下,H的后验概率,就是在已知样本符合某种特征时,H发生的概率

    P(H)是先验概率,或者说H的先验概率,就是所有样本中H发生的概率

    P(X|H),P(X)同理

    2. 条件概率

    P(A|B)=P(AB)/P(B)

    P(B|A)=P(AB)/P(A)

    P(A|B)=P(B|A)P(A)/P(B)

    经典案例

    我们想计算含有单词drugs的邮件为垃圾邮件的概率

    A为“这是封垃圾邮件”。P(A)也被称为先验信念(prior belief)。

    计算方法是,统计训练集中垃圾邮件的比例。
    如果我们的数据集每100封邮件有30封垃圾邮件,P(A)为30/100或0.3。

    B表示“该封邮件含有单词drugs”。P(B)也被称为先验信念(prior belief)。

    类似地,我们可以通过计算数据集中含有单词drugs的邮件数量得到P(B)。
    如果每100封邮件中有10封邮件包含单词drugs,那么P(B)就为10/100或0.1。

    P(B|A)指的是垃圾邮件中含有单词drugs的概率,计算起来也很容易,统计训练集中所有垃圾邮件的数量以及其中含有单词drugs的数量。
    30封垃圾邮件中,如果有6封含有单词drugs,那么P(B|A)就为6/30或0.2。

    现在,我们根据贝叶斯定理就能计算出P(A|B),得到含有drugs的邮件为垃圾邮件的概率。
    把上面求出来的各项代入前面的贝叶斯公式,得到结果0.6。
    这表明如果邮件中含有drugs这个词,那么该邮件为垃圾邮件的概率为60% 。

    算法原理

    1. 朴素贝叶斯之所以朴素,是其认为样本中属性间相互独立,体现在概率论上就是彼此是独立事件,故 P(AB)=P(A)P(B),所以文本就可以表示为P(X)=P(x1)P(x2)P(x3)...P(xk)

    2. P(C|X)=P(X|C)P(C)/P(X)

    P(X)表示样本集中含有某特征的样本的比例,是个常数,且对所有样本都一样,故可省略

    P(C)其实也是个常数,但是要计算样本属于不同类别时要用不同类别的P(C),故不可省略

    3. 计算出每个类别的概率,取概率最大对应的类别即可

    粘张图吧,懒得写了

    示例代码

    import numpy as np
    
    def loadDataSet():
        # 样本
        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]          # 1表示侮辱性文档,0表示正常文档。
        return postingList,classVec
    
    def createVocabList(dataSet):
        # 词向量集合
        vocabSet=set([])
        for document in dataSet:
            vocabSet=vocabSet|set(document)
        return list(vocabSet)
    
    def setOfWords2Vec(vocabList,inputSet):
        # word2vec  只有0 1
        returnVec=[0]*len(vocabList)               # 每个文档的大小与词典保持一致,此时returnVec是空表
        for word in inputSet:
            if word in vocabList:
                returnVec[vocabList.index(word)]=1 # 当前文档中有某个词条,则根据词典获取其位置并赋值1
            else:print "the word :%s is not in my vocabulary" %word
        return returnVec
    
    def bagOfWords2Vec(vocabList,inputSet):
        # 词袋  出现次数
        returnVec=[0]*len(vocabList)
        for word in inputSet:
            if word in vocabList:
                returnVec[vocabList.index(word)]+=1 # 与词集模型的唯一区别就表现在这里
            else:print "the word :%s is not in my vocabulary" %word
        return returnVec
    
    def trainNB(trainMatrix,trainCategory):
        # 训练模型 trainMatrix---X, trainCategory--->Y
        numTrainDocs=len(trainMatrix)       # 样本数量
        numWords=len(trainMatrix[0])        # 属性集,次向量长度
        pAbusive=sum(trainCategory)/float(numTrainDocs)     # 统计侮辱性文档的总个数,然后除以总文档个数     P(A)
        #p0Num=zeros(numWords);p1Num=zeros(numWords)
        #p0Denom=0.0;p1Denom=0.0
        p0Num=np.ones(numWords)
        p1Num=np.ones(numWords)
        p0Denom=2.0
        p1Denom=2.0
        for i in range(numTrainDocs):
            # 遍历样本
            if trainCategory[i]==1:
                # 标签为1, 用于计算该类别下词的概率,P(X|A)
                p1Num+=trainMatrix[i]           # 把属于同一类的文本向量相加,实质是统计某个词条在该类文本中出现频率
                p1Denom+=sum(trainMatrix[i])    # 把侮辱性文档向量的所有元素加起来
            else:
                # 标签为0, 用于计算该类别下词的概率,P(X|B)
                p0Num+=trainMatrix[i]
                p0Denom+=sum(trainMatrix[i])
        # p1Vec=p1Num/float(p1Denom)
        # p0Vec=p0Num/float(p0Denom)
        p1Vec=np.log(p1Num/p1Denom) # 统计词典中所有词条在侮辱性文档中出现的概率
        p0Vec=np.log(p0Num/p0Denom) # 统计词典中所有词条在正常文档中出现的概率
        return pAbusive,p1Vec,p0Vec
    
    ### 注意:被注释掉的代码代表不太好的初始化方式,在那种情况下某些词条的概率值可能会非常非常小,甚至约
    ### 等于0,那么在不同词条的概率在相乘时结果就近似于0
    
    def classifyNB(vec2classify,p0Vec,p1Vec,pClass1):
        # 预测
        # 参数1是测试文档向量,参数2和参数3是词条在各个类别中出现的概率,参数4是P(C1)
        p1=sum(vec2classify*p1Vec)+np.log(pClass1)      # 这里没有直接计算P(x,y|C1)P(C1),而是取其对数这样做也是防止概率之积太小,以至于为0
        p0=sum(vec2classify*p0Vec)+np.log(1.0-pClass1)  # 取对数后虽然P(C1|x,y)和P(C0|x,y)的值变了,但是不影响它们的大小关系。
        if p1>p0:
            return 1
        else:
            return 0
    
    
    if __name__ == '__main__':
        postingList,classVec = loadDataSet()
        vocab = createVocabList(postingList)
        setvec = [setOfWords2Vec(vocab, i) for i in postingList]
        bagvec = [bagOfWords2Vec(vocab, i) for i in postingList]
    
        pAbusive,p1Vec,p0Vec = trainNB(bagvec, classVec)
    
        for i in setvec:
            print classifyNB(i,p0Vec,p1Vec,pAbusive)

    只是个示例,拿训练样本测试的。

    注意在预测时,计算概率用了加法,本身应该是乘法的,但是因为取了log,乘法变加法,注意在计算P(xk)时直接取了log,所以在预测时直接sum了。

    log 避免了因概率因子远小于1而连乘造成的下溢出。

    还有一种避免0概率的方法叫拉普拉斯平滑,其实很简单,就是在每个类别的样本数上+1,那么样本最少是1,所以不会是0概率,注意每个类别加1,总样本要加类别数。

    总结 

    朴素贝叶斯对小规模数据集表现很好,适合多分类,但是如果属性间相关性较强,表现不好。

    参考资料:

    https://blog.csdn.net/amds123/article/details/70173402

    https://blog.csdn.net/li8zi8fa/article/details/76176597

  • 相关阅读:
    substring(x)和substring(x,y)的用法
    js 判断字符串中是否包含某个字符串
    js如何将纯数字字符串转换为long型
    union和union all的区别
    JavaScript 中 var 和 let 和 const 关键字的区别
    JS操作文件
    java 字符串和集合互相转换
    关于sql中in 和 exists 的效率问题,in真的效率低吗
    sql 同一张表查询不同数据合并之后关联查询
    Android input输入框 移动页面input手机键盘中的“搜索”按键
  • 原文地址:https://www.cnblogs.com/yanshw/p/10647384.html
Copyright © 2020-2023  润新知