入门机器学习(西瓜书+南瓜书)贝叶斯分类器总结(python代码实现)

16 篇文章 6 订阅
14 篇文章 5 订阅

入门机器学习(西瓜书+南瓜书)贝叶斯分类器总结(python代码实现)

一、贝叶斯分类器

1.1 通俗理解

先来看两个公式
P ( A B ) = P ( A ) P ( B ) ( 1 ) P(AB)=P(A)P(B) (1) P(AB)=P(A)P(B)1
P ( A B ) = P ( B ∣ A ) P ( A ) = P ( A ∣ B ) P ( B ) ( 2 ) P(AB)=P(B|A)P(A)=P(A|B)P(B) (2) P(AB)=P(BA)P(A)=P(AB)P(B)2
我先说第一个公式,首先这里的A,B分别代表两个不同的事件。如果事件A和事件B的独立,也就是说事件A的发生不影响事件B。事件B的发生不影响事件A。则 P ( A B ) = P ( A ) P ( B ) P(AB)=P(A)P(B) P(AB)=P(A)P(B)
那么,如何事件B很依赖事件A。毕竟如果没有事件A,事件B不可能发生。因此,在这个情景,我们会把 P ( B ) P(B) P(B)改为 P ( B ∣ A ) P(B|A) P(BA),即在事件A发生的前提下,事件B发生的概率。
理清楚了这个关系,亲爱的读者,你才有基本的数学基础来学习贝叶斯分类器,否则,大概率是学习的过程是一头雾水。
下面有请我们概率界的大哥——贝叶斯公式登场。或许有同学会问,为啥叫贝叶斯公式呢,因为这个公式是1763年由英国数学家托马斯贝叶斯的一篇论文中为解决一个逆概率问题提出的。因此被称为贝叶斯公式(定理)。
我们顶着公式(2)的后两项 P ( B ∣ A ) P ( A ) = P ( A ∣ B ) P ( B ) P(B|A)P(A)=P(A|B)P(B) P(BA)P(A)=P(AB)P(B),我做这样一个处理,等式两边分别除以 P ( B ) P(B) P(B),得到公式(3)
P ( A ∣ B ) = P ( B ∣ A ) P ( A ) P ( B ) ( 3 ) P(A|B)=\frac{P(B|A)P(A)}{P(B)} (3) P(AB)=P(B)P(BA)P(A)(3)
这就是贝叶斯公式,看似简单,但是为概率打开了一扇大门,如果说刚才我们的分析是从前到后,由因朔果,那么贝叶斯公式就是从后往前,由果朔因。通过这个公式得到的 P ( B ∣ A ) P(B|A) P(BA)为事件A发生条件下,事件B发生的概率。

1.2 全概率公式

在概率中,还有一个公式相当重要,他被叫做全概率公式。
我们现在再假设一个场景。小王要从家去公司上班,但是时间来不及了,路远的道路很耗费时间,路近的道路更容易堵车。我们可以跟据经验估计小王
选择每条路的概率,已经根据每条路的交通概况推测每条路通畅的概率。
选择每条路的概率分别为:0.5,0.3,0.2.每条路通畅的概率分别为:0.2,0.4,0.7

在这里插入图片描述
计算小明不迟到(不拥堵就不会迟到)的概率。
解:A,B1,B2,B3分别表示小明没有迟到,小明选择道路L1,道路L2,道路L3,则
P ( A ) = P ( A B 1 ) + P ( A B 2 ) + P ( A B 3 ) = P ( A ∣ B 1 ) P ( B 1 ) + P ( A ∣ B 2 ) P ( B 2 ) + P ( A ∣ B 3 ) P ( B 3 ) = 0.2 × 0.5 + 0.4 × 0.3 + 0.7 × 0.2 = 0.36 \begin{aligned} P(A)&=P(AB_{1})+P(AB_{2})+P(AB_{3})\\ &=P(A|B_{1})P(B_{1})+P(A|B_{2})P(B_{2})+P(A|B_{3})P(B_{3})\\ &=0.2\times0.5+0.4\times0.3+0.7\times0.2 \\ &=0.36 \end{aligned} P(A)=P(AB1)+P(AB2)+P(AB3)=P(AB1)P(B1)+P(AB2)P(B2)+P(AB3)P(B3)=0.2×0.5+0.4×0.3+0.7×0.2=0.36
因此全概率公式,被定义为表示要达到某个目的,有多种方式(或者造成某种结果,有多种原因),问达到目的的概率是多少(或造成这种结果的概率是多少),若要达到目标 A A A,有 n n n种方式,记作 B 1 , B 2 , … , B n B1 ,B2,…,Bn B1,B2,,Bn ,之间相互独立,并且其概率和为1。那么对于任意一个事件 A A A发生的概率可以用下面的全概率公式计算:
P ( A ) = P ( A B 1 ) + P ( A B 2 ) + . . . + P ( A B n ) = P ( A ∣ B 1 ) P ( B 1 ) + P ( A ∣ B 2 ) P ( B 2 ) + . . . + P ( A ∣ B n ) P ( B n ) = ∑ i = 1 n P ( A ∣ B i ) P ( B i ) \begin{aligned} P(A)&=P(AB_{1})+P(AB_{2})+...+P(AB_{n})\\ &=P(A|B_{1})P(B_{1})+P(A|B_{2})P(B_{2})+...+P(A|B_{n})P(B_{n})\\ &=\sum_{i=1}^{n}P(A|B_{i})P(B_{i}) \end{aligned} P(A)=P(AB1)+P(AB2)+...+P(ABn)=P(AB1)P(B1)+P(AB2)P(B2)+...+P(ABn)P(Bn)=i=1nP(ABi)P(Bi)

1.3 贝叶斯公式

我们往往认为事件B会依赖事件A,而事件A不依赖于事件B。但是事实不是这样。实际上,贝叶斯公式反应的结果是,结果发生,条件发生的概率。
两者的关系是互项影响的。只是这个数值反映了两者影响的程度而已。
举个例子
你约你喜欢的女神看电影,他同意了,那么究竟她是否对你心动呢?
我们设:
P(s) 是你女神暗恋你/无所谓的可能性分布
P(o) 是观测值比如她同意跟你一起看电影
P(o|s) 是在不同背景态度下她同意的可能性,可以看作她心中两个情绪小人对跟你一起看电影的不同态度
P(s|o) 则是从可以看电影这个现实得到的各个态度的可能性
P ( s ∣ o ) = P ( o ∣ s ) P ( o ) P ( s ) ( 3 ) P(s|o)=\frac{P(o|s)}{P(o)}P(s) (3) P(so)=P(o)P(os)P(s)(3)

比如,现在你女神40%喜欢你,60%概率无所谓。如果女神喜欢你,那么你约她看电影的成功率是100%,如果无所谓,那么成功率只有30%。那么你约到女神出去的期望概率应该是40%*100%+60%*30%=58%。这也就是我们后面要讲的全概率公式。
最近一次,你又约了她,她答应了(这是一次观测值)。那么那么女神到底喜不喜欢你呢?
在已知女神答应你去看电影的条件下:
她喜欢你的概率是 0.4 ∗ 1 0.58 = 0.69 \frac{0.4*1}{0.58}=0.69 0.580.41=0.69
无所谓的概率是 0.6 ∗ 3 0.58 = 0.31 \frac{0.6*3}{0.58}=0.31 0.580.63=0.31
这也就说明了我们从女神答应你看电影这个结果,去推测女神是否喜欢我这个原因的概率。是不是概率该挺大对不对。也很符合我们的直观感觉。

1.3 朴素贝叶斯分类器

给定 N N N 个类别,设随机样本向量 x = x 1 , x 2 , … , x d x={x_1,x_2,…,x_d} x=x1,x2,,xd ,相关的三个概率:
(1)先验概率 P ( c ) P(c) P(c) :根据以前的知识和经验得出的c类样本出现的概率,与现在无关。
(2)后验概率 P ( c ∣ x ) P(c|x) P(cx) :相对于先验概率而言,表示x 属于c类的概率。
(3)条件概率 P ( x ∣ c ) P(x|c) P(xc) :已知属于c类的样本中发生x的概率。

在朴素贝叶斯分类器:假设所有的属性都相互独立。
P ( c ∣ x ) = P ( x ∣ c ) P ( c ) P ( x ) P ( c ∣ x ) = P ( x 1 , x 2 , … , x d ∣ c ) P ( c ) P ( x ) = P ( c ) P ( x ) ∏ i = 1 d P ( x i ∣ c ) \begin{aligned} P(c|x) &= \frac{P(x|c)P(c)}{P(x)}\\ P(c|x) &= \frac{P(x_{1},x_{2},…,x_{d}|c)P(c)}{P(x)}=\frac{P(c)}{P(x)}\prod_{i=1}^{d}P(x_{i}|c)\\ \end{aligned} P(cx)P(cx)=P(x)P(xc)P(c)=P(x)P(x1,x2,,xdc)P(c)=P(x)P(c)i=1dP(xic)
m a x c P ( c ∣ x ) = m a x c P ( c ) ∏ i = 1 d P ( x i ∣ c ) max_{c}P(c|x)=max_{c}P(c)\prod_{i=1}^{d}{P(x_{i}|c)} maxcP(cx)=maxcP(c)i=1dP(xic)
先验概率
P ( c ) = ∣ D c ∣ ∣ D ∣ P(c)=\frac{|D_{c}|}{|D|} P(c)=DDc, D c D_{c} Dc表示训练集中类别为c的样本组成的集合
条件概率
(1)离散属性
P ( x i ∣ c ) = ∣ D c , x i ∣ ∣ D c ∣ P(x_{i}|c)=\frac{|D_{c,x_{i}}|}{|D_{c}|} P(xic)=DcDc,xi其中 D c , x i i D_{c,xi_{i}} Dc,xii为第i个属性值为 x i x_{i} xi样本组成的集合。
(2)连续属性
P ( x i ∣ c ) = 1 2 π σ c , i e − x i − μ c , i 2 σ c , i 2 P(x_{i}|c)=\frac{1}{\sqrt{2\pi}\sigma_{c,i}}e^{-\frac{x_{i}-\mu_{c,i}}{2\sigma_{c,i}^2}} P(xic)=2π σc,i1e2σc,i2xiμc,i

1.4 拉普拉斯平滑

拉普拉斯平滑:为了避免其他属性携带的信息被其他未出现过的属性值“抹去”,在估计概率值时通常要进行平滑。即为了在 ∣ D c ∣ |D_{c}| Dc为0的基础上,对其分子加1,为了使得其不大于1,分母同时加上类别数( N > = 1 N>=1 N>=1).因此概率被修正为一个很小但不为零的数。具体的说,令 N N N表示训练集 D D D中的类别数, N i N_i Ni表示第 i i i个属性可能的取值数,则:
P ( c ) = ∣ D c ∣ + 1 ∣ D ∣ + N P(c)=\frac{|D_{c}|+1}{|D|+N} P(c)=D+NDc+1
P ( x i ∣ c ) = ∣ D c , x i ∣ + 1 ∣ D c ∣ + N i P(x_{i}|c)=\frac{|D_{c,x_{i}}|+1}{|D_{c}|+N_{i}} P(xic)=Dc+NiDc,xi+1
具体公式推导请详见,西瓜书第147页,南瓜书见第40页,及南瓜书贝叶斯分类器对应配套视频

二、代码实现

2.1 Sklearn库的应用

在这里我们介绍一下,sklearn的贝叶斯分类器,更多内容请关注sklearn官方链接,这里附上sklearn的中文社区,里面对每个函数都有详细的讲解和例子,sklearn中文社区

from sklearn.naive_bayes import GaussianNB, MultinomialNB,BernoulliNB

#GaussianNB:高斯分布的朴素贝叶斯
#MultinomialNB:多项式分布的朴素贝叶斯
#BernoulliNB:伯努利分布的朴素贝叶斯

#这里附上一个高斯分布的朴素分布的贝叶斯分类器简单使用,对鸢尾花数据集分类。
from sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split
from sklearn.naive_bayes import GaussianNB
X, y = load_iris(return_X_y=True)
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.5, random_state=0)
gnb = GaussianNB()
y_pred = gnb.fit(X_train, y_train).predict(X_test)
print("Number of mislabeled points out of a total %d points : %d" % (X_test.shape[0], (y_test != y_pred).sum()))
#Number of mislabeled points out of a total 75 points : 4

2.2 朴素贝叶斯判断善意和恶意评论

import numpy as np
from functools import reduce
'''
函数说明:创建实验样本
Parameters:
    无
Returns:
        postingList: 实验样本切分的词条
        classVec: 类别标签向量
'''
def loadDataSet():
    postingList = [['my','dog','has','files','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'],
                   ['quit','buying','worthless','dog','food','stupid']]
    classVec = [0,1,0,1,0,1] #类别标签向量,1代表侮辱性词汇,0代表非侮辱性词汇
    return postingList,classVec #返回实验样本切分的向量词条和类别标签向量
'''
函数说明:将切分的实验样本词条整理成不重复的词条列表,也就是词汇表
Parameters:
    dataSet:整理的样本数据集
Returns:
    vocabSet:返回不重复的词条列表,也就是词汇表
'''
def createVocabList(dataSet):
    vocabSet = set([])                      #创建一个空的不重复列表
    for document in dataSet:
        vocabSet = vocabSet | set(document) #取并集
    return list(vocabSet)
'''
函数说明:根据vocabList词汇表,将inputSet向量化,向量的每个元素为1或0
Parameters:
    vocabList:createVocabList返回的列表
    inputSet:切分的词条列表
Returns:
    returnVec:文档向量,词集模型
'''
def setOfWords2Vec(vocabList,inputSet):             #创建一个其中所含元素都为0的向量
    returnVec = [0]* len(vocabList)                 #遍历每个词条
    for word in inputSet:                           #如果词条存在于词汇表中,则置1
        if word in vocabList:
            returnVec[vocabList.index(word)] = 1
        else:
            print("the word:%s is not in my Vocabulary!"%word)
    return returnVec                                #返回文档向量
'''
函数说明:朴素贝叶斯分类器训练函数
Parameters:
    trainMatrix:训练文档矩阵
    trainCategory:训练类别标签向量
Returns:
    p0Vect:侮辱类的条件概率数组
    p1Vect:非侮辱类的条件概率数组
    PAbusive:文档属于侮辱类的概率
'''
def trainNB0(trainMatrix,trainCategory):
    numTrainDocs = len(trainMatrix)         #计算训练的文档数目
    numWords = len(trainMatrix[0])          #计算每篇文档的词条数
    pAbusive = sum(trainCategory)/float(numTrainDocs)       #文档属于侮辱类的概率
    p0Num = np.zeros(numWords)
    p1Num = np.zeros(numWords)
    #创建numpt.zeros数组
    p0Denom = 0.0
    p1Denom = 0.0
    p1Vect = 0.0
    p0Vect = 0.0
    for i in range(numTrainDocs):
        if trainCategory[i] == 1:           #统计属于侮辱类的条件概率所需的数据,即p(w0|1),p(w1|1),p(w2|2)...
            p1Num += trainMatrix[i]
            p1Denom += sum(trainMatrix[i])
        else:                               #统计属于侮辱类的条件概率数组,属于非侮辱类的条件概率数组、文档属于侮辱类的概率
            p0Num += trainMatrix[i]
            p0Denom += sum(trainMatrix[i])
    if(p1Denom!=0):
        p1Vect = p1Num/p1Denom              #相除
    if(p0Denom!=0):
        p0Vect = p0Num/p0Denom
    return p0Vect,p1Vect,pAbusive
        #返回属于侮辱类的条件概率数组、属于非侮辱类的条件概率数组,文档属于侮辱类的概率
'''
函数说明:朴素贝叶斯分类器分类函数
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
'''
函数说明:测试朴素贝叶斯分类器
Parameters:
    无
Returns:
    无
'''
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']                               #测试样本1
    #测试样本向量化
    thisDoc = np.array(setOfWords2Vec(myvocabList, testEntry))
    if classifyNB(thisDoc, p0v, p1v, pAb):
        print(testEntry, '属于侮辱类')                                     #执行分类并打印分类结果
    else:
        print(testEntry, '属于非侮辱类')                                   #执行分类并打印分类结果

    testEntry = ['stupid','garbage']
    #测试样本向量化
    thisDoc = np.array(setOfWords2Vec(myvocabList,testEntry))            #测试样本2
    if classifyNB(thisDoc,p0v,p1v,pAb):
        print(testEntry,'属于侮辱类')                                      #执行分类并打印分类结果
    else:
        print(testEntry,'属于非侮辱类')                                     #执行分类并打印分类结果
if(__name__=='__main__'):
    testingNB()

结果如图

p0: 0.10869565217391304
p1: 0.0
['love', 'my', 'dalmation'] 属于非侮辱类
p0: 0.0
p1: 0.10526315789473684
['stupid', 'garbage'] 属于侮辱类

结果分析,可以看到部分概率为0,这样的结果过于绝对,容错率偏低,我们可以尝试加入拉普拉斯平滑,来对概率进行修正。

2.3 新闻文本分类

数据集我们使用sklearn自带的新闻数据集20news-bydate_py3.pkz,将它划分训练集与测试集进行预测。摘自简书贝叶斯分类器

#导包
from sklearn.datasets import fetch_20newsgroups
from sklearn.model_selection import train_test_split
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.naive_bayes import MultinomialNB
from sklearn.metrics.classification import classification_report
def naivebayes():
    """
    朴素贝叶斯进行分本分类
    :return:
    """
    #获取数据集
    news = fetch_20newsgroups(data_home="./scikit_learn_data/",subset='all')
 
    #对数据集进行分割
    x_train,x_test,y_train,y_test = train_test_split(news.data,news.target,test_size=0.25)
    #进行特征抽取
    tf = TfidfVectorizer()
    x_train = tf.fit_transform(x_train)
    #print(tf.get_feature_names())#输出特征名
    x_test = tf.transform(x_test)
    #进行贝叶斯算法预测
    mlt = MultinomialNB(alpha=1.0)
    #print(x_train.toarray())

    mlt.fit(x_train,y_train)
    y_predict = mlt.predict(x_test)
    print("预测的文章类型为:",y_predict)

    #得出准确率
    print("准确率为:",mlt.score(x_test,y_test))

    #得出精确率召回率
    print("每个类别的精确率和召回率:",classification_report(y_test,y_predict,target_names=news.target_names))#target_names表示每个类别的名称(比如文章分科技、娱乐等)

    return None

if __name__ == "__main__":
    naivebayes()

运行结果:

预测的文章类型为: [ 5 13  8 ...  2  3 10]
准确率为: 0.8580220713073005
每个类别的精确率和召回率:    recision    recall  f1-score   support

             alt.atheism       0.88      0.78      0.82       199
           comp.graphics       0.89      0.77      0.82       245
 comp.os.ms-windows.misc       0.86      0.86      0.86       236
comp.sys.ibm.pc.hardware       0.80      0.79      0.79       261
   comp.sys.mac.hardware       0.94      0.87      0.90       239
          comp.windows.x       0.93      0.89      0.91       241
            misc.forsale       0.93      0.72      0.81       243
               rec.autos       0.87      0.92      0.89       238
         rec.motorcycles       0.96      0.92      0.94       260
      rec.sport.baseball       0.97      0.91      0.94       263
        rec.sport.hockey       0.92      0.99      0.95       244
               sci.crypt       0.81      0.98      0.89       253
         sci.electronics       0.82      0.86      0.84       219
                 sci.med       0.98      0.88      0.93       272
               sci.space       0.84      0.97      0.90       231
  soc.religion.christian       0.59      0.97      0.74       265
      talk.politics.guns       0.76      0.96      0.85       228
   talk.politics.mideast       0.90      0.98      0.94       229
      talk.politics.misc       0.99      0.75      0.85       169
      talk.religion.misc       1.00      0.16      0.27       177
               micro avg       0.86      0.86      0.86      4712
               macro avg       0.88      0.85      0.84      4712
            weighted avg       0.88      0.86      0.85      4712

三、代码文件

小程序员将代码文件和相关素材整理到了百度网盘里,因为文件大小基本不大,大家也不用担心限速问题。后期小程序员有能力的话,将在gitee或者github上上传相关素材。
链接:https://pan.baidu.com/s/1Ce14ZQYEYWJxhpNEP1ERhg?pwd=7mvf
提取码:7mvf

  • 4
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值