机器学习——朴素贝叶斯

家住魔仙堡 提交于 2020-01-30 21:59:43

参考
https://cuijiahua.com/blog/2017/11/ml_4_bayes_1.html
https://cuijiahua.com/blog/2017/11/ml_5_bayes_2.html
https://www.jianshu.com/p/5953923f43f0

一、朴素贝叶斯简介

1.1、朴素贝叶斯算法简介

朴素贝叶斯算法(Naive Bayesian algorithm) ,朴素贝叶斯方法是在贝叶斯算法的基础上进行了相应的简化,即假定给定目标值时属性之间相互条件独立

1.2 贝叶斯定理

贝叶斯决策理论:选择具有高概率的发生情况为最终判断。
在这里插入图片描述
根据已知的基础条件概率和部分概率,推断出在某种条件下下的概率。

1.3、条件概率推断

在这里插入图片描述

全部事件的概率是 S
A 事件的概率是 A
B 事件的概率是 B
A 的对立事件概率是 A’
A 与 B 共同事件概率是 A∩B
说明:A 与 A‘ 对立且共同构成 S。

我们可以推断出在 B 条件下发生 A 事件的概率,然后一步步把 A∩B 改变成另一个表示
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
这就是条件概率的计算公式。
如果考虑到下面的全概率公式和上面图片只考虑 A 和 A‘ 条件概率公式可改为:
在这里插入图片描述

1.4、全概率推断

若事件 A1、 A2 、……An 构成一个完备事件组即
在这里插入图片描述
且都有正概率,那么对于任意一个事件A,有如下全概率公式:
在这里插入图片描述

1.5、贝叶斯推断

在这里插入图片描述
P(A)称为"先验概率"(Prior probability),即在B事件发生之前,我们对A事件概率的一个判断。
P(A|B)称为"后验概率"(Posterior probability),即在B事件发生之后,我们对A事件概率的重新评估。
P(B|A)/P(B)称为"可能性函数"(Likelyhood),这是一个调整因子,使得预估概率更接近真实概率。

所以,条件概率可以理解成下面的式子:
后验概率 = 先验概率 x 调整因子

1.6、拉普拉斯平滑

在这里插入图片描述
在用极大似然估计时,可能特征X(j)的某些取值在Ck标签样本中没有出现,这时似然函数为0,同时导致目标函数为0,这会使分类产生偏差。为解决这一问题采用贝叶斯估计:
在这里插入图片描述其中Sj 是Ck标签中第 j 个特征不重复数值的个数。当 λ = 0是就是极大似然估计,当 λ = 1时,称为拉普拉斯平滑。同样,先验概率的贝叶斯估计是:
在这里插入图片描述

二、示例

2.1、示例解释

以在线社区留言为例。为了不影响社区的发展,我们要屏蔽侮辱性的言论,所以要构建一个快速过滤器,如果某条留言使用了负面或者侮辱性的语言,那么就将该留言标志为内容不当。过滤这类内容是一个很常见的需求。对此问题建立两个类型:侮辱类和非侮辱类,使用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']
]
# 类别标签向量,1代表侮辱性词汇,0代表不是
classVec = [0, 1, 0, 1, 0, 1]

2.2、数据处理步骤

如果要判断是否是侮辱单词组,我们就需要通过基础判断的数据经过计算出在一组单词中每个单词为侮辱性单词的概率,然后把词组中一样的单词概率加起来就可以得到单词为侮辱性概率、非侮辱性概率。得到基础概率后,在计算要判断的单词组就可以得到最后的判断。

  1. 统计所有的单词汇总(去重)
  2. 复制总数数,把总数据比对每个词组,得到对应数量的总数据向量化
  3. 根据单词组分类,把向量化总数据利用全概率公式,得到每个单词为侮辱性概率、非侮辱性概率
  4. 然后就是利用概率对测试单词组分类

不过如果仅仅是这么做,就会发现侮辱性概率、非侮辱性概率要么有数值,要么就是0,在后续的处理中,根据条件概率公式计算就会因为数值过小导致概率为0,分类出错。

所以还需要进行朴素贝叶斯改进之拉普拉斯平滑

2.3、完整代码

# !/usr/bin/python
# -*- coding: utf-8 -*- 
# @Time : 2020/1/1 22:48 
# @Author : ljf
# @File : NB_test6.py
import numpy as np


def loadDataSet():
    """
    函数说明:创建实验样本
    Returns:
        postingList:    实验样本切分的词条
        classVec:       类别标签向量
    """
    # 切分的词条
    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


def setOfWords2Vec(vocabList, inputSet):
    """
    函数说明:根据vocabList词汇表,将inputSet向量化,向量的每个元素为1或0
    Args:
        vocabList:  createVocabList返回的列表
        inputSet:   切分的词条列表
    Returns:
        returnVec:  文档向量,词集模型
    """
    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 createVocabList(dataSet):
    """
    函数说明:将切分的实验样本词条整理成不重复的词条列表,也就是词汇表
    Args:
        dataSet:    整理的样本数据集
    Returns:
        vocabSet:   返回不重复的词条列表,也就是词汇表
    """
    vocabSet = set([])  # 创建一个空的不重复列表
    for document in dataSet:
        vocabSet = vocabSet | set(document)  # 取并集
    return list(vocabSet)


def trainNB0(trainMatrix, trainCategory):
    """
    函数说明:朴素贝叶斯分类器训练函数
    Args:
        trainMatrix:    训练文档矩阵,即setOfWords2Vec返回的returnVec构成的矩阵
        trainCategory:  训练类别标签向量,即loadDataSet返回的classVec
    Returns:
        p0Vect:     非侮辱类的条件概率数组
        p1Vect:     侮辱类的条件概率数组
        pAbusive:   文档属于侮辱类的概率
    """
    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  # 返回属于侮辱类的条件概率数组,属于非侮辱类的条件概率数组,文档属于侮辱类的概率


def classifyNB(vec2Classify, p0Vec, p1Vec, pClass1):
    """
    函数说明:朴素贝叶斯分类器分类函数
    Args:
        vec2Classify:   待分类的词条数组
        p0Vec:          侮辱类的条件概率数组
        p1Vec:          非侮辱类的条件概率数组
        pClass1:        文档属于侮辱类的概率
    Returns:
        0:              属于非侮辱类
        1:              属于侮辱类
    """
    p1 = sum(vec2Classify * p1Vec) + np.log(pClass1)  # 对应元素相乘。logA * B = logA + logB,所以这里加上log(pClass1)
    p0 = sum(vec2Classify * p0Vec) + np.log(1.0 - pClass1)
    print('p0:', p0)
    print('p1:', p1)
    if p1 > p0:
        return 1
    else:
        return 0


def testingNB():
    """
    函数说明:测试朴素贝叶斯分类器
    Returns:
        无
    """
    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()

三、总结

  • 在训练朴素贝叶斯分类器之前,要处理好训练集,文本的清洗还是有很多需要学习的东西。
  • 根据提取的分类特征将文本向量化,然后训练朴素贝叶斯分类器。
  • 去高频词汇数量的不同,对结果也是有影响的的。
  • 拉普拉斯平滑对于改善朴素贝叶斯分类器的分类效果有着积极的作用。
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!