机器学习(三):朴素贝叶斯(NB)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/eeeee123456/article/details/79933031

引言:
    朴素贝叶斯(naïve Bayes)法是基于贝叶斯定理与特征条件独立假设的分类方法。在统计学中,相互独立的含义是它们中一个已发生,不影响另一个发生的概率,即特征条件独立指一个特征出现的可能性与它的相邻没有关系。
    基本思想如下:
    假设训练集标记类别集合为{c1,c2},P(c1|x1,x2)为给定数据点(x1,x2)来自类别c1的概率;P(c2|x1,x2)为给定数据点(x1,x2)来自类别c2的概率。若P(c1|x1,x2) > P(c2|x1,x2),数据点属于c1;若P(c1|x1,x2) < P(c2|x1,x2),数据点属于c2
    具体地可利用贝叶斯定理计算概率值。运算中,利用了特征条件独立的假设,由于这是一个较强的假设,使整个形式化过程变得原始简单,“朴素”由此得名。

一、数学预备知识

1.条件概率

设A,B是两个事件,且P(A)>0,称

P ( B | A ) = P ( A B ) P ( A )
为在事件A发生的条件下事件B发生的条件概率。

2.乘法公式

P(A)>0,则有

P ( A B ) = P ( B | A ) P ( A )
设上式称为乘法公式。

3.全概率公式

设试验E的样本空间为S,A为E的事件,B1,B2,…,Bn为S的一个划分(即BiBj=∅,i≠j,i,j=1,2,…,n,且P{B1∪B2∪…∪Bn}=1),且P(Bi)>0(i=1,2,…,n),则

P ( A ) = P ( A | B 1 ) P ( B 1 ) + P ( A | B 2 ) P ( B 2 ) + + P ( A | B n ) P ( B n )
上式称为全概率公式。

4.贝叶斯公式

设试验E的样本空间为S,A为E的事件,B1,B2,…,Bn为S的一个划分(即BiBj=∅,i≠j,i,j=1,2,…,n,且P{B1∪B2∪…∪Bn}=1),且P(A)>0,P(Bi)>0(i=1,2,…,n),则

P ( B i | A ) = P ( B i A ) P ( A ) = P ( A | B i ) P ( B i ) ) j = 1 n P ( A | B j ) P ( B j )
上式称为贝叶斯公式。

5.独立的定义

设A,B是两事件,如果满足等式

P ( A B ) = P ( A ) P ( B )
则称事件A,B相互独立,简称A,B独立。



二、朴素贝叶斯分类器

    设输入空间X是n维实数向量空间 R n ,输出空间为标记类别集合 Y = { c 1 , c 2 , , c k } 。训练集为:

D = { ( x 1 , y 1 ) , ( x 2 , y 2 ) , , ( x N , y N ) }
其中 x i = ( x i ( 1 ) , x i ( 2 ) , , x i ( i ) , , x i ( n ) ) T x i ( j ) 是第i个实例的第j个特征。
    设测试实例为 x = ( x ( 1 ) , x ( 2 ) , , x ( n ) ) T ,其中 x ( i ) 表示测试实例的第i个特征。计算 P ( Y = c k | X = x ) ,k=1,2,…,K,即得测试实例x属于 c k 的概率,在K个概率中选择概率最大的,所对应的 c k 则为测试实例x所属类别。
将上式条件代入贝叶斯公式可得:
P ( Y = c k | X = x ) = P ( X = x | Y = c k ) P ( Y = c k ) i = 1 K P ( X = x | Y = c i ) P ( Y = c i )                             ( 1 )

1.条件概率

根据定义计算条件概率 P ( X = x | Y = c k )

P ( X = x | Y = c k ) = P ( X ( 1 ) = x ( 1 ) , X ( 2 ) = x ( 2 ) , , X ( n ) = x ( n ) | Y = c k ) k = 1 , 2 , , K
因为朴素贝叶斯法对条件概率分布作了条件独立性的假设,则可以根据独立的定义得:
P ( X = x | Y = c k ) = P ( X ( 1 ) = x ( 1 ) | Y = c k ) P ( X ( 2 ) = x ( 2 ) | Y = c k ) P ( X ( n ) = x ( n ) | Y = c k ) = j = 1 n P ( X ( j ) = x ( j ) | Y = c k )                             ( 2 )

2.朴素贝叶斯公式

将(2)式代入(1),可得朴素贝叶斯公式:

P ( Y = c k | X = x ) = P ( Y = c k ) j = 1 n P ( X ( j ) = x ( j ) | Y = c k ) i = 1 K P ( Y = c i ) j = 1 n P ( X ( j ) = x ( j ) | Y = c i )                             ( 3 )

3.朴素贝叶斯分类器

x所属类别为能使(3)使取得最大值的 c k ,于是,朴素贝叶斯分类器可表示为:

y = f ( x ) = a r g   m a x c k P ( Y = c k ) j = 1 n P ( X ( j ) = x ( j ) | Y = c k ) i = 1 K P ( Y = c i ) j = 1 n P ( X ( j ) = x ( j ) | Y = c i )
注意到上式中分母对所有的 c k 都是相同的,所以
y = f ( x ) = a r g   m a x c k P ( Y = c k ) j = 1 n P ( X ( j ) = x ( j ) | Y = c k )




三、朴素贝叶斯的参数估计(未完,待续)


四、代码实现(python)

以下代码来自Peter Harrington《Machine Learing in Action》
    本例利用朴素贝叶斯对文档进行分类。
    以下文档数据来自斑点犬爱好者留言板。我们将文档看成单词向量,也就是说将句子转换为向量,每个单词对应一个特征。每条文档对应一个类别,本例类别为0和1,分别代表正常言论和侮辱性文字。
    根据贝叶斯公式,可得测试实例类别为 c k k = 0 , 1 c 0 = 0 c 1 = 1 的概率如下:

P ( Y = c k | X = x ) = P ( Y = c k ) j = 1 n P ( X ( j ) = x ( j ) | Y = c k ) i = 1 2 P ( Y = c i ) j = 1 n P ( X ( j ) = x ( j ) | Y = c i )
    本例需分别计算 P ( Y = c 0 | X = x ) P ( Y = c 1 | X = x ) ,并对比它们的大小,大概率对应的 c k 为最终值。观察上式可得对于k=0和k=1,分母相同,则对于 P ( Y = c k | X = x ) 的计算可化简为只求(假设P(0)为最终分类为0的概率,P(1)为最终分类为1的概率)
P ( 0 ) = P ( Y = c 0 ) j = 1 n P ( X ( j ) = x ( j ) | Y = c 0 )
P ( 1 ) = P ( Y = c 1 ) j = 1 n P ( X ( j ) = x ( j ) | Y = c 1 )

    利用贝叶斯分类器对文档进行分类时,要计算多个概率的乘积以获得文档属于某个类别的概率,即计算 j = 1 n P ( X ( j ) = x ( j ) | Y = c k ) ,如果其中一个概率值为0,那么最后的乘积也为0。为降低这种影响,可以将所有词的出现数初始化为1,并将分母初始化为2。
    另一个遇到的问题是下溢出,这是由于太多很小的数相乘造成的。当计算 j = 1 n P ( X ( j ) = x ( j ) | Y = c k ) 时,由于大部分因子都非常小,所以程序会下溢出或者得到不正确的答案。一种解决办法是对乘积取自然对数,在代数中有 l o g ( a × b ) = l o g ( a ) + l o g ( b ) ,于是通过求对数可以避免下溢出或者浮点数舍入导致的错误。同时,采用自然对数进行处理不会有任何损失。则最终需对比的数值为log(P(0))与log(P(1)),求法如下:
l o g   P ( 0 ) = l o g   P ( Y = c 0 ) + l o g   j = 1 n P ( X ( j ) = x ( j ) | Y = c 0 ) = l o g   P ( Y = c 0 ) + l o g   P ( X ( 1 ) = x ( 1 ) | Y = c 0 ) + + l o g   P ( X ( n ) = x ( n ) | Y = c 0 )
l o g   P ( 1 ) = l o g   P ( Y = c 1 ) + l o g   j = 1 n P ( X ( j ) = x ( j ) | Y = c 1 ) = l o g   P ( Y = c 1 ) + l o g   P ( X ( 1 ) = x ( 1 ) | Y = c 1 ) + + l o g   P ( X ( n ) = x ( n ) | Y = c 1 )
代码如下(保存为bayes.py):

# -- coding: utf-8 --
from numpy import *

def loadDataSet():
    # 创建单词向量及对应的分类,1代表侮辱性文字,0代表正常言论
    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]
    return postingList,classVec

def createVocabList(dataSet):               # 创建一个过滤dataSet重复数据的表
    vocabSet = set([])                      # 创建一个空集
    for document in dataSet:
        vocabSet = vocabSet | set(document) # 创建两个集合的并集
    return list(vocabSet)

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

def trainNB0(trainMatrix,trainCategory):
    numTrainDocs = len(trainMatrix)         # 计算训练样本数量
    numWords = len(trainMatrix[0])          # 计算不重复词表中单词数量
    pAbusive = sum(trainCategory)/float(numTrainDocs) # 类别为1的训练样本的概率,即P(Y=c1)
    # 创建一个长度与不重复词表一样的一维数组,计算各单词出现次数,初始化为1
    p0Num = ones(numWords); p1Num = ones(numWords)
    p0Denom = 2.0; p1Denom = 2.0            # 将分母(所有单词量)初始化为2
    for i in range(numTrainDocs):
        if trainCategory[i] == 1:
            p1Num += trainMatrix[i]         # 若类别为1,将相应样本列相加,得该单词在全部文档中出现次数
            p1Denom += sum(trainMatrix[i])  # 计算类别为1的样本的所有单词量
        else:
            p0Num += trainMatrix[i]         # 若类别为0,将相应样本列相加,得该单词在全部文档中出现次数
            p0Denom += sum(trainMatrix[i])  # 计算类别为0的样本的所有单词量
    # 在类别为1的条件下,各单词在文档中出现的概率,并求其对数,即log(P(x=xi|Y=c1))
    p1Vect = log(p1Num/p1Denom)
    # 在类别为0的条件下,各单词在文档中出现的概率,并求其对数,即log(P(x=xi|Y=c0))
    p0Vect = log(p0Num/p0Denom)
    return p0Vect,p1Vect,pAbusive

def classifyNB(vec2Classify, p0Vec, p1Vec, pClass1):
    # 假设传入的测试样本特征为第1,3,4个
    # 则vec2Classify * p0Vec表示为log(P(x=x1|Y=c0))+log(P(x=x3|Y=c0))+log(P(x=x4|Y=c0))
    # 则vec2Classify * p1Vec表示为log(P(x=x1|Y=c1))+log(P(x=x3|Y=c1))+log(P(x=x4|Y=c1))

    # p1=log(P(x=x1|Y=c1))+...+log(P(x=xn|Y=c1))+log(P(Y=c1))
    p1 = sum(vec2Classify * p1Vec) + log(pClass1)
    # p0=log(P(x=x1|Y=c0))+...+log(P(x=xn|Y=c0))+log(P(Y=c0))
    p0 = sum(vec2Classify * p0Vec) + log(1.0 - pClass1)
    # 对比p1和p0的大小,大的对应的值及为最终的分类结果
    if p1 > p0:
        return 1
    else:
        return 0

def testingNB():
    listOPosts,listClasses = loadDataSet()     # 获取单词向量及对应分类
    myVocabList = createVocabList(listOPosts)  # 获取不重复的词表(此时假设每个特征同等重要)
    trainMat=[]
    for postinDoc in listOPosts:
        # 为每个单词构建一个特征
        # 输入某文档,输出文档向量,向量为1或0,分别表示词表myVocabList中的单词在输入文档是否出现
        trainMat.append(setOfWords2Vec(myVocabList, postinDoc))
    p0V,p1V,pAb = trainNB0(array(trainMat),array(listClasses))
    testEntry = ['love', 'my', 'dalmation']
    thisDoc = array(setOfWords2Vec(myVocabList, testEntry))
    print testEntry,'classified as: ',classifyNB(thisDoc,p0V,p1V,pAb)
    testEntry = ['stupid', 'garbage']
    thisDoc = array(setOfWords2Vec(myVocabList, testEntry))
    print testEntry,'classified as: ',classifyNB(thisDoc,p0V,p1V,pAb)

运行命令如下:
运行命令
上述命令行为实际效果,下面通过执行部分函数深入了解:
执行核心函数
listOPosts为单词集合,listClasses为所属类别。
通过createVocabList()函数,可获得不重复词表myVocabList。
再利用setOfWords2Vec()函数,得到最终构造成的特征向量trainMat。具体可看trainMat[0],对应的是listOPosts[0],以listOPosts[0]中的单词help为例,help出现在myVocabList的第3个位置,则trainMat[0][2]=1。









以上全部内容参考书籍如下:
李航《统计学习方法》
Peter Harrington《Machine Learing in Action》
《概率论与数理统计》高等教育出版社

猜你喜欢

转载自blog.csdn.net/eeeee123456/article/details/79933031