机器学习实战之朴素贝叶斯

一、 朴素贝叶斯理论

1、什么是朴素贝叶斯

朴素贝叶斯(naive Bayes)是贝叶斯决策理论的一部分,是基于贝叶斯定理与特征条件独立假设的分类方法,它是一种简单但极为强大的预测建模算法。之所以称为朴素贝叶斯,是因为它假设每个输入变量是独立的。这个假设很硬,现实生活中根本不满足,但是这项技术对于绝大部分的复杂问题仍然非常有效。最为广泛的两种分类模型是决策树模型(Decision Tree Model)和朴素贝叶斯模型(Naive Bayesian Model,NBM)。

2、朴素贝叶斯的算法原理

朴素贝叶斯分类(NBC)是以贝叶斯定理为基础并且假设特征条件之间相互独立的方法,先通过已给定的训练集,以特征词之间独立作为前提假设,学习从输入到输出的联合概率分布,再基于学习到的模型,输入X求出使得后验概率最大的输出Y。

计算原理:
我们使用P1(x,y)表示数据点(x,y)属于类别1(如下图橘色的圆点)的概率,使用P2(x,y)表示数据点(x,y)属于类别2(如下图蓝色的叉叉)的概率,那么,对于一个新的数据点(x1,y1),我们可以用以下规则来判断它的类别:

  1. 如果p1(x1,y1) > p2(x1,y1),那么这个点类别为1;
  2. 如果p1(x1,y1) < p2(x1,y1),那么这个点类别为2;
  3. 如果p1(x1,y1) = p2(x1,y1),那么这个点可设为1也可设为2。

在这里插入图片描述

3、贝叶斯分类典型应用

  1. 垃圾邮件过滤
  2. 文本情感分析:电影评论
  3. 论坛真实账号监测
  4. 浏览器输入自动提示

二、几个必知的概率

1、先验概率和后验概率

  1. 先验概率(Prior probability) :指根据以往经验和分析得到的概率,如全概率公式,它往往作为"由因求果"问题中的"因"出现的概率。
  2. 后验概率(Posterior probability ):指在得到“结果”的信息后重新修正的概率,是“执果寻因”问题中的"果"。就是发生结果之后,推测原因的概率。

举个例子:

  • 如果一个不透明袋子中有十个球,4个黑球、6个白球,你现在把手伸进去随机抓取一个,那么抓到黑球的概率很明显就是4/10,也就是2/5,这就是所谓的先验概率。
  • 而后验概率就是你在不知道里面有多少球的情况下,你随机抓取几次,根据你抓取的情况去判断黑球以及白球的比例,最终算出来的每次抓取得到哪种球的概率。

2、联合概率和边缘概率

  1. 联合概率(joint probability)指的是包含多个条件且所有条件同时成立的概率,记作P(X=a,Y=b)或P(a,b),有的书上也习惯记作P(ab),但是这种记法个人不太习惯,所以下文采用以逗号分隔的记法。
  2. 边缘概率(marginal probability)是与联合概率对应的,P(X=a)或P(Y=b),这类仅与单个随机变量有关的概率称为边缘概率。

这个很好理解,举一个和之前差不多的例子:

  • 如果一个不透明袋子中有十个球,4个黑球、6个白球,你现在把手伸进去随机抓取一个,这时你抽取黑球的概率,这就是边缘概率,也就是只有一个条件下某件事情出现的概率。
  • 这时,如果还有一个装了6个球,其中3个黑球、3个白球的袋子,两个袋子不知道哪个是哪个的情况下,请问你在第二个袋子中抽到黑球的概率,这个就是一个求联合概率的问题。

3、条件概率

条件概率是我们朴素贝叶斯算法的核心,是必须要理解的一个概率问题。

条件概率 (conditional probability) 是指在事件 B 发生的情况下,事件 A 发生的概率,通常记为 P(A | B)。推导如下:
P ( A ∣ B ) = P ( A ⋂ B ) P ( B ) P(A|B) = \frac{P(A \bigcap B)}{P(B)} P(AB)=P(B)P(AB)因此: P ( A ⋂ B ) = P ( A ∣ B ) P ( B ) P(A \bigcap B)=P(A|B)P(B) P(AB)=P(AB)P(B) P ( A ⋂ B ) = P ( B ∣ A ) P ( A ) P(A \bigcap B) = P(B|A)P(A) P(AB)=P(BA)P(A)可得: P ( A ∣ B ) P ( B ) = P ( B ∣ A ) P ( A ) P(A|B)P(B)=P(B|A)P(A) P(AB)P(B)=P(BA)P(A)由此可以推出朴素贝叶斯公式: P ( A ∣ B ) = P ( B ∣ A ) P ( A ) P ( B ) P(A|B)= \frac{P(B|A)P(A)}{P(B)} P(AB)=P(B)P(BA)P(A)这也就是条件概率的计算公式。

这个公式指的是:
在B事件的条件之下,算A事件的概率,我们可以拆分为:已知的在A事件概率下B事件的概率乘以B事件的概率,再除以A事件的概率。

三、如何去计算

由前面我们知道了一个求条件概率的公式: P ( A ∣ B ) = P ( B ∣ A ) P ( A ) P ( B ) P(A|B)= \frac{P(B|A)P(A)}{P(B)} P(AB)=P(B)P(BA)P(A)在我们的朴素贝叶斯中,如何去运用这个公式呢?

根据我们的条件概率公式,我们对此进行一些推导,则可转化为: p ( y i ∣ x ) = p ( x ∣ y i ) p ( y i ) p ( x ) p(y_i|x)=\frac{p(x|y_i)p(y_i)}{p(x)} p(yix)=p(x)p(xyi)p(yi)由此我们展开可得: p ( y i ∣ x ) = p ( x 1 ∣ y i ) p ( x 2 ∣ y i ) . . . p ( x n ∣ y i ) p ( y i ) p(y_i|x)=p(x_1|y_i)p(x_2|y_i)...p(x_n|y_i)p(y_i) p(yix)=p(x1yi)p(x2yi)...p(xnyi)p(yi)注意:在这个地方,因为我们求的不是概率,而是概率大小,而同条件下,求不同事件的概率,在最后进行除法时,除的是同一个概率,所以仅仅比较大小的情况下不需要再进行一次除法,从而得到上面的公式。

接下来我由一个示例图举一个例子讲述一下怎么计算的:

编号X1X2Y
11S-1
21M-1
31M1
41S1
51S-1
62S-1
72M-1
82M1
92L1
102L1
113L1
123M1
133M1
143L1
153L-1

如表,可知,总共有15条数据,我们可知几个基础的概率:
P(Y=1) = 9/15, P(Y=-1) = 6/15
由此我们可以求一些相关的条件概率:

Y=1时X1各个值的概率Y=1时X2各个值的概率Y=-1时X1各个值的概率Y=-1时X2各个值的概率
P(X1=1|Y=1)=2/9P(X2=S|Y=1)=1/9P(X1=1|Y=-1)=3/6P(X2=S|Y=-1)=3/6
P(X1=2|Y=1)=3/9P(X2=M|Y=1)=4/9P(X1=2|Y=-1)=2/6P(X2=M|Y=-1)=2/6
P(X1=3|Y=1)=4/9P(X2=L|Y=1)=4/9P(X1=3|Y=-1)=1/6P(X2=L|Y=-1)=1/6

所以,我们可以求出X1=2,X2=S时Y=1的概率大还是Y=-1的概率大: P ( Y = 1 ∣ X 1 = 2 , X 2 = S ) = P ( X 1 = 2 ∣ Y = 1 ) ∗ P ( X 2 = S ∣ Y = 1 ) ∗ P ( Y = 1 ) = 3 9 ∗ 1 9 ∗ 9 15 = 1 45 P(Y=1|X_1=2,X_2=S)=P(X_1=2|Y=1)*P(X_2=S|Y=1)*P(Y=1)=\frac{3}{9} * \frac{1}{9}* \frac{9}{15}=\frac{1}{45} P(Y=1X1=2,X2=S)=P(X1=2Y=1)P(X2=SY=1)P(Y=1)=9391159=451 P ( Y = − 1 ∣ X 1 = 2 , X 2 = S ) = P ( X 1 = 2 ∣ Y = − 1 ) ∗ P ( X 2 = S ∣ Y = − 1 ) ∗ P ( Y = − 1 ) = 2 6 ∗ 3 6 ∗ 6 15 = 1 15 P(Y=-1|X_1=2,X_2=S)=P(X_1=2|Y=-1)*P(X_2=S|Y=-1)*P(Y=-1)=\frac{2}{6} * \frac{3}{6} * \frac{6}{15} =\frac{1}{15} P(Y=1X1=2,X2=S)=P(X1=2Y=1)P(X2=SY=1)P(Y=1)=6263156=151
明显可知,该条件下它的标记应该是Y=-1。这就是朴素贝叶斯在计算概率分类时的原理。

四、代码实现

1、数据准备工作

from numpy import *
import re
import feedparser

# 词表到向量的转换函数
def loadDataSet():    # 创建一些实验样本
    posttingList = [['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 posttingList, classVec

def createVocabList(dataSet):    # 创建一个包含在所有文档中出现的不重复词的列表,为此使用Python的set数据类型
    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: ' + word + 'is not in my Vocabulary!')
    return returnVec

在这里,我们创建了一个6条数据,并对每条数据进行了分类,这个过程得到的数据如下:

listOPosts, listClasses = loadDataSet()
myVovatbList = createVocabList(listOPosts)
myVovatbList

>>> ['food', 'love', 'is', 'quit', 'stop', 'steak', 'posting', 'has', 'how', 'maybe', 'worthless', 'not', 'him', 'licks', 'ate', 'problems', 'please', 'dog', 'stupid', 'mr', 'my', 'park', 'dalmation', 'I', 'help', 'cute', 'flea', 'garbage', 'buying', 'take', 'so', 'to']

setOfWords2Vec(myVovatbList, listOPosts[3])

>>> [0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0]

2、朴素贝叶斯分类器训练函数

# 朴素贝叶斯分类器训练函数
def trainNB0(trainMatrix, trainCategory):
    numTrainDocs = len(trainMatrix)    # 计算训练的文档数目
    numWords = len(trainMatrix[0])     # 计算每篇文档的词条数
    pAbusive = sum(trainCategory) / float(numTrainDocs)    # 文档属于侮辱类的概率
    p0Num = ones(numWords)
    p1Num = ones(numWords)    # 创建NumPy.ones数组,词条出现数初始化为1,拉普拉斯平滑
    p0Denom = 2.0
    p1Denom = 2.0    # 分母初始化为2,拉普拉斯平滑
    for i in range(numTrainDocs):
        if trainCategory[i] == 1:    # 统计属于侮辱类的条件概率所需的数据
            p1Num += trainMatrix[i]
            p1Denom += sum(trainMatrix[i])
        else:    # 统计属于非侮辱类的条件概率所需的数据
            p0Num += trainMatrix[i]
            p0Denom += sum(trainMatrix[i])
    p1Vect = log(p1Num / p1Denom)    # 取对数,防止下溢出
    p0Vect = log(p0Num / p0Denom)
    return p0Vect, p1Vect, pAbusive  # 返回属于侮辱类的条件概率数组,属于非侮辱类的条件概率数组,文档属于侮辱类的概率

3、朴素贝叶斯分类函数

# 朴素贝叶斯分类函数
def classifyNb(vec2Classify, p0Vec, p1Vec, pClass1):   # 对词进行分类
    p1 = sum(vec2Classify * p1Vec) + log(pClass1)
    p0 = sum(vec2Classify * p0Vec) + log(1.0 - pClass1)
    if p1 > p0:
        return 1
    else:
        return 0

def testingNB():   # 测试分类效果
    listOPosts, listClasses = loadDataSet()
    myVocabList = createVocabList(listOPosts)   # 创造一个合并的去重集合
    trainMat = []
    for postinDoc in listOPosts:   # 6个向量
        trainMat.append(setOfWords2Vec(myVocabList, postinDoc))    # 将一个一个样本,利用词汇表转换为词向量
    p0V, p1V, pAb = trainNB0(array(trainMat), array(listClasses))  # 训练-计算条件概率  0-非侮辱类条件概率集  1-侮辱类条件概率集
    testEntry = ['love', 'my', 'dalmation']
    thisDoc = array(setOfWords2Vec(myVocabList, testEntry))    # 测试样本向量化
    print(str(testEntry) + ' classified as: ' + str(classifyNb(thisDoc, p0V, p1V, pAb)))
    testEntry = ['stupid', 'garbage']
    thisDoc = array(setOfWords2Vec(myVocabList, testEntry))
    print(str(testEntry) + ' classified as: ' + str(classifyNb(thisDoc, p0V, p1V, pAb)))

在这里,我们输入了一些词进行测试,运行结果如下:

testingNB()

>>> ['love', 'my', 'dalmation'] classified as: 0
['stupid', 'garbage'] classified as: 1

五、垃圾邮件过滤示例

在这里给大家演示一下书上的例子,垃圾邮件过滤,本示例的数据集来自书中所给,如果有兴趣的同学可以点击此处下载书中相关的数据集以及代码,同时里面也带有《机器学习实战》英文版和中文版PDF电子书(侵权请告知)。下面我演示一下这个示例,以及最后的结果。

# 文件解析及完整的垃圾邮件测试函数
def textParse(bigString):    # 接收一个大写字符串并将其解析为字符串列表
    regEx = re.compile('\\W*')
    listOfTokens = regEx.split(str(bigString))
    return [tok.lower() for tok in listOfTokens if len(tok) > 2]

def spamTest():    # 对贝叶斯垃圾邮件分类器进行自动化处理
    docList = []
    classList = []
    fullText = []
    for i in range(1, 26):   # 访问25个txt文件
        wordList = textParse(open('email/spam/'+ str(i) +'.txt', 'rb').read())    # 读取每个垃圾邮件,并将字符串转换成字符串列表
        docList.append(wordList)
        fullText.extend(wordList)
        classList.append(1)    # 标记垃圾邮件,1表示垃圾文件
        wordList = textParse(open('email/ham/'+ str(i) +'.txt', 'rb').read())
        docList.append(wordList)
        fullText.extend(wordList)
        classList.append(0)    # 标记非垃圾邮件,0表示非垃圾邮件
    vocabList = createVocabList(docList)    # 创建词汇表,并去重
    trainingSet = list(range(50))
    testSet = []    # 创建存车训练集的索引值的列表和测试集的索引值的列表
    for i in range(10):   # 从50个邮件中,随机抽选40封电子邮件作为训练集,10封作为测试集
        randIndex = int(random.uniform(0, len(trainingSet)))    # 随机选取索引值
        testSet.append(trainingSet[randIndex])    # 添加测试集的索引值
        del(trainingSet[randIndex])    # 在训练集列表中删除添加到测试集的索引值
    trainMat = []
    trainClasses = []    # 创建训练集矩阵和训练集类别标签系向量
    for docIndex in trainingSet:    # 遍历训练集
        trainMat.append(setOfWords2Vec(vocabList, docList[docIndex]))    # 将生成的词集模型添加到训练矩阵中
        trainClasses.append(classList[docIndex])    # 将类别添加到训练集类别标签系向量中
    p0V, p1V, pSpam = trainNB0(array(trainMat), array(trainClasses))    # 训练朴素贝叶斯模型
    errorCpunt = 0    # 错误分类计数
    for docIndex in testSet:    # 遍历测试集
        wordVector = setOfWords2Vec(vocabList, docList[docIndex])    # 测试集的词集模型
        if classifyNb(array(wordVector), p0V, p1V, pSpam) != classList[docIndex]:    # 如果分类错误的情况发生
            errorCpunt += 1    # 错误计数加1
    print('the error rate is: ' + str(float(errorCpunt)/len(testSet)))

通过运行该模型,最终我们可以得到一个结果,但是,请注意: 因为每次在取数据集的时候是随机的,所有最终的结果会有一定的误差,这里我给出三次运行的结果:

spamTest()
>>> the error rate is: 0.1

spamTest()
>>> the error rate is: 0.2

spamTest()
>>> the error rate is: 0.0

六、总结

优点:

  1. 朴素贝叶斯模型发源于古典数学理论,有稳定的分类效率。
  2. 对小规模的数据表现很好,能个处理多分类任务,适合增量式训练,尤其是数据量超出内存时,我们可以一批批的去增量训练。
  3. 对缺失数据不太敏感,算法也比较简单,常用于文本分类。

缺点:

  1. 理论上,朴素贝叶斯模型与其他分类方法相比具有最小的误差率。但是实际上并非总是如此,这是因为朴素贝叶斯模型给定输出类别的情况下,假设属性之间相互独立,这个假设在实际应用中往往是不成立的,在属性个数比较多或者属性之间相关性较大时,分类效果不好。而在属性相关性较小时,朴素贝叶斯性能最为良好。对于这一点,有半朴素贝叶斯之类的算法通过考虑部分关联性适度改进。
  2. 需要知道先验概率,且先验概率很多时候取决于假设,假设的模型可以有很多种,因此在某些时候会由于假设的先验模型的原因导致预测效果不佳。
  3. 由于我们是通过先验和数据来决定后验的概率从而决定分类,所以分类决策存在一定的错误率。
  4. 对输入数据的表达形式很敏感。
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值