Python3《机器学习实战》学习笔记(三):朴素贝叶斯基础篇之言论过滤器

文章目录

  • 一、简介
  • 二、朴素贝叶斯理论
    • 2.1 贝叶斯决策论
    • 2.2 条件概率
    • 2.3 全概率公式
    • 2.4 贝叶斯推断
    • 2.5 朴素贝叶斯推断
  • 三、代码实现
    • 3.1创建实验样本
    • 3.2创建词汇表
    • 3.3数据样本转化为对应尺寸的出现频率表
    • 3.4朴素贝叶斯分类器训练函数
    • 3.5朴素贝叶斯分类器分类函数
    • 3.6启动函数
  • 四、总结

一、简介

一种有监督学习算法,解决的是分类问题,如客户是否流失、是否值得投资、信用等级评定等多分类问题。在某些领域的分类问题中能够与决策树、神经网络相媲美。但由于该算法以自变量之间的独立(条件特征独立)性和连续变量的正态性假设为前提,就会导致算法精度在某种程度上受影响。
是基于概率和误判损失选择最优的类别标记

二、朴素贝叶斯理论

2.1 贝叶斯决策论

是框架下实施决策的基本方法。
Python3《机器学习实战》学习笔记(三):朴素贝叶斯基础篇之言论过滤器_第1张图片
Python3《机器学习实战》学习笔记(三):朴素贝叶斯基础篇之言论过滤器_第2张图片
假设现在我们有一个数据集,它由两类数据组成,数据分布如下图所示:
Python3《机器学习实战》学习笔记(三):朴素贝叶斯基础篇之言论过滤器_第3张图片
我们现在用p1(x,y)表示数据点(x,y)属于类别1(图中红色圆点表示的类别)的概率,用p2(x,y)表示数据点(x,y)属于类别2(图中蓝色三角形表示的类别)的概率,那么对于一个新数据点(x,y),可以用下面的规则来判断它的类别:

如果p1(x,y) > p2(x,y),那么类别为1
如果p1(x,y) < p2(x,y),那么类别为2

也就是说,我们会选择高概率对应的类别。这就是贝叶斯决策理论的核心思想,即选择具有最高概率的决策。已经了解了贝叶斯决策理论的核心思想,那么接下来,就是学习如何计算p1和p2概率。

2.2 条件概率

Python3《机器学习实战》学习笔记(三):朴素贝叶斯基础篇之言论过滤器_第4张图片
Python3《机器学习实战》学习笔记(三):朴素贝叶斯基础篇之言论过滤器_第5张图片

2.3 全概率公式

Python3《机器学习实战》学习笔记(三):朴素贝叶斯基础篇之言论过滤器_第6张图片
Python3《机器学习实战》学习笔记(三):朴素贝叶斯基础篇之言论过滤器_第7张图片
Python3《机器学习实战》学习笔记(三):朴素贝叶斯基础篇之言论过滤器_第8张图片
Python3《机器学习实战》学习笔记(三):朴素贝叶斯基础篇之言论过滤器_第9张图片

2.4 贝叶斯推断

Python3《机器学习实战》学习笔记(三):朴素贝叶斯基础篇之言论过滤器_第10张图片
我们把P(A)称为"先验概率"(Prior probability),即在B事件发生之前,我们对A事件概率的一个判断。

P(A|B)称为"后验概率"(Posterior probability),即在B事件发生之后,我们对A事件概率的重新评估。

P(B|A)/P(B)称为"可能性函数"(Likelyhood),这是一个调整因子,使得预估概率更接近真实概率。

后验概率 = 先验概率 x 调整因子

这就是贝叶斯推断的含义。我们先预估一个"先验概率",然后加入实验结果,看这个实验到底是增强还是削弱了"先验概率",由此得到更接近事实的"后验概率"。

在这里,如果"可能性函数"P(B|A)/P(B)>1,意味着"先验概率"被增强,事件A的发生的可能性变大;如果"可能性函数"=1,意味着B事件无助于判断事件A的可能性;如果"可能性函数"<1,意味着"先验概率"被削弱,事件A的可能性变小。

Python3《机器学习实战》学习笔记(三):朴素贝叶斯基础篇之言论过滤器_第11张图片
一号碗有30颗水果糖和10颗巧克力糖,二号碗有水果糖和巧克力糖各20颗。现在随机选择一个碗,从中摸出一颗糖,发现是水果糖。请问这颗水果糖来自一号碗的概率有多大?

H1表示一号碗,H2表示二号碗。由于这两个碗是一样的,所以P(H1)=P(H2),也就是说,在取出水果糖之前,这两个碗被选中的概率相同。因此,P(H1)=0.5,我们把这个概率就叫做"先验概率",即没有做实验之前,来自一号碗的概率是0.5。
Python3《机器学习实战》学习笔记(三):朴素贝叶斯基础篇之言论过滤器_第12张图片
Python3《机器学习实战》学习笔记(三):朴素贝叶斯基础篇之言论过滤器_第13张图片
Python3《机器学习实战》学习笔记(三):朴素贝叶斯基础篇之言论过滤器_第14张图片

这表明,来自一号碗的概率是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)的大小,所以为了减少计算量,全概率公式在实际编程中可以不使用。

2.5 朴素贝叶斯推断

增加了独立性的前提条件
Python3《机器学习实战》学习笔记(三):朴素贝叶斯基础篇之言论过滤器_第15张图片
Python3《机器学习实战》学习笔记(三):朴素贝叶斯基础篇之言论过滤器_第16张图片

症状 职业 疾病
打喷嚏 护士 感冒
打喷嚏 农夫 过敏
头痛 建筑工人 脑震荡
头痛 建筑工人 感冒
打喷嚏 教师 感冒
头痛 教师 脑震荡
现在又来了第七个病人,是一个打喷嚏的建筑工人。请问他患上感冒的概率有多大?

Python3《机器学习实战》学习笔记(三):朴素贝叶斯基础篇之言论过滤器_第17张图片
根据朴素贝叶斯条件独立性的假设可知,"打喷嚏"和"建筑工人"这两个特征是独立的,因此,上面的等式就变成了

Python3《机器学习实战》学习笔记(三):朴素贝叶斯基础篇之言论过滤器_第18张图片
这就是贝叶斯分类器的基本方法:在统计资料的基础上,依据某些特征,计算各个类别的概率,从而实现分类。

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

三、代码实现

我们设置一个言论过滤器,样本数据是一些单词列表,并且每个列表都有对应的是否是不当言论的标志。

侮辱类和非侮辱类,使用1和0分别表示。

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

3.1创建实验样本

import numpy as np
from functools import reduce
'''
函数说明:创建实验样本
'''
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

3.2创建词汇表

'''
函数说明:将切分的实验样本词条整理成不重复的词条列表,也就是词汇表
'''
def createVocabList(dataSet):
    vocabSet = set([])
    for li in dataSet:
        vocabSet = vocabSet | set(li)   # 每一行去重复,再取交集,确保没重复行
    return list(vocabSet)

3.3数据样本转化为对应尺寸的出现频率表

'''
函数说明:根据vocabList词汇表,将inputSet向量化,向量的每个元素为1或0
vocabList - createVocabList返回的列表
inputSet - 切分的词条列表,即原始数据的每一行
这里遍历每一行传入的每一个单词,如果单词在词汇表中出现了,在词汇表的相应下标位置置为1
'''
def setOfWords2Vec(vocabList, inputSet):
    returnVec = [0] * len(vocabList)    # 创建一个其中所含元素都为0的向量
    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

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

这张图去理解就好!
Python3《机器学习实战》学习笔记(三):朴素贝叶斯基础篇之言论过滤器_第19张图片

返回值是对应词汇属于侮辱或者非侮辱的概率
具体计算是 对应词汇在当前句子是否侮辱出现词频 / 是否侮辱类型的总词频

'''
函数说明:朴素贝叶斯分类器训练函数
trainMatrix - 训练文档矩阵,即setOfWords2Vec返回的returnVec构成的矩阵
trainCategory - 训练类别标签向量,即loadDataSet返回的classVec    [0,1,0,1,0,1]
'''
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

    # 其实这里就是一个分类    把trainMatrix的每一行,分为两种,并且将两种分别累加
    for i in range(numTrainDocs):
        if(trainCategory[i] == 1):  # 这个if就相当于在已知
            p1Num += trainMatrix[i]
            p1Denom += sum(trainMatrix[i])

        else:
            p0Num += trainMatrix[i]
            p0Denom += sum(trainMatrix[i])

    print('p1Num:\n', p1Num)
    print('p1Denom:\n', p1Denom)
    print('p0Num:\n', p0Num)
    print('p0Denom:\n', p0Denom)
    p1Vect = p1Num / p1Denom
    p0Vect = p0Num / p0Denom

    return p0Vect, p1Vect, pAbusive

3.5朴素贝叶斯分类器分类函数

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

Parameters:
    vec2Classify - 待分类的词条数组
    p0Vec - 侮辱类的条件概率数组
    p1Vec -非侮辱类的条件概率数组
    pClass1 - 文档属于侮辱类的概率
Returns:
    0 - 属于非侮辱类
    1 - 属于侮辱类
'''
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

3.6启动函数

if __name__ == '__main__':
    postingLIst, classVec = loadDataSet()
    # for each in postingLIst:
    #     print(each)
    print("--------")
    myVocabList = createVocabList(postingLIst)
    print("去重复以后的词汇表{}".format(myVocabList))
    trainMat=[]
    for post in postingLIst:
        trainMat.append(setOfWords2Vec(myVocabList, post))
    print("--------")
    print('trainMat:\n', trainMat)
    print("--------")
    p0V, p1V, pAb = trainNB0(trainMat, classVec)
    print('p0V:\n', p0V)
    print('p1V:\n', p1V)
    print('classVec:\n', classVec)
    print('pAb:\n', pAb)


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

Python3《机器学习实战》学习笔记(三):朴素贝叶斯基础篇之言论过滤器_第20张图片
p0V存放的是每个单词属于类别0,也就是非侮辱类词汇的概率。同理,p1V存放的就是各个单词属于侮辱类的条件概率。pAb就是先验概率。

四、总结

朴素贝叶斯推断的一些优点:

生成式模型,通过计算概率来进行分类,可以用来处理多分类问题。
对小规模的数据表现很好,适合多分类任务,适合增量式训练,算法也比较简单。

朴素贝叶斯推断的一些缺点:

对输入数据的表达形式很敏感。
由于朴素贝叶斯的“朴素”特点,所以会带来一些准确率上的损失。
需要计算先验概率,分类决策存在错误率。

你可能感兴趣的:(机器学习,机器学习,人工智能)