分类算法之朴素贝叶斯分类(Naive Bayesian classification)

来源:互联网 发布:疯播号是哪个直播软件 编辑:程序博客网 时间:2024/05/11 14:18

1.1、摘要

      贝叶斯分类是一类分类算法的总称,这类算法均以贝叶斯定理为基础,故统称为贝叶斯分类。本文作为分类算法的第一篇,将首先介绍分类问题,对分类问题进行一个正式的定义。然后,介绍贝叶斯分类算法的基础——贝叶斯定理。最后,通过实例讨论贝叶斯分类中最简单的一种:朴素贝叶斯分类。

  1.2、分类问题综述

      对于分类问题,其实谁都不会陌生,说我们每个人每天都在执行分类操作一点都不夸张,只是我们没有意识到罢了。例如,当你看到一个陌生人,你的脑子下意识判断TA是男是女;你可能经常会走在路上对身旁的朋友说“这个人一看就很有钱、那边有个非主流”之类的话,其实这就是一种分类操作。

      从数学角度来说,分类问题可做如下定义:

      已知集合:,确定映射规则,使得任意有且仅有一个使得成立。(不考虑模糊数学里的模糊集情况)

      其中C叫做类别集合,其中每一个元素是一个类别,而I叫做项集合,其中每一个元素是一个待分类项,f叫做分类器。分类算法的任务就是构造分类器f。

      这里要着重强调,分类问题往往采用经验性方法构造映射规则,即一般情况下的分类问题缺少足够的信息来构造100%正确的映射规则,而是通过对经验数据的学习从而实现一定概率意义上正确的分类,因此所训练出的分类器并不是一定能将每个待分类项准确映射到其分类,分类器的质量与分类器构造方法、待分类数据的特性以及训练样本数量等诸多因素有关。

      例如,医生对病人进行诊断就是一个典型的分类过程,任何一个医生都无法直接看到病人的病情,只能观察病人表现出的症状和各种化验检测数据来推断病情,这时医生就好比一个分类器,而这个医生诊断的准确率,与他当初受到的教育方式(构造方法)、病人的症状是否突出(待分类数据的特性)以及医生的经验多少(训练样本数量)都有密切关系。

  1.3、贝叶斯分类的基础——贝叶斯定理

      每次提到贝叶斯定理,我心中的崇敬之情都油然而生,倒不是因为这个定理多高深,而是因为它特别有用。这个定理解决了现实生活里经常遇到的问题:已知某条件概率,如何得到两个事件交换后的概率,也就是在已知P(A|B)的情况下如何求得P(B|A)。这里先解释什么是条件概率:

      表示事件B已经发生的前提下,事件A发生的概率,叫做事件B发生下事件A的条件概率。其基本求解公式为:

      贝叶斯定理之所以有用,是因为我们在生活中经常遇到这种情况:我们可以很容易直接得出P(A|B),P(B|A)则很难直接得出,但我们更关心P(B|A),贝叶斯定理就为我们打通从P(A|B)获得P(B|A)的道路。

      下面不加证明地直接给出贝叶斯定理:

      

  1.4、朴素贝叶斯分类

      1.4.1、朴素贝叶斯分类的原理与流程

      朴素贝叶斯分类是一种十分简单的分类算法,叫它朴素贝叶斯分类是因为这种方法的思想的很朴素,朴素贝叶斯的思想基础是这样的:对于给出的待分类项,求解在此项出现的条件下各个类别出现的概率,哪个最大,就认为此待分类项属于哪个类别。通俗来说,就好比这么个道理,你在街上看到一个黑人,我问你你猜这哥们哪里来的,你十有八九猜非洲。为什么呢?因为黑人中非洲人的比率最高,当然人家也可能是美洲人或亚洲人,但在没有其它可用信息下,我们会选择条件概率最大的类别,这就是朴素贝叶斯的思想基础。

      朴素贝叶斯分类的正式定义如下:

      1、设为一个待分类项,而每个a为x的一个特征属性。

      2、有类别集合

      3、计算

      4、如果,则

      那么现在的关键就是如何计算第3步中的各个条件概率。我们可以这么做:

      1、找到一个已知分类的待分类项集合,这个集合叫做训练样本集。

      2、统计得到在各类别下各个特征属性的条件概率估计。即

      3、如果各个特征属性是条件独立的,则根据贝叶斯定理有如下推导:

      

      因为分母对于所有类别为常数,因为我们只要将分子最大化皆可。又因为各特征属性是条件独立的,所以有:

      

      根据上述分析,朴素贝叶斯分类的流程可以由下图表示(暂时不考虑验证):

      可以看到,整个朴素贝叶斯分类分为三个阶段:

      第一阶段——准备工作阶段,这个阶段的任务是为朴素贝叶斯分类做必要的准备,主要工作是根据具体情况确定特征属性,并对每个特征属性进行适当划分,然后由人工对一部分待分类项进行分类,形成训练样本集合。这一阶段的输入是所有待分类数据,输出是特征属性和训练样本。这一阶段是整个朴素贝叶斯分类中唯一需要人工完成的阶段,其质量对整个过程将有重要影响,分类器的质量很大程度上由特征属性、特征属性划分及训练样本质量决定。

      第二阶段——分类器训练阶段,这个阶段的任务就是生成分类器,主要工作是计算每个类别在训练样本中的出现频率及每个特征属性划分对每个类别的条件概率估计,并将结果记录。其输入是特征属性和训练样本,输出是分类器。这一阶段是机械性阶段,根据前面讨论的公式可以由程序自动计算完成。

      第三阶段——应用阶段。这个阶段的任务是使用分类器对待分类项进行分类,其输入是分类器和待分类项,输出是待分类项与类别的映射关系。这一阶段也是机械性阶段,由程序完成。

      1.4.2、估计类别下特征属性划分的条件概率及Laplace校准

      这一节讨论P(a|y)的估计。

      由上文看出,计算各个划分的条件概率P(a|y)是朴素贝叶斯分类的关键性步骤,当特征属性为离散值时,只要很方便的统计训练样本中各个划分在每个类别中出现的频率即可用来估计P(a|y),下面重点讨论特征属性是连续值的情况。

      当特征属性为连续值时,通常假定其值服从高斯分布(也称正态分布)。即:

      

      而

      因此只要计算出训练样本中各个类别中此特征项划分的各均值和标准差,代入上述公式即可得到需要的估计值。均值与标准差的计算在此不再赘述。

      另一个需要讨论的问题就是当P(a|y)=0怎么办,当某个类别下某个特征项划分没有出现时,就是产生这种现象,这会令分类器质量大大降低。为了解决这个问题,我们引入Laplace校准,它的思想非常简单,就是对没类别下所有划分的计数加1,这样如果训练样本集数量充分大时,并不会对结果产生影响,并且解决了上述频率为0的尴尬局面。

      1.4.3、朴素贝叶斯分类实例:检测SNS社区中不真实账号

      下面讨论一个使用朴素贝叶斯分类解决实际问题的例子,为了简单起见,对例子中的数据做了适当的简化。

      这个问题是这样的,对于SNS社区来说,不真实账号(使用虚假身份或用户的小号)是一个普遍存在的问题,作为SNS社区的运营商,希望可以检测出这些不真实账号,从而在一些运营分析报告中避免这些账号的干扰,亦可以加强对SNS社区的了解与监管。

      如果通过纯人工检测,需要耗费大量的人力,效率也十分低下,如能引入自动检测机制,必将大大提升工作效率。这个问题说白了,就是要将社区中所有账号在真实账号和不真实账号两个类别上进行分类,下面我们一步一步实现这个过程。

      首先设C=0表示真实账号,C=1表示不真实账号。

      1、确定特征属性及划分

      这一步要找出可以帮助我们区分真实账号与不真实账号的特征属性,在实际应用中,特征属性的数量是很多的,划分也会比较细致,但这里为了简单起见,我们用少量的特征属性以及较粗的划分,并对数据做了修改。

      我们选择三个特征属性:a1:日志数量/注册天数,a2:好友数量/注册天数,a3:是否使用真实头像。在SNS社区中这三项都是可以直接从数据库里得到或计算出来的。

      下面给出划分:a1:{a<=0.05, 0.05<a<0.2, a>=0.2},a1:{a<=0.1, 0.1<a<0.8, a>=0.8},a3:{a=0(不是),a=1(是)}。

      2、获取训练样本

      这里使用运维人员曾经人工检测过的1万个账号作为训练样本。

      3、计算训练样本中每个类别的频率

      用训练样本中真实账号和不真实账号数量分别除以一万,得到:

      

      

      4、计算每个类别条件下各个特征属性划分的频率

      

      

      

      

      

      

      

      

      

      

      

      

      

      

      

      

      5、使用分类器进行鉴别

      下面我们使用上面训练得到的分类器鉴别一个账号,这个账号使用非真实头像,日志数量与注册天数的比率为0.1,好友数与注册天数的比率为0.2。

      

      

      可以看到,虽然这个用户没有使用真实头像,但是通过分类器的鉴别,更倾向于将此账号归入真实账号类别。这个例子也展示了当特征属性充分多时,朴素贝叶斯分类对个别属性的抗干扰性。

  1.5、分类器的评价

      虽然后续还会提到其它分类算法,不过这里我想先提一下如何评价分类器的质量。

      首先要定义,分类器的正确率指分类器正确分类的项目占所有被分类项目的比率。

      通常使用回归测试来评估分类器的准确率,最简单的方法是用构造完成的分类器对训练数据进行分类,然后根据结果给出正确率评估。但这不是一个好方法,因为使用训练数据作为检测数据有可能因为过分拟合而导致结果过于乐观,所以一种更好的方法是在构造初期将训练数据一分为二,用一部分构造分类器,然后用另一部分检测分类器的准确率。




基于朴素贝叶斯的文本分类算法  


摘要:常用的文本分类方法有支持向量机、K-近邻算法和朴素贝叶斯。其中朴素贝叶斯具有容易实现,运行速度快的特点,被广泛使用。本文详细介绍了朴素贝叶斯的基本原理,讨论了两种常见模型:多项式模型(MM)和伯努利模型(BM),实现了可运行的代码,并进行了一些数据测试。

关键字:朴素贝叶斯;文本分类

Text Classification Algorithm Based on Naive Bayes

Author: soulmachine

Email:soulmachine@gmail.com

Blog:www.yanjiuyanjiu.com

Abstract:Usually there are three methods for text classification: SVM、KNN and Na?ve Bayes. Na?ve Bayes is easy to implement and fast, so it is widely used. This article introduced the theory of Na?ve Bayes and discussed two popular models: multinomial model(MM) and Bernoulli model(BM) in details, implemented runnable code and performed some data tests.

Keywords: na?ve bayes; text classification

第1章 贝叶斯原理

1.1 贝叶斯公式

设A、B是两个事件,且P(A)>0,称

clip_image002

为在事件A发生的条件下事件B发生的条件概率

乘法公式 P(XYZ)=P(Z|XY)P(Y|X)P(X)

全概率公式 P(X)=P(X|Y1)+ P(X|Y2)+…+ P(X|Yn)

贝叶斯公式clip_image004

在此处,贝叶斯公式,我们要用到的是clip_image006

以上公式,请读者参考《概率论与数理统计(第五版)》的1.4节“条件概率”(这里将原书中的A换成了X,B换成了Y),获得更深的理解。

1.2贝叶斯定理在分类中的应用

在分类(classification)问题中,常常需要把一个事物分到某个类别。一个事物具有很多属性,把它的众多属性看做一个向量,即x=(x1,x2,x3,…,xn),用x这个向量来代表这个事物。类别也是有很多种,用集合Y={y1,y2,…ym}表示。如果x属于y1类别,就可以给x打上y1标签,意思是说x属于y1类别。这就是所谓的分类(Classification)

x的集合记为X,称为属性集。一般X和Y的关系是不确定的,你只能在某种程度上说x有多大可能性属于类y1,比如说x有80%的可能性属于类y1,这时可以把X和Y看做是随机变量,P(Y|X)称为Y的后验概率(posterior probability),与之相对的,P(Y)称为Y的先验概率(prior probability)[2]

在训练阶段,我们要根据从训练数据中收集的信息,对X和Y的每一种组合学习后验概率P(Y|X)。分类时,来了一个实例x,在刚才训练得到的一堆后验概率中找出所有的P(Y|x), 其中最大的那个y,即为x所属分类。根据贝叶斯公式,后验概率为

clip_image008

在比较不同Y值的后验概率时,分母P(X)总是常数,因此可以忽略。先验概率P(Y)可以通过计算训练集中属于每一个类的训练样本所占的比例容易地估计。

我们来举个简单的例子,让读者对上述思路有个形象的认识[3]

考虑一个医疗诊断问题,有两种可能的假设:(1)病人有癌症。(2)病人无癌症。样本数据来自某化验测试,它也有两种可能的结果:阳性和阴性。假设我们已经有先验知识:在所有人口中只有0.008的人患病。此外,化验测试对有病的患者有98%的可能返回阳性结果,对无病患者有97%的可能返回阴性结果。

上面的数据可以用以下概率式子表示:

P(cancer)=0.008,P(无cancer)=0.992

P(阳性|cancer)=0.98,P(阴性|cancer)=0.02

P(阳性|无cancer)=0.03,P(阴性|无cancer)=0.97

假设现在有一个新病人,化验测试返回阳性,是否将病人断定为有癌症呢?

在这里,Y={cancer,无cancer},共两个类别,这个新病人是一个样本,他有一个属性阳性,可以令x=(阳性)。

我们可以来计算各个类别的后验概率:

P(cancer | 阳性) = P(阳性 | cancer)p(cancer)=0.98*0.008 = 0.0078

P(无cancer | 阳性) =P(阳性 | 无cancer)*p(无cancer)=0.03*0.992 = 0.0298

因此,应该判断为无癌症。

在这个例子中,类条件概率,P(cancer|阳性)和P(无cancer|阳性)直接告诉了我们。

一般地,对类条件概率P(X|Y)的估计,有朴素贝叶斯分类器和贝叶斯信念网络两种方法,这里介绍朴素贝叶斯分类器。 

1.3朴素贝叶斯分类器

1、条件独立性 

给定类标号y,朴素贝叶斯分类器在估计类条件概率时假设属性之间条件独立。条件独立假设可以形式化的表达如下:

clip_image010

其中每个训练样本可用一个属性向量X=(x1,x2,x3,…,xn)表示,各个属性之间条件独立。

比如,对于一篇文章,

Good good study,Day day up.

可以用一个文本特征向量来表示,x=(Good, good, study, Day, day , up)。一般各个词语之间肯定不是相互独立的,有一定的上下文联系。但在朴素贝叶斯文本分类时,我们假设个单词之间没有联系,可以用一个文本特征向量来表示这篇文章,这就是“朴素”的来历。

2、朴素贝叶斯如何工作

有了条件独立假设,就不必计算X和Y的每一种组合的类条件概率,只需对给定的Y,计算每个xi的条件概率。后一种方法更实用,因为它不需要很大的训练集就能获得较好的概率估计。

3、估计分类属性的条件概率

P(xi|Y=y)怎么计算呢?它一般根据类别y下包含属性xi的实例的比例来估计。以文本分类为例,xi表示一个单词,P(xi|Y=y)=包含该类别下包含单词的xi的文章总数/ 该类别下的文章总数。

4、贝叶斯分类器举例

假设给定了如下训练样本数据,我们学习的目标是根据给定的天气状况判断你对PlayTennis这个请求的回答是Yes还是No。

DayOutlookTemperatureHumidityWindPlayTennisD1SunnyHotHighWeakNoD2SunnyHotHighStrongNoD3OvercastHotHighWeakYesD4RainMildHighWeakYesD5RainCoolNormalWeakYesD6RainCoolNormalStrongNoD7OvercastCoolNormalStrongYesD8SunnyMildHighWeakNoD9SunnyCoolNormalWeakYesD10RainMildNormalWeakYesD11SunnyMildNormalStrongYesD12OvercastMildHighStrongYesD13OvercastHotNormalWeakYesD14RainMildHighStrongNo

可以看到这里样本数据集提供了14个训练样本,我们将使用此表的数据,并结合朴素贝叶斯分类器来分类下面的新实例:

x = (Outlook = Sunny,Temprature = Cool,Humidity = High,Wind = Strong)

在这个例子中,属性向量X=(Outlook, Temperature, Humidity, Wind),类集合Y={Yes, No}。我们需要利用训练数据计算后验概率P(Yes|x)和P(No|x),如果P(Yes|x)>P(No|x),那么新实例分类为Yes,否则为No。

为了计算后验概率,我们需要计算先验概率P(Yes)和P(No)和类条件概率P(xi|Y)。

因为有9个样本属于Yes,5个样本属于No,所以P(Yes)=9/14, P(No)=5/14。

类条件概率计算如下:

P(Outlook = Sunny|Yes)=2/9 P(Outlook = Sunny|No)=3/5

P(Temprature = Cool |Yes) =3/9 P(Temprature = Cool |No) =1/5

P(Humidity = High |Yes) =3/9 P(Humidity = High |No) =4/5

P(Wind = Strong |Yes) =3/9 P(Wind = Strong |No) =3/5

后验概率计算如下:

P(Yes | x)= P(Outlook = Sunny|Yes)×P(Temprature = Cool |Yes)×P(Humidity = High |Yes)×P(Wind = Strong |Yes)×P(Yes)=2/9×3/9×3/9×3/9×3/9×9/14=2/243=9/1701≈0.00529

P(No | x)= P(Outlook = Sunny|No)×P(Temprature = Cool |No)×P(Humidity = High |No)× P(Wind = Strong |No)×P(No)=3/5×1/5×4/5×3/5×5/14=18/875≈0.02057

通过计算得出P(No | x)> P(Yes | x),所以该样本分类为No[3]。

5、条件概率的m估计

假设有来了一个新样本 x1= (Outlook = Cloudy,Temprature = Cool,Humidity = High,Wind = Strong),要求对其分类。我们来开始计算,

P(Outlook = Cloudy|Yes)=0/9=0 P(Outlook = Cloudy |No)=0/5=0

计算到这里,大家就会意识到,这里出现了一个新的属性值,在训练样本中所没有的。如果有一个属性的类条件概率为0,则整个类的后验概率就等于0,我们可以直接得到后验概率P(Yes | x1)= P(No | x1)=0,这时二者相等,无法分类。

当训练样本不能覆盖那么多的属性值时,都会出现上述的窘境。简单的使用样本比例来估计类条件概率的方法太脆弱了,尤其是当训练样本少而属性数目又很大时。

解决方法是使用m估计(m-estimate)方法来估计条件概率:

clip_image012

n是类yj中的样本总数,nc是类yj中取值xi的样本数,m是称为等价样本大小的参数,而p是用户指定的参数。如果没有训练集(即n=0),则P(xi|yj)=p, 因此p可以看作是在类yj的样本中观察属性值xi的先验概率。等价样本大小决定先验概率和观测概率nc/n之间的平衡[2]。

第2章 朴素贝叶斯文本分类算法

现在开始进入本文的主旨部分:如何将贝叶斯分类器应用到文本分类上来。

2.1文本分类问题

在文本分类中,假设我们有一个文档d∈X,X是文档向量空间(document space),和一个固定的类集合C={c1,c2,…,cj},类别又称为标签。显然,文档向量空间是一个高维度空间。我们把一堆打了标签的文档集合<d,c>作为训练样本,<d,c>∈X×C。例如:

<d,c>={Beijing joins the World Trade Organization, China}

对于这个只有一句话的文档,我们把它归类到 China,即打上china标签。

我们期望用某种训练算法,训练出一个函数γ,能够将文档映射到某一个类别:

γ:X→C

这种类型的学习方法叫做有监督学习,因为事先有一个监督者(我们事先给出了一堆打好标签的文档)像个老师一样监督着整个学习过程。

朴素贝叶斯分类器是一种有监督学习,常见有两种模型,多项式模型(multinomial model)和伯努利模型(Bernoulli model)。

2.2多项式模型

1、基本原理

在多项式模型中, 设某文档d=(t1,t2,…,tk),tk是该文档中出现过的单词,允许重复,则

先验概率P(c)= 类c下单词总数/整个训练样本的单词总数

类条件概率P(tk|c)=(类c下单词tk在各个文档中出现过的次数之和+1)/(类c下单词总数+|V|)

V是训练样本的单词表(即抽取单词,单词出现多次,只算一个),|V|则表示训练样本包含多少种单词。在这里,m=|V|, p=1/|V|。

P(tk|c)可以看作是单词tk在证明d属于类c上提供了多大的证据,而P(c)则可以认为是类别c在整体上占多大比例(有多大可能性)。

2、伪代码

//C,类别集合,D,用于训练的文本文件集合

TrainMultiNomialNB(C,D) {

// 单词出现多次,只算一个

V←ExtractVocabulary(D)

// 单词可重复计算

N←CountTokens(D)

for each c∈C

// 计算类别c下的单词总数

// N和Nc的计算方法和Introduction to Information Retrieval上的不同,个人认为

//该书是错误的,先验概率和类条件概率的计算方法应当保持一致

Nc←CountTokensInClass(D,c)

prior[c][/c]←Nc/N

// 将类别c下的文档连接成一个大字符串

textc←ConcatenateTextOfAllDocsInClass(D,c)

for each t∈V

// 计算类c下单词t的出现次数

Tct←CountTokensOfTerm(textc,t)

for each t∈V

//计算P(t|c)

condprob[t][c][/c]←clip_image014

return V,prior,condprob

}

ApplyMultiNomialNB(C,V,prior,condprob,d) {

// 将文档d中的单词抽取出来,允许重复,如果单词是全新的,在全局单词表V中都

// 没出现过,则忽略掉

W←ExtractTokensFromDoc(V,d)

for each c∈C

score[c][/c]←prior[c][/c]

for each t∈W

if t∈Vd

score[c][/c] *= condprob[t][c][/c]

return max(score[c][/c])

}

3、举例

给定一组分类好了的文本训练数据,如下:

docIddoc类别

 

In c=China?

1Chinese Beijing Chineseyes2Chinese Chinese Shanghaiyes3Chinese Macaoyes4Tokyo Japan Chineseno

给定一个新样本Chinese Chinese Chinese Tokyo Japan,对其进行分类。

该文本用属性向量表示为d=(Chinese, Chinese, Chinese, Tokyo, Japan),类别集合为Y={yes, no}。

类yes下总共有8个单词,类no下总共有3个单词,训练样本单词总数为11,因此P(yes)=8/11, P(no)=3/11。类条件概率计算如下:

P(Chinese | yes)=(5+1)/(8+6)=6/14=3/7

P(Japan | yes)=P(Tokyo | yes)= (0+1)/(8+6)=1/14

P(Chinese|no)=(1+1)/(3+6)=2/9

P(Japan|no)=P(Tokyo| no) =(1+1)/(3+6)=2/9

分母中的8,是指yes类别下textc的长度,也即训练样本的单词总数,6是指训练样本有Chinese,Beijing,Shanghai, Macao, Tokyo, Japan 共6个单词,3是指no类下共有3个单词。

有了以上类条件概率,开始计算后验概率,

P(yes | d)=(3/7)3×1/14×1/14×8/11=108/184877≈0.00058417

P(no | d)= (2/9)3×2/9×2/9×3/11=32/216513≈0.00014780

因此,这个文档属于类别china。

2.3伯努利模型

1、基本原理

P(c)= 类c下文件总数/整个训练样本的文件总数

P(tk|c)=(类c下包含单词tk的文件数+1)/(类c下单词总数+2)

在这里,m=2, p=1/2。

后验概率的计算,也有点变化,见下面的伪代码。

2、伪代码

//C,类别集合,D,用于训练的文本文件集合

TrainBernoulliNB(C, D) {

// 单词出现多次,只算一个

V←ExtractVocabulary(D)

// 计算文件总数

N←CountDocs(D)

for each c∈C

// 计算类别c下的文件总数

Nc←CountDocsInClass(D,c)

prior[c][/c]←Nc/N

for each t∈V

// 计算类c下包含单词t的文件数

Nct←CountDocsInClassContainingTerm(D,c,t)

//计算P(t|c)

condprob[t][c][/c]←(Nct+1)/(Nct+2)

return V,prior,condprob

}

ApplyBernoulliNB(C,V,prior,condprob,d) {

// 将文档d中单词表抽取出来,如果单词是全新的,在全局单词表V中都没出现过,

// 则舍弃

Vd←ExtractTermsFromDoc(V,d)

for each c∈C

score[c][/c]←prior[c][/c]

for each t∈V

if t∈Vd

score[c][/c] *= condprob[t][c][/c]

else

score[c][/c] *= (1-condprob[t][c][/c])

return max(score[c][/c])

}

3、举例

还是使用前面例子中的数据,不过模型换成了使用伯努利模型。

类yes下总共有3个文件,类no下有1个文件,训练样本文件总数为11,因此P(yes)=3/4, P(Chinese | yes)=(3+1)/(3+2)=4/5

P(Japan | yes)=P(Tokyo | yes)=(0+1)/(3+2)=1/5

P(Beijing | yes)= P(Macao|yes)= P(Shanghai |yes)=(1+1)/(3+2)=2/5

P(Chinese|no)=(1+1)/(1+2)=2/3

P(Japan|no)=P(Tokyo| no) =(1+1)/(1+2)=2/3

P(Beijing| no)= P(Macao| no)= P(Shanghai | no)=(0+1)/(1+2)=1/3

有了以上类条件概率,开始计算后验概率,

P(yes | d)=P(yes)×P(Chinese|yes) ×P(Japan|yes) ×P(Tokyo|yes)×(1-P(Beijing|yes)) ×(1-P(Shanghai|yes))×(1-P(Macao|yes))

=3/4×4/5×1/5×1/5×(1-2/5) ×(1-2/5)×(1-2/5)=81/15625≈0.005

P(no | d)= 1/4×2/3×2/3×2/3×(1-1/3)×(1-1/3)×(1-1/3)=16/729≈0.022

因此,这个文档不属于类别china。

2.4两个模型的区别

二者的计算粒度不一样,多项式模型以单词为粒度,伯努利模型以文件为粒度,因此二者的先验概率和类条件概率的计算方法都不同。

计算后验概率时,对于一个文档d,多项式模型中,只有在d中出现过的单词,才会参与后验概率计算,伯努利模型中,没有在d中出现,但是在全局单词表中出现的单词,也会参与计算,不过是作为“反方”参与的。

第3章 代码详解

本文附带了一个eclipse工程,有完整的源代码,以及一个微型文本训练库。

ChineseSpliter用于中文分词,StopWordsHandler用于判断一个单词是否是停止词,ClassifyResult用于保存结果,IntermediateData用于预处理文本语料库,TrainnedModel用于保存训练后得到的数据,NaiveBayesClassifier是基础类,包含了贝叶斯分类器的主要代码,MultiNomialNB是多项式模型,类似的,BernoulliNB是伯努利模型,二者都继承自NaiveBayesClassifier,都只重写了父类的计算先验概率,类条件概率和后验概率这3个函数。

3.1中文分词

中文分词不是本文的重点,这里我们直接使用第三方工具,本源码使用的是极易中文分词组件,你还可以使用MMSEG,中科院的ICTCLAS等等。

/**

* 对给定的文本进行中文分词.

*

@param text

* 给定的文本

@param splitToken

* 用于分割的标记,如”|”

@return 分词完毕的文本

*/

public String split(final String text, final String splitToken) {

String result = null;

try {

result = analyzer.segment(text, splitToken);

catch (IOException e) {

e.printStackTrace();

}

return result;

}

3.2停止词处理

停止词(Stop Word)是指那些无意义的字或词,如“的”、“在”等。去掉文档中的停止词也是必须的一项工作,这里简单的定义了一些常见的停止词,并根据这些常用停止词在分词时进行判断。

/** 常用停用词. */

private static String[] stopWordsList = {

// 来自 c:\Windows\System32\NOISE.CHS

“的”, “一”, “不”, “在”, “人”, “有”, “是”, “为”, “以”, “于”, “上”, “他”, “而”,

“后”, “之”, “来”, “及”, “了”, “因”, “下”, “可”, “到”, “由”, “这”, “与”, “也”,

“此”, “但”, “并”, “个”, “其”, “已”, “无”, “小”, “我”, “们”, “起”, “最”, “再”,

“今”, “去”, “好”, “只”, “又”, “或”, “很”, “亦”, “某”, “把”, “那”, “你”, “乃”,

“它”,

// 来自网络

“要”, “将”, “应”, “位”, “新”, “两”, “中”, “更”, “我们”, “自己”, “没有”, ““”, “””,

“,”, “(”, “)”, “” };

/**

* 判断一个词是否是停止词.

*

@param word

* 要判断的词

@return 是停止词,返回true,否则返回false

*/

public static boolean isStopWord(final String word) {

for (int i = 0; i < stopWordsList.length; ++i) {

if (word.equalsIgnoreCase(stopWordsList[i])) {

return true;

}

}

return false;

}

3.3预处理数据

我们这里使用搜狗的文本分类语料库作为训练样本,把SogouC.reduced.20061102.tar.gz解压到D盘,目录结构为

D:\Reduced

|– C000008

|– C000010

|– C000013

|– C000014

|– C000016

|– C000020

|– C000022

|– C000023

|– C000024

IntermediateData.java主要用于处理文本数据,将所需要的信息计算好,存放到数据库文件中。

中间数据文件主要保存了如下信息,

/** 单词X在类别C下出现的总数. */

public HashMap[] filesOfXC;

/** 给定分类下的文件数目. */

public int[] filesOfC;

/** 根目录下的文件总数. */

public int files;

/** 单词X在类别C下出现的总数 */

public HashMap[] tokensOfXC;

/** 类别C下所有单词的总数. */

public int[] tokensOfC;

/** 整个语料库中单词的总数. */

public int tokens;

/** 整个训练语料所出现的单词. */

public HashSet<String> vocabulary;

我们使用命令

IntermediateData d:\Reduced\ gbk d:\reduced.db

将文本训练库的信息计算好,保存到中间文件中。以后的阶段,我们都不再需要文本语料库了,只需要reduced.db。

3.3训练

基本的框架代码都在NaiveBayesClassifier中,MultiNomialNB和BernoulliNB都只是重新实现(override)了

/** 计算先验概率P(c). */

protected void calculatePc() {

}

/** 计算类条件概率P(x|c). */

protected void calculatePxc() {

}

/**

* 计算文本属性向量X在类Cj下的后验概率P(Cj|X).

*

@param x

* 文本属性向量

@param cj

* 给定的类别

@return 后验概率

*/

protected double calcProd(final String[] x, final int cj) {

return 0;

}

这三个函数。

训练函数如下:

public final void train(String intermediateData, String modelFile) {

// 加载中间数据文件

loadData(intermediateData);

model = new TrainnedModel(db.classifications.length);

model.classifications = db.classifications;

model.vocabulary = db.vocabulary;

// 开始训练

calculatePc();

calculatePxc();

db = null;

try {

// 用序列化,将训练得到的结果存放到模型文件中

ObjectOutputStream out = new ObjectOutputStream(

new FileOutputStream(modelFile));

out.writeObject(model);

out.close();

catch (IOException e) {

e.printStackTrace();

}

}

我们使用命令:

MultiNomialNB –t d:\reduced.db d:\reduced.mdl

开始训练,得到的模型文件保存在reduced.mdl中。

3.4分类

有了模型文件,就可以用它来进行分类了。

可以使用命令

MultiNomialNB d:\reduced.mdl d:\temp.txt gbk

对文本文件temp.txt进行分类。

还可以将当初训练出这个模型文件的文本库,进行分类,看看正确率有多少,即“吃自己的狗食”,命令行如下

MultiNomialNB -r d:\reduced\ gbk d:\reduced.mdl

分类函数如下:

/**

* 对给定的文本进行分类.

*

@param text

* 给定的文本

@return 分类结果

*/

public final String classify(final String text) {

String[] terms = null;

// 中文分词处理(分词后结果可能还包含有停用词)

terms = textSpliter.split(text, ” “).split(” “);

// 去掉停用词,以免影响分类

terms = ChineseSpliter.dropStopWords(terms);

double probility = 0.0;

// 分类结果

List<ClassifyResult> crs = new ArrayList<ClassifyResult>();

for (int i = 0; i < model.classifications.length; i++) {

// 计算给定的文本属性向量terms在给定的分类Ci中的分类条件概率

probility = calcProd(terms, i);

// 保存分类结果

ClassifyResult cr = new ClassifyResult();

cr.classification = model.classifications[i]; // 分类

cr.probility = probility; // 关键字在分类的条件概率

System.out.println(“In process….”);

System.out.println(model.classifications[i] + “:” + probility);

crs.add(cr);

}

// 找出最大的元素

ClassifyResult maxElem = (ClassifyResult) java.util.Collections.max(

crs, new Comparator() {

public int compare(final Object o1, final Object o2) {

final ClassifyResult m1 = (ClassifyResult) o1;

final ClassifyResult m2 = (ClassifyResult) o2;

final double ret = m1.probility – m2.probility;

if (ret < 0) {

return -1;

else {

return 1;

}

}

});

return maxElem.classification;

}

测试正确率的函数getCorrectRate(),核心代码就是对每个文本文件调用classify(),将得到的类别和原始的类别比较,经过统计后就可以得到百分比。

更多细节请读者阅读源代码。

参考文献

[1] Christopher D. Manning, Prabhakar Raghavan, Hinrich Schütze, Introduction to Information Retrieval, Cambridge University Press, 2008, chapter 13, Text classification and Naive Bayes.

[2]Pang-Ning Tan, Michael Steinbach, Vipin Kumar, 《数据挖掘导论》,北京:人民邮电出版社,2007,第140~145页。

[2] 石志伟, 吴功宜, “基于朴素贝叶斯分类器的文本分类算法”, 第一届全国信息检索与内容安全学术会议,2004

[3] 洞庭散人,“基于朴素贝叶斯分类器的文本分类算法(上)”,“基于朴素贝叶斯分类器的文本分类算法(下)”,2008

[4]DL88250, “朴素贝叶斯中文文本分类器的研究与实现(1)”,“朴素贝叶斯中文文本分类器的研究与实现(2)”,2008

由于笔者水平有限,本文出现错误在所难免,欢迎读者批评指正,请到原文发表评论或给我发email,谢谢


原创粉丝点击