机器学习(其四)贝叶斯分类器

  • 机器学习(其四)贝叶斯分类器
    • 前言
    • 贝叶斯定理及贝叶斯分类器
      • 条件概率
      • 全概率公式
      • 贝叶斯推断
      • 概率事件的优势
      • 贝叶斯决策论
      • 极大似然估计(maximum likelihood estimation)
        • 估计原理
        • 求解极大似然函数
      • 拉普拉斯修正(拉普拉斯平滑)(Laplacian correction)
      • 其他贝叶斯分类算法
        • SPODE
        • TAN
        • AODE
      • 信念网(belief network)
        • 结构
        • sklearn中贝叶斯的实现
    • 参考资料
    • 注释

机器学习(其四)贝叶斯分类器

前言

另外文中引用了他人的文章,会在参考资料部分标明,如有版权.转载等问题我会删掉那部分内容.
最后本人也是刚刚入门机器学习这一领域,如有问题欢迎探讨,希望大家看完都能有点收获
运行环境是Anaconda3.
其中python版本为3.6.4
skearn版本0.19.1
(感觉很多人写文章都不说运行环境,写的很用心但是别人却运行不了,这太可惜了.)

贝叶斯定理及贝叶斯分类器

朴素贝叶斯算法是有监督的学习算法,解决的是分类问题,如客户是否流失、是否值得投资、信用等级评定等多分类问题。该算法的优点在于简单易懂、学习效率高、在某些领域的分类问题中能够与决策树、神经网络相媲美。但由于该算法以自变量之间的独立(条件特征独立)性和连续变量的正态性假设为前提,就会导致算法精度在某种程度上受影响。
我们会选择高概率对应的类别。这就是贝叶斯决策理论的核心思想,即选择具有最高概率的决策

条件概率

机器学习(其四)贝叶斯分类器_第1张图片

在学习贝叶斯定理之前,我们需要了解什么是条件概率(Condittional probability),就是指在事件B发生的情况下,事件A发生的概率,用P(A|B)来表示。 根据文氏图(韦恩图),可以很清楚地看到在事件B发生的情况下,事件A发生的概率就是P(A∩B)除以P(B)。

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

因此,
P(AB)=P(A|B)P(B) P ( A ∩ B ) = P ( A | B ) P ( B )

同理可得,
P(AB)=P(B|A)P(A) P ( A ∩ B ) = P ( B | A ) P ( A )

所以,
P(A|B)P(B)=P(B|A)P(B) P ( A | B ) P ( B ) = P ( B | A ) P ( B )

即,
P(A|B)=P(B|A)P(A)P(B) P ( A | B ) = P ( B | A ) P ( A ) P ( B )

这就是条件概率的计算公式

全概率公式

假定样本空间S,是两个事件A与A’的和。

机器学习(其四)贝叶斯分类器_第2张图片

上图中,红色部分是事件A,绿色部分是事件A’,它们共同构成了样本空间S。
在这种情况下,事件B可以划分成两个部分。A’表示A事件的非事件。


P(B)=P(BA)+P(BA) P ( B ) = P ( B ∩ A ) + P ( B ∩ A ′ )

由式(3)
P(AB)=P(B|A)P(A) P ( A ∩ B ) = P ( B | A ) P ( A )

代入得
P(BA)=P(B|A)P(A) P ( B ∩ A ) = P ( B | A ) P ( A )


P(B)=P(B|A)P(A)+P(B|A)P(A) P ( B ) = P ( B | A ) P ( A ) + P ( B | A ′ ) P ( A ′ )

这就是全概率公式。它的含义是,如果A和A’构成样本空间的一个划分,那么事件B的概率,就等于A和A’的概率分别乘以B对这两个事件的条件概率之和。
将这个公式代入上一节的条件概率公式,就得到了条件概率的另一种写法:
P(A|B)=P(B|A)P(A)P(B|A)P(A)+P(B|A)P(A) P ( A | B ) = P ( B | A ) P ( A ) P ( B | A ) P ( A ) + P ( B | A ′ ) P ( A ′ )

机器学习(其四)贝叶斯分类器_第3张图片

贝叶斯推断

对条件概率公式进行变形,可以得到如下形式:

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

我们把P(A)称为 ”先验概率”(Prior probability),即在B事件发生之前,我们对A事件概率的一个判断。
P(A|B)称为 ”后验概率”(Posterior probability),即在B事件发生之后,我们对A事件概率的重新评估。
P(B|A)/P(B)称为 ”可能性函数”(Likelyhood),这是一个调整因子,使得预估概率更接近真实概率。
所以,条件概率可以理解成下面的式子:
=× 后 验 概 率 = 先 验 概 率 × 调 整 因 子

这就是贝叶斯推断的含义。我们先预估一个”先验概率”,然后加入实验结果,看这个实验到底是增强还是削弱了”先验概率”,由此得到更接近事实的”后验概率”。
在这里,如果”可能性函数”P(B|A)/P(B)>1,意味着”先验概率”被增强,事件A的发生的可能性变大;如果”可能性函数”=1,意味着B事件无助于判断事件A的可能性;如果”可能性函数”<1,意味着”先验概率”被削弱,事件A的可能性变小。
为了加深对贝叶斯推断的理解,我们一个例子。
两个一模一样的碗,一号碗有30颗水果糖和10颗巧克力糖,二号碗有水果糖和巧克力糖各20颗。现在随机选择一个碗,从中摸出一颗糖,发现是水果糖。请问这颗水果糖来自一号碗的概率有多大?
我们假定,H1表示一号碗,H2表示二号碗。由于这两个碗是一样的,所以P(H1)=P(H2),也就是说,在取出水果糖之前,这两个碗被选中的概率相同。因此,P(H1)=0.5,我们把这个概率就叫做”先验概率”,即没有做实验之前,来自一号碗的概率是0.5。

再假定,E表示水果糖,所以问题就变成了在已知E的情况下,来自一号碗的概率有多大,即求P(H1|E)。我们把这个概率叫做”后验概率”,即在E事件发生之后,对P(H1)的修正。

根据条件概率公式,得到

P(H1|E)=P(H1)P(E|H1)P(E) P ( H 1 | E ) = P ( H 1 ) P ( E | H 1 ) P ( E )

已知,P(H1)等于0.5,P(E|H1)为一号碗中取出水果糖的概率,等于30÷(30+10)=0.75,那么求出P(E)就可以得到答案。根据全概率公式,
P(E)=P(E|H1)P(H1)+P(E|H2)P(H2) P ( E ) = P ( E | H 1 ) P ( H 1 ) + P ( E | H 2 ) P ( H 2 )

所以,
P(E)=0.75×0.5+0.5×0.5=0.625 P ( E ) = 0.75 × 0.5 + 0.5 × 0.5 = 0.625

将数字代入原方程,得到
P(H1|E)=0.5×0.750.625=0.6 P ( H 1 | E ) = 0.5 × 0.75 0.625 = 0.6

这表明,来自一号碗的概率是0.6。也就是说,取出水果糖之后,H1事件的可能性得到了增强。
同时再思考一个问题,在使用该算法的时候,如果不需要知道具体的类别概率,即上面P(H1|E)=0.6,只需要知道所属类别,即来自一号碗,我们有必要计算P(E)这个全概率吗?要知道我们只需要比较 P(H1|E)和P(H2|E)的大小,找到那个最大的概率就可以。既然如此,两者的分母都是相同的,那我们只需要比较分子即可。即比较P(E|H1)P(H1)和P(E|H2)P(H2)的大小,所以为了减少计算量,全概率公式在实际编程中可以不使用。

理解了贝叶斯推断,那么让我们继续看看朴素贝叶斯(native Bayes)。贝叶斯和朴素贝叶斯的概念是不同的,区别就在于“朴素”二字,朴素贝叶斯对条件个概率分布做了条件独立性的假设。 比如下面的公式,假设有n个特征:

P(a|X)=p(X|a)p(a)=p(x1,x2,x3,...xn|a)P(a) P ( a | X ) = p ( X | a ) p ( a ) = p ( x 1 , x 2 , x 3 , . . . x n | a ) P ( a )

由于每个特征都是独立的,我们可以进一步拆分公式:
p(a|X)=p(X|a)p(a)={p(x1|a)×p(x2|a)×p(x3|a)×...×p(p(xn|a))}p(a) p ( a | X ) = p ( X | a ) p ( a ) = { p ( x 1 | a ) × p ( x 2 | a ) × p ( x 3 | a ) × . . . × p ( p ( x n | a ) ) } p ( a )

朴素贝叶斯基础假设是,对于每一个特征都有:

  • 独立
  • 相等

来支持输出结果。

与我们的数据集关联起来,我们可以这样理解这个概念:

我们假设没有特征对是相互依赖的。温度热不热跟湿度没有任何关系,天气是否下雨也不影响是否刮风。因此,这就是假设特征相互独立。
其次,每个特征都有相同的权重(或者是重要性)。例如,只知道温度和湿度是不能准确地推断出结果的。任何属性都与结果是有关系的,并且影响程度是相同的。

这样我们就可以进行计算了。如果有些迷糊,让我们从一个例子开始讲起,你会看到贝叶斯分类器很好懂,一点都不难。

某个医院早上来了六个门诊的病人,他们的情况如下表所示:

症状 职业 疾病
打喷嚏 护士 感冒
打喷嚏 农夫 过敏
头痛 建筑工人 脑震荡
头痛 建筑工人 感冒
打喷嚏 教师 感冒
头痛 教师 脑震荡

现在又来了第七个病人,是一个打喷嚏的建筑工人。请问他患上感冒的概率有多大?

根据贝叶斯定理:

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

可得:
P(|×)=P(×|×P())P(×) P ( 感 冒 | 打 喷 嚏 × 建 筑 工 人 ) = P ( 打 喷 嚏 × 建 筑 工 人 | 感 冒 × P ( 感 冒 ) ) P ( 打 喷 嚏 × 建 筑 工 人 )

根据朴素贝叶斯条件独立性的假设可知,”打喷嚏”和”建筑工人”这两个特征是独立的,因此,上面的等式就变成了
P(|×)=P(|)×P(|)×P()P()×P() P ( 感 冒 | 打 喷 嚏 × 建 筑 工 人 ) = P ( 打 喷 嚏 | 感 冒 ) × P ( 建 筑 工 人 | 感 冒 ) × P ( 感 冒 ) P ( 打 喷 嚏 ) × P ( 建 筑 工 人 )

这里可以计算:
P(|×)=0.66×0.33×0.50.5×0.33=0.66 P ( 感 冒 | 打 喷 嚏 × 建 筑 工 人 ) = 0.66 × 0.33 × 0.5 0.5 × 0.33 = 0.66

因此,这个打喷嚏的建筑工人,有66%的概率是得了感冒。同理,可以计算这个病人患上过敏或脑震荡的概率。比较这几个概率,就可以知道他最可能得什么病。

这就是贝叶斯分类器的基本方法:在统计资料的基础上,依据某些特征,计算各个类别的概率,从而实现分类。

同样,在编程的时候,如果不需要求出所属类别的具体概率,P(打喷嚏) = 0.5和P(建筑工人) = 0.33的概率是可以不用求的。

概率事件的优势

事件A的优势定义为:

P(A)P(A)=P(A)1P(A) P ( A ) P ( A ′ ) = P ( A ) 1 − P ( A ′ )

也即,事件A的优势告诉我们该事件发生的可能性是不发生的可能性的倍率,举例来说,如果P(A)=2/3,那么
P(A)=2×P(A) P ( A ) = 2 × P ( A ′ )
,因此事件A的优势等于2.如果某事件优势等于α,那么通常称支持假设成立的优势为阿尔法比1也是同样的意思.

这个定义告诉我们,当我们要比较2个互斥事件的概率的时候只需要计算其中一个事件的概率,再利用互斥事件概率相加为1的特性即可求出每一个事件的概率了.
下面我们来看一个例子
当抛掷硬币A时,其正面朝上的概率是1/4,而抛掷硬币B时,正面朝上的概率是3/4,假设随机挑选一枚硬币,并投掷两次,如果两次都是正面朝上,那么选中的是硬币B的概率有多大?

在这个例子里面,显然选中硬币A和选中硬币B是互斥事件,我们容易求得使用硬币A投掷时,两次正面朝上的概率是 1/4×1/4=1/16 1 / 4 × 1 / 4 = 1 / 16 ,而使用硬币B投掷的时候,两次正面朝上的概率是 3/4×3/4=9/16 3 / 4 × 3 / 4 = 9 / 16 ,所以事件B相对于事件A的优势为9,又因为选中硬币A和选中硬币B是互斥的,他们的概率和为1,可得选中硬币B的概率是0.9.

贝叶斯决策论

再次强调,贝叶斯分类是根据概率选择最优的决策,那么知道的上述的知识之后,我们具体来看一下贝叶斯决策论是如何决策的.
假设有N种可能的类别标记,即
y=c1,c2,c3...,cN y = c 1 , c 2 , c 3 . . . , c N , λij λ i j 是将一个真实标记为 cj c j 的样本误分类为 ci c i 所产生的瞬时.基于后验概率 P(ci|x) P ( c i | x ) 可获得将样本 x x 分类为ci c i 所产生的期望损失(expected loss).即在样本x上的“条件风险”(conditional risk)

R(ci|x)=j=1NλijP(cj|x) R ( c i | x ) = ∑ j = 1 N λ i j P ( c j | x )

我们要寻找一个判定准则h:样本空间 χy χ ↦ y ( 注释5)
以最小化总体风险

那么现在来看一个应用贝叶斯分类器的例子
以在线社区留言为例。为了不影响社区的发展,我们要屏蔽侮辱性的言论,所以要构建一个快速过滤器,如果某条留言使用了负面或者侮辱性的语言,那么就将该留言标志为内容不当。过滤这类内容是一个很常见的需求。对此问题建立两个类型:侮辱类和非侮辱类,使用1和0分别表示。

我们把文本看成单词向量或者词条向量,也就是说将句子转换为向量。考虑出现所有文档中的单词,再决定将哪些单词纳入词汇表或者说所要的词汇集合,然后必须要将每一篇文档转换为词汇表上的向量。简单起见,我们先假设已经将本文切分完毕,存放到列表中,并对词汇向量进行分类标注。编写代码如下:

# -*- coding: UTF-8 -*-

"""
函数说明:创建实验样本

Parameters:
    无
Returns:
    postingList - 实验样本切分的词条
    classVec - 类别标签向量
Author:
    Jack Cui
Blog:
    http://blog.csdn.net/c406495762
Modify:
    2017-08-11
"""
def loadDataSet():
    postingList=[['my', 'dog', 'has', 'flea', 'problems', 'help', 'please'],                #切分的词条
                 ['maybe', 'not', 'take', 'him', 'to', 'dog', 'park', 'stupid'],
                 ['my', 'dalmation', 'is', 'so', 'cute', 'I', 'love', 'him'],
                 ['stop', 'posting', 'stupid', 'worthless', 'garbage'],
                 ['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

if __name__ == '__main__':
    postingLIst, classVec = loadDataSet()
    for each in postingLIst:
        print(each)
    print(classVec)
['my', 'dog', 'has', 'flea', 'problems', 'help', 'please']
['maybe', 'not', 'take', 'him', 'to', 'dog', 'park', 'stupid']
['my', 'dalmation', 'is', 'so', 'cute', 'I', 'love', 'him']
['stop', 'posting', 'stupid', 'worthless', 'garbage']
['mr', 'licks', 'ate', 'my', 'steak', 'how', 'to', 'stop', 'him']
['quit', 'buying', 'worthless', 'dog', 'food', 'stupid']
[0, 1, 0, 1, 0, 1]

从运行结果可以看出,我们已经将postingList是存放词条列表中,classVec是存放每个词条的所属类别,1代表侮辱类 ,0代表非侮辱类。 继续编写代码,前面我们已经说过我们要先创建一个词汇表,并将切分好的词条转换为词条向量

# -*- coding: UTF-8 -*-

"""
函数说明:创建实验样本

Parameters:
    无
Returns:
    postingList - 实验样本切分的词条
    classVec - 类别标签向量
Author:
    Jack Cui
Blog:
    http://blog.csdn.net/c406495762
Modify:
    2017-08-11
"""
def loadDataSet():
    postingList=[['my', 'dog', 'has', 'flea', 'problems', 'help', 'please'],                #切分的词条
                 ['maybe', 'not', 'take', 'him', 'to', 'dog', 'park', 'stupid'],
                 ['my', 'dalmation', 'is', 'so', 'cute', 'I', 'love', 'him'],
                 ['stop', 'posting', 'stupid', 'worthless', 'garbage'],
                 ['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

"""
函数说明:根据vocabList词汇表,将inputSet向量化,向量的每个元素为1或0

Parameters:
    vocabList - createVocabList返回的列表
    inputSet - 切分的词条列表
Returns:
    returnVec - 文档向量,词集模型
Author:
    Jack Cui
Blog:
    http://blog.csdn.net/c406495762
Modify:
    2017-08-11
"""
def setOfWords2Vec(vocabList, inputSet):
    returnVec = [0] * len(vocabList)                                    #创建一个其中所含元素都为0的向量
    for word in inputSet:                                                #遍历每个词条
        if word in vocabList:                                            #如果词条存在于词汇表中,则置1
            returnVec[vocabList.index(word)] = 1
        else: print("the word: %s is not in my Vocabulary!" % word)
    return returnVec                                                    #返回文档向量

"""
函数说明:将切分的实验样本词条整理成不重复的词条列表,也就是词汇表

Parameters:
    dataSet - 整理的样本数据集
Returns:
    vocabSet - 返回不重复的词条列表,也就是词汇表
Author:
    Jack Cui
Blog:
    http://blog.csdn.net/c406495762
Modify:
    2017-08-11
"""
def createVocabList(dataSet):
    vocabSet = set([])                      #创建一个空的不重复列表
    for document in dataSet:               
        vocabSet = vocabSet | set(document) #取并集
    return list(vocabSet)

if __name__ == '__main__':
    postingList, classVec = loadDataSet()
    print('postingList:\n',postingList)
    myVocabList = createVocabList(postingList)
    print('myVocabList:\n',myVocabList)
    trainMat = []
    for postinDoc in postingList:
        trainMat.append(setOfWords2Vec(myVocabList, postinDoc))
    print('trainMat:\n', trainMat)
postingList:
 [['my', 'dog', 'has', 'flea', 'problems', 'help', 'please'], ['maybe', 'not', 'take', 'him', 'to', 'dog', 'park', 'stupid'], ['my', 'dalmation', 'is', 'so', 'cute', 'I', 'love', 'him'], ['stop', 'posting', 'stupid', 'worthless', 'garbage'], ['mr', 'licks', 'ate', 'my', 'steak', 'how', 'to', 'stop', 'him'], ['quit', 'buying', 'worthless', 'dog', 'food', 'stupid']]
myVocabList:
 ['park', 'him', 'dalmation', 'cute', 'posting', 'has', 'stupid', 'help', 'maybe', 'ate', 'licks', 'please', 'take', 'worthless', 'food', 'is', 'to', 'garbage', 'problems', 'dog', 'quit', 'so', 'stop', 'how', 'mr', 'flea', 'I', 'buying', 'love', 'my', 'steak', 'not']
trainMat:
 [[0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0], [1, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1], [0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1, 1, 0, 0], [0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 1, 1, 0], [0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0]]

我们已经得到了词条向量。接下来,我们就可以通过词条向量训练朴素贝叶斯分类器。

# -*- coding: UTF-8 -*-
import numpy as np

"""
函数说明:创建实验样本

Parameters:
    无
Returns:
    postingList - 实验样本切分的词条
    classVec - 类别标签向量
Author:
    Jack Cui
Blog:
    http://blog.csdn.net/c406495762
Modify:
    2017-08-11
"""
def loadDataSet():
    postingList=[['my', 'dog', 'has', 'flea', 'problems', 'help', 'please'],                #切分的词条
                 ['maybe', 'not', 'take', 'him', 'to', 'dog', 'park', 'stupid'],
                 ['my', 'dalmation', 'is', 'so', 'cute', 'I', 'love', 'him'],
                 ['stop', 'posting', 'stupid', 'worthless', 'garbage'],
                 ['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

"""
函数说明:根据vocabList词汇表,将inputSet向量化,向量的每个元素为1或0

Parameters:
    vocabList - createVocabList返回的列表
    inputSet - 切分的词条列表
Returns:
    returnVec - 文档向量,词集模型
Author:
    Jack Cui
Blog:
    http://blog.csdn.net/c406495762
Modify:
    2017-08-11
"""
def setOfWords2Vec(vocabList, inputSet):
    returnVec = [0] * len(vocabList)                                    #创建一个其中所含元素都为0的向量
    for word in inputSet:                                                #遍历每个词条
        if word in vocabList:                                            #如果词条存在于词汇表中,则置1
            returnVec[vocabList.index(word)] = 1
        else: print("the word: %s is not in my Vocabulary!" % word)
    return returnVec                                                    #返回文档向量

"""
函数说明:将切分的实验样本词条整理成不重复的词条列表,也就是词汇表

Parameters:
    dataSet - 整理的样本数据集
Returns:
    vocabSet - 返回不重复的词条列表,也就是词汇表
Author:
    Jack Cui
Blog:
    http://blog.csdn.net/c406495762
Modify:
    2017-08-11
"""
def createVocabList(dataSet):
    vocabSet = set([])                      #创建一个空的不重复列表
    for document in dataSet:
        vocabSet = vocabSet | set(document) #取并集
    return list(vocabSet)

"""
函数说明:朴素贝叶斯分类器训练函数

Parameters:
    trainMatrix - 训练文档矩阵,即setOfWords2Vec返回的returnVec构成的矩阵
    trainCategory - 训练类别标签向量,即loadDataSet返回的classVec
Returns:
    p0Vect - 侮辱类的条件概率数组
    p1Vect - 非侮辱类的条件概率数组
    pAbusive - 文档属于侮辱类的概率
Author:
    Jack Cui
Blog:
    http://blog.csdn.net/c406495762
Modify:
    2017-08-12
"""
def trainNB0(trainMatrix,trainCategory):
    numTrainDocs = len(trainMatrix)                            #计算训练的文档数目
    numWords = len(trainMatrix[0])                            #计算每篇文档的词条数
    pAbusive = sum(trainCategory)/float(numTrainDocs)        #文档属于侮辱类的概率
    p0Num = np.zeros(numWords); p1Num = np.zeros(numWords)    #创建numpy.zeros数组,词条出现数初始化为0
    p0Denom = 0.0; p1Denom = 0.0                            #分母初始化为0
    for i in range(numTrainDocs):
        if trainCategory[i] == 1:                            #统计属于侮辱类的条件概率所需的数据,即P(w0|1),P(w1|1),P(w2|1)···
            p1Num += trainMatrix[i]
            p1Denom += sum(trainMatrix[i])
        else:                                                #统计属于非侮辱类的条件概率所需的数据,即P(w0|0),P(w1|0),P(w2|0)···
            p0Num += trainMatrix[i]
            p0Denom += sum(trainMatrix[i])
    p1Vect = p1Num/p1Denom                                      
    p0Vect = p0Num/p0Denom         
    return p0Vect,p1Vect,pAbusive                            #返回属于侮辱类的条件概率数组,属于非侮辱类的条件概率数组,文档属于侮辱类的概率

if __name__ == '__main__':
    postingList, classVec = loadDataSet()
    myVocabList = createVocabList(postingList)
    print('myVocabList:\n', myVocabList)
    trainMat = []
    for postinDoc in postingList:
        trainMat.append(setOfWords2Vec(myVocabList, postinDoc))
    p0V, p1V, pAb = trainNB0(trainMat, classVec)
    print('p0V:\n', p0V)
    print('p1V:\n', p1V)
    print('classVec:\n', classVec)
    print('pAb:\n', pAb)
myVocabList:
 ['park', 'him', 'dalmation', 'cute', 'posting', 'has', 'stupid', 'help', 'maybe', 'ate', 'licks', 'please', 'take', 'worthless', 'food', 'is', 'to', 'garbage', 'problems', 'dog', 'quit', 'so', 'stop', 'how', 'mr', 'flea', 'I', 'buying', 'love', 'my', 'steak', 'not']
p0V:
 [0.         0.08333333 0.04166667 0.04166667 0.         0.04166667
 0.         0.04166667 0.         0.04166667 0.04166667 0.04166667
 0.         0.         0.         0.04166667 0.04166667 0.
 0.04166667 0.04166667 0.         0.04166667 0.04166667 0.04166667
 0.04166667 0.04166667 0.04166667 0.         0.04166667 0.125
 0.04166667 0.        ]
p1V:
 [0.05263158 0.05263158 0.         0.         0.05263158 0.
 0.15789474 0.         0.05263158 0.         0.         0.
 0.05263158 0.10526316 0.05263158 0.         0.05263158 0.05263158
 0.         0.10526316 0.05263158 0.         0.05263158 0.
 0.         0.         0.         0.05263158 0.         0.
 0.         0.05263158]
classVec:
 [0, 1, 0, 1, 0, 1]
pAb:
 0.5

运行结果如下,p0V存放的是每个单词属于类别0,也就是非侮辱类词汇的概率。比如p0V的倒数第6个概率,就是stupid这个单词属于非侮辱类的概率为0。同理,p1V的倒数第6个概率,就是stupid这个单词属于侮辱类的概率为0.15789474,也就是约等于15.79%的概率。我们知道stupid的中文意思是蠢货,难听点的叫法就是傻逼。显而易见,这个单词属于侮辱类。pAb是所有侮辱类的样本占所有样本的概率,从classVec中可以看出,一用有3个侮辱类,3个非侮辱类。所以侮辱类的概率是0.5。因此p0V存放的就是P(him|非侮辱类) = 0.0833、P(is|非侮辱类) = 0.0417,一直到P(dog|非侮辱类) = 0.0417,这些单词的条件概率。同理,p1V存放的就是各个单词属于侮辱类的条件概率。pAb就是先验概率。

# -*- coding: UTF-8 -*-
import numpy as np
from functools import reduce

"""
函数说明:创建实验样本

Parameters:
    无
Returns:
    postingList - 实验样本切分的词条
    classVec - 类别标签向量
Author:
    Jack Cui
Blog:
    http://blog.csdn.net/c406495762
Modify:
    2017-08-11
"""
def loadDataSet():
    postingList=[['my', 'dog', 'has', 'flea', 'problems', 'help', 'please'],                #切分的词条
                ['maybe', 'not', 'take', 'him', 'to', 'dog', 'park', 'stupid'],
                ['my', 'dalmation', 'is', 'so', 'cute', 'I', 'love', 'him'],
                ['stop', 'posting', 'stupid', 'worthless', 'garbage'],
                ['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                                                             #返回实验样本切分的词条和类别标签向量

"""
函数说明:将切分的实验样本词条整理成不重复的词条列表,也就是词汇表

Parameters:
    dataSet - 整理的样本数据集
Returns:
    vocabSet - 返回不重复的词条列表,也就是词汇表
Author:
    Jack Cui
Blog:
    http://blog.csdn.net/c406495762
Modify:
    2017-08-11
"""
def createVocabList(dataSet):
    vocabSet = set([])                      #创建一个空的不重复列表
    for document in dataSet:                
        vocabSet = vocabSet | set(document) #取并集
    return list(vocabSet)

"""
函数说明:根据vocabList词汇表,将inputSet向量化,向量的每个元素为1或0

Parameters:
    vocabList - createVocabList返回的列表
    inputSet - 切分的词条列表
Returns:
    returnVec - 文档向量,词集模型
Author:
    Jack Cui
Blog:
    http://blog.csdn.net/c406495762
Modify:
    2017-08-11
"""
def setOfWords2Vec(vocabList, inputSet):
    returnVec = [0] * len(vocabList)                                    #创建一个其中所含元素都为0的向量
    for word in inputSet:                                               #遍历每个词条
        if word in vocabList:                                           #如果词条存在于词汇表中,则置1
            returnVec[vocabList.index(word)] = 1
        else: print("the word: %s is not in my Vocabulary!" % word)
    return returnVec                                                    #返回文档向量


"""
函数说明:朴素贝叶斯分类器训练函数

Parameters:
    trainMatrix - 训练文档矩阵,即setOfWords2Vec返回的returnVec构成的矩阵
    trainCategory - 训练类别标签向量,即loadDataSet返回的classVec
Returns:
    p0Vect - 侮辱类的条件概率数组
    p1Vect - 非侮辱类的条件概率数组
    pAbusive - 文档属于侮辱类的概率
Author:
    Jack Cui
Blog:
    http://blog.csdn.net/c406495762
Modify:
    2017-08-12
"""
def trainNB0(trainMatrix,trainCategory):
    numTrainDocs = len(trainMatrix)                         #计算训练的文档数目
    numWords = len(trainMatrix[0])                          #计算每篇文档的词条数
    pAbusive = sum(trainCategory)/float(numTrainDocs)       #文档属于侮辱类的概率
    p0Num = np.zeros(numWords); p1Num = np.zeros(numWords)  #创建numpy.zeros数组,
    p0Denom = 0.0; p1Denom = 0.0                            #分母初始化为0.0
    for i in range(numTrainDocs):
        if trainCategory[i] == 1:                           #统计属于侮辱类的条件概率所需的数据,即P(w0|1),P(w1|1),P(w2|1)···
            p1Num += trainMatrix[i]
            p1Denom += sum(trainMatrix[i])
        else:                                               #统计属于非侮辱类的条件概率所需的数据,即P(w0|0),P(w1|0),P(w2|0)···
            p0Num += trainMatrix[i]
            p0Denom += sum(trainMatrix[i])
    p1Vect = p1Num/p1Denom                                  #相除        
    p0Vect = p0Num/p0Denom          
    return p0Vect,p1Vect,pAbusive                           #返回属于侮辱类的条件概率数组,属于非侮辱类的条件概率数组,文档属于侮辱类的概率

"""
函数说明:朴素贝叶斯分类器分类函数

Parameters:
    vec2Classify - 待分类的词条数组
    p0Vec - 侮辱类的条件概率数组
    p1Vec -非侮辱类的条件概率数组
    pClass1 - 文档属于侮辱类的概率
Returns:
    0 - 属于非侮辱类
    1 - 属于侮辱类
Author:
    Jack Cui
Blog:
    http://blog.csdn.net/c406495762
Modify:
    2017-08-12
"""
def classifyNB(vec2Classify, p0Vec, p1Vec, pClass1):
    p1 = reduce(lambda x,y:x*y, vec2Classify * p1Vec) * pClass1             #对应元素相乘
    p0 = reduce(lambda x,y:x*y, vec2Classify * p0Vec) * (1.0 - pClass1)
    print('p0:',p0)
    print('p1:',p1)
    if p1 > p0:
        return 1
    else: 
        return 0

"""
函数说明:测试朴素贝叶斯分类器

Parameters:
    无
Returns:
    无
Author:
    Jack Cui
Blog:
    http://blog.csdn.net/c406495762
Modify:
    2017-08-12
"""
def testingNB():
    listOPosts,listClasses = loadDataSet()                                  #创建实验样本
    myVocabList = createVocabList(listOPosts)                               #创建词汇表
    trainMat=[]
    for postinDoc in listOPosts:
        trainMat.append(setOfWords2Vec(myVocabList, postinDoc))             #将实验样本向量化
    p0V,p1V,pAb = trainNB0(np.array(trainMat),np.array(listClasses))        #训练朴素贝叶斯分类器
    testEntry = ['love', 'my', 'dalmation']                                 #测试样本1
    thisDoc = np.array(setOfWords2Vec(myVocabList, testEntry))              #测试样本向量化
    if classifyNB(thisDoc,p0V,p1V,pAb):
        print(testEntry,'属于侮辱类')                                        #执行分类并打印分类结果
    else:
        print(testEntry,'属于非侮辱类')                                       #执行分类并打印分类结果
    testEntry = ['stupid', 'garbage']                                       #测试样本2

    thisDoc = np.array(setOfWords2Vec(myVocabList, testEntry))              #测试样本向量化
    if classifyNB(thisDoc,p0V,p1V,pAb):
        print(testEntry,'属于侮辱类')                                        #执行分类并打印分类结果
    else:
        print(testEntry,'属于非侮辱类')                                       #执行分类并打印分类结果

if __name__ == '__main__':
    testingNB()
    p0: 0.0
    p1: 0.0
    ['love', 'my', 'dalmation'] 属于非侮辱类
    p0: 0.0
    p1: 0.0
    ['stupid', 'garbage'] 属于非侮辱类

我们抽取我们认为是非侮辱类的词汇和认为是侮辱类的词汇让我们的分类器进行判别,发现他们是侮辱内的概率都为0.这个问题并不是来自我们的计算过程出错了.而是没有对数据进行处理.在计算的过程中,我们计算一个词是不是侮辱类的时候我们的流程可以描述为:
1. 采集数据创建词汇表
2. 把样本进行向量化
3. 把样本的标签对句子中的所有词汇进行分权
4. 把词汇的侮辱类的权重叠加,把词汇的非侮辱类的权重叠加
5. 利用4中产生的侮辱类高频词以及非侮辱类高频词的出现期望(注释2)判别句子的性质

那么在这个过程中,我们判别一个句子的性质的时候是用他每个词的侮辱类概率相乘所得(如果相加会对句子长度大的句子产生较大误差),这个过程中如果出现任意一个词的侮辱类概率为0的时候,这个句子将不能判别为侮辱类.那么我们在这个过程中可能会需要用到2个处理手段.极大似然法估计,用以处理连乘时对计算机造成的计算负担.以及拉普拉斯修正用以处理部分概率为0的词汇带来的负面影响.

极大似然估计(maximum likelihood estimation)

通过事实,推断出最有可能的硬币情况,就是最大似然估计。
概率vs似然 让我们先来比较下概率和似然.
为了避免和我们想讨论的概率混淆,我们把硬币的“花”出现的概率称为硬币的参数。


概率

已知硬币的参数,就可以去推测抛硬币的各种情况的可能性,这称为概率
似然

我们对硬币的参数并不清楚,要通过抛硬币的情况去推测硬币的参数,这称为似然


这里讲述一下如何用极大似然法得出估计值.

估计原理

此部分内容选读,实际计算机一般都会为我们处理计算上的麻烦,除非你要自己编写算法而不用其他人的库.只理解概念的话请详读参考资料的如何通俗地理解概率论中的「极大似然估计法」?
由于样本集中的样本都是独立同分布(注释1),可以只考虑一类样本集D,来估计参数向量θ。记已知的样本集为:

D={x1,x2,...,xN} D = { x 1 , x 2 , . . . , x N }

其中
x1,x2,...,xN x 1 , x 2 , . . . , x N 为 样 本 集 中 的 样 本
似然函数(likelihood function): 联合概率密度函数(注释9)
P(D|θ) P ( D | θ ) 称为相对于
x1,x2,...,xN x 1 , x 2 , . . . , x N 的似然函数.
l(θ)=p(D|θ)=p(x1,x2,...,xN|θ)=i=1Np(xi|θ) l ( θ ) = p ( D | θ ) = p ( x 1 , x 2 , . . . , x N | θ ) = ∏ i = 1 N p ( x i | θ )

其中 Ni=1p(xi|θ),p(x1|θ)×p(x2|θ)×...×p(xN|θ) ∏ i = 1 N p ( x i | θ ) 表 示 连 乘 , p ( x 1 | θ ) × p ( x 2 | θ ) × . . . × p ( x N | θ )
如果 θ^ θ ^ 是 参数空间(注释3)中能使得似然函数 l(θ) l ( θ ) 最大的 θ θ 值,则 θ^ θ ^ 应该是”最可能”的参数值,那么 θ^ θ ^ 就是 θ θ 的极大似然估计量.它是样本集的函数,记作:
θ^=d(x1,x2,...,xN)=d(D) θ ^ = d ( x 1 , x 2 , . . . , x N ) = d ( D )

求解极大似然函数

ML估计:求使得出现该组样本的概率最大的 θ θ 值.

θ^=argmaxθl(θ)=argmaxθi=1Np(xi)|θ) θ ^ = a r g m a x θ l ( θ ) = a r g m a x θ ∏ i = 1 N p ( x i ) | θ )

实际中为了 便于分析(注释4),定义对数似然函数:
H(θ)=lnl(θ) H ( θ ) = l n l ( θ )

θ^=argmaxθH(θ)=argmaxθlnl(θ)=argmaxθi=1Np(xi)|θ) θ ^ = a r g m a x θ H ( θ ) = a r g m a x θ l n l ( θ ) = a r g m a x θ ∑ i = 1 N p ( x i ) | θ )

1) 未知参数只有一个( θ θ 为标量)
在似然函数瞒住连续,可微的正则条件下,极大似然估计是下面微分方程的解:
dl(θ)dθ=0 d l ( θ ) d θ = 0
或者等价于
dH(θ)dθ=dlnl(θ)dθ=0 d H ( θ ) d θ = d l n l ( θ ) d θ = 0

即求对 θ θ 的偏导数
2) 未知参数有多个( θ θ 为向量)
θ θ 可表示为具有S个分量的未知向量:
θ=[θ1,θ2,...,θs]T θ = [ θ 1 , θ 2 , . . . , θ s ] T

[θ1,θ2,...,θs]T [ θ 1 , θ 2 , . . . , θ s ] T 中上标T表示向量转置,
[θ1,θ2,...,θs] [ θ 1 , θ 2 , . . . , θ s ] 表示行向量,
[θ1,θ2,...,θs]T [ θ 1 , θ 2 , . . . , θ s ] T 表示列向量
记梯度算子:
θ=[θ1,θ2,...,θs]T ▽ θ = [ ∂ ∂ θ 1 , ∂ ∂ θ 2 , . . . , ∂ ∂ θ s ] T

若似然函数满足连续可导的条件,则最大似然估计量就是如下方方程的解.
θH(θ)=θlnl(θ)=i=1NθlnP(xi|θ)=0 ▽ θ H ( θ ) = ▽ θ l n l ( θ ) = ∑ i = 1 N ▽ θ l n P ( x i | θ ) = 0

方程的解只是一个估计值,只有在样本数趋于无限多的时候,它才会接近于真实值。
总结

    求最大似然估计量的一般步骤:

    (1)写出似然函数;

    (2)对似然函数取对数,并整理;

    (3)求导数;

    (4)解似然方程。

    最大似然估计的特点:

    1.比其他估计方法更加简单;

    2.收敛性:无偏或者渐近无偏,当样本数目增加时,收敛性质会更好;

    3.如果假设的类条件概率模型正确,则通常能获得较好的结果。但如果假设模型出现偏差,将导致非常差的估计结果。

拉普拉斯修正(拉普拉斯平滑)(Laplacian correction)

拉普拉斯平滑(Laplace Smoothing)又被称为加 1 平滑,是比较常用的平滑方法。平滑方法的存在时为了解决零概率问题。
在实际的使用中也经常使用加
λ λ (1≥
λ λ ≥0)来代替简单加1。如果对N个计数都加上
λ λ ,这时分母也要记得加上 N×λ N × λ
在上述分类句子性质的例子中,因为即使在明显是侮辱性句子中也包含未被观测(P(x)=0)的字眼或词汇.这个时候判断出来的结果因为是把概率相乘获得的结果,此时会得到是侮辱性概率为0的最终结果,显然是不符合实际情况的.为了解决这个情况,我们加入拉普拉斯修正(平滑).即把

P(c)=|Dc||D| P ( c ) = | D c | | D |
修改为
P^(c)=|Dc|+1|D|+N P ^ ( c ) = | D c | + 1 | D | + N

P(xi|c)=|Dc,xi||Dc| P ( x i | c ) = | D c , x i | | D c |
修改为
P^(xi|c)=|Dc,xi|+1|Dc|+Ni P ^ ( x i | c ) = | D c , x i | + 1 | D c | + N i

P(c) P ( c ) 为先验概率
|D| | D | 为样本集的元素个数
|Dc| | D c | 为样本集中第c类样本的元素个数
N N 表示训练集D种可能的类别数
Ni N i 表示第i个属性可能的取值数
此时结合极大似然估计和拉普拉斯修正修改代码如下

# coding:utf-8
# -*- coding: UTF-8 -*-
import numpy as np
from functools import reduce

"""
函数说明:创建实验样本

Parameters:
    无
Returns:
    postingList - 实验样本切分的词条
    classVec - 类别标签向量
Author:
    Jack Cui
Blog:
    http://blog.csdn.net/c406495762
Modify:
    2017-08-11
"""
def loadDataSet():
    postingList=[['my', 'dog', 'has', 'flea', 'problems', 'help', 'please'],                #切分的词条
                ['maybe', 'not', 'take', 'him', 'to', 'dog', 'park', 'stupid'],
                ['my', 'dalmation', 'is', 'so', 'cute', 'I', 'love', 'him'],
                ['stop', 'posting', 'stupid', 'worthless', 'garbage'],
                ['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                                                             #返回实验样本切分的词条和类别标签向量

"""
函数说明:将切分的实验样本词条整理成不重复的词条列表,也就是词汇表

Parameters:
    dataSet - 整理的样本数据集
Returns:
    vocabSet - 返回不重复的词条列表,也就是词汇表
Author:
    Jack Cui
Blog:
    http://blog.csdn.net/c406495762
Modify:
    2017-08-11
"""
def createVocabList(dataSet):
    vocabSet = set([])                      #创建一个空的不重复列表
    for document in dataSet:
        vocabSet = vocabSet | set(document) #取并集
    return list(vocabSet)

"""
函数说明:根据vocabList词汇表,将inputSet向量化,向量的每个元素为1或0

Parameters:
    vocabList - createVocabList返回的列表
    inputSet - 切分的词条列表
Returns:
    returnVec - 文档向量,词集模型
Author:
    Jack Cui
Blog:
    http://blog.csdn.net/c406495762
Modify:
    2017-08-11
"""
def setOfWords2Vec(vocabList, inputSet):
    returnVec = [0] * len(vocabList)                                    #创建一个其中所含元素都为0的向量
    for word in inputSet:                                               #遍历每个词条
        if word in vocabList:                                           #如果词条存在于词汇表中,则置1
            returnVec[vocabList.index(word)] = 1
        else: print("the word: %s is not in my Vocabulary!" % word)
    return returnVec                                                    #返回文档向量


"""
函数说明:朴素贝叶斯分类器训练函数

Parameters:
    trainMatrix - 训练文档矩阵,即setOfWords2Vec返回的returnVec构成的矩阵
    trainCategory - 训练类别标签向量,即loadDataSet返回的classVec
Returns:
    p0Vect - 侮辱类的条件概率数组
    p1Vect - 非侮辱类的条件概率数组
    pAbusive - 文档属于侮辱类的概率
Author:
    Jack Cui
Blog:
    http://blog.csdn.net/c406495762
Modify:
    2017-08-12
"""
def trainNB0(trainMatrix,trainCategory):
    numTrainDocs = len(trainMatrix)                            #计算训练的文档数目
    numWords = len(trainMatrix[0])                            #计算每篇文档的词条数
    pAbusive = sum(trainCategory)/float(numTrainDocs)        #文档属于侮辱类的概率
    p0Num = np.ones(numWords); p1Num = np.ones(numWords)    #创建numpy.ones数组,词条出现数初始化为1,拉普拉斯平滑
    p0Denom = 2.0; p1Denom = 2.0                            #分母初始化为2,拉普拉斯平滑
    for i in range(numTrainDocs):
        if trainCategory[i] == 1:                            #统计属于侮辱类的条件概率所需的数据,即P(w0|1),P(w1|1),P(w2|1)···
            p1Num += trainMatrix[i]
            p1Denom += sum(trainMatrix[i])
        else:                                                #统计属于非侮辱类的条件概率所需的数据,即P(w0|0),P(w1|0),P(w2|0)···
            p0Num += trainMatrix[i]
            p0Denom += sum(trainMatrix[i])
    p1Vect = np.log(p1Num/p1Denom)                            #取对数,防止下溢出
    p0Vect = np.log(p0Num/p0Denom)
    return p0Vect,p1Vect,pAbusive                            #返回属于侮辱类的条件概率数组,属于非侮辱类的条件概率数组,文档属于侮辱类的概率


"""
函数说明:朴素贝叶斯分类器分类函数

Parameters:
    vec2Classify - 待分类的词条数组
    p0Vec - 侮辱类的条件概率数组
    p1Vec -非侮辱类的条件概率数组
    pClass1 - 文档属于侮辱类的概率
Returns:
    0 - 属于非侮辱类
    1 - 属于侮辱类
Author:
    Jack Cui
Blog:
    http://blog.csdn.net/c406495762
Modify:
    2017-08-12
"""
def classifyNB(vec2Classify, p0Vec, p1Vec, pClass1):
    p1 = sum(vec2Classify * p1Vec) + np.log(pClass1)        #对应元素相乘。logA * B = logA + logB,所以这里加上log(pClass1)
    p0 = sum(vec2Classify * p0Vec) + np.log(1.0 - pClass1)
    if p1 > p0:
        return 1
    else:
        return 0

"""
函数说明:测试朴素贝叶斯分类器

Parameters:
    无
Returns:
    无
Author:
    Jack Cui
Blog:
    http://blog.csdn.net/c406495762
Modify:
    2017-08-12
"""
def testingNB():
    listOPosts,listClasses = loadDataSet()                                  #创建实验样本
    myVocabList = createVocabList(listOPosts)                               #创建词汇表
    trainMat=[]
    for postinDoc in listOPosts:
        trainMat.append(setOfWords2Vec(myVocabList, postinDoc))             #将实验样本向量化
    p0V,p1V,pAb = trainNB0(np.array(trainMat),np.array(listClasses))        #训练朴素贝叶斯分类器
    testEntry = ['love', 'my', 'dalmation']                                 #测试样本1
    thisDoc = np.array(setOfWords2Vec(myVocabList, testEntry))              #测试样本向量化
    if classifyNB(thisDoc,p0V,p1V,pAb):
        print(testEntry,'属于侮辱类')                                        #执行分类并打印分类结果
    else:
        print(testEntry,'属于非侮辱类')                                       #执行分类并打印分类结果
    testEntry = ['stupid', 'garbage']                                       #测试样本2

    thisDoc = np.array(setOfWords2Vec(myVocabList, testEntry))              #测试样本向量化
    if classifyNB(thisDoc,p0V,p1V,pAb):
        print(testEntry,'属于侮辱类')                                        #执行分类并打印分类结果
    else:
        print(testEntry,'属于非侮辱类')                                       #执行分类并打印分类结果

if __name__ == '__main__':
    testingNB()

其他贝叶斯分类算法

SPODE

上文中的例子采用的是朴素贝叶斯定理,需要假定样本中的每个属性(随机事件)是独立的,相互不干扰的.
在生产应用中为了降低贝叶斯公式中估计后验概率
P(c|x) P ( c | x ) 的困难,朴素贝叶斯分类器采用了属性条件独立性假设,但是在显示任务重这个假设往往很难成立,于是,人们尝试对属性条件独立性假设进行一定程度的放松,由此产生了一类称为”半朴素贝叶斯分类器“(semi-naive Bayes classifiers)的学习方法.
半朴素贝叶斯分类器的基本想法是适当考虑一部分属性间的相互依赖信息,从而既不需进行完全联合概率计算,又不至于策底忽略了比较强的属性依赖关系.”独依赖估计”(One-Dependent Estimator,ODE)是半朴素贝叶斯分类器最常用的一种策略,顾名思义,所谓”独依赖”就是假设每个属性在类别之外最多仅依赖于一个其他属性.即

P(c|x)P(c)i=1dP(xi|c,pai) P ( c | x ) ∝ P ( c ) ∏ i = 1 d P ( x i | c , p a i )
(注释6)
其中,
pai p a i 为属性 xi x i 所依赖的属性,称为 xi x i 的父属性,此时,对每个属性 xi x i ,若其父属性,
如果 假设所有的属性都依赖于同一个属性,则该属性称为”超父”(super-parent),然后可以通过 交叉验证(注释8)等模型选择方法来确定超父属性,即SPODE(Super-Parent ODE)

TAN

TAN(Tree Augmented naive Bayes)是计算每个属性之间的条件互信息(conditional mutual information)(注释7)然后保留强关联的属性依赖性.
TAN方法。每个属性依赖的另外的属性由最大带权生成树来确定

(1)先求每个属性之间的互信息来作为他们之间的权值。

(2)构件完全图。权重是刚才求得的互信息。然后用最大带权生成树算法求得此图的最大带权的生成树。

(3)找一个根变量,然后依次将图变为有向图。

(4)添加类别y到每个属性的的有向边。

AODE

AODE(Averaged One-Dependent Estimator)是一种基于集成学习机制,更为强大的独依赖分类器,与SPODE不同,AODE尝试将每个属性作为超父属性来构建SPODE,然后把具有猪狗训练数据支撑的SPODE集成起来作为最终结果.

机器学习(其四)贝叶斯分类器_第4张图片

信念网(belief network)

他借助有向无环图(Directed Acyclic Graph,DAG)来刻画属性之间的依赖关系,并使用条件概率表(Conditional Probability Table,CPT)来描述属性的联合概率分布.
具体来说,一个贝叶斯网B由结构G和参数 θ θ 两个部分构成, B=<G,θ> B =< G , θ > .王国结构G是一个有向无环图,其每个节点对应于一个属性,若两个属性有直接依赖关系,则它们由一条边连接起来;参数 θ θ 定量描述这个依赖关系,假设属性 xi x i 在G中的父结点集为 πi π i θ θ 包含了每个属性的条件概率表 θxi|πi=PB(xi|πi) θ x i | π i = P B ( x i | π i )
作为一个例子,图7.2给出了西瓜问题的一种贝叶斯网结构和属性”根蒂”的条件概率表,从图中网络结构可看出,”色泽”直接依赖于”好瓜”和”甜度”,而”根蒂”则直接依赖于”甜度”;进一步从条件概率表能得到”根蒂”对”甜度”量化依赖关系,如P(根蒂=硬挺|甜度=高)=0.1等

机器学习(其四)贝叶斯分类器_第5张图片

结构

贝叶斯网结构有效的表达了属性见的条件独立性,给定父结点集,贝叶斯网假设每个属性与他的非后裔属性独立,于是 B=<G,θ> B =< G , θ > 将属性 x1,x2,...,xd x 1 , x 2 , . . . , x d 的联合概率分布定义为

PB(x1,x2,...,xd)=i=1dPB(xi|πi)=i=1dθxi|πi P B ( x 1 , x 2 , . . . , x d ) = ∏ i = 1 d P B ( x i | π i ) = ∏ i = 1 d θ x i | π i

以下图为例,联合概率分布定义为
P(x1,x2,x3,x4,x5)=P(x1)P(x2)P(x3|x1)P(x4|x1,x2)P(x5|x2) P ( x 1 , x 2 , x 3 , x 4 , x 5 ) = P ( x 1 ) P ( x 2 ) P ( x 3 | x 1 ) P ( x 4 | x 1 , x 2 ) P ( x 5 | x 2 )

显然, x3 x 3 x4 x 4 在给定 x1 x 1 的取值时独立, x4 x 4 x5 x 5 在给定 x2 x 2 的取值时独立,分别简记为 x3x4|x1 x 3 ⊥ x 4 | x 1 x4x5|x2 x 4 ⊥ x 5 | x 2

sklearn中贝叶斯的实现

详见
朴素贝叶斯之新浪新闻分类(Sklearn)
之前描述的区别句子性质的例子是英文的,而如果把这个判别方法应用到中文上就需要对中文进行分词.文中利用中文分词库结巴对新闻进行拆分,然后去除掉含义不明显的词语,比如说”但是”,”的”…等,然后用含有意义的词汇进行判别.

参考资料

朴素贝叶斯分类器(Naive Bayes Classifiers)
Python3《机器学习实战》学习笔记(四):朴素贝叶斯基础篇之言论过滤器
Python3《机器学习实战》学习笔记(五):朴素贝叶斯实战篇之新浪新闻分类
《概率论基础教程》(美)罗斯
《机器学习》周志华
极大似然估计详解
如何通俗地理解概率论中的「极大似然估计法」?

注释

①.独立同分布

在概率统计理论中,指随机过程中,任何时刻的取值都为随机变量,如果这些随机变量服从同一分布,并且互相独立,那么这些随机变量是独立同分布。独立同分布最早应用于统计学,随着科学的发展,独立同分布已经应用数据挖掘,信号处理等不同的领域。

②.期望

在概率论和统计学中,数学期望(mean)(或均值,亦简称期望)是试验中每次可能结果的概率乘以其结果的总和,是最基本的数学特征之一。它反映随机变量平均取值的大小。
需要注意的是,期望值并不一定等同于常识中的“期望”——“期望值”也许与每一个结果都不相等。期望值是该变量输出值的平均数。期望值并不一定包含于变量的输出值集合里。

③.参数空间

参数空间是数学术语,自然科学计算机术语。设(X1,……,Xn)为来自总体X的样本,(x1,…xn)为相应的样本值,θ是总体分布的未知参数,θ∈Θ, Θ表示θ的取值范围,称Θ为参数空间。

④.便于分析

因为对数有以下性质.ln(abc)=lna+lnb+lnc.在对于一些数学处理的时候,连乘会导致数值非常趋近于0或者数值非常大,所以在这种情况下我们使用取对数的做法把连乘变成连加,使得数值更有价值.  

其中
lnea=a l n e a = a
因为对数函数和原函数的取得最大值的位置一样(如下图),即两个函数有相同的单调性,所以可以求取对数函数的最大值来估计原函数的最大值.

机器学习(其四)贝叶斯分类器_第6张图片

⑤. h:χy h : χ ↦ y

指函数h是从

χ χ 映射到
y y 的,符号
读作maps to

⑥. ab a ∝ b

表示a与b成比例,读作a proportional to b

⑦.条件互信息

首先说明互信息
互信息(Mutual Information)是信息论里一种有用的信息度量,它可以看成是一个随机变量中包含的关于另一个随机变量的信息量,或者说是一个随机变量由于已知另一个随机变量而减少的不肯定性 。
条件互信息就是在某个概率事件发生的情况下的(即对计算互信息的两个随机事件的概率加权)互信息

⑧.交叉检验

交叉验证(Cross-validation)主要用于建模应用中,例如PCR 、PLS 回归建模中。在给定的建模样本中,拿出大部分样本进行建模型,留小部分样本用刚建立的模型进行预报,并求这小部分样本的预报误差,记录它们的平方加和。

⑨.联合概率密度函数

在数学中,连续型随机变量的概率密度函数(在不至于混淆时可以简称为密度函数)是一个描述这个随机变量的输出值,在某个确定的取值点附近的可能性的函数。而随机变量的取值落在某个区域之内的概率则为概率密度函数在这个区域上的积分。当概率密度函数存在的时候,累积分布函数是概率密度函数的积分。概率密度函数一般以小写标记。
联合概率分布简称联合分布,是两个及以上随机变量组成的随机变量的概率分布。根据随机变量的不同,联合概率分布的表示形式也不同。对于离散型随机变量,联合概率分布可以以列表的形式表示,也可以以函数的形式表示;对于连续型随机变量,联合概率分布通过非负函数的积分表示。

你可能感兴趣的:(机器学习,python,人工智能,python,机器学习,贝叶斯分类器)