机器学习入门研究(八)-朴素贝叶斯算法
发布日期:2021-05-10 17:16:31 浏览次数:0 分类:技术文章

目录

 


贝叶斯公式

如果交换条件概率中的条件与结果,即已知P(x|h)怎么去求解P(h|x)

https://img-blog.csdnimg.cn/20191125110355422.png

(1)P(h|x)在事件x发生之后,h事件发生的概率。称为后验概率。比如在一份特定邮件中,是垃圾邮件的概率

(2)P(h)是事件h发生的先验概率。先验概率就是根据经验或者历史材料来推断的概率。比如在总体邮件中,是垃圾邮件的概率

(3)P(x)是事件x的先验概率。比例总体邮件中是特定特征的邮件的概率。

(4)https://img-blog.csdnimg.cn/20191125110955344.png称为可能性因子

  • >1 先验概率被增强,事件h的概率增大
  • =1 对h发生的概率无影响
  • <1 先验概率被削弱,事件h发生的概率变小

抽样的数量越大越容易接近真实的概率分布,当数量比较小的时候,计算概率会与实际不相符。

朴素贝叶斯算法

1.定义

朴素就是指的是特征与特征之间相互独立;贝叶斯就是贝叶斯公式。朴素贝叶斯算法就是从训练集得到一些概率。

举个例子

对于一个垃圾邮件分类系统,我们最后统计分类中对应如下:

我们可以看到对应着金融的类别的概率最大,那么该垃圾邮件就被归属为金融类别。注意在使用概率推断分类的时候,如果数据量比较小的话,计算概率会与实际不相符。

我们通过一个实例分析下使用贝叶斯公式的过程。我们有以下样本集,是根据职业、体型来分析是否被女神喜欢。

样本数

职业

体型

是否被女神喜欢

1

程序员

超重

不喜欢

2

产品

匀称

喜欢

3

程序员

匀称

喜欢

4

程序员

超重

喜欢

5

美工

匀称

不喜欢

6

美工

超重

不喜欢

7

产品

匀称

喜欢

那么已知小明是产品,体型超重,那么被女神喜欢的概率会是多少呢?

很显然在这个样本集中,职业和体型为特征值,是否被女神喜欢是目标值,是一个二分类的问题。我们首先看下下面几个概率值:

(1)女神喜欢的概率?

(2)职业是程序员&体型匀称的概率?

(3)在女神喜欢的条件下,职业是程序员概率?

(4)在女神喜欢的条件下,职业是程序员,并且是体型超重 概率?

在计算这几个概率之前,我们先看下下面几个概率的概念:

  • 联合概率:包含多个条件,并且所有条件都成立的概率,即P(A,B)
  • 条件概率:在事件B已经发生条件下,事件A发生的概率,即P(A|B)
  • 相互独立:如果P(A,B)=P(A)xP(B),则可以认为事件A和事件B相互独立

下面可以看下这个几个概率值:

(1)女神喜欢的概率?

即在所有样本集中女神喜欢的概率占比

(2)职业是程序员&体型匀称的概率?

联合概率,即职业是程序员&体型匀称在所有样本的占比

(3)在女神喜欢的条件下,职业是程序员概率?

条件概率,即在女神喜欢的样本集中,职业是程序员的占比

(4)在女神喜欢的条件下,职业是程序员,并且是体型超重 概率?

条件概率和联合概率的集合,在女神喜欢的样本集中,职业是程序员&体型超重的占比

好了,我们在看下小明是产品&超重,会被女神喜欢的概率呢?即P(喜欢|产品,超重)?

 这就需要用到我们前面提到的贝叶斯公式,https://img-blog.csdnimg.cn/20191125110355422.png,该公式中的x可以看出特征值,h看出为目标值, P(h|x)为后验概率,即我们要通过一些特征或历史经验来推测目标值。将上述的P(喜欢|产品,超重)?代入到该贝叶斯公式中,即

 

我们分别求解概率值:

(1)P((产品,超重)|喜欢)

即在喜欢的样本集中,产品&超重的占比

(2)P(喜欢)

(3)P(产品,超重)

显然与实际不相符,因为在实际中,这种情况还是有被喜欢的概率。主要原因就是我们提供的样本集的数据很少的原因。

根据我们刚才提到的朴素贝叶斯算法中,假设特征之间是相互独立的,所以P((产品,超重)|喜欢)就可以转换成下面的方式:

(1)P((产品,超重)|喜欢)

(3)P(产品,超重)

综合(1)(2)(3),我们最终得到

所以朴素贝叶斯算法会因为样本集少,假设特征之间相互独立,造成预测不准。在日常中,各个特征之间很难做到相互独立。所以朴素贝叶斯算法通常用于数据量比较大的样本集中。

2.应用场景

通常用于文本分类、文本分析,与文本相关的归类、文本情感分析。在作为文本分析的时候,单词作为特征,词与词之间是相互独立的

3.实例分析

实例

通过一个实例分析下朴素贝叶斯算法。我们有下面几个文档,里面含有一些特征词,然后通过特征词来推断该文档是否属于c=China的类别

样本集

文档中的词

属于c=China类别

1

Chinese Beijing Chinese

Yes

2

Chinese Chinese Shanghai

Yes

3

Chinese Macao Tokyo Japan Chinese

Yes

4

Chinese Japan Tokyo

No

那么一个文档中含有Chinese Chinese Chinese Tokyo Japan是否属于c=China类别呢?

朴素贝叶斯算法用在分类中主要有两种:多项式模型和伯努利模型。另外在回归中,还有一个高斯模型。

  • 多项式模型

适用于服从多项分布的特征数据。多项式分布是二项式分布的推广,二项分布就是随机结果值只有两个(例如抛硬币);而多项式分布则随机结果值多个(例如摇骰子)

  • 伯努利模型

用于多重伯努利分布的数据,即有多个特征,但每个特征都假设是一个二元变量。

伯努利分布又称零一分布或两点分布(即结果只有1和0),是一个二项分布的特殊情况,是因为二项分布是多重伯努利实验的概率分布。举个例子:伯努利分布是只扔一次硬币正面反面的概率,而二项分布是扔多次硬币以后得到正面反面的概率。

  • 高斯模型

高斯分布又叫正太分布,我们把一个随机变量X服从数学期望为μ、方差为σ^2的数据分布称为正太分布,当数学期望μ=0,方差σ=1时称为标准正态分布。

多项式模型朴素贝叶斯和伯努利模型朴素贝叶斯常用在文本分类问题中,高斯分布的朴素贝叶斯主要用于连续变量中,且假设连续变量是服从正太分布的。

针对上面的例子我们分别用多项式模型和伯努利模型来进行分析。我们要分析上面的问题,其实就是计算P(C|(Ch Ch Ch To Ja))和P(非C|(Ch Ch Ch To Ja)大小(为了方便表示,分别取单词的前两个字母来标示该单词),公式如下:

我们可以看到分母相同,只要比较分子即可。

多项式模型

在多项式模型中分析文本分类的问题时,是以单词为粒度,并且在计算的时候允许单词重复。

在上面的公式其实就是如下

  • C就是对应的文档类别,F1,F2…就是对应的各个单词。
  • P(C):就是该C类别下的文档中的单词总数占所有样本集中的文档的单词总数的占比

  • P(F1|C):在指定的C类别文档下,F1这个单词出现的次数占该C类别文档的所有单词总数的占比

  • P(F1):该单词F1的出现的次数在所有文档中的单词总数的占比

将P(C|(Ch Ch Ch To Ja))和P(非C|(Ch Ch Ch To Ja)代入到公式:

(1)P( (Ch Ch Ch To Ja) | C)

统计上面表格中的数据为:

我们发现计算出来的概率为0,但这个里面含有3个Chinese,是c类别的文档有很大的可能性,之所以出现这个问题,主要是由于样本量太少造成的。

为了减少由于样本数少引起的误差,我们需要引入拉普拉斯平滑系数。

  • 拉普拉斯平滑系数

  1. 其中n1即该F1单词在C类文档中出现的次数;
  2. 𝛂为指定系数,一般为1;
  3. m为特征词的个数,也可以认为特征维度;
  4. n即C类别 文档中所有的单词的个数。

那么上述的公式就变成了:

那么将上面的计算过程重新代入引入拉普拉斯系数的公式中,计算如下:

(2)P(C)

(3)P( (Ch Ch Ch To Ja) |非 C)

(4)P(非C)

结合(1)和(2)得到

结合(3)和(4)得到

显然P(C|(Ch Ch Ch To Ja))>P(非C|(Ch Ch Ch To Ja),那么该文档属于c=China的类别。

伯努利模型

在多项式模型中分析文本分类的问题时,是以文档为粒度,并且在计算的时候不允许单词重复。主要用于特征值是二元离散值或者很稀疏的多元离散值时

在计算后验概率P(C|F1,F2..),对于一个预测文档d来说,多形式模型,只有在预测文档d出现的单词才会参与后验概率计算;但是伯努利模型,即使没有在预测文档d中出现的其他特征词,也要参与计算只不过是取反值来计算。结合公式来说明下这个问题

在上面的公式其实就是如下

  • P(F1,F2,…|C):C类别的所有的特征词在C类文档总数中的占比。只不过出现过的特征值使用P(F1=1│C),而没有出现过的特征值采用的是P(F1=0│C)的值。

因为对于伯努利模型中,F1对应的特征值要不为1要不为0,1就表示在该文档中出现,0表示在该文档中未出现。所以在求P(F1|C)的概率值的时候,就有下面的两种情况:

  • P(C):C类别的文档数占所有类别的文档数的占比

  • P(F1,F2…):所有的特征词在文档总数中的占比。只不过出现过的特征值使用P(F1=1),而没有出现过的特征值采用的是P(F1=0)的值

同样为了减少样本少带来的误差,引入拉普拉斯系数,公式如下:

  • n1:前提是在所有C类文档中的个数出现F1这个单词的文档数
  • n:所有的C类文档的个数

这里之所以直接是1/2。因为伯努利模型中的特征值就只有两种情况1和0,所以和多项式模型中的一样的理解,都是指的是特征的维度(后来想明白的,嘿嘿开心)。

同样上面那个例子中的P(C|(Ch Ch Ch To Ja))和P(非C|(Ch Ch Ch To Ja),公式还是分母相同,只需要比较分子即可。

(1)P( (Ch Ch Ch To Ja) | C)

由于在伯努利模型的特征用的是所有的特征词,求概率的时候,需要考虑其他未出现的特征词对该值的影响,即特征值为0的时候的概率。所以我们先计算下所有特征词在特征值为1的时候的概率值。

总共有6个特征词:Chinese Macao Tokyo Japan Beijing Shanghai,其在C类别下的出现该单词的概率为:

代入到公式计算该值P( (Ch Ch Ch To Ja) | C),未出现的特征词即特征值为0的概率。

(2)P(C)

(3)P( (Ch Ch Ch To Ja) |非 C)

代入到公式计算该值P( (Ch Ch Ch To Ja) | 非C),即

(4)P(非C)

结合(1)(2)可以得出:

结合(3)(4)可以得出:

显然P(C|(Ch Ch Ch To Ja))<P(非C|(Ch Ch Ch To Ja),那么该文档不属于c=China的类别。我认为之所以两种模型下计算的结果不一致是由于样本量太少引起的。

由于该实例是分类问题,而高斯模型是针对的回归问题,所以等后面学习到回归之后再去对比。

对比多项式模型和伯努利模型

(1)多项式模型以单词为粒度,计算的是单词出现的次数;而伯努利模型以文档为模型,统计的是单词出现在文档中的文档的数量,所以在计算先验概率P(C)和类别下的条件概率P(F1|C)的时候会不同。

(2)在计算后验概率P(C |F1,F2..)时,对于一个预测文档D的分类,多项式模型只会对在预测文档D中出现的单词的概率来计算,但是伯努利模型,由于每个文档的特征就是全局的特征词,每个特征词的特征值要么为1要么为0,所以即使特征词没有出现,也会以该特征词的特征值为0的情况下来计算概率值,也就是没有某个特征词也是一个特征。

在Sklearn中的API

多项式分布模型

sklearn.naive_bayes.MultinomialNB(alpha=1.0, fit_prior=True, class_prior=None)

参数

含义

alpha

拉普拉斯平滑系数,就是我们前面提到的𝛂,默认为1

fit_prior

是否学习类的先验概率即刚才的公式中的P(C)。默认为True,与class_prior配合使用

False:所有样本类别输出相同的类别先验概率,即P(C)=1/C类别的样本数

class_prior

各个类别的先验概率。默认为None,没有指定,则模型会根据数据自动学习,每个类别的先验概率相同,P(C) = C类别对应的样本数/所有样本总数

返回值

返回参数

含义

class_log_prior

每个类别平滑后的先验概率

intercept_

朴素叶斯对应线性模型,其class_log_prior_相同

feature_log_prob_

给定特征类别的对数概率(条件概率),即我前面提到的添加了拉普拉斯系数之后算P(F1|C)

class_count_

训练样本中各类别对应的样本

feature_count_

每个类别中各个特征出现的次数

coef_

是朴素叶斯对应线性模型,其feature_log_prob相同

伯努利模型

sklearn.naive_bayes. BernoulliNB (alpha=1.0, binarize=0.0, fit_prior=True, class_prior=None)

参数

含义

alpha

拉普拉斯平滑系数,就是我们前面提到的𝛂,默认为1

binarize

样本特征二值化的法治,默认为0.如果不输入,则模型会默认认为所有的特征值都已经二值化。如果输入具体的值,则把>该值归为一类,<该值的归为另外一类

fit_prior

是否学习类的先验概率即刚才的公式中的P(C)。默认为True,与class_prior配合使用

False:所有样本类别输出相同的类别先验概率,即P(C)=1/C类别的样本数

class_prior

各个类别的先验概率。默认为None,没有指定,则模型会根据数据自动学习,每个类别的先验概率相同,P(C) = C类别对应的样本数/所有样本总数

返回值

返回参数

含义

class_log_prior

每个类别平滑后的先验概率

feature_log_prob_

给定特征类别的对数概率(条件概率),即我前面提到的添加了拉普拉斯系数之后算P(F1|C)

class_count_

拟合过程中每个样本

feature_count_

拟合过程中每个特征的数量

高斯模型

sklearn.naive_bayes. GaussianNB ()

这个我想着等着学习回归的时候再来看这个,暂时先不看了。

实例

我们仍然直接使用sklearn中自带的数据集进行学习这个MultinomialNB。使用的是 sklearn中自带的fetch_20newsgroups。20newsgroups数据集是用来进行文本分类、文本挖掘和信息检索研究的国际标准数据集之一。

该数据集大约收集了20000左右的新闻组文档,均匀分成20个不同主题的新闻组集合。每个新闻组对应着不同的主题,然后我们将数据集分为训练集和测试集,通过训练集进行训练,然后测试测试集的新闻样本划分到相应的主题内。根据之前的过程分为下面几步:

(1)获取数据集

(2)将数据集划分为训练集和测试集

(3)特征工程

         也就是文本特征抽取提取,就是将所有的新闻文档中的特征词提取出来,可采用两种方式:

  • 使用CountVectorizer来统计词频
  • 使用TfidfVectorizer来统计单词的重要性

这部分内容可以参见

(4)朴素贝叶斯训练模型

(5)模型调优和评估

调优一般就是调整的拉普拉斯系数𝛂的大小,一般𝛂=1的效果就比较好了。

代码如下:

from sklearn.datasets import fetch_20newsgroupsfrom sklearn.model_selection import train_test_splitfrom sklearn.feature_extraction.text import TfidfVectorizerfrom sklearn.feature_extraction.text import CountVectorizerfrom sklearn.naive_bayes import MultinomialNBfrom sklearn.naive_bayes import GaussianNBfrom sklearn.naive_bayes import BernoulliNBfrom sklearn.metrics import accuracy_scoredef nb_news():   # 朴素贝叶斯对新闻进行分类    # 1)获取数据    news = fetch_20newsgroups(data_home="/Users/j1/Documents/机器学习/code/machinelearning/estimator/", subset='all')    # 2)划分数据集    x_train, x_test, y_train, y_test = train_test_split(news.data, news.target, random_state=12)    print("新闻的集合中的第一个元素的内容如下: \n", news.data[0])    print("新闻的集合中的目标值: \n", news.target)    # 3)特征工程    transform = TfidfVectorizer()    x_train = transform.fit_transform(x_train)    # transform.get_feature_names()会把样本集中所有的特征词列举出来    # 经过Tfidf之后也就是列举了第一个样本中所有的单词的重要程序    x_test = transform.transform(x_test)    # 4)朴素贝叶斯预估    estimator = MultinomialNB()    estimator.fit(x_train, y_train)    # 5)模型评估    print("测试集的第一个样本内容如下: \n", x_test[0])    y_predict = estimator.predict(x_test)    print("测试集的前10个样本的目标值: \n", y_predict[0:10])    print("对比真实值的前10个样本的目标值 : \n", y_test[0:10])    accuracy = accuracy_score(y_test, y_predict)    print("accuracy_score : ", accuracy)    score = estimator.score(x_test, y_test)    print("score : ", score)    return None

运行后输出的结果如下:

新闻的集合中的第一个元素的内容如下:  From: Mamatha Devineni Ratnam <mr47+@andrew.cmu.edu>Subject: Pens fans reactionsOrganization: Post Office, Carnegie Mellon, Pittsburgh, PALines: 12NNTP-Posting-Host: po4.andrew.cmu.eduI am sure some bashers of Pens fans are pretty confused about the lackof any kind of posts about the recent Pens massacre of the Devils. Actually,I am  bit puzzled too and a bit relieved. However, I am going to put an endto non-PIttsburghers' relief with a bit of praise for the Pens. Man, theyare killing those Devils worse than I thought. Jagr just showed you whyhe is much better than his regular season stats. He is also a lotfo fun to watch in the playoffs. Bowman should let JAgr have a lot offun in the next couple of games since the Pens are going to beat the pulp out of Jersey anyway. I was very disappointed not to see the Islanders lose the finalregular season game.          PENS RULE!!!新闻的集合中的目标值:  [10  3 17 ...  3  1  7]测试集的第一个样本内容如下:    (0, 140978)	0.07568532852853732  (0, 137608)	0.02789887735167451  (0, 137436)	0.032873594666792245  (0, 136882)	0.07895810931941949  (0, 136373)	0.07658983401820674  (0, 131756)	0.07960010957543483  (0, 131729)	0.08107250671154628  (0, 129022)	0.16894648007834584  (0, 127219)	0.04924715003660238  (0, 127010)	0.09290556650624715  (0, 126251)	0.04707255890012915  (0, 126174)	0.0254425733151055  (0, 125999)	0.03215741115095162  (0, 125859)	0.07164527106159452  (0, 125840)	0.04513779800710828  (0, 123937)	0.0524491399814409  (0, 123170)	0.10116459134597126  (0, 122750)	0.0987899571474934  (0, 122746)	0.09388835515040506  (0, 122430)	0.01676403480702623  (0, 121327)	0.07523671465646209  (0, 117225)	0.0987899571474934  (0, 116827)	0.0708563508626909  (0, 116492)	0.07440401806090016  (0, 110847)	0.12051854794218744  :	:  (0, 61108)	0.1720479230443984  (0, 60727)	0.14693089433367704  (0, 60658)	0.09511255105558246  (0, 58699)	0.0680305843019348  (0, 58694)	0.06768080039447152  (0, 57475)	0.16534806898033597  (0, 57338)	0.12110682004892807  (0, 53868)	0.218157550752436  (0, 53494)	0.03371276609938615  (0, 52123)	0.049562991467476857  (0, 49634)	0.10350756541264601  (0, 45734)	0.07555007022489536  (0, 45501)	0.031373902445453274  (0, 43114)	0.09575989151249617  (0, 41603)	0.03009958801900097  (0, 41462)	0.06042826190713555  (0, 39593)	0.02792424421570409  (0, 35599)	0.026421982868297417  (0, 33886)	0.06245499900960776  (0, 32582)	0.029478005795639084  (0, 32198)	0.02771097887635141  (0, 30826)	0.031619512800165915  (0, 28192)	0.06231079136933087  (0, 7495)	0.1443467069715261  (0, 3294)	0.0600789985491237测试集的前10个样本的目标值:  [ 2  8  5  9  2 18 16  5 10  8]对比真实值的前10个样本的目标值 :  [ 2  8  5  6  2 18 13  5 10  8]accuracy_score :  0.8503820033955858score :  0.8503820033955858

从打印出来的数据来看,每个样本集就是一片文档,我们通过特征工程 TfidfVectorizer打印出样本集对应的特征词的稀疏矩阵,最后通过训练得到预测值的目标值。

但是这里有个想不通的地方,还是不太清楚,在使用 estimator.fit(x_train, y_train)的时候这个训练集的数据到底是种什么样的格式,为什么我经过特征工程处理完的这些数据,我就可以直接用预估器进行训练呢?

想了一晚上终于知道这个地方自己的理解误区了,其实这个训练集仍然是[[特征值1,特征值2,特征值3...],[特征值1,特征值2,特征值3...]...],最里面的一个数组是一个样本中所有特征值对应的数量或者词频,最外面的那个数组就是所有样本的集合。只不过现在每个样本的特征值数组已经转换成稀疏矩阵的形式,只标记该单词对应的特征值词组的位置以及个数。

如果转换成这种形式输出,我估计当时就不会一下子有上面的那个理解误区了。

 print("训练集集合中的转换成非稀疏矩阵的方式: \n", x_train.toarray()[0])

看下这个输出内容:

训练集集合中的转换成非稀疏矩阵的方式:  [0 0 0 ... 0 0 0]

因为内容比较多,不会完全输出 

总结

这篇博文太不容易了,从第一天开始在网页上编辑了一半,结果第二天打开网页的时候,草稿箱里面的东西还剩下两段话了,我崩溃了,重新编辑了一天,编辑一点就抓紧时间点保存,快收尾的时候,想着第三天添加实例完善起来,没想到第三天打开电脑的时候,又没了。彻底崩溃,第三天果断的先放到本地word,然后一点点的粘过来进行编辑才让这篇博文诞生,不过对于这个朴素贝叶斯算法我也是彻底的搞清楚了,加油!

 

 

上一篇:机器学习入门研究(九)-决策树
下一篇:机器学习入门研究(七)-模型选择与调优