Naive Bayes(NB)算法,亦称为朴素贝叶斯算法,是一个经典的、有代表性的分类算法。该算法是基于贝叶斯定理的分类方法,也是一种用后验概率公式推导出的算法。在NB算法中,采用了“属性条件独立性假设”(attribute conditional independence assumption):对已知类别,假设所有属性相互独立。换言之,假设每个属性独立地对分类结果发生影响。NB算法不仅能够处理符号型数据,也能够处理数值型数据。
朴素贝叶斯中的“朴素”就代表着:特征之间是相互独立的。但事实上,在现实生活中许多属性之间并不是相互独立的。因此这是一种理想状态。
NB算法是基于概率论的分类算法,因此本文需要花大量篇幅进行公式推导。朴素贝叶斯算法易于理解,易于实现,常用于文本分类、垃圾邮件分类、推荐系统等。
二、Naive Bayes算法基本思想朴素贝叶斯算法是通过计算某事件发生概率来进行判断、预测或分类。算法最重要、最抽象的地方就是其数学表达。
·基本公式首先引入条件概率的数学表达:
P
(
A
B
)
=
P
(
A
)
P
(
B
∣
A
)
(1)
P(AB)=P(A)P(B\mid A)\tag{1}
P(AB)=P(A)P(B∣A)(1)
其中,
P
(
A
)
P(A)
P(A)表示事件
A
A
A发生的概率;
P
(
B
∣
A
)
P(B\mid A)
P(B∣A)表示在事件
A
A
A发生的条件下事件
B
B
B发生的概率;
P
(
A
B
)
P(AB)
P(AB)表示事件
A
A
A和事件
B
B
B同时发生的概率。
那么,在事件
A
A
A发生的条件下事件
B
B
B发生的概率
P
(
B
∣
A
)
P(B\mid A)
P(B∣A)可以表示为:
P
(
B
∣
A
)
=
P
(
A
B
)
P
(
A
)
(2)
P(B\mid A)=\frac{P(AB)}{P(A)} \tag{2}
P(B∣A)=P(A)P(AB)(2)
令
x
=
x
1
∧
x
2
∧
.
.
.
∧
x
m
x=x_{1} \wedge x_{2} \wedge... \wedge x_{m}
x=x1∧x2∧...∧xm表示一个条件的组合,如:outlook=sunny ∧ temperature=hot ∧ humidity=high,对应数据集中的一行数据。令
D
i
D_{i}
Di表示一个事件,如play=no。根据(2)式可知,在各种条件都发生的情况下,不能去打球的概率
P
(
D
i
∣
x
)
P(D_{i}\mid x)
P(Di∣x)可以表示为:
P
(
D
i
∣
x
)
=
P
(
x
D
i
)
P
(
x
)
=
P
(
D
i
)
P
(
x
∣
D
i
)
P
(
x
)
(3)
P(D_{i}\mid x)=\frac{P(xD_{i})}{P(x)} =\frac{P(D_{i})P(x\mid D_{i})}{P(x)}\tag{3}
P(Di∣x)=P(x)P(xDi)=P(x)P(Di)P(x∣Di)(3)
由于是“朴素”贝叶斯,因此在该问题中,默认各个条件之间是独立的。如:事件
D
i
D_{i}
Di表示各项条件综合表明今天不适合打球,
x
x
x表示条件的组合。那么在不适合打球的情况下各项条件的总概率
P
(
x
∣
D
i
)
P(x\mid D_{i})
P(x∣Di)可以表示为:
P
(
x
∣
D
i
)
=
P
(
x
1
∣
D
i
)
P
(
x
2
∣
D
i
)
.
.
.
P
(
x
m
∣
D
i
)
=
∏
j
=
1
m
P
(
x
j
∣
D
j
)
(4)
P(x\mid D_{i})=P(x_{1}\mid D_{i})P(x_{2}\mid D_{i})...P(x_{m}\mid D_{i})=\prod_{j=1 }^{m} P(x_{j}\mid D_{j})\tag{4}
P(x∣Di)=P(x1∣Di)P(x2∣Di)...P(xm∣Di)=j=1∏mP(xj∣Dj)(4)
综合(3)式与(4)式可得:
P
(
D
i
∣
x
)
=
P
(
x
D
i
)
P
(
x
)
=
P
(
D
i
)
∏
j
=
1
m
P
(
x
j
∣
D
j
)
P
(
x
)
(5)
P(D_{i}\mid x)=\frac{P(xD_{i})}{P(x)} =\frac{P(D_{i})\prod_{j=1 }^{m} P(x_{j}\mid D_{j})}{P(x)}\tag{5}
P(Di∣x)=P(x)P(xDi)=P(x)P(Di)∏j=1mP(xj∣Dj)(5)
在(5)式中,条件组合发生的概率
P
(
x
)
P(x)
P(x)是无法计算的。但由于NB算法的最终目标是分类。不同的类别,对于这个的分母也是完全相同的。因此我们可以只关注分子。此外,为了能够便于计算,将分子整体进行取对数处理,最终目标就转变成找到使
P
(
D
i
∣
x
)
P(D_{i}\mid x)
P(Di∣x)最大的参数。预测方案可描述为:
d
(
x
)
=
1
≤
i
≤
k
a
r
g
m
a
x
P
(
D
i
∣
x
)
=
1
≤
i
≤
k
a
r
g
m
a
x
P
(
D
i
)
∏
j
=
1
m
P
(
x
j
∣
D
i
)
=
1
≤
i
≤
k
a
r
g
m
a
x
(
log
P
(
D
i
)
+
∑
j
=
1
m
log
P
(
x
j
∣
D
i
)
)
(6)
d(x)=_{1\le i\le k}^{arg max}P(D_{i}\mid x)=_{1\le i\le k}^{arg max}P(D_{i})\prod_{j=1}^{m}P(x_{j}\mid D_{i}) =_{1\le i\le k}^{arg max}(\log_{}{P(D_{i})+\sum_{j=1}^{m}\log_{}{P(x_{j}\mid D_{i})} } ) \tag{6}
d(x)=1≤i≤kargmaxP(Di∣x)=1≤i≤kargmaxP(Di)j=1∏mP(xj∣Di)=1≤i≤kargmax(logP(Di)+j=1∑mlogP(xj∣Di))(6)
在不同条件同时发生的情况下,事件发生的概率可能为0。如:在下雨天不能出门打球,即:
P
(
D
i
∣
x
r
a
i
n
y
)
=
0
P(D_{i}\mid x_{rainy})=0
P(Di∣xrainy)=0。由于在(5)式中,
∏
j
=
1
m
P
(
x
j
∣
D
i
)
\prod_{j=1}^{m}P(x_{j}\mid D_{i})
∏j=1mP(xj∣Di)的连乘 *** 作只要有一个概率为0,那么连乘后结果一定为0。最终会导致
P
(
D
i
∣
x
)
P(D_{i}\mid x)
P(Di∣x)计算结果为0。这种“一票否决权”的现象会对预测结果造成影响。
为了防止这种“一票否决权”现象,避免其他属性携带的信息被训练集中未出现的属性值“抹去”,我们需要对概率值进行“平滑,常用方法为拉普拉斯平滑(Laplacian Smoothing)。
令
n
n
n表示数据集
D
i
D_{i}
Di中可能的类别数,
v
j
v_{j}
vj表示第
j
j
j个属性可能的取值个数。如;outlook的取值分别为:sunny、overcast、rainy,那么
v
o
u
t
l
o
o
k
=
3
v_{outlook}=3
voutlook=3。修正后的
P
(
x
j
∣
D
j
)
P(x_{j}\mid D_{j})
P(xj∣Dj)可以表示为:
P
L
(
x
j
∣
D
j
)
=
n
P
(
x
j
D
i
)
+
1
n
P
(
D
i
)
+
v
j
(7)
P^{L}(x_{j}\mid D_{j})= \frac{nP(x_{j}D_{i})+1}{nP(D_{i})+v_{j}} \tag{7}
PL(xj∣Dj)=nP(Di)+vjnP(xjDi)+1(7)
同时,也需要对
P
(
D
i
)
P(D_{i})
P(Di)进行平滑处理,修正后的
P
(
D
i
)
P(D_{i})
P(Di)可以表示为:
P
L
(
D
j
)
=
n
P
(
D
i
)
+
1
n
+
c
(8)
P^{L}(D_{j})=\frac{nP(D_{i})+1}{n+c} \tag{8}
PL(Dj)=n+cnP(Di)+1(8)
考虑平滑后,最终的预测方案可以描述为:
d
(
x
)
=
1
≤
i
≤
k
a
r
g
m
a
x
log
P
L
(
D
i
)
+
∑
j
=
1
m
log
P
L
(
x
j
∣
D
i
)
(9)
d(x)=_{1\le i\le k}^{arg max}\log_{}{P^{L}(D_{i})+\sum_{j=1}^{m}\log_{}{P^{L}(x_{j}\mid D_{i}) } } \tag{9}
d(x)=1≤i≤kargmaxlogPL(Di)+j=1∑mlogPL(xj∣Di)(9)
上述基本公式都是对于符号型数据处理。对于数值型数据,无法直接使用概率
P
P
P,因为
P
(
T
e
m
p
e
r
a
t
u
r
e
=
37
)
P(Temperature=37)
P(Temperature=37)在现实生活中出现的概率很低,一般数值都具有一定的误差。因此,在假定数据符合正态分布的前提下,我们可以通过离散化处理,将数据作成区间内的数据进行计算。由此,我们引入单变量高斯分布概率密度函数。且该样本的均值与中位数相同,则该分布被称为正态分布或高斯分布。
举一个常见的例子,一次难度适中的考试,考试成绩分布符合高斯分布。因为总是“两头小,中间大”,高分与低分的人数少,但是中等成绩的人数较多。
在介绍高斯分布的数学表达式之前,首先引入两个概念:均值
μ
\mu
μ与标准差
σ
\sigma
σ。
均值
μ
\mu
μ也被称为期望,数学表达式为:
μ
=
E
(
x
)
=
∫
−
∞
∞
x
p
(
x
)
d
x
(10-1)
\mu =E(x)=\int_{-∞}^{∞}xp(x)dx \tag{10-1}
μ=E(x)=∫−∞∞xp(x)dx(10-1)
标准差
σ
\sigma
σ的平方
σ
2
\sigma ^{2}
σ2被称为方差,数学表达式为:
σ
2
=
∫
−
∞
∞
(
x
−
μ
)
2
p
(
x
)
d
x
(10-2)
\sigma ^{2} =\int_{-∞}^{∞} (x-\mu )^2p(x)dx \tag{10-2}
σ2=∫−∞∞(x−μ)2p(x)dx(10-2)
那么概率密度函数的数学表达式为:
p
(
x
)
=
1
2
π
σ
exp
{
−
1
2
(
x
−
μ
σ
)
2
}
(10-3)
p(x)=\frac{1}{\sqrt{2\pi \sigma } } \exp\left \{ -\frac{1}{2}(\frac{x-\mu }{\sigma } ) ^{2} \right \} \tag{10-3}
p(x)=2πσ
1exp{−21(σx−μ)2}(10-3)
显然,通过均值与方差就可以确定该函数。高斯分布的样本主要集中在均值附件,且分散程度可以通过标准差来表示。标准差越大,分散程度也越大。
利用NB算法对数值型数据进行分类或预测,我们需要比处理符号型数据多做两件事情:
(1)根据公式,求得概率密度函数
p
p
p。
(2)将求得的
p
p
p代替(6)式中的
P
P
P。
将(5)式改写,得到数值型数据预测方案:
d
(
x
)
=
1
≤
i
≤
k
a
r
g
m
a
x
log
P
L
(
D
i
)
+
∑
j
=
1
m
−
log
σ
i
j
−
(
x
j
−
μ
i
j
)
2
2
σ
i
j
2
(11)
d(x)=_{1\le i\le k}^{arg max}\log_{}{P^{L}(D_{i})+\sum_{j=1}^{m}-\log_{}{\sigma _{ij}-\frac{(x_{j}-\mu _{ij})^{2}}{2 \sigma _{ij}^{2} } } } \tag{11}
d(x)=1≤i≤kargmaxlogPL(Di)+j=1∑m−logσij−2σij2(xj−μij)2(11)
以处理字符型数据集为例介绍算法流程。在介绍算法流程之前,先分析数据集。本次实验使用天气数据集,下载地址在此。数据量为14。若下载速度较慢,可直接将下列数据复制后生成weather.arff文件。代码都有注释,便于理解。
@relation weather
@attribute Outlook {Sunny, Overcast, Rain}
@attribute Temperature {Hot, Mild, Cool}
@attribute Humidity {High, Normal, Low}
@attribute Windy {FALSE, TRUE}
@attribute Play {N, P}
@data
Sunny,Hot,High,FALSE,N
Sunny,Hot,High,TRUE,N
Overcast,Hot,High,FALSE,P
Rain,Mild,High,FALSE,P
Rain,Cool,Normal,FALSE,P
Rain,Cool,Normal,TRUE,N
Overcast,Cool,Normal,TRUE,P
Sunny,Mild,High,FALSE,N
Sunny,Cool,Normal,FALSE,P
Rain,Mild,Normal,FALSE,P
Sunny,Mild,Normal,TRUE,P
Overcast,Mild,High,TRUE,P
Overcast,Hot,Normal,FALSE,P
Rain,Mild,High,TRUE,N
算法目标是判断天气是否适合出门打球。数据属性有四种:Outlook、Temperature、Humidity、Windy,决策属性为Play。如:[Sunny,Hot,High,FALSE,N]表示该实例Outlook=Sunny,Temperature=Hot,Humidity=High,Windy=FALSE,Play=N。
Outlook | Temperature | Humidity | Windy | Play |
---|---|---|---|---|
Sunny | Hot | High | FALSE | N |
Overcast | Hot | High | FALSE | P |
… | … | … | … | … |
Rain | Mild | High | TRUE | N |
Instances dataset;//初始化数据集
int numClasses;//类别数量
int numInstances;//实例数量
int numConditions;//属性条件数量
int[] predicts;//决策数组
double[] classDistribution;//类别概率
double[] classDistributionLaplacian;//平滑后概率
double[][][] conditionalCounts;//属于该条件属性的实例个数
double[][][] conditionalProbabiltiesLaplacian;//平滑后条件属性概率
GaussianParamters[][] gaussianParamters;//高斯参数矩阵
int dataType;//数据类型
public static final int NOMINAL=0;//字符型数据
public static final int NUMERICAL=1;//数值型数据
public NaiveBayes(String paraFilename) {//初始化朴素贝叶斯
dataset=null;
try {
FileReader fileReader=new FileReader(paraFilename);//读数据
dataset=new Instances(fileReader);//生成数据集
fileReader.close();//关闭
}catch (Exception ee) {//否则
System.out.println("Cannot read the file " + paraFilename);
System.exit(0);
// TODO: handle exception
}
dataset.setClassIndex(dataset.numAttributes()-1);
numConditions=dataset.numAttributes()-1;//属性个数
numInstances=dataset.numInstances();//实例个数
numClasses=dataset.attribute(numConditions).numValues();//类别个数
}
public void setDataType(int paraDataType) {//设置要处理的数据类型
dataType=paraDataType;
}
②计算不同类别的分布以及条件属性概率。
public void calculateConditionProbabilities() {//计算条件概率
conditionalCounts=new double[numClasses][numConditions][];
//sunny,hot,high,FALSE,no
//对应[0][0][1]代表:不能出去;天气;晴朗
conditionalProbabiltiesLaplacian=new double[numClasses][numConditions][];//平滑后的条件概率数组
//分配空间
for(int i=0;i<numClasses;i++) {//类
for(int j=0;j<numConditions;j++) {//条件
int tempNumValues=(int)dataset.attribute(j).numValues();//某属性数量
conditionalCounts[i][j]=new double[tempNumValues];//存入
conditionalProbabiltiesLaplacian[i][j]=new double[tempNumValues];//也存入平滑矩阵
}//of for j
}//of for i
//计数
int[] tempClassCounts=new int[numClasses];//类计数矩阵
for(int i=0;i<numInstances;i++) {//每一行实例对象读取
int tempClass=(int)dataset.instance(i).classValue();//实例i的类别
tempClassCounts[tempClass]++;//该类别数目+1
for(int j=0;j<numConditions;j++) {//实例中属性
int tempValue=(int)dataset.instance(i).value(j);//实例i中属性为j的值
conditionalCounts[tempClass][j][tempValue]++;//计数:实例i中j属性的值
}//of for j
}//of for i
for(int i=0;i<numClasses;i++) {
for(int j=0;j<numConditions;j++) {
int tempNumValues=(int)dataset.attribute(j).numValues();//记录属性j的值
for(int k=0;k<tempNumValues;k++) {//数据平滑处理
conditionalProbabiltiesLaplacian[i][j][k]=(conditionalCounts[i][j][k]+1)/(tempClassCounts[i]+tempNumValues);
}//of for j
}//of for j
}//of for i
System.out.println("Conditional probabilities: " + Arrays.deepToString(conditionalCounts));
}
③初始化私有的高斯参数类,并计算相应的高斯参数。
private class GaussianParamters{//高斯参数类
double mu;//均值
double sigma;//方差
public GaussianParamters(double paraMu,double paraSigma) {//定义高斯参数的函数重载
mu=paraMu;
paraSigma=sigma;
}
public String toString() {//输出高斯参数
return "(" + mu + ", " + sigma + ")";
}
}
public void calculateGausssianParameters() {
gaussianParamters = new GaussianParamters[numClasses][numConditions];//高斯参数,不同类别的对象矩阵
double[] tempValuesArray=new double[numInstances];
int tempNumValues=0;
double tempSum=0;
for(int i=0;i<numClasses;i++) {
for(int j=0;j<numConditions;j++) {
tempSum=0;//记录和
tempNumValues=0;//记录值
for(int k=0;k<numInstances;k++) {
if((int)dataset.instance(k).classValue()!=i) {//若不符合当前计算的条件
continue;//忽略
}
tempValuesArray[tempNumValues]=dataset.instance(k).value(j);//记录当前处理的对象
tempSum+=tempValuesArray[tempNumValues];//加上值
tempNumValues++;//符合处理条件的对象数目+1
}//of for k
double tempMu=tempSum/tempNumValues;//获得均值mu
double tempSigma=0;
for(int k=0;k<tempNumValues;k++) {
tempSigma+=(tempValuesArray[k]-tempMu)*(tempValuesArray[k]-tempMu);//计算方
}
tempSigma/=tempNumValues;
tempSigma=Math.sqrt(tempSigma);//获得方差
gaussianParamters[i][j]=new GaussianParamters(tempMu, tempSigma);//更新高斯参数
}//of for k
}//of for i
System.out.println(Arrays.deepToString(gaussianParamters));//输出结果
}
④选择不同的数据类型进行分类,在这里我以字符型数据为例。
public void classify() {
predicts=new int[numInstances];//预测矩阵
for(int i=0;i<numInstances;i++) {//逐个实例对象进行预测
predicts[i]=classify(dataset.instance(i));//预测
}
}
private int classify(Instance instance) {
if(dataType==NOMINAL) {//不同的分类方式
return classifyNominal(instance);
}
else if(dataType==NUMERICAL) {//不同的分类方式
return classifyNumerical(instance);
}
return -1;
}
private int classifyNominal(Instance paraInstance) {
double tempBiggest=-10000;
int resultBestIndex=0;
for(int i=0;i<numClasses;i++) {
double tempClassProbabilityLaplacian=Math.log(classDistributionLaplacian[i]);//将之前计算过的平滑数组中存储的概率开方
double tempPseudoProbability=tempClassProbabilityLaplacian;
for(int j=0;j<numConditions;j++) {
int tempAttributeValue = (int) paraInstance.value(j);
//拉普拉斯平移
tempPseudoProbability+=Math.log(conditionalCounts[i][j][tempAttributeValue])-tempClassProbabilityLaplacian;
}//of for j
if(tempBiggest<tempPseudoProbability) {
tempBiggest=tempPseudoProbability;
resultBestIndex=i;
}//of if
}//of for i
return resultBestIndex;
}
⑤通过将测试数据与预测矩阵中的预测结果进行比对,计算准确率。
public double computeAccuracy() {
double tempCorrect=0;
for(int i=0;i<numInstances;i++) {
if(predicts[i]==(int)dataset.instance(i).classValue()) {//预测正确
tempCorrect++;
}//of if
}//of for i
double resultAccuracy=tempCorrect/numInstances;
return resultAccuracy;
}
⑥浅测一下。
public static void testNominal() {
System.out.println("Hello, Naive Bayes. I only want to test the nominal data.");
String tempFilename="C:/Users/11989/Desktop/sampledata-main/sampledata-main/weather.arff";
NaiveBayes tempLearner = new NaiveBayes(tempFilename);
tempLearner.setDataType(NOMINAL);
tempLearner.calculateClassDistribution();
tempLearner.calculateConditionProbabilities();
tempLearner.classify();
System.out.println("The accuracy is: " + tempLearner.computeAccuracy());
}
public static void main(String args[]) {
testNominal();
}
四、一些问题
1、NB算法的优缺点?
朴素贝叶斯算法常用于文本分类、垃圾邮箱分类等。由此可以看出NB算法对缺失数据不太敏感,少一点也无伤大雅。同时,NB算法的分类准确度较高,算法速度快,能够处理多分类任务。
但是,朴素贝叶斯也是十分的“朴素”,因为它假设条件属性之间相互独立,但这种情况在现实生活中往往是不成立的。因此,当条件属性较多或属性之间关联性较大时,分类效果不好。
朴素贝叶斯算法可以通过伯努利模型来对布尔型数据进行处理。如:用true和false来表示正常言论与不良言论,通过NB算法构建一个过滤器来屏蔽网站中不当言论。
3、NB算法既然对缺失数据不太敏感,那么它对异常数据敏感吗?朴素贝叶斯算法对异常值不敏感,因此在进行数据处理时,可以不用去除异常值,同时也可以保持NB算法的整体精度。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)