朴素贝叶斯

朴素贝叶斯(naive Bayes)法是基于贝叶斯定理特征条件独立假设的分类方法。

贝叶斯定理

贝叶斯定理就是假设有两个事件:事件A和事件B,求已知事件B发生的概率下事件A发生的概率,记为 P ( A ∣ B ) P(A|B) P(AB)。为了了解上式,我们先用文氏图来直观的反应下两者的关系:

如图所示,在事件B发生的概率下事件A的发生概率为 P ( A B ) P ( B ) \frac{P(AB)}{P(B)} P(B)P(AB),如果反过来,在事件A发生的概率下事件B发生的概率是多少呢?你肯定很快的就能想到是 P ( A B ) P ( A ) \frac{P(AB)}{P(A)} P(A)P(AB),很好那我们整理一下就可以得到如下结果:
P ( A ∣ B ) = P ( A B ) P ( B ) P ( B ∣ A ) = P ( A B ) P ( A ) P ( A B ) = P ( A ∣ B ) P ( B ) = P ( B ∣ A ) P ( A ) P(A|B)=\frac{P(AB)}{P(B)} \\ P(B|A)=\frac{P(AB)}{P(A)} \\ P(AB)=P(A|B)P(B)=P(B|A)P(A) P(AB)=P(B)P(AB)P(BA)=P(A)P(AB)P(AB)=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)
这就是贝叶斯定理的基本知识了。

而百度百科中是这样描述贝叶斯定理的:

通常,事件A在事件B(发生)的条件下的概率,与事件B在事件A的条件下的概率是不一样的;然而,这两者是有确定的关系,贝叶斯法则就是这种关系的陈述。

作为一个规范的原理,贝叶斯法则对于所有概率的解释是有效的;然而,频率主义者和贝叶斯主义者对于在应用中概率如何被赋值有着不同的看法:频率主义者根据随机事件发生的频率,或者总体样本里面的个数来赋值概率;贝叶斯主义者要根据未知的命题来赋值概率。一个结果就是,贝叶斯主义者有更多的机会使用贝叶斯法则。

贝叶斯法则是关于随机事件A和B的条件概率和边缘概率的。
P ( A i ∣ B ) = P ( B ∣ A i ) P ( A i ) ∑ j P ( B ∣ A j ) P ( A i ) P(A_i|B)=\frac{P(B|A_i)P(A_i)}{\sum_jP(B|A_j)P(A_i)} P(AiB)=jP(BAj)P(Ai)P(BAi)P(Ai)
其中P(A|B)是在B发生的情况下A发生的可能性。 A 1 , … , A n A_1,\dots,A_n A1,,An为完备事件组,即 ∪ i = 1 n A i = Ω ∪_{i=1}^nA_i=Ω i=1nAi=Ω A i A j = ∅ , P ( A i ) > 0 A_iA_j=∅,P(A_i)>0 AiAj=,P(Ai)>0
在贝叶斯法则中,每个名词都有约定俗成的名称:

P(A)是A的先验概率或边缘概率。之所以称为"先验"是因为它不考虑任何B方面的因素。

P(A|B)是已知B发生后A的条件概率,也由于得自B的取值而被称作A的后验概率

P(B|A)是已知A发生后B的条件概率,也由于得自A的取值而被称作B的后验概率。

P(B)是B的先验概率或边缘概率,也作标准化常量(normalized constant)。

举个例子:比如在我们“小学二年级”中遇到的工厂生产零件问题中:假设一个工厂要生产一批零件,这批零件由三个车间 B 1 , B 2 , B 3 B_1,B_2,B_3 B1,B2,B3完成,每个车间分配到的份额为a,b,c。若从这个工厂中随机抽取一个零件,这个零件是次品为事件A。如果用 P ( A ∣ B 1 ) P(A|B_1) P(AB1)表示 B 1 B_1 B1车间的次品率, P ( A ∣ B 2 ) P(A|B_2) P(AB2)表示 B 1 B_1 B1车间的次品率, P ( A ∣ B 2 ) P(A|B_2) P(AB2)表示 B 1 B_1 B1车间的次品率,那么我们可以算出 P ( A ) P(A) P(A)的值。
P ( A ) = P ( A ∣ B 1 ) P ( B 1 ) + P ( A ∣ B 2 ) P ( B 2 ) + P ( A ∣ B 3 ) P ( B 3 ) = ∑ i = 1 3 P ( A ∣ B i ) P ( B i ) P(A)=P(A|B_1)P(B_1)+P(A|B_2)P(B_2)+P(A|B_3)P(B_3)=\sum_{i=1}^3P(A|B_i)P(B_i) P(A)=P(AB1)P(B1)+P(AB2)P(B2)+P(AB3)P(B3)=i=13P(ABi)P(Bi)
上述公式就是我们所说的全概率公式

而贝叶斯定理是:已知这个零件是次品,他来自 P ( B 1 ) P(B_1) P(B1)车间的概率为多少,它的计算公式如下:
P ( B 1 ∣ A ) = P ( A ∣ B 1 ) P ( B 1 ) P ( A ) P(B_1|A)=\frac{P(A|B_1)P(B_1)}{P(A)} P(B1A)=P(A)P(AB1)P(B1)

机器学习中的朴素贝叶斯法

上例中已知次品,求其来自哪个车间。机器学习中的朴素贝叶斯是用来求解已知实例x,求该实例属于哪一个类别。

假设训练数据集为 T = ( x 1 , y 1 ) , ( x 2 , y 2 ) , …   ( x n , y n ) T={(x_1,y_1),(x_2,y_2),\dots\,(x_n,y_n)} T=(x1,y1),(x2,y2),(xn,yn),其中 x i x_i xi为样本, y i y_i yi为类别(假设共有K个类别,用 C k C_k Ck表示, k = 1 , 2 , … , K k=1,2,\dots,K k=1,2,,K

先验概率:
P ( Y = c k ) = ∑ i = 1 n I ( y i = c k ) n P(Y=c_k)=\frac{\sum_{i=1}^nI(y_i=c_k)}{n} P(Y=ck)=ni=1nI(yi=ck)
后验概率:
P ( Y = c k ∣ X = x ) = P ( X = x ∣ Y = c k ) P ( c k ) P ( X = x ) P(Y=c_k|X=x)=\frac{P(X=x|Y=c_k)P(c_k)}{P(X=x)} P(Y=ckX=x)=P(X=x)P(X=xY=ck)P(ck)
由上面公式可知, P ( Y = c k ∣ X = x ) P(Y=c_k|X=x) P(Y=ckX=x)主要与 P ( X = x ∣ Y = c k ) P ( c k ) P(X=x|Y=c_k)P(c_k) P(X=xY=ck)P(ck)有关,这是因为上式 P ( X = x ) P(X=x) P(X=x)对任何一个实例都是固定的,故 P ( X = x ∣ Y = c k ) P ( c k ) P(X=x|Y=c_k)P(c_k) P(X=xY=ck)P(ck) P ( Y = c k ∣ X = x ) P(Y=c_k|X=x) P(Y=ckX=x)的大小起到决定性作用。如果真的想计算 P ( X = x ) P(X=x) P(X=x)的大小,可以参考下面公式:

《统计学习方法》中关于 P ( X = x ) P(X=x) P(X=x)的计算是使用全概率公式
P ( X = x ) = ∑ k P ( X = x ∣ Y = c k ) P ( Y = c k ) P(X=x)=\sum_kP(X=x|Y=c_k)P(Y=c_k) P(X=x)=kP(X=xY=ck)P(Y=ck)
之后我看到了一篇文章,它使用了朴素贝叶斯中的特征条件独立,其描述如下:
P ( X = x ) = P ( X ( 1 ) = x 1 , X ( 2 ) = x 2 , … , X ( n ) = x n ) = P ( X ( 1 ) = x ( 1 ) ) P ( X ( 2 ) = x ( 2 ) ) … P ( X ( n ) = x ( n ) ) = ∏ i = 1 n P ( X ( i ) = x ( i ) ) \begin{aligned} P(X=x)&=P(X^{(1)}=x^{1},X^{(2)}=x^{2},\dots,X^{(n)}=x^{n}) \\ &=P(X^{(1)}=x^{(1)})P(X^{(2)}=x^{(2)})\dots P(X^{(n)}=x^{(n)}) \\ &=\prod_{i=1}^nP(X^{(i)}=x^{(i)}) \end{aligned} P(X=x)=P(X(1)=x1,X(2)=x2,,X(n)=xn)=P(X(1)=x(1))P(X(2)=x(2))P(X(n)=x(n))=i=1nP(X(i)=x(i))
两种解释都还是比较合理的,大家可以参考一下。

我们都已经知道 P ( Y = c k ∣ X = x ) P(Y=c_k|X=x) P(Y=ckX=x) P ( X = x ∣ Y = c k ) P ( c k ) P(X=x|Y=c_k)P(c_k) P(X=xY=ck)P(ck)有关。而到这里我们的工作已经简化很多了,我们只需要计算出 P ( X = x ∣ Y = c k ) P ( c k ) P(X=x|Y=c_k)P(c_k) P(X=xY=ck)P(ck),就能判断出实例x属于哪一个类别。

这里我们求 P ( X = x ∣ Y = c k ) P(X=x|Y=c_k) P(X=xY=ck)用到了特征条件独立
P ( X = x ∣ Y = c k ) ) = P ( X ( 1 ) = x 1 , X ( 2 ) = x 2 , … , X ( n ) = x n ∣ 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 ) \begin{aligned} P(X=x|Y=c_k))&=P(X^{(1)}=x^{1},X^{(2)}=x^{2},\dots,X^{(n)}=x^{n}|Y=c_k) \\ &=P(X^{(1)}=x^{(1)}|Y=c_k)P(X^{(2)}=x^{(2)}|Y=c_k)\dots P(X^{(n)}=x^{(n)}|Y=c_k) \\ &=\prod_{j=1}^nP(X^{(j)}=x^{(j)}|Y=c_k) \end{aligned} P(X=xY=ck))=P(X(1)=x1,X(2)=x2,,X(n)=xnY=ck)=P(X(1)=x(1)Y=ck)P(X(2)=x(2)Y=ck)P(X(n)=x(n)Y=ck)=j=1nP(X(j)=x(j)Y=ck)
故我们可以设
y c k = f ( x ) = a r g m a x P ( c k ) ∏ j = 1 n P ( X ( j ) = x ( j ) ∣ Y = c k ) y_{c_k}=f(x)=argmaxP(c_k)\prod_{j=1}^nP(X^{(j)}=x^{(j)}|Y=c_k) yck=f(x)=argmaxP(ck)j=1nP(X(j)=x(j)Y=ck)
当在已知实例x,哪一类列的概率大,实例x就属于哪一个类别。

学习与分类算法

输入:训练数据 T = ( x 1 , y 1 ) , ( x 2 , y 2 ) , … , ( x N , y N ) T={(x_1,y_1),(x_2,y_2),\dots,(x_N,y_N)} T=(x1,y1),(x2,y2),,(xN,yN),其中 x i = ( x i ( 1 ) , x i ( 2 ) , … , x i ( n ) ) T x_i=(x_i^{(1)},x_i^{(2)},\dots,x_i^{(n)})^T xi=(xi(1),xi(2),,xi(n))T x i j x_i^j xij是第i个样本的第j个特征, x i j x_i^j xij特征的取值有 ( a j 1 a j 2 , … , a j l ) (a_{j1}a_{j2},\dots,a_{jl}) (aj1aj2,,ajl) y i ∈ c 1 , c 2 , … , c K y_i∈{c_1,c_2,\dots,c_K} yic1,c2,,cK;实例x

输出:实例x的分类

(1)计算先验概率及条件概率
P ( Y = c k ) = ∑ i = 1 n I ( y i = c k ) N , k = 1 , 2 , … , K P(Y=c_k)=\frac{\sum_{i=1}^nI(y_i=c_k)}{N},k=1,2,\dots,K P(Y=ck)=Ni=1nI(yi=ck),k=1,2,,K
条件概率表示在类别为 c k c_k ck的情况下,所有样本第j个特征取值为 a j l a_{jl} ajl的概率
P ( X ( j ) = a j l ∣ Y = c k ) = ∑ j = 1 N I ( x i ( j ) = a j l , y i = c k ) ∑ i = 1 N I ( y i = c k ) P(X^{(j)}=a_{jl}|Y=c_k)=\frac{\sum_{j=1}^NI(x_i^{(j)}=a_{jl},y_i=c_k)}{\sum_{i=1}^NI(y_i=c_k)} P(X(j)=ajlY=ck)=i=1NI(yi=ck)j=1NI(xi(j)=ajl,yi=ck)
(2)对于给定的实例 x = ( x ( 1 ) , x ( 2 ) , … , x ( n ) ) T x=(x^{(1)},x^{(2)},\dots,x^{(n)})^T x=(x(1),x(2),,x(n))T,计算
P ( Y = c k ) ∏ j = 1 n P ( X ( j ) = x ( j ) ∣ Y = c k ) , k = 1 , 2 , … , K P(Y=c_k)\prod_{j=1}^nP(X^{(j)}=x^{(j)}|Y=c_k),k=1,2,\dots,K P(Y=ck)j=1nP(X(j)=x(j)Y=ck),k=1,2,,K
(3)确定实例x的类
y c k = f ( x ) = a r g m a x P ( c k ) ∏ j = 1 n P ( X ( j ) = x ( j ) ∣ Y = c k ) y_{c_k}=f(x)=argmaxP(c_k)\prod_{j=1}^nP(X^{(j)}=x^{(j)}|Y=c_k) yck=f(x)=argmaxP(ck)j=1nP(X(j)=x(j)Y=ck)

image-20211127205542747

上述中共由15个样本,每个样本有两个特征 X ( 1 ) , X ( 2 ) X^{(1)},X^{(2)} X(1),X(2)
在这里插入图片描述

在这里插入图片描述

代码

加载数据集

from numpy import *
def loadDataSet():
    """加载数据集合及其对应的分类"""
    wordsList = [['周六', '公司', '一起', '聚餐', '时间'],
                 ['优惠', '返利', '打折', '优惠', '金融', '理财'],
                 ['喜欢', '机器学习', '一起', '研究', '欢迎', '贝叶斯', '算法', '公式'],
                 ['公司', '发票', '税点', '优惠', '增值税', '打折'],
                 ['北京', '今天', '雾霾', '不宜', '外出', '时间', '在家', '讨论', '学习'],
                 ['招聘', '兼职', '日薪', '保险', '返利']]
    # 1 是, 0 否
    classVec = [0, 1, 0, 1, 0, 1]
    return wordsList, classVec

将样本传入该函数,for循环遍历整个样本,使用并集(“ | ”)构造为一个元素不重复的集合

#将数据集合并为一个集合,且元素不重复
def doc2VecList(docList):
    vocabSet = set([])
    for document in docList:
        vocabSet = vocabSet | set(document) #并集
    return list(vocabSet)

传入之前构造出的元素不重复的集合及一个样本,在元素不重复的集合中标记样本元素的个数。重复出现的元素就在标记位置的基数上加一,即构造一个单词向量。

def words2Vec(vecList, inputWords):
    # 生成一个全为0的list,用于标记样本特征所处位置
    resultVec = [0] * len(vecList)
    for word in inputWords:
        if word in vecList:
            # 在单词出现的位置上的计数加1
            resultVec[vecList.index(word)] += 1
        else:
            print('没有发现此单词')
    return array(resultVec)
words2Vec(allWordsVec, docList[0])
array([1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0,
       0, 0, 0, 0, 0, 0, 1, 0, 0, 0])

在这里有一个与上述公式不对应的地方,在上述我们讲到条件概率是所有样本第j个特征取值为 a j l a_{jl} ajl的个数除以 c k c_k ck的个数。但在这里,我们对类别为 c k c_k ck的样本求取了它们的特征个数,以此作为分母。而这并不会影响到类别的判断,因为 P ( X ( j ) = a j l ∣ Y = c k ) = ∑ j = 1 N I ( x i ( j ) = a j l , y i = c k ) ∑ i = 1 N I ( y i = c k ) P(X^{(j)}=a_{jl}|Y=c_k)=\frac{\sum_{j=1}^NI(x_i^{(j)}=a_{jl},y_i=c_k)}{\sum_{i=1}^NI(y_i=c_k)} P(X(j)=ajlY=ck)=i=1NI(yi=ck)j=1NI(xi(j)=ajl,yi=ck)其分母对于所有类别为 c k c_k ck的样本来说都是一个定值,真正影响概率的是样本的特征数。

def trainNB(trainMatrix, trainClass):
    """计算,生成每个词对于类别上的概率"""
    # 类别行数
    numTrainClass = len(trainClass)
    # 列数
    numWords = len(trainMatrix[0])
    #重点
    p0Num = ones(numWords)
    p1Num = ones(numWords)
    p0Words = 2.0
    p1Words = 2.0
    #统计不同类别下所有样本各个特征的总数
    for i in range(numTrainClass): 
        if trainClass[i] == 1:
            # 数组在对应的位置上相加
            p1Num += trainMatrix[i] #记录每个特征出现的次数
            p1Words += sum(trainMatrix[i]) 
        else:
            p0Num += trainMatrix[i]
            p0Words += sum(trainMatrix[i])
            
    p0Vec = log(p0Num / p0Words)
    p1Vec = log(p1Num / p1Words)
    # 计算在类别中1出现的概率,0出现的概率可通过1-p得到
    pClass1 = sum(trainClass) / float(numTrainClass)
    return p0Vec, p1Vec, pClass1

这里使用了log,使得连乘变为了连加。
l o g ( P ( Y = c k ) ∏ j = 1 n P ( X ( j ) = x ( j ) ∣ Y = c k ) ) = l o g P ( Y = c k ) + ∑ j = 1 n l o g P ( X ( j ) = x ( j ) ∣ Y = c k ) log(P(Y=c_k)\prod_{j=1}^nP(X^{(j)}=x^{(j)}|Y=c_k)) \\ =logP(Y=c_k)+\sum_{j=1}^nlogP(X^{(j)}=x^{(j)}|Y=c_k) log(P(Y=ck)j=1nP(X(j)=x(j)Y=ck))=logP(Y=ck)+j=1nlogP(X(j)=x(j)Y=ck)
image-20211127222049431

def classifyNB(testVec, p0Vec, p1Vec, pClass1):
    p1 = sum(testVec * p1Vec) + log(pClass1)
    p0 = sum(testVec * p0Vec) + log(1 - pClass1)
    if p0 > p1:
        return 0
    return 1
def tNB():
    # 从训练数据集中提取出属性矩阵和分类数据
    docList, classVec = loadDataSet()
    allWordsVec = doc2VecList(docList)
    # 构建词向量矩阵
    trainMat = []
    for i in range(len(docList)):
        temporary = words2Vec(allWordsVec, docList[i])
        trainMat.append(temporary)
    # 训练计算每个词在分类上的概率, p0V:每个单词在非分类出现的概率, p1V:每个单词在是分类出现的概率
    # 其中概率是以ln进行计算的
    # pClass1为类别中是1的概率
    p0V, p1V, pClass1 = trainNB(trainMat, classVec)
    # 测试数据集
    testWords = ['公司', '聚餐', '讨论', '贝叶斯']
    # 转换成单词向量,32个单词构成的数组,如果此单词在数组中,数组的项值置1
    testVec = words2Vec(allWordsVec, testWords)
    # 通过将单词向量testVec代入,根据贝叶斯公式,比较各个类别的后验概率,判断当前数据的分类情况
    testClass = classifyNB(testVec, p0V, p1V, pClass1)
    # 打印出测试结果
    printClass(testWords, testClass)
    
    testWords = ['公司', '保险', '金融']
    # 转换成单词向量,32个单词构成的数组,如果此单词在数组中,数组的项值置1
    testVec = words2Vec(allWordsVec, testWords)
    # 通过将单词向量testVec代入,根据贝叶斯公式,比较各个类别的后验概率,判断当前数据的分类情况
    testClass = classifyNB(testVec, p0V, p1V, pClass1)
    # 打印出测试结果
    printClass(testWords, testClass)
 
if __name__ == '__main__':
    tNB()
['公司', '聚餐', '讨论', '贝叶斯'] 属于类别0
['公司', '保险', '金融'] 属于类别1
posted @ 2021-11-28 18:12  Kim的小破院子  阅读(33)  评论(0编辑  收藏  举报