1 引言

各位朋友大家好,欢迎来到月来客栈,我是掌柜空字符。

在上一篇文章中,掌柜详细介绍了一种常见的朴素贝叶斯算法,也被称之为Categorical Naive Bayes。但实际上,”朴素贝叶斯“算法远不止这一种,而它们之间的主要却别在于对条件概率的处理上[1],即式(1)中的P(xiy)部分

y^=argmaxyP(y)i=1nP(xiy)(1)

在接下来的这篇文章中,掌柜将会介绍第二种基于朴素贝叶斯思想的分类模型,多项朴素贝叶斯(Multinomial Naive Bayes, MNB)

2 多项朴素贝叶斯

在通过Categorical NB来进行文本分类的场景中,在计算条件概率时都是将词表中的每个词以是否出现为标准进行类别化(categorization)处理,因为如果将词频作为特征维度的取值类别,那么将会出现在测试集中特征维度的取值情况数大于训练集中的情况。

例如在训练集中“客栈”这个词出现的最大次数为10,那么模型在拟合过程中就会认为“客栈”这个维度的特征取值有10种情况,并以此进行建模;但是当测试集中的某个样本里“客栈”这个词出现的频次为11时,那么模型便会认为该维度多了一种取值情况,进而无法取到对应的条件概率。

2.1 算法原理

通常,在利用词袋模型对文本进行表示时词频也是一个重要的考量因素,而多项朴素贝叶斯算法在处理这一问题时则是将每个维度的词频在总词频中的占比来作为条件概率进行建模[2]。

在MNB中,我们可以将类别y下条件概率的分布参数化为θy=(θy1,θy2...,θyn)这样的形式,其中n表示训练集中的特征维度(例如在文本分类中则是词表的长度),θyi则是类别y下特征i的条件概率P(xi|y)。进一步,参数θy可以通过极大似然估计来计算得到[1]:

θ^yi=Nyi+αiNy+α(2)

其中Nyi表示在训练集T中样本属于y这个类别下特征i出现的频次,即Nyi=xTxiNy表示类别y下所有特征的总频次,即Ny=i=1nNyiαi表示每个特征维度对应的平滑系数,α表示所有平滑系数的总和[2]。但是在实际处理时通常会将每个维度的平滑系数设为相等,因此式(2)可以改写为:

θ^yi=Nyi+αNy+αn(3)

其中α表示每个特征维度的平滑系数。

在根据式(3)估计得到每个类别下各个特征的条件概率(词频占比)后,便可以通过式(1)来最大化后验概率以此确定样本的分类类别。可尽管如此这里依旧存在一个问题。通过式(1)可以知道,在最大化后验概率时各个特征维度的条件概率是进行的累乘操作,而在动则上千维的文本向量中,这样累乘计算得到的结果将会出现下溢的情况。

因此,常见的一种做法是在式(1)的两边同时取自然对数log,且由于log函数是单调的因此这并不影响最终的预测结果[3]。所以式(1)可以改写为如下形式

y^=argmaxylog(P(y)i=1nP(xiy))(4)

进一步,根据log(xy)=log(x)+log(y),式(4)可以改写为

y^=argmaxy[log(P(y))+i=1nlog(P(xiy))](5)

同时对于MNB算法来说,从式(3)可以看出,此时条件概率计算的是训练集中特征维度的词频占比,因此在计算后验概率时需要同时考虑到每个维度的词频情况,即[2]

y^=argmaxy[log(P(y))+i=1nfilog(P(xiy))](6)

其中fi表示特征维度i对应的词频。

此时根据式(6)的形式来看,还可以将log(P(xiy))理解为特征xi对于类别重要性大小的权重,而先验概率log(P(y))则可以理解为数据集中各类别的相对频次(偏置),频次越大则当前样本越可能归属于该类别。因此,从这个角度看还可以将多项贝叶斯理解一个简单的线性模型。也正是因为这样的特性,MNB算法在处理TF-IDF这类文本特征表示时依旧有着很好的效果[1]。

到此,对于多项贝叶斯算法的基本原理就介绍完了,下面掌柜再来通过一个实际的计算示例来帮助大家更加清晰地理解。

2.2 计算示例

假设现在有一批基于词袋模型表示的文本数据,其一共包含有x0,x1,x2这3个特征维度,每个维度表示词表中相应词的词频,y表示样本对应的所属类别,如表1所示。现需要预测x=[17,25,39]这个样本的所属类别。

表 1. 示例计算数据

根据式(1),由表1易知,各个类别的先验概率为

P(y=0)=log(515)1.098P(y=1)=log(715)0.762P(y=2)=log(315)1.609(7)

各类别下不同特征取值的条件概率为(设α=1,即拉普拉斯平滑)

P(x0|y=0)=log(2+1+3+0+0+117+11+24+5+16+13)2.384(8)

其中分子中的2,1,3,0,0表示在y=0这个类别下,特征x0出现的总频次;分母17,11,24,5,16分别表示在y=0这个类别下所有特征维度的总频次,例如17=2+6+9。同时,从这里也可以再次看出MNB算法在计算特征维度时考虑的是:类别y中所有样本当前特征维度的频次总数在类别y中所有样本所有特征维度总频次中的占比,这样便能够计算得到对于类别y来说哪个特征维度的重要性更高。

同理,对于其它情况来说,对应的条件概率为

P(x1|y=0)=log(31+173+13)0.864P(x2|y=0)=log(36+173+13)0.719P(x0|y=1)=log(44+194+13)0.768P(x1|y=1)=log(22+194+13)1.439P(x2|y=1)=log(28+194+13)1.207P(x0|y=2)=log(19+143+13)0.832P(x1|y=2)=log(10+143+13)1.430P(x2|y=2)=log(14+143+13)1.120(9)

根据计算得到的先验概率、条件概率和式(5),样本x=[17,25,39]的后验概率为

P(y=0|x)=P(y=0)+x0P(x0|y=0)+x1P(x1|y=0)+x2P(x2|y=0)=[1.098+17×2.384+25×0.864+39×0.719]91.267(10)

同理,其它两个类别的后验概率分别为

P(y=1|x)96.888P(y=2|x)95.240(11)

根据上述结果可知,样本x=[17,25,39]应该属于类别y=0

3 多项贝叶斯实现

在有了前面Categorical贝叶斯算法的实现经验后,Multinomial贝叶斯的实现过程就非常容易理解了。下面,掌柜依旧分步进行讲解实现。需要说明的是以下实现代码均参考自sklearn 0.24.0 中的MultinomialNB模块,只是对部分处理逻辑进行了修改与简化。

3.1 特征计数实现

通过第2.1节的内容可知,不管是计算先验概率还是条件概率,在这之前都需要先统计训练集中各个样本及样本特征取值的分布情况。因此,这里首先需要初始化相关的计数器;然后再对样本和特征样本特征取值的分布情况进行统计。

如下代码所示便是对两个重要计数器初始化工作:

在上述代码中,第3-4行是初始化平滑项系数alpha。第6行class_count_被初始化成了一个形状为[n_classes,]的全零向量,其中n_classes表示分类的类别数量,而每个维度分别表示每个类别的样本数量(例如[2,2,3]表示0,1,2这三个类别的样本数分别是2,2,3),其目的是后续用于计算每个类别的先验概率。第7行feature_count_被初始化成了一个形状为[n_classes,n_features]的全零矩阵,其中feature_count_[i][j]表示,对于整个训练集来说,在第i个类别中第j个特征的出现频次。

例如:

那么feature_count_[i][j]表示,对于整个训练集来说,在第i个类别中第j个特征的出现频次为44。

在初始化完两个计数器之后,下面就可以来对样本类别和特征分布进行计数了,代码如下:

在上述代码中,第1行参数Y是原始标签经过one-hot编码后的形式,例如3分类问题中类别1会被编码成[0,1,0]的形式,因此Y的形状为[n,n_classes];第2行代码是计算得到每个类别对应的样本数量,形状为[n_classes,]。第4行则是统计每个类别下所有样本在各个特征维度下的取值频次。

例如:

在上述代码中,等号右边第1行5的含义便是在训练样本中第0个类别下第0个特征总频次为3+2;第1行中的9则表示在训练样本中第0个类别下第1个特征总频次为7+2。

进一步,在计算得到训练集中样本及特征的分布情况后,便可以计算先验概率和条件概率。

3.2 先验概率实现

先验概率实现较为简单,整体实现代码如下:

在上述代码中,第2-3便是用来计算各个类别的先验概率,并同时进行了取对数处理。需要提示一点的是logab=logalogb

3.3 条件概率实现

根据式(3)可知,条件概率实现过程如下:

从3.1节最后的示例结果可知,feature_count_中每一行求和便是第i个类别下所有特征频次的总和,这对应的便是上述代码第3行;第4-5行代码则是根据式(3)来计算条件概率(同时取了对数)。

3.4 模型拟合实现

上述先验概率和条件概率的计算对应便是整个模型的拟合过程,换句话说对于贝叶斯算法来说,所谓的模型拟合就是计算先验概率和条件概率。在实现这部分代码之后,在通过一个函数将整个过程串起来即可,代码如下:

在上述代码中,第2-4行用来将原始[0,1,2,3...]这样的标签转换为one-hot编码形式的标签值,形状为[n,n_classes];第5行用来记录原始的标签类别,形状为[n_classes,];第6-7行用来处理当数据集为二分类时fit_transform处理后的结果并不是one-hot形式,需要添加一列来转换成one-hot形式;第8行是获取数据集的类别数量;第9-13行则分别是上面3节内容介绍到的初始化计数器、特征取值情况统计、计算先验概率和计算条件概率。

3.5 后验概率实现

在完成模型的拟合过程后,对于新输入的样本来说其最终的预测结果则取决于对应的极大后验概率。根据式(6)可知,后验概率实现代码如下所示:

在上述代码中,第2行便是根据拟合得到的条件概率(权重)和先验概率(偏置)来计算得到样本的后验概率。

在实现每个样本后验概率的计算结果后,最后一步需要完成的便是极大化操作,即从所有后验概率中选择最大的概率值对应的类别作为该样本的预测类别即可。实现代码如下所示:

在上述代码中,第4行便是极大化后验概率的操作;第5-8样则是根据对应的参数来返回预测后的结果。

3.6 使用示例

下面,掌柜先以表1中的模拟数据来通过上述实现的代码进行示例。

在上述代码中,第2-7行便是构造表1中的模拟数据;第10-14行是根据实现的MyMultinomialNB来进行训练模型并预测;第15-19行则是利用sklearn中的MultinomialNB模块来完成模型训练与预测的工作。 更多与Logging模块的详细使用可参加文章xxxxx

上述代码运行结束后变换得到类似如下结果:

下面,掌柜再通过一个垃圾邮件分类实例来测试上述代码实现。

上述代码便是一个基于朴素贝叶斯模型的垃圾邮件分类示例,需要注意的是在对文本进行表示的时候采用的是TF-IDF模型,具体做法可以参见文章文本特征表示与模型复用第7.1节内容。

上述代码运行结束后便可以看到如下所示的结果:

4 总结

在这篇文章中,掌柜首先介绍了多项朴素贝叶斯的基本原理;然后通过一个实际的示例对其原理进行了进一步解释与阐述;接着一步一步详细地介绍了多项贝叶斯的实现方法;最后,掌柜分别以模拟数据和真实的文本数据来测试了实现代码的正确性。

本次内容就到此结束,感谢您的阅读!如果你觉得上述内容对你有所帮助,欢迎点赞分享!若有任何疑问与建议,请添加掌柜微信nulls8(备注来源)或文末留言进行交流。青山不改,绿水长流,我们月来客栈见

引用

[1]https://scikit-learn.org/stable/modules/naive_bayes.html#naive-bayes

[2] Rennie J D, Shih L, Teevan J, et al. Tackling the poor assumptions of naive bayes text classifiers[C] (ICML-03). 2003: 616-623.https://www.aaai.org/Papers/ICML/2003/ICML03-081.pdf

[3]https://nlp.stanford.edu/IR-book/html/htmledition/naive-bayes-text-classification-1.html

[4训练模型时如何保存训练日志?

[5]文本特征提取与模型的复用

[6]示例代码:https://github.com/moon-hotel/MachineLearningWithMe