朴素贝叶斯详解及其python实现

  • 简介

         贝叶斯定理用Thomas Bayes的名字命名。早在18世纪,英国学者贝叶斯提出计算条件概率的公式用来解决如下问题:

         假设B[1]、B[2]…B[n]互斥并且构成一个完备事件组,已知他们的概率P(B[i]),i=1,2,...,n,已知某一事件A与B相伴随机出现,并且已知条件概率P(A|B[i])的概率,求条件概率p(B[i]|A)

         贝叶斯定理具体形式为:

                                              https://gss3.bdstatic.com/7Po3dSag_xI4khGkpoWK1HF6hhy/baike/s%3D215/sign=07ef5cb4b03eb13540c7b0ba931fa8cb/1ad5ad6eddc451da062e7cb7bafd5266d1163295.jpg

其中P(A|B)是在 B 发生的情况下 A 发生的可能性。P(B|A)是在A发生的情况下B发生的概率可能性。

         P(A)是 A 的先验概率,之所以称为“先验”是因为它不考虑任何 B 方面的因素。P(A|B)是已知 B 发生后 A 的条件概率,也由于得自 B 的取值而被称作 A 的后验概率。P(B|A)是已知 A 发生后 B 的条件概率,也由于得自 A 的取值而被称作 B 的后验概率。P(B)是 B 的先验概率,也作标淮化常量(normalizing constant)。后验概率 = (相似度 * 先验概率)/标淮化常量,既是P(A|B) =P(AB)/P(B),比例P(B|A)/P(B)也有时被称作标淮相似度(standardised likelihood),Bayes定理可表述为:后验概率 = 标淮相似度 * 先验概率        

  • 朴素贝叶斯

在数据挖掘中,朴素贝叶斯是一个比较经典的算法,给定训练数据集A,类别B,其中A

的属性列为Ai,i=1,2,…,n;B={B1,B2,…,Bn};贝叶斯算法核心思想是给定待判定数据元组,根据训练数据集进行分类预测,通过贝叶斯定理计算当前待判定元组属于某一类别的概率,概率最大者即为该待判定元组的类别归属。

         所谓的朴素贝叶斯是指在给定训练集数据元组中,每一个属性列之间是独立的,相互之间互不影响,A1,A2,…,An之间互相独立:

         通过贝叶斯公式计算后验概率:

                                                      

此公式是表名给定元组数据A,其在A的条件下属于类别Bj的概率,由于A的每一列之间相互独立的,互不影响,因此

                                                     

         在求出之后,因为P(A)对每一个类别都相同,因此可通过的数值进行比较,因此就有:得出概率最大的即为相应的预测类别。

     在进行贝叶斯算法应用时候由于数据量较大而且较为复杂,因此经常会遇见概率为0的值,那么概率为0的值怎么办呐?有一个简单的技巧来避免该问题,可以假定训练数据库D很大,以至于对每个计数加1造成的估计概率的变化忽略不计,但可以方便的避免概率值为0.这种概率估计技术成为拉普拉斯校准或者拉普拉斯估计法。对元组中每一列属性的不重复的数据项计数加1,并且在分母计算上也加1,这样可以有效地避免概率为0的问题。

同时在运用python进行计算概率时候,我们可以通过取对数进行相应的计算,因为数据量过大的话会造成某项概率过小,但是我们按照数学的方法,y=x是单调递增,那么y=lnx也

是单调递增的这个思想,即x1>x2 那么lnx1>lnx2;通过对数之间的公式运算,如

                                                        

                                                        

                                                       

  • 朴素贝叶斯的python实现
    # -*- coding: utf-8 -*-
    
    from numpy import *
    from functools import reduce
    
    # 广告、垃圾标识
    adClass = 1
    
    
    def loadDataSet():
        """加载数据集合及其对应的分类"""
        wordsList = [['周六', '公司', '一起', '聚餐', '时间'],
                     ['优惠', '返利', '打折', '优惠', '金融', '理财'],
                     ['喜欢', '机器学习', '一起', '研究', '欢迎', '贝叶斯', '算法', '公式'],
                     ['公司', '发票', '税点', '优惠', '增值税', '打折'],
                     ['北京', '今天', '雾霾', '不宜', '外出', '时间', '在家', '讨论', '学习'],
                     ['招聘', '兼职', '日薪', '保险', '返利']]
        # 1 是, 0 否
        classVec = [0, 1, 0, 1, 0, 1]
        return wordsList, classVec
    
    
    # python中的& | 是位运算符   and or是逻辑运算符 当and的运算结果为true时候返回的并不是true而是运算结果最后一位变量的值
    # 当and返回的结果是false时候,如果A AND B 返回的是第一个false的值,如果a为false 则返回a,如果a不是false,那么返回b
    # 如果a or b 为true时候,返回的是第一个真的变量的值,如果a,b都为真时候那么返回a 如果a为假b为真那么返回b
    # a & b a和b为两个set,返回结果取a和b的交集  a|b a和b为两个set,返回结果为两个集合的不重复并集
    def doc2VecList(docList):
        # 从第一个和第二个集合开始进行并集操作,最后返回一个不重复的并集
        a = list(reduce(lambda x, y: set(x) | set(y), docList))
        return a
    
    
    def words2Vec(vecList, inputWords):
        """把单子转化为词向量"""
        # 转化成以一维数组
        resultVec = [0] * len(vecList)
        for word in inputWords:
            if word in vecList:
                # 在单词出现的位置上的计数加1
                resultVec[vecList.index(word)] += 1
            else:
                print('没有发现此单词')
    
        return array(resultVec)
    
    
    def trainNB(trainMatrix, trainClass):
        """计算,生成每个词对于类别上的概率"""
        # 类别行数
        numTrainClass = len(trainClass)
        # 列数
        numWords = len(trainMatrix[0])
    
        # 全部都初始化为1, 防止出现概率为0的情况出现
        # 见于韩家炜的数据挖掘概念与技术上的讲解,避免出现概率为0的状况,影响计算,因为在数量很大的情况下,在分子和分母同时+1的情况不会
        # 影响主要的数据
        p0Num = ones(numWords)
        p1Num = ones(numWords)
        # 相应的单词初始化为2
        # 为了分子分母同时都加上某个数λ
        p0Words = 2.0
        p1Words = 2.0
        # 统计每个分类的词的总数
        # 训练数据集的行数作为遍历的条件,从1开始
        # 如果当前类别为1,那么p1Num会加上当前单词矩阵行数据,依次遍历
        # 如果当前类别为0,那么p0Num会加上当前单词矩阵行数据,依次遍历
        # 同时统计当前类别下单词的个数和p1Words和p0Words
        for i in range(numTrainClass):
            if trainClass[i] == 1:
                # 数组在对应的位置上相加
                p1Num += trainMatrix[i]
                p1Words += sum(trainMatrix[i])
            else:
                p0Num += trainMatrix[i]
                p0Words += sum(trainMatrix[i])
        # 计算每种类型里面, 每个单词出现的概率
        # 朴素贝叶斯分类中,y=x是单调递增函数,y=ln(x)也是单调的递增的
        # 如果x1>x2 那么ln(x1)>ln(x2)
        # 在计算过程中,由于概率的值较小,所以我们就取对数进行比较,根据对数的特性
        # ln(MN) = ln(M)+ln(N)
        # ln(M/N) = ln(M)-ln(N)
        # ln(M**n)= nln(M)
        # 注:其中ln可替换为log的任意对数底
        p0Vec = log(p0Num / p0Words)
        p1Vec = log(p1Num / p1Words)
        # 计算在类别中1出现的概率,0出现的概率可通过1-p得到
        pClass1 = sum(trainClass) / float(numTrainClass)
        return p0Vec, p1Vec, pClass1
    
    
    def classifyNB(testVec, p0Vec, p1Vec, pClass1):
        # 朴素贝叶斯分类, max(p0, p1)作为推断的分类
        # y=x 是单调递增的, y=ln(x)也是单调递增的。 , 如果x1 > x2, 那么ln(x1) > ln(x2)
        # 因为概率的值太小了,所以我们可以取ln, 根据对数特性ln(ab) = lna + lnb, 可以简化计算
        # sum是numpy的函数,testVec是一个数组向量,p1Vec是一个1的概率向量,通过矩阵之间的乘机
        # 获得p(X1|Yj)*p(X2|Yj)*...*p(Xn|Yj)*p(Yj)
        # 其中pClass1即为p(Yj)
        # 此处计算出的p1是用对数表示,按照上面所说的,对数也是单调的,而贝叶斯分类主要是通过比较概率
        # 出现的大小,不需要确切的概率数据,因此下述表述完全正确
        p1 = sum(testVec * p1Vec) + log(pClass1)
        p0 = sum(testVec * p0Vec) + log(1 - pClass1)
        if p0 > p1:
            return 0
        return 1
    
    
    def printClass(words, testClass):
        if testClass == adClass:
            print(words, '推测为:广告邮件')
        else:
            print(words, '推测为:正常邮件')
    
    
    def tNB():
        # 从训练数据集中提取出属性矩阵和分类数据
        docList, classVec = loadDataSet()
        # 生成包含所有单词的list
        # 此处生成的单词向量是不重复的
        allWordsVec = doc2VecList(docList)
        # 构建词向量矩阵
        # 计算docList数据集中每一行每个单词出现的次数,其中返回的trainMat是一个数组的数组
        trainMat = list(map(lambda x: words2Vec(allWordsVec, x), docList))
        # 训练计算每个词在分类上的概率, p0V:每个单词在非分类出现的概率, p1V:每个单词在是分类出现的概率
        # 其中概率是以ln进行计算的
        # pClass1为类别中是1的概率
        p0V, p1V, pClass1 = trainNB(trainMat, classVec)
        # 测试数据集
        testWords = ['公司', '聚餐', '讨论', '贝叶斯']
        # 转换成单词向量,32个单词构成的数组,如果此单词在数组中,数组的项值置1
        testVec = words2Vec(allWordsVec, testWords)
        # 通过将单词向量testVec代入,根据贝叶斯公式,比较各个类别的后验概率,判断当前数据的分类情况
        testClass = classifyNB(testVec, p0V, p1V, pClass1)
        # 打印出测试结果
        printClass(testWords, testClass)
    
        testWords = ['公司', '保险', '金融']
        # 转换成单词向量,32个单词构成的数组,如果此单词在数组中,数组的项值置1
        testVec = words2Vec(allWordsVec, testWords)
        # 通过将单词向量testVec代入,根据贝叶斯公式,比较各个类别的后验概率,判断当前数据的分类情况
        testClass = classifyNB(testVec, p0V, p1V, pClass1)
        # 打印出测试结果
        printClass(testWords, testClass)
    
    
    if __name__ == '__main__':
        tNB()
    

     

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值