机器学习之朴素贝叶斯

文章目录

  • 一、朴素贝叶斯基本介绍
  • 二、朴素贝叶斯分类原理
    • 1. 贝叶斯决策理论
    • 2. 贝叶斯推断
      • 例子
    • 3. 朴素贝叶斯推断
  • 三、手写朴素贝叶斯分类器
  • 四、sklearn中的朴素贝叶斯算法
    • 1. 高斯朴素贝叶斯(最常用)
    • 2. 伯努利朴素贝叶斯
    • 3. 多项式分布朴素贝叶斯
  • 总结
    • 优缺点
    • 问题

一、朴素贝叶斯基本介绍

Naive Bayesian Model

  1. 只能解决分类问题
  2. 监督学习算法

(概率模型,不需要数据归一化)


二、朴素贝叶斯分类原理

1. 贝叶斯决策理论

假设一个数据集有两个类别C1和C2,对于一个新的测试点(x,y)

  • 若p1(x,y)>p2(x,y),那么类别为C1
  • 若p1(x,y)

即选择具有最高概率的类别作为决策!

2. 贝叶斯推断

贝叶斯定理(即条件概率计算公式):
机器学习之朴素贝叶斯_第1张图片
简单变形:
机器学习之朴素贝叶斯_第2张图片
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的可能性变小。

例子

两个一模一样的碗,一号碗(H1)有30颗水果糖和10颗巧克力糖,二号碗(H2)有水果糖(E)和巧克力糖各20颗。现在随机选择一个碗,从中摸出一颗糖,发现是水果糖。请问这颗水果糖来自一号碗的概率有多大?
机器学习之朴素贝叶斯_第3张图片

由于这两个碗是一样的,所以P(H1)=P(H2),也就是说,在取出水果糖之前,这两个碗被选中的概率相同。因此,P(H1)=0.5,我们把这个概率就叫做"先验概率",即没有做实验之前,来自一号碗的概率是0.5。问题变成了已知E的情况下,来自一号碗的概率有多大,即求P(H1|E)。我们把这个概率叫做"后验概率",即在E事件发生之后,对P(H1)的修正。公式为:
机器学习之朴素贝叶斯_第4张图片
在实际应用中,我们往往只需要知道所属类别,而不需要计算具体的类别概率。在上式中,因为两个碗相同,只需要比较 P(H1|E)和P(H2|E)的大小,找到那个更大的概率就可以。既然如此,两者的分母P(E)都是相同的,那我们只需要比较分子即可,即比较P(E|H1)P(H1)和P(E|H2)P(H2)的大小,所以为了减少计算量,全概率公式在实际编程中可以不使用,即分母不需要计算问题转化成如何基于训练数据D来估计先验概率P©和似然P(X|c)

3. 朴素贝叶斯推断

朴素贝叶斯对条件个概率分布做了条件独立性的假设
机器学习之朴素贝叶斯_第5张图片
由于每个特征都是独立的,我们可以将分子上的条件概率进行拆分。


三、手写朴素贝叶斯分类器

"""
import numpy as np

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


# 创建包含在所有文档中的词汇表(不重复)
def createVocabList(dataSet):
    vocabSet = set([])      # 创建一个空的不重复列表
    for document in dataSet:
        vocabSet = vocabSet | set(document)  # 求并集
    return list(vocabSet)


# 根据vocabList词汇表,将inputSet向量化,向量的每个元素为1或0
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                                                 # 返回文档向量


# 函数说明: 朴素贝叶斯分类器训练函数
# trainMatrix - 训练文档矩阵,即setOfWords2Vec返回的returnVec构成的矩阵,文档含有这个词1,文档不含这个词0
# trainCategory - 训练类别标签向量,即loadDataSet返回的classVe
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
    p0Num = np.ones(numWords); p1Num = np.ones(numWords)      # change to np.ones() 防止有一项概率为0,乘积为0
    p0Denom = 2.0; p1Denom = 2.0                        # change to 2.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      # P(cute|侮辱类)
    # p0Vect = p0Num/p0Denom      # P(cute|非侮辱类)
    p1Vect = np.log(p1Num/p1Denom)          # change to np.log() 防止概率乘积过小,被python约为0
    p0Vect = np.log(p0Num/p0Denom)          # change to np.log()
    return p0Vect, p1Vect, pAbusive              # 返回属于侮辱类的条件概率数组,属于非侮辱类的条件概率数组,文档属于侮辱类的概率


def classifyNB(vec2Classify, p0Vec, p1Vec, pClass1):        # p(w|ci)*p(ci)
    p1 = sum(vec2Classify * p1Vec) + np.log(pClass1)        # sum也是乘积转化为log的和而来
    p0 = sum(vec2Classify * p0Vec) + np.log(1.0 - pClass1)  # log(a*b) = log(a) + log(b)
    if p1 > p0:
        return 1
    else:
        return 0


def bagOfWords2VecMN(vocabList, inputSet):
    returnVec = [0]*len(vocabList)
    for word in inputSet:
        if word in vocabList:
            returnVec[vocabList.index(word)] += 1
    return returnVec


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']
    thisDoc = np.array(setOfWords2Vec(myVocabList, testEntry))
    print(testEntry, 'classified as: ', classifyNB(thisDoc, p0V, p1V, pAb))
    testEntry = ['stupid', 'garbage']
    thisDoc = np.array(setOfWords2Vec(myVocabList, testEntry))
    print(testEntry, 'classified as: ', classifyNB(thisDoc, p0V, p1V, pAb))


testingNB()

四、sklearn中的朴素贝叶斯算法

在sklearn中,朴素贝叶斯有三种方法,分别是GaussianNB(先验为高斯分布即正态分布)MultinomialNB(先验为多项式分布)BernoulliNB(先验为伯努利分布)

1. 高斯朴素贝叶斯(最常用)

通过假设P (Xi∣Y) 是服从高斯分布(也就是正态分布),来估计每个特征下每个类别上的条件概率

分类效果在二分数据和月亮型数据上表现优秀,但是环形数据不太擅长(但远远胜过其他线性模型)

import numpy as np
from sklearn.datasets import make_blobs     # 导入数据集
from sklearn.model_selection import train_test_split

from sklearn.naive_bayes import GaussianNB

x, y = make_blobs(n_samples=500, centers=5, random_state=8)  # 这里构建了一个500个样本,5类的数据
x_train, x_test, y_train, y_test = train_test_split(x, y, random_state=8)

gnb = GaussianNB()
gnb.fit(x_train, y_train)
print(gnb.score(x_test, y_test)

2. 伯努利朴素贝叶斯

二项分布,适合0—1变量的分类任务

from sklearn.naive_bayes import BernoulliNB

bnb = BernoulliNB()
bnb.fit(x_train, y_train)
bnb.score(x_test, y_test)

3. 多项式分布朴素贝叶斯

多项式分布擅长的是分类型变量(离散),不接受负值的输入

from sklearn.naive_bayes import MultinomialNB

mnb=MultinomialNB()
mnb.fit(x_train,y_train)
mnb.score(x_test,y_test)


总结

优缺点

1. 优点

  • 通过计算概率来进行分类,可以处理多分类问题
  • 对缺失数据不太敏感,算法也比较简单,常用于文本分类。

2. 缺点

  • 朴素贝叶斯模型假设属性之间相互独立,这个假设在实际应用中往往是不成立的,在属性个数比较多或者属性之间相关性较大时,分类效果不好
  • 需要知道先验概率,且先验概率很多时候取决于假设,假设的模型可以有很多种,因此在某些时候会由于假设的先验模型的原因导致预测效果不佳,分类决策存在一定的错误率。
  • 对输入数据的表达形式很敏感(布尔型)
  • sklearn中贝叶斯没有太多的参数可以调整,因此贝叶斯算法的成长空间并不是太大。如果贝叶斯算法的效果不是太理想,一般都会考虑换模型。

问题

  1. sklearn中,有三种不同的朴素贝叶斯模型。朴素贝叶斯将问题转化成如何基于训练数据D来估计先验概率P(A)和似然P(B|A)。P(A)由大数定律,是不是可以由训练集的某分类除以训练集总数得到?

    那么P(A)的算法统一了,问题转化成了如何求似然估计P(B|A),资料说根据对“似然度 P(Bi|A)”计算方法的不同,我们将朴素贝叶斯大致分为三种:多项式朴素贝叶斯、伯努利分布朴素贝叶斯、高斯分布朴素贝叶斯。例如高斯朴素贝叶斯就是用正态分布模型来估算P(B|A)。如何理解这里的计算方法不同呢?

    是否这样理解:真实的数据集,并不像我们的例子,例如“碗中取糖果等”有着固定的分布规律,这三个算法是否就是在假设数据集符合某种分布,例如用伯努利朴素贝叶斯,就是在假设训练集的每个属性都满足二项分布?然后去计算条件概率?那如果有的特征符合正态分布,有的特征符合多项式分布,有的特征符合伯努利怎么办呢?

    机器学习之朴素贝叶斯_第6张图片


Reference:
机器学习实战教程(四):朴素贝叶斯基础篇之言论过滤器

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