作者: 龙心尘 && 寒小阳
时间:2016年2月。
出处:http://blog.csdn.net/longxinchen_ml/article/details/50629110
http://blog.csdn.net/han_xiaoyang/article/details/50629587
声明:版权所有,转载请联系作者并注明出处
上一篇文章我们主要从理论上梳理了朴素贝叶斯方法进行文本分类的基本思路。这篇文章我们主要从实践上探讨一些应用过程中的tricks,并进一步分析贝叶斯方法,最后以情绪褒贬分析和拼写纠错为例展示这种简单强大的方法在自然语言处理问题上的具体应用。
看了上一篇文章的一些同学可能会问:“何必费这么大劲算那么多词的概率?直接看邮件中有没有‘代开发票’、‘转售发票’之类的关键词不就得了?如果关键词比较多就认为是垃圾邮件呗。”
咳咳,其实关键词匹配的方法如果有效的话真不必用朴素贝叶斯。毕竟这种方法简单嘛,就是一个字符串匹配。从历史来看,之前没有贝叶斯方法的时候主要也是用关键词匹配。但是这种方法准确率太低。我们在工作项目中也尝试过用关键词匹配的方法去进行文本分类,发现大量误报。感觉就像扔到垃圾箱的邮件99%都是正常的!这样的效果不忍直视。而加一个朴素贝叶斯方法就可能把误报率拉低近一个数量级,体验好得不要不要的。
另一个原因是词语会随着时间不断变化。发垃圾邮件的人也不傻,当他们发现自己的邮件被大量屏蔽之后,也会考虑采用新的方式,如变换文字、词语、句式、颜色等方式来绕过反垃圾邮件系统。比如对于垃圾邮件“我司可办理正规发票,17%增值税发票点数优惠”,他们采用火星文:“涐司岢办理㊣規髮票,17%增値稅髮票嚸數優蕙”,那么字符串匹配的方法又要重新找出这些火星文,一个一个找出关键词,重新写一些匹配规则。更可怕的是,这些规则可能相互之间的耦合关系异常复杂,要把它们梳理清楚又是大一个数量级的工作量。等这些规则失效了又要手动更新新的规则……无穷无尽猫鼠游戏最终会把猫给累死。
而朴素贝叶斯方法却显示出无比的优势。因为它是基于统计方法的,只要训练样本中有更新的垃圾邮件的新词语,哪怕它们是火星文,都能自动地把哪些更敏感的词语(如“髮”、“㊣”等)给凸显出来,并根据统计意义上的敏感性给他们分配适当的权重 ,这样就不需要什么人工了,非常省事。你只需要时不时地拿一些最新的样本扔到训练集中,重新训练一次即可。
小补充一下,对于火星文、同音字等替代语言,一般的分词技术可能会分得不准,最终可能只把一个一个字给分出来,成为“分字”。当然也可以用过n-gram之类的语言模型(后续博客马上提到,尽请关注),拿到最常见短语。对于英文等天生自带空格来间隔单词的语言,分词则不是什么问题,使用朴素贝叶斯方法将会更加顺畅。
应用朴素贝叶斯方法的过程中,一些tricks能显著帮助工程解决问题。我们毕竟经验有限,无法将它们全都罗列出来,只能就所知的一点点经验与大家分享,欢迎批评指正。
我们上一篇文章用来识别垃圾邮件的方法是比较以下两个概率的大小(字母S表示“垃圾邮件”,字母H表示“正常邮件”):
C=P(“我”|S)P(“司”|S)P(“可”|S)P(“办理”|S)P(“正规发票”|S)
×P(“保真”|S)P(“增值税”|S)P(“发票”|S)P(“点数”|S)P(“优惠”|S)P(“垃圾邮件”)
C???=P(“我”|H)P(“司”|H)P(“可”|H)P(“办理”|H)P(“正规发票”|H)
×P(“保真”|H)P(“增值税”|H)P(“发票”|H)P(“点数”|H)P(“优惠”|H)P(“正常邮件”)
但这里进行了很多乘法运算,计算的时间开销比较大。尤其是对于篇幅比较长的邮件,几万个数相乘起来还是非常花时间的。如果能把这些乘法变成加法则方便得多。刚好数学中的对数函数log就可以实现这样的功能。两边同时取对数(本文统一取底数为2),则上面的公式变为:
logC=logP(“我”|S)+logP(“司”|S)+logP(“可”|S)+logP(“办理”|S)+logP(“正规发票”|S)
+logP(“保真”|S)+logP(“增值税”|S)+logP(“发票”|S)+logP(“点数”|S)+logP(“优惠”|S)+logP(“垃圾邮件”)
logC???=logP(“我”|H)+logP(“司”|H)+logP(“可”|H)+logP(“办理”|H)+logP(“正规发票”|H)
+logP(“保真”|H)+logP(“增值税”|H)+logP(“发票”|H)+logP(“点数”|H)+logP(“优惠”|H)+logP(“正常邮件”)
有同学可能要叫了:“做对数运算岂不会也很花时间?”的确如此,但是可以在训练阶段直接计算
对于二分类,我们还可以继续提高判断的速度。既然要比较
logCC??=logP(“我”|S)P(“我”|H)+logP(“司”|S)P(“司”|H)+logP(“可”|S)P(“可”|H)+logP(“办理”|S)P(“办理”|H)+logP(“正规发票”|S)P(“正规发票”|H)
+logP(“保真”|S)P(“保真”|H)+logP(“增值税”|S)P(“增值税”|H)+logP(“发票”|S)P(“发票”|H)+logP(“点数”|S)P(“点数”|H)+logP(“优惠”|S)P(“优惠”|H)+logP(“正常邮件”|S)P(“正常邮件”)
关键词hash表的样子如下,左列是权重,右列是其对应的词语,权重越高的说明越“关键”:
前文说过可以通过提前选取关键词来提高判断的速度。有一种方法可以省略提前选取关键词的步骤,就是直接选取一段文本中权重最高的K个词语,将其权重进行加和。比如Paul Graham 在《黑客与画家》中是选取邮件中权重最高的15个词语计算的。
通过权重hash表可知,如果是所有词语的权重,则权重有正有负。如果只选择权重最高的K个词语,则它们的权重基本都是正的。所以就不能像之前那样判断
如下图所示,蓝色点代表垃圾邮件,绿色点代表正常邮件,横坐标为计算出来的
选取topk个词语的方法对于篇幅变动不大的邮件样本比较有效。但是对篇幅过大或者过小的邮件则会有判断误差。
比如这个垃圾邮件的例子:(“我”,“司”,“可”,“办理”,“正规发票”,“保真”,“增值税”,“发票”,“点数”,“优惠”)。分词出了10个词语,其中有“正规发票”、“发票”2个关键词。关键词的密度还是蛮大的,应该算是敏感邮件。但因为采用最高15个词语的权重求和,并且相应的阈值是基于15个词的情况有效,可能算出来的结果还小于之前的阈值,这就造成漏判了。
类似的,如果一封税务主题的邮件有1000个词语,其中只有“正规发票”、“发票”、“避税方法”3个权重比较大的词语,它们只是在正文表述中顺带提到的内容。关键词的密度被较长的篇幅稀释了,应该算是正常邮件。但是却被阈值判断成敏感邮件,造成误判了。
这两种情况都说明topk关键词的方法需要考虑篇幅的影响。这里有许多种处理方式,它们的基本思想都是选取词语的个数及对应的阈值要与篇幅的大小成正比,本文只介绍其中一种方方法:
- 对于长篇幅邮件,按一定的大小,比如每500字,将其分割成小的文本段落,再对小文本段落采用topk关键词的方法。只要其中有一个小文本段落超过阈值就判断整封邮件是垃圾邮件。
- 对于超短篇幅邮件,比如50字,可以按篇幅与标准比较篇幅的比例来选取topk,以确定应该匹配关键词语的个数。比如选取
到目前为止,我们对词语权重求和的过程都没有考虑邮件篇章结构的因素。比如“正规发票”如果出现在标题中应该比它出现在正文中对判断整个邮件的影响更大;而出现在段首句中又比其出现在段落正文中对判断整个邮件的影响更大。所以可以根据词语出现的位置,对其权重再乘以一个放大系数,以扩大其对整封邮件的影响,提高识别准确度。
比如一封邮件其标题是“正规发票”(假设标题的放大系数为2),段首句是“发票”,“点数”,“优惠”(假设段首的放大系数为1.5),剩下的句子是(“我”,“司”,“可”,“办理”,“保真”)。则计算
logCC??=2×logP(“正规发票”|S)P(“正规发票”|H)+1.5×logP(“发票”|S)P(“发票”|H)+1.5×logP(“点数”|S)P(“点数”|H)+1.5×logP(“优惠”|S)P(“优惠”|H)
+logP(“我”|S)P(“我”|H)+logP(“司”|S)P(“司”|H)+logP(“可”|S)P(“可”|H)+logP(“办理”|S)P(“办理”|H)+logP(“保真”|S)P(“保真”|H)+logP(“正常邮件”|S)P(“正常邮件”)
我们通过辛辛苦苦的统计与计算,好不容易得到了不同词语的权重。然而这并不是一劳永逸的。我们我们之前交代过,词语及其权重会随着时间不断变化,需要时不时地用最新的样本来训练以更新词语及其权重。
而搜集最新垃圾邮件有一个技巧,就是随便注册一些邮箱,然后将它们公布在各大论坛上。接下来就坐等一个月,到时候收到的邮件就绝大部分都是垃圾邮件了(好奸诈)。再找一些正常的邮件,基本就能够训练了。这些用于自动搜集垃圾邮件的邮箱叫做“蜜罐”。“蜜罐”是网络安全领域常用的手段,因其原理类似诱捕昆虫的装有蜜的罐子而得名。比如杀毒软件公司会利用蜜罐来监视或获得计算机网络中的病毒样本、攻击行为等。
讲了这么多tricks,但这些手段都是建立在贝叶斯方法基础之上的。因此有必要探讨一下贝叶斯方法的思维方式,以便更好地应用这种方法解决实际问题。
我们重新看一眼贝叶斯公式:
P(Y|X)=P(X|Y)P(Y)P(X)
先不考虑先验概率
从贝叶斯公式的发现历史来看,其就是为了处理所谓“逆概”问题而诞生的。比如
好吧,我们说人话。基于邮件的文本内容判断其属于垃圾邮件的概率不好求(不可通过直接观测、统计得到),但是基于已经搜集好的垃圾邮件样本,去统计(直接观测)其文本内部各个词语的概率却非常方便。这就可以用贝叶斯方法。
引申一步,基于样本特征去判断其所属标签的概率不好求,但是基于已经搜集好的打上标签的样本(有监督),却可以直接统计属于同一标签的样本内部各个特征的概率分布。因此贝叶斯方法的理论视角适用于一切分类问题的求解。
前面我们一直在探讨二分类(判断题)问题,现在可以引申到多分类(单选题)问题了。
还是用邮件分类的例子,这是现在不只要判断垃圾邮件,还要将正常邮件细分为私人邮件、工作邮件。现在有这3类邮件各1万封作为样本。需要训练出一个贝叶斯分类器。这里依次用
P(Y1|X)=P(X|Y1)P(Y1)P(X)
P(Y2|X)=P(X|Y2)P(Y2)P(X)
P(Y3|X)=P(X|Y3)P(Y3)P(X)
通过比较3个概率值的大小即可得到
P(Yi|X)∝P(X|Yi)P(Yi);i=1,2,3
其中
这里只是以垃圾邮件3分类问题举了个例子,对于任意多分类的问题都可以用这样的思路去理解。比如新闻分类、情感喜怒哀乐分类等等。
在垃圾邮件的例子中,先验概率都相等,
P(Yi|X)∝P(X|Yi);i=1,2,3
只需比较右边式子(也就是“似然函数”)的大小就可以了。这种方法就是传说中的最大似然法:不考虑先验概率而直接比较似然函数。
关于选出最佳分类
比如我们在采集垃圾邮件样本的时候,不小心delete掉了一半的数据,就剩下5000封邮件。则计算出来的先验概率为:
P(Y1)=5000/25000=1/5 ,
P(Y2)=P(Y3)=10000/25000=2/5
如果还用贝叶斯方法,就要在似然函数后面乘上先验概率。比如之前用最大似然法算出
但是,如果我们有足够的自信,训练集中这三类的样本分布的确很接近真实的情况,这时就应该用贝叶斯方法。难怪前面的贝叶斯学派强调的是“靠谱的先验概率”。所以说贝叶斯学派的适用范围更广,关键要先验概率靠谱,而频率学派有效的前提也是他们的先验概率同样是经验统计的结果。
说了这么多理论的问题,咱们就可以探讨一下(朴素)贝叶斯方法在自然语言处理中的一些常见应用了。以下只是从原理上进行探讨,对于具体的技术细节顾及不多。
一个比较常见的应用场景是情感褒贬分析。比如你要统计微博上人们对一个新上映电影的褒贬程度评价:好片还是烂片。但是一条一条地看微博是根本看不过来,只能用自动化的方法。我们可以有一个很粗略的思路:
接下来的核心问题就是训练出一个靠谱的分类器。首先需要有打好标签的文本。这个好找,豆瓣影评上就有大量网友对之前电影的评价,并且对电影进行1星到5星的评价。我们可以认为3星以上的评论都是好评,3星以下的评论都是差评。这样就分别得到了好评差评两类的语料样本。剩下就可以用朴素贝叶斯方法进行训练了。基本思路如下:
但是由于自然语言的特点,在提取特征的过程当中,有一些tricks需要注意:
当然经过以上的处理,情感分析还是会有一部分误判。这里涉及到许多问题,都是情感分析的难点:
拼写纠错本质上也是一个分类问题。但按照错误类型不同,又分为两种情况:
真词错误复杂一些,我们将在接下来的文章中进行探讨。而对于非词错误,就可以直接采用贝叶斯方法,其基本思路如下:
判别公式:
P(候选词i|错误词)∝P(错误词|候选词i)P(候选词i);i=1,2,3,...
训练样本1:该场景下的正常用词语料库,用于计算
P(候选词i)=候选词出现的次数所有词出现的次数
训练样本2:该场景下错误词与正确词对应关系的语料库,用于计算
P(错误词|候选词i)=候选词被拼写成该“错误词”的次数候选词出现的次数
由于自然语言的特点,有一些tricks需要注意:
由于我们只选择编辑距离为1或2的词,其差别只是一两个字母级别差别。因此计算似然函数的时候,可以只统计字母层面的编辑错误,这样搜集的样本更多,更满足大数定律,也更简单。对于编辑距离为1的似然函数计算公式可以进化为:
P(错误词|候选词i)=?????????字母“xy”被拼写成“y”的次数字母“xy”出现的次数,字母“x”被拼写成“xy”的次数字母“x”出现的次数,字母“x”被拼写成“y”的次数字母“x”出现的次数,字母“xy”被拼写成“yx的次数字母“xy”出现的次数,
键盘上临近的按键更容易拼写错误,据此可以对上面这个条件概率进行加权。
从这两篇文章大家基本可以看出,工程应用不同于学术理论,有许多tricks需要考虑,而理论本质就是翻来倒去折腾贝叶斯公式,都快玩出花来了。但是如果只用朴素贝叶斯,很多情况还是无法应付,需要我们在贝叶斯公式上再折腾出一些花样。详细内容,请听下回分解。
原文:http://blog.csdn.net/han_xiaoyang/article/details/50629587