python机器学习——朴素贝叶斯算法
背景与原理:
朴素贝叶斯算法是机器学习领域最经典的算法之一,仍然是用来解决分类问题的。
那么对于分类问题,我们的模型始终是:用$m$组数据,每条数据形如$(x_{1},...,x_{n},y)$,表示数据共有$n$个特征维度,而$y$表示该数据所属的类别,不妨设有$k$个取值$C_{1},...,C_{k}$
我们想一想,当我们谈论分类问题的时候,我们究竟在谈论什么?
从一个角度来理解,我们实际上是在研究:当给定了$X=x$的条件下,求概率$P(y=C_{i})$,而我们对分类的预测实际上就是使得$P(y=C_{i})$最大的$C_{i}$!
那么我们实际想求出的是$P(y=C_{i}|X=x)$这样的一个条件概率。
我们需要一些数学原理:
贝叶斯公式:$P(X|Y)=\dfrac{P(Y|X)P(X)}{P(Y)}$
这个公式的逻辑是很简单的:$P(X\cap Y)=P(X|Y)P(Y)=P(Y|X)P(X)$,即两个事件同时发生的概率始终等于一个事件在另一个事件发生条件下的概率乘以另一个事件发生的概率。
而基于这个逻辑,我们有:
$P(y=C_{i}|X=x)=\dfrac{P(X=x|y=C_{i})P(y=C_{i})}{P(X=x)}$
而我们再要求$X$的各个特征是彼此独立的,那么我们有:
$P(X=x|y=C_{i})=\prod_{j=1}^{n}P(X_{j}=x_{j}|y=C_{i})$
于是我们有最终的表达式:
$P(y=C_{i}|X=x)=\dfrac{\prod_{j=1}^{n}P(X_{j}=x_{j}|y=C_{i}) P(y=C_{i})}{P(X=x)}$
而我们想要的是:
$argmax_{C_{i}}\dfrac{\prod_{j=1}^{n}P(X_{j}=x_{j}|y=C_{i}) P(y=C_{i})}{P(X=x)}$
可以看到,对不同的$C_{i}$,上式的分母是相同的,那么我们将其省略,就得到了:
$argmax_{C_{i}}P(y=C_{i})\prod_{j=1}^{n}P(X_{j}=x_{j}|y=C_{i}) $
这就是我们真正想求的东西
那么$P(y=C_{i})$是多少呢?它是一个无关条件的先验概率,那么我们可以估计其为数据集中类别为$C_{i}$的数据出现的频率,即出现次数与数据集大小之比。那么我们设数据集中类别为$C_{i}$的数据有$c_{i}$条,那么我们有:
$P(y=C_{i})=\dfrac{c_{i}}{m}$(其中$m$为数据集大小)
但是如果我们数据集选取的很不好,某一个类别虽然我们的先验知识告诉我们有,但数据集中却没有这一个分类,这只能说明这个类别出现概率可能比较小,但并不意味着一定不会出现,所以我们需要给一个补偿项,我们选取一个常数$\lambda$,有:
$P(y=C_{i})=\dfrac{c_{i}+\lambda}{m+k\lambda}$(其中$k$为类别数,相当于对每一类我们都人工添加了$\lambda$个属于这一类的数据)
这样兜兜转转,我们发现不太好求出的实际上是$P(X_{j}=x_{j}|y=C_{i})$
那么怎么求呢?如果$X_{j}$是一个离散的量,那么我们只需再次引入贝叶斯公式:
$P(X_{j}=x_{j}|y=C_{i})=\dfrac{P(X_{j}=x_{j},y=C_{i})}{P(y=C_{i})}$
这样我们只需统计在原数据集中$X_{j}=x_{j}$且$y=C_{i}$的数据集出现的频率近似为概率再除以$y=C_{i}$出现的概率就好了。
当然,和上面的情况一样,我们同样可能会遇到倒霉的情况,即这种数据没有出现在数据集中,那我们当然也不能直接认为这种概率是零,那么和上述处理类似地引入拉普拉斯平滑,选取一个常数$\lambda$(通常为1),此时有:
$P(X_{j}=x_{j}|y=C_{i})=\dfrac{\sum_{p=1}^{m}[X_{j}=x_{j},y=C_{i}]+\lambda}{\sum_{p=1}^{m}[y=C_{i}]+O_{j}\lambda}$
这里的记号$[...]$表示如果中括号里内容为真取值为1,否则取值为0,而$O_{j}$是特征$j$的取值个数
但是...如果特征是连续的呢?
那么我们会认为这样的分布服从正态分布,即这个条件概率为:
$P(X_{j}=x_{j}|y=C_{i})=\dfrac{1}{\sqrt{2\pi \sigma_{i}^{2}}}e^{-\frac{(x_{j}-\mu_{i})^{2}}{2\sigma_{i}^{2}}}$
即我们认为在$y=C_{i}$的条件下,所有的$X_{j}$的分布服从均值为$\mu_{i}$,方差为$\sigma_{i}^{2}$的正态分布,而这个均值与方差可以通过极大似然估计求得。
这样我们就解决了所有的问题。
代码实现:
import numpy as np from sklearn import datasets from sklearn.linear_model import LogisticRegression from sklearn.model_selection import train_test_split from sklearn.ensemble import RandomForestClassifier from sklearn.svm import LinearSVC import matplotlib.pyplot as plt import pylab as plt from sklearn.naive_bayes import GaussianNB from sklearn.model_selection import train_test_split X,y=datasets.make_classification(n_samples=10000,n_features=20,n_informative=2,n_redundant=2) X_train,X_test,Y_train,Y_test=train_test_split(X,y,test_size=0.25) lr=LogisticRegression() svc=LinearSVC(C=1.0) rfc=RandomForestClassifier(n_estimators=100)#森林中树的个数 lr=lr.fit(X_train,Y_train) score1=lr.score(X_test,Y_test) print(score1) svc=svc.fit(X_train,Y_train) score2=svc.score(X_test,Y_test) print(score2) rfc=rfc.fit(X_train,Y_train) score3=rfc.score(X_test,Y_test) print(score3) gnb=GaussianNB() gnb=gnb.fit(X_train,Y_train) score4=gnb.score(X_test,Y_test) print(score4) y_pred=gnb.predict_proba(X_test) print(y_pred[:10])
这是在上一篇博客基础上对四种分类器的对比,在sklearn中朴素贝叶斯分类器主要有三种:高斯朴素贝叶斯GaussianNB,多项式朴素贝叶斯MultinomialNB和伯努利朴素贝叶斯BernoulliNB,高斯朴素贝叶斯用于处理特征是连续值的情况,而多项式朴素贝叶斯用于处理特征是离散值的情况,而伯努利朴素贝叶斯则用于处理特征只有0/1的情况。
这里我们使用sklearn自带的高斯朴素贝叶斯对与前文类似生成的数据集进行分类并对比分类效果,可以看到由于朴素贝叶斯对特征的分布有严格的要求(独立、正态等),因此分类效果相较别的分类器相对较差,但是朴素贝叶斯的训练效率比较高,而且可解释性也比较好,这是朴素贝叶斯的优点所在。
小结与优化:
朴素贝叶斯由于基础要求太高,导致很多情况下分类效果并不好,那么为了解决这个问题,人们作出了一些优化。比如针对要求各个维度相互独立的要求,一种方法是在朴素贝叶斯基础上增加属性间可能存在的依赖关系,另一种则是重新构建数据集,通过变换等手段将原来相关的属性变为独立的属性。最著名的优化方法是TAN算法,通过发现属性间的依赖关系来降低独立性假设。