从一个风控案例讲起-古老而经典的朴素贝叶斯

共 6997字,需浏览 14分钟

 ·

2022-01-24 08:51

大家好,我是爱生活,AI风控的小伍哥,今天给大家带来的文章,关于朴素贝叶斯的,一个古老而经典的算法,充分的理解有利于对风控特征或者识别的开拓新的思路。


一、从一个案例讲起

假如我们的目标是判断邮件是否是垃圾邮件,邮件内容是【代开增值税发票4533224】,那么我们要怎么做呢?
回忆下高中的数学知识,数学原理
贝叶斯公式: P(Y|X) = P(X|Y) * P(Y) / P(X)
当X条件独立时:P(X|Y) = P(X1|Y) * P(X2|Y) * ...*P(Xn|Y)
把该公式,应用到我们的风险识别上,可以得到如下的公式
垃圾邮件的概率
正常邮件的概率
 
到这一步,上面的  P(Text="代开增值税发票4533224") 是算不出来的,我们进一步处理,这样分母就自己约掉了
 
于是,我们只要计算分子的值就可以,下面的几个值,都是可以根据训练集进行计算的
样本集中垃圾样本的比例
P(Y=垃圾)
样本集中正常样本的比例
P(Y=正常)
黑样本集中每个词的比例然后相乘
P(Text="代开"|Y=垃圾 )
P(Text="增值税"|Y=垃圾 )
P(Text="发票"|Y=垃圾 )
P(Text="4533224"|Y=垃圾 )
白样本集中每个词的比例然后相乘
P(Text="代开"|Y=正常 )
P(Text="增值税"|Y=正常 )
P(Text="发票"|Y=正常 )
P(Text="4533224"|Y=正常 )
就这样,我们把公式和实际的应用联系起来了。并且每个指标都是可以计算的。
当然要计算上面的概率,需要我们有历史数据,于是我从我的邮箱里面复制了一些垃圾邮件,以及一些正常的邮件,并对数据做了一下简单的预处理处理,打上了垃圾邮件和正常邮件的标签,我们就可以得到历史数据,进行计算了。
 
首先我们计算垃圾邮件的概率
P(代开|垃圾)   = 1/5  = 0.2
P(增值税|垃圾)= 1/5  = 0.2
P(发票|垃圾)   = 4/5 = 0.8
P(sep|垃圾)    = 5/5 = 1.0
计算垃圾邮件的概率,一共10个样本,5个垃圾邮件,5个正常邮件
P(垃圾)= 5/10    = 0.5
垃圾邮件的分子 = 0.2*0.2*0.8*1.0*0.5 = 0.0160000
 
现在我们计算正常邮件的概率
编号
邮件内容
清洗后内容
是否垃圾邮件
1
尽快完成工作
尽快,完成,工作
正常
4
明天一起约个饭
明天,一起,约个饭
正常
5
完成的非常棒
完成,的,非常,棒
正常
6
项目效果很好
项目,效果,很好
正常
9
账单请查收
账单,请,查收
正常
但是每个都是0,我们要做下拉普拉斯变换(分子分母分别加1,严格的拉普拉斯并不是这样的,这里简化计算了),采用拉普拉斯变换计算得到,具体定义见后文,专门解决0概率问题:
P(代开|正常)   = 1/(5+1)  = 0.167
P(增值税|正常)= 1/(5+1)  =  0.167
P(发票|正常)    = 1/(5+1) =  0.167
P(sep|正常)     = 1/(5+1) =  0.167
计算垃圾邮件的概率,一共10个样本,5个垃圾邮件,5个正常邮件
P(正常)= 5/10 = 0.5
正常邮件的分子 = 0.167*0.167*0.167*0.167*0.5 = 0.0003889
 
最后的结果(虽然拉普拉斯不大严谨,但是计算过程没问题)
P(垃圾|代开,增值税,发票,sep) =   0.0160000/(0.0160000+0.0003889) = 0.976271
P(正常|代开,增值税,发票,sep) =   0.0003889/(0.0160000+0.0003889) = 0.023729
 
我们计算下正常邮件的概率为多大,按公式直接计算,结果为0,但是直接计算,会导致很多样本都为0,所以后面会引入拉普拉斯平滑。
P(正常|代开,增值税,发票,sep) = 0
 
当然我,我们用Python内置的库,就更简单了,几行代码就搞定了。
data = ['尽快,完成,工作',    '可,代开,各,行业,发票,sep',    '可,开,发票,请联系,sep',    '明天,一起,约个饭',    '完成,的,非常,棒',    '项目,效果,很好',    '有,发票,薇,sep',    '有,开票,微,sep',    '账单,请,查收',    '正规,增值税,发票,sep']label=[0,1,1,0,0,0,1,1,0,1]vectorizer_word = TfidfVectorizer()vectorizer_word.fit(data,)#vectorizer_word.vocabulary_train = vectorizer_word.transform(data)test  = vectorizer_word.transform(['代开,增值税,发票,sep'])from sklearn.naive_bayes import BernoulliNBclf = BernoulliNB()clf.fit(train.toarray(), label)clf.predict(test.toarray(),)clf.predict_proba(test.toarray(),)array([[0.00154805, 0.99845195]])算出来的概率也是0.99845195左右,和我们手动计算的差不多

二、贝叶斯定理概述

贝叶斯定理由英国数学家贝叶斯 ( Thomas Bayes 1702-1761 ) 发展,用来描述两个条件概率之间的关系,比如 P(A|B) 和 P(B|A)。主要用于文本分类。朴素贝叶斯法是基于贝叶斯定理与特征条件独立假设的分类方法。贝叶斯定理太有用了,不管是在投资领域,还是机器学习,或是日常生活中高手几乎都在用到它。


生命科学家用贝叶斯定理研究基因是如何被控制的,教育学家意识到,学生的学习过程其实就是贝叶斯法则的运用,基金经理用贝叶斯法则找到投资策略,谷歌用贝叶斯定理改进搜索功能,帮助用户过滤垃圾邮件,无人驾驶汽车接收车顶传感器收集到的路况和交通数据运用贝叶斯定理更新从地图上获得的信息,人工智能、机器翻译中大量用到贝叶斯定理,比如肝癌的检测、垃圾邮件的过滤。

朴素贝叶斯分类(NBC)是以贝叶斯定理为基础并且假设特征条件之间相互独立的方法,先通过已给定的训练集,以特征词之间独立作为前提假设,学习从输入到输出的联合概率分布,再基于学习到的模型,输入X求出使得后验概率最大的输出Y 。 

设有样本数据集D={d1,d2,...,dn},对应样本数据的特征属性集为X={x1,x2,...,xd}类变量为Y={y1,y2,...ym} ,即D可以分为ym类别。其中{x1,x2,...,xd} 相互独立且随机,则Y的先验概率P(Y) ,Y的后验概率P(Y|X) ,由朴素贝叶斯算法可得,后验概率可以由先验概率P(Y) ,证据P(X) ,类条件概率P(X|Y)计算出
朴素贝叶斯基于各特征之间相互独立,在给定类别为y的情况下,上式可以进一步表示为下式:
由以上两式可以计算出后验概率为:
由于P(X)的大小是固定不变的,因此在比较后验概率时,只比较上式的分子部分即可,因此可以得到一个样本数据属于类别yi的朴素贝叶斯计算:
对于上述例题,就得到如下的表达式
 

 

三、零概率问题连乘操作

零概率问题:即测试样例的标签属性中,出现了模型训练过程中没有记录的值,或者某个分类没有记录的值,从而出现该标签属性值的出现概率 P(wi|Ci) = 0 的现象。因为 P(w|Ci) 等于各标签属性值 P(wi|Ci) 的乘积,当分类 Ci 中某一个标签属性值的概率为 0,最后的 P(w|Ci) 结果也为 0。很显然,这不能真实地代表该分类出现的概率
解决方法:拉普拉斯修正,又叫拉普拉斯平滑,这是为了解决零概率问题而引入的处理方法。其修正过程:
浮点数溢出问题:在计算 P(w|Ci) 时,各标签属性概率的值可能很小,而很小的数再相乘,可能会导致浮点数溢出。解决方法:对 P(w|Ci) 取对数,把概率相乘转换为相加。

四、连续型变量概率计算

对于离散型变量,直接统计频数分布就可以了。对于连续型的变量,为了计算对应的概率,此时又引入了一个假设,假设特征的分布为正态分布,计算样本的均值和方差,然后通过密度函数计算取值时对应的概率
示例如下
从上面的例子可以看出,朴素贝叶斯假设样本特征相互独立,而且连续型的特征分布符合正态分布,这样的假设前提是比较理想化的,所以称之为"朴素"贝叶斯,因为实际数据并不一定会满足这样的要求。
 

五、Python库中的贝叶斯

在scikit-learn库,根据特征数据的先验分布不同,给我们提供了5种不同的朴素贝叶斯分类算法(sklearn.naive_bayes: Naive Bayes模块),分别是伯努利朴素贝叶斯(BernoulliNB),类朴素贝叶斯(CategoricalNB),高斯朴素贝叶斯(GaussianNB)、多项式朴素贝叶斯(MultinomialNB)、补充朴素贝叶斯(ComplementNB) 。
naive_bayes.BernoulliNB
Naive Bayes classifier for multivariate Bernoulli models.
naive_bayes.CategoricalNB
Naive Bayes classifier for categorical features
naive_bayes.ComplementNB
The Complement Naive Bayes classifier described in Rennie et al.
naive_bayes.GaussianNB
Gaussian Naive Bayes (GaussianNB)
naive_bayes.MultinomialNB
Naive Bayes classifier for multinomial models
这5种算法适合应用在不同的数据场景下,我们应该根据特征变量的不同选择不同的算法,下面是一些常规的区别和介绍。

GaussianNB

高斯朴素贝叶斯,特征变量是连续变量,符合高斯分布,比如说人的身高,物体的长度。
这种模型假设特征符合高斯分布。

MultinomialNB

特征变量是离散变量,符合多项分布,在文档分类中特征变量体现在一个单词出现的次数,或者是单词的 TF-IDF 值等。不支持负数,所以输入变量特征的时候,别用StandardScaler进行标准化数据,可以使用MinMaxScaler进行归一化。
这个模型假设特征复合多项式分布,是一种非常典型的文本分类模型,模型内部带有平滑参数

ComplementNB

是MultinomialNB模型的一个变种,实现了补码朴素贝叶斯(CNB)算法。CNB是标准多项式朴素贝叶斯(MNB)算法的一种改进,比较适用于不平衡的数据集,在文本分类上的结果通常比MultinomialNB模型好,具体来说,CNB使用来自每个类的补数的统计数据来计算模型的权重。CNB的发明者的研究表明,CNB的参数估计比MNB的参数估计更稳定。

BernoulliNB

模型适用于多元伯努利分布,即每个特征都是二值变量,如果不是二值变量,该模型可以先对变量进行二值化,在文档分类中特征是单词是否出现,如果该单词在某文件中出现了即为1,否则为0。在文本分类的示例中,统计词语是否出现的向量(word occurrence vectors)(而非统计词语出现次数的向量(word count vectors))可以用于训练和使用这个分类器。BernoulliNB 可能在一些数据集上表现得更好,特别是那些更短的文档。如果时间允许,建议对两个模型都进行评估。

CategoricalNB

对分类分布的数据实施分类朴素贝叶斯算法,专用于离散数据集, 它假定由索引描述的每个特征都有其自己的分类分布。对于训练集中的每个特征 X,CategoricalNB估计以类y为条件的X的每个特征i的分类分布。样本的索引集定义为J=1,…,m,m作为样本数。
 
案例测试from sklearn.naive_bayes import MultinomialNB,GaussianNB,BernoulliNB,ComplementNB from sklearn.datasets        import load_breast_cancerfrom sklearn.model_selection import cross_val_scoreX,y = load_breast_cancer().data,load_breast_cancer().targetnb1= GaussianNB()nb2= MultinomialNB()nb3= BernoulliNB()nb4= ComplementNB()for model in [nb1,nb2,nb3,nb4]:    scores=cross_val_score(model,X,y,cv=10,scoring='accuracy')    print("Accuracy:{:.4f}".format(scores.mean()))Accuracy:0.9368Accuracy:0.8928Accuracy:0.6274Accuracy:0.8928
from sklearn.naive_bayes import MultinomialNB,GaussianNB,BernoulliNB,ComplementNB ,CategoricalNBfrom sklearn.datasets import load_irisfrom sklearn.model_selection import cross_val_scoreX,y = load_iris().data,load_iris().targetgn1 = GaussianNB()gn2 = MultinomialNB()gn3 = BernoulliNB() gn4 = ComplementNB()gn5 = CategoricalNB(alpha=1)for model in [gn1,gn2,gn3,gn4,gn5]: scores = cross_val_score(model,X,y,cv=10,scoring='accuracy') print("Accuracy:{:.4f}".format(scores.mean()))Accuracy:0.9533Accuracy:0.9533Accuracy:0.3333Accuracy:0.6667Accuracy:0.9267

 
进行one-hot变化,可以发现BernoulliNB分类准确率还是很高的
from sklearn import preprocessing  enc = preprocessing.OneHotEncoder()  # 创建对象from sklearn.datasets        import load_irisX,y = load_iris().data,load_iris().target
enc.fit(X) array = enc.transform(X).toarray()
from sklearn.naive_bayes import MultinomialNB,GaussianNB,BernoulliNB,ComplementNB ,CategoricalNBfrom sklearn.datasets import load_irisfrom sklearn.model_selection import cross_val_scoreX,y = load_iris().data,load_iris().targetgn1 = GaussianNB()gn2 = MultinomialNB()gn3 = BernoulliNB()gn4 = ComplementNB()for model in [gn1,gn2,gn3,gn4]: scores=cross_val_score(model,array,y,cv=10,scoring='accuracy') print("Accuracy:{:.4f}".format(scores.mean()))Accuracy:0.8933Accuracy:0.9333Accuracy:0.9333Accuracy:0.9400

长按关注公众号           长按加作者好友
      
浏览 34
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报
评论
图片
表情
推荐
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报