NB以及其在工程上的一些应用

贝叶斯算法算是一个非常非常老的一个算法了,工业界用到这个算法运用最多的场景是NLP的分类问题。这个算法很简单,好理解,可解释性很强,比如说是见到了哪些词才做出了这样的判定。这个算法通常需要大量的数据,也就是说需要有足够大的训练语料。

简要说一下贝叶斯公式,很easy的。P(Y, X) = P(X| Y)P(Y) = P(Y|X)P(X)。这里一般来说,X是某些特征,Y为属于某类,就是类别标签。P(Y|X)是后验概率,P(Y)是先验概率,是可以直接从样本中统到的。

一个典型的应用是垃圾邮件识别。需要做的就是对这些语料进行预处理 (去掉标点符号、数字、停用词等) ,然后进行分词,把这些细粒度的词作为特征X灌到模型中进行训练。

 

但是直接用原始公式(贝叶斯公式)算条件的话,概率计算量太大。因此做了一个强假设(但是现实生活中不一定会成立):每个词之间没有关联,项与项之间相互独立,不考虑顺序,这就是所谓的朴素贝叶斯模型。这样的话,只需要统计各类邮件中该关键出现的概率就OK了。可以认为说这样进行处理的话,就变成了一个词袋模型。可以说贝叶斯公式+条件独立假设=朴素贝叶斯。

这模型看似很low,但是模型效果却普遍比较好。这是为啥呢?很大的一个可能性就是各个独立假设所产生的消极影响或积极影响互相抵消,最终导致结果受到的影响不大。一定要注意这里有一个前提:训练集要足够的大!

 

在所有的NLP问题中,都涉及到一个非常非常重要的步骤:预处理。

一、对处理重复词语(特征)有以下三种方式:

1)多项式模型:不做处理,重复就重复,不做什么处理。

2)伯努利模型:重复词只视为出现一次。

3)混合模型:计算句子概率时,不考虑重复词语出现的次数,但是在统计具体的词语的概率P(“词语”|S)时,考虑其出现的次数。(S为spam,即垃圾邮件,H为正常邮件)

二、去除停用词与选择关键词:

可以自己去做一个停用词,针对不同的语料场景,维护不同的停用词词典,同理也可以针对不同的业务场景维护不同的关键词表,停用词和关键词一般可以靠人工经验指定。这个工作虽然low,但是对模型效果提升却很好。去掉停用词可以减小训练的计算量,减少干扰。

三、工业界进行模型训练时涉及到的一些trick:

1.取对数:如果就是单纯按原始的公式去算的话,会用到很多的乘法运算,但是这里存在一个精度的问题,尤其是对于浮点数,此外乘法的计算时间开销比较大,如果把这些乘法变成加法就方便多了。在进行垃圾邮件识别这个具体的例子中,分别计算这个邮件为正常邮件和垃圾邮件的概率,进行比较后选概率大的那个所在的类别。如果对两个计算完的概率求log,这两个求完对数之后的值的差要比原始得概率值之间的差要放大几倍,会更准确。

做对数操作也会花时间,但是如果把这些log值存在一个大的hash表里,在判断的时候直接查hash表中已经计算好的对数概率,这样使得帕努单所需要的计算时间被转移到了训练过程中,实时运行速度比之前快得多,提升几个数量级。

2.转换为权重:如果是二分类的话,要比较两个log值的大小,做减法,通过对数函数的性质,就转换成log的除,如果log除的值大于0则属于垃圾邮件。这时候我们可以把其中每一项作为其对应词语的权重,比如说log(P("发票|S") / P("发票"|H))视为词语“发票”的权重,权重越大越说明“发票”更可能是与“垃圾邮件”相关的特征,由此可以根据权重大小来评估和筛选显著地特征,比如说关键词。而这些权重值可以直接提前计算好,存在一个hash表里(放到内存里),判断的时候直接将权重求和即可。

3.选取topK的关键词:还是要缩短时间,越快越好。要加和的项越多时间越长,因此要减少加和项的数量。直接选取一段文本中权重最高的K个词语,将其权重进行加和。这个权重有正有负,如果只选权重最高的topK个词的话,它们的权重基本都是正的,这时候就不能用是否大于0来判断邮件了,这时需要依靠经验人为设定一个阈值来判断。K的选取需要做实验去找,取决于具体场景下文本的长度和应用的情景。

4.分割样本:选取topK的方法对于篇幅变动不大的邮件样本比较有效,但是对于篇幅过大或者过小的邮件则会有判断误差。比如说对于分词后有10个词的邮件,提取出3个关键词,这个密度还蛮大的。对于分词后有1000个词的邮件,提取出3个关键词,但是很多的敏感词在这种长邮件中是突然插进去的一小句,这个密度就被大幅稀释,提的关键词根本没这些敏感词什么事儿,由于阈值的设定,导致邮件被误判。因此,topK关键词的方法是需要考虑篇幅的影响的。针对这个问题有很多种处理方式,基本思想都是选取词语的个数及对应的阈值要与篇幅的大小成正比。

1)对于长篇幅,按一定的大小,比如每500字,将其分割成小的文本段落,再对小文本采用topK关键词的方法。只要其中有一个小文本段落的值超过阈值就可以判定整封邮件是垃圾邮件。

2)对于超短篇邮件,比如50字,可按篇幅与标准比较篇幅的比例来选取topK,以确定应该匹配关键词与的个数,比如50/500 *15 = 2个词语进行匹配,相应的阈值可以是之前的2/15,以此来判断更为合理。

5.位置权重:考虑邮件篇章结构的因素。比如说“正规发票”如果出现在标题中应该比他出现在正文中对判断整个邮件的影响更大;而出现在段首句中比其出现在段落正文中对判断整个邮件的影响更大。所以可以根据词语出现的位置,对其权重再乘以一个放大系数,以扩大其对整封邮件的影响,提高识别准确度。这其实就是个加权操作。

6.蜜罐:词语及其权重会随着时间不断变化,需要时不时地用最新的样本来训练词语及其权重。这个方法主要是用来搜集新的垃圾邮件的。做这件事有个技巧,随便注册一些邮箱,然后将他们公布在各大论坛上,接下来坐等一个月,到时候收到的邮件就绝大部分是垃圾邮件了。再找一些正常邮件,基本就可以训练了。这些用于自动搜集垃圾邮件的邮箱就叫蜜罐。在安全领域也常用蜜罐来监视计算机网络中的病毒样本、攻击行为等。

这里有一个问题,为什么不直接匹配关键词来识别垃圾邮件?如果用关键词来匹配的话,那整个问题就是一个字符串匹配,但是这种方法的准确率太低,有大量误报。而且,词语会随着时间不断变化,比如一堆乱码七糟的火星文什么的,用字符串匹配的话要重新找出这些火星文,再重新找关键词,重新写匹配规则,更恐怖的是。这些规则之间可能耦合关系非常复杂,会把人搞死的。

上边就是整个贝叶斯及朴素贝叶斯的一些基本思想和工程实践的一些trick。总体来说,贝叶斯方法的思维方式就是把问题转成逆概问题:形式上的交换。我们能观测到的是P(X|Y),而对于P(Y|X)不能直接观测到,可以从贝叶斯公式间接推断这个不可直接观测的对象的情况。

 

前边一直说的是二分类,如果对于多分类问题,解决思路是一样的。假设我们要把邮件分为私人邮件、工作邮件、垃圾邮件这三个类。那就分别计算一个给定邮件分别属于三个类的概率值,比较这三者的大小来判断X的分类。这个概率的大小只和分子相关,其实也就是P(Yi|X)∝P(X|Yi)P(Yi), i=1,2,3。其中的P(X|Yi)就是传说中的似然函数,就是个概率,直接理解为P(Yi|X)的逆反条件概率就OK。任意的多分类问题都可以用这个思路去理解。

这里还要理解一下先验概率问题。最大似然法:不考虑先验概率而直接比较似然函数。在我们对于先验概率一无所知时,只能假设每种猜测的先验概率是均等的,这个时候就只能用最大似然了。在现实运用过程中如果发现最大似然法有偏差,可以考虑对不同的似然函数设定一些系数或者阈值,使其接近真实情况。但是如果我们有足够的自信,训练集中这三类的样本分布的确很接近真实的情况,这时就应该用贝叶斯方法,所以说贝叶斯学派的适用范围更广,关键要先验概率靠谱。

 

贝叶斯方法的常见应用:

1.褒贬分析(情感分析):要有一个打好标签的样本,特征就是评论分词后的词语,一个简单的方法是只选择其中的形容词,在网上有现成的情绪词库可以直接利用。对于自然语言的特点,在提取特征的过程中,有一些trick:

1)对否定句进行特别的处理。比如说“我不是很喜欢这部电影,因为他让我开心不起来”。分词后,其中的“喜欢”、“开心”都是褒义词,但是句子是否定句,所以整体是贬义的。这时候去掉停用词的话,对结果的影响是致命的。有一种比较简单粗暴的处理方式,就是“对否定词(不,非,没等)与句尾标点之间的所有形容词都采用其否定形式”。人为去分析一些样本,制定一些转换规则,则这句话提取出来的形容词就是“不喜欢”和“不开心”。

2)一般来说,最相关的情感词在一些文本片段中仅仅出现一次,这时词频模型起的作用很有限,甚至是负作用。此时使用伯努利模型(出现多次也算一次)来代替多项式模型(出现多次就算多次)。这种情况在微博这样的小篇幅文本中似乎不太明显,但是在博客、空间、论坛之类允许长篇幅文本出现的平台中需要注意。

3)副词对情感的评价有一定的影响。“不很喜欢”和“很不喜欢”的程度就有很大差异,如果是朴素贝叶斯方法的话就比较难处理这样的情况,我们可以考虑用语言模型或者加入词性标注的信息进行综合判断。

4)情绪的含蓄表达。比如“导演你出来,我保证不打死你”,这个就需要人为去定一些规则来进行判别,否则就会误判。

5)转折性表达。“我非常喜欢这些演员,非常崇拜这个导演,非常欣赏这个剧本,最后进了电影院发现这是个噩梦”这句话里有3个褒义词,一个贬义词,机器自然判断成褒义,但这句话妥妥的是贬义。

2.拼写纠错:本质上也是个分类问题。按错误类型不同分为两种情况:非词错误,即指那些拼写错误后的词本身就不合法,比如说将"wifi"写成“wify”;另一种是真词错误,即指那些拼写错误后的词仍然是合法的情况,比如说将“wifi”写成“wife”。真词错误复杂一些,而对于非词错误,就可以直接采用贝叶斯方法,基本思路如下:

1)标签:通过计算错误词语的最小编辑距离,获取最相似的候选词,每个候选词作为一个分类。(编辑距离这个东西在query的匹配、query的改写中有很多的应用)

2)特征:拼写错误的词本身,因为它就一个特征,所以没有什么条件独立性假设、朴素贝叶斯啥的。它就是纯而又纯的贝叶斯方法。

3)判别公式:P(候选词i|错误词) ∝ P(错误词|候选词i)P(候选词i), i=1,2,3...

训练样本1:该场景下的正常用词语料库,用于计算P(候选词i)

训练样本2:该场景下错误词与正确词对应关系的语料库,用于计算P(错误词|候选词i)

由于自然语言的特点,有一些trick需要注意:据统计,80%的拼写错误编辑距离为1,几乎所有的拼写错误编辑距离小于等于2。我们只选择编辑距离为1或2的词作为候选词,这样就可以减少大量不必要的计算。由于我们只选择编辑距离为1或2的词,其差别只是一两个字母级别的差别,因此计算似然函数的时候,可以只统计字母层面的编辑错误,这样搜集的样本更多,更满足大数定律,也更简单。

3.语种检测:用正则表达式去掉噪声数据(@XXX,#XXX#,网址),抽取1-gram和2-gram的统计特征。用NB来做语种检测效果还是很不错的,能达到97.5%,加大样本的话,准确率会非常高。

 

语言模型(n-gram):

NB的局限性来自于其条件独立假设,它将文本看成是词袋模型,不考虑词语之间的顺序信息。n-gram可以提高其对词语顺序的识别能力。

假设每个xi表示一个词语,则如果是独立性假设的话,P(x1, x2, x3, x4) = P(x1)P(x2)P(x3)P(x4),如果去掉这个独立性假设,应该有如下恒等式,P(x1, x2, x3, x4) = P(x1)P(x2 | x1)P(x3 | x1, x2)P(x4| x1, x2, x3),这个就是联合概率链规则。但是这个联合概率链规则还是好麻烦,实际项目中几乎没法用,于是就用一些方法去近似这个公式,n-gram就是其中一种。如果考虑一个词语和其前一个词语的依赖关系,公式就简化为如下形式:P(x1, x2, x3, x4) = P(x1)P(x2 | x1)P(x3 | x2)P(x4| x3),这就叫做bigram。如果把依赖词长度拉长一点,考虑一个词和其前边两个词的依赖关系,公式就简化为如下形式:P(x1, x2, x3, x4) = P(x1)P(x2 | x1)P(x3 | x1, x2)P(x4|  x2, x3),这叫做trigram。如果再考虑得长一点的话,考虑其前边n个词的关系,则就是n-gram。其实这就是马尔科夫假设,即下一个词的出现仅依赖于它前面的一个或几个词。

问题是如何选择n?理论上,只要有足够大的语料,n越大越好,因为考虑的信息更多。但是实际情况往往是训练语料很有限,很容易产生数据稀疏,不满足大数定律,算出来的概率失真。有的元组在训练集中没有出现,会有零概率的问题。如果n很大,参数空间过大,也不实用。假设词汇表大小为100000,则n-gram模型的参数数量就是100000^n。语言模型这个东西,加载成表,是要放到内存里的,这么多参数,内存是会爆掉的。根据一些人为的经验,对于n的选择有如下几点:经验上,trigram用的最多。但是原则上,能用bigram解决,绝不用trigram。n取≥4的情况非常少;n值越大,约束力越强;n值越小,在训练语料库中出现的次数更多,有更可靠的统计信息,有更高的可靠性、实用性

 

语言模型的应用主要有以下几个场景:

1.词性标注:典型的多分类问题。一个词可能有多种词性,比如说“爱”这个词,一般来说它作为动词是比较常见的,所以统一给“爱”分配为动词准确率也还足够高,这种方法简单粗暴好实现,如果准确率要求不高的话也比较常用。它只需要基于词性标注语料库做一个统计就够了,连贝叶斯方法、最大似然法都不用。这个词性标注语料库一般是由专业人员搜集好的。但是这种方法没有考虑上下文的信息,一般来说形容词后接名词居多等等。考虑到词性会受到前面一两个词的影响,可以引入bigram来提升精确度。这里边就涉及一个问题,对于第一个词来说,没有比它更靠前的词了,这时需要用简单粗暴的统计来判断。p.s.这个方法还是要求语料要足!!!

2.垃圾邮件识别:先对文本进行断句,将邮件内容拆分成不同的句子,然后用n-gram分类器判断每个句子是否为垃圾邮件中的敏感句子,最后当被判断为敏感句子的数量超过一定数量的时候,认为整个邮件就是垃圾邮件。这种方法考虑到了词语前面的一个词语的信息,同时也考虑了部分语序信息,因此区分效果要比单纯的朴素贝叶斯要好。

这里有几个trick:从区分度来看,trigram的效果更好一些;对于句子开头的词,因为要考虑其本身作为开头的特征,可以考虑在其前面添加一个句子起始符《S》,这样就不必单独计算P(起始词|Yi),而是替换为计算P(起始词|《S》,Yi),这样的话形式上与bigram统一,统计和预测起来都比较方便;一般来说,如果采用n-gram,可以在文本开头加入n-1个虚拟的开始符号,这样在所有情况下预测下一个词的可依赖词数都是一致的。与朴素贝叶斯一样,n-gram也会发生零概率问题,也需要平滑技术,这个在后边介绍。

3.中文分词:可以把这个问题理解成多分类问题。用Yi表示该句子的一个分词方案,这个分词方案其实就是个词串,或者说是向量。这里P(X| Yi)的意思就是,在分类方案为Yi的前提下,对应的句子为X的概率。也就说,任一假想的一种分词方式之下生成的句子总是唯一的,因此可以将P(X| Yi)看做是恒等于1的。这时贝叶斯公式简化为:P(Yi | X)∝P(Yi),这时就只要取最大化的P(Yi)就行了,而Yi就是个词串,这时候就可以用n-gram,这个场景下bigram效果还可以。

4.机器翻译和语音识别:在这两个任务中n-gram只是其中的一个部分。筛选候选中最靠谱的那个,在MT中就是选最能体现出我们表达某种语言时候的说话习惯的那个。在语音识别中,选有具体含义的那个。

 

平滑技术是为了解决零概率问题。对于没有出现过的词,如果不做平滑处理的话,P值就是0,那么在概率连乘的时候就整个概率变成了0。这个问题的本质是样本数量太少,不满足大数定律,计算出来的概率失真。如果没出现就扔掉的话,就相当于默认P(词|S) = 1,效果不太好,大量统计信息被浪费掉了。平滑技术是因为数据集太小而产生的现实需求,如果数据集足够大,平滑技术对结果的影响将会变小。平滑技术的基本思想是给未出现的n-gram条件概率分布一个非零估计值,相应的需要降低已出现n-gram条件概率分布,且经数据平滑后一定保证概率和为1。

1.拉普拉斯平滑:加一平滑,保证每个n-gram在训练语料中至少出现一次。如P(优惠| 发票,点数) = ((发票,优惠,点数)出现的次数+1) / ((发票,点数)出现的次数 + 所有不重复三元组的个数)

在所有不重复的三元组的个数远大于(发票,点数)出现的次数时,即训练语料库中绝大部分n-gram都是未出现的情况(一般都是如此),拉普拉斯平滑有“喧宾夺主”的现象,效果不佳。

2.good-Turing平滑:出现0次的n元组不能认为是0次,而是给一个比较小的估计值。但是为了保证总共(出现的和未出现的)n-gram的数量不变,要对已出现的n元组出现的次数打一个折扣。

3.组合估计平滑:如线性插值和katz平滑

 

推荐使用一个工具包:kenLM Language Model Toolkit,这个包效果很好,性能好,对大数据集处理快。

 

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值