机器学习之Naive Bayes算法:日撸Java三百行day58-59

机器学习之Naive Bayes算法:日撸Java三百行day58-59,第1张

一、什么是Naive Bayes算法

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(BA)(1)
其中, P ( A ) P(A) P(A)表示事件 A A A发生的概率; P ( B ∣ A ) P(B\mid A) P(BA)表示在事件 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(BA)可以表示为:
P ( B ∣ A ) = P ( A B ) P ( A ) (2) P(B\mid A)=\frac{P(AB)}{P(A)} \tag{2} P(BA)=P(A)P(AB)(2)
x = x 1 ∧ x 2 ∧ . . . ∧ x m x=x_{1} \wedge x_{2} \wedge... \wedge x_{m} x=x1x2...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(Dix)可以表示为:
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(Dix)=P(x)P(xDi)=P(x)P(Di)P(xDi)(3)
由于是“朴素”贝叶斯,因此在该问题中,默认各个条件之间是独立的。如:事件 D i D_{i} Di表示各项条件综合表明今天不适合打球, x x x表示条件的组合。那么在不适合打球的情况下各项条件的总概率 P ( x ∣ D i ) P(x\mid D_{i}) P(xDi)可以表示为:
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(xDi)=P(x1Di)P(x2Di)...P(xmDi)=j=1mP(xjDj)(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(Dix)=P(x)P(xDi)=P(x)P(Di)j=1mP(xjDj)(5)
在(5)式中,条件组合发生的概率 P ( x ) P(x) P(x)是无法计算的。但由于NB算法的最终目标是分类。不同的类别,对于这个的分母也是完全相同的。因此我们可以只关注分子。此外,为了能够便于计算,将分子整体进行取对数处理,最终目标就转变成找到使 P ( D i ∣ x ) P(D_{i}\mid x) P(Dix)最大的参数。预测方案可描述为:
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)=1ikargmaxP(Dix)=1ikargmaxP(Di)j=1mP(xjDi)=1ikargmax(logP(Di)+j=1mlogP(xjDi))(6)

·拉普拉斯平滑(Laplacian Smoothing)

在不同条件同时发生的情况下,事件发生的概率可能为0。如:在下雨天不能出门打球,即: P ( D i ∣ x r a i n y ) = 0 P(D_{i}\mid x_{rainy})=0 P(Dixrainy)=0。由于在(5)式中, ∏ j = 1 m P ( x j ∣ D i ) \prod_{j=1}^{m}P(x_{j}\mid D_{i}) j=1mP(xjDi)的连乘 *** 作只要有一个概率为0,那么连乘后结果一定为0。最终会导致 P ( D i ∣ x ) P(D_{i}\mid x) P(Dix)计算结果为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(xjDj)可以表示为:
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(xjDj)=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)=1ikargmaxlogPL(Di)+j=1mlogPL(xjDi)(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)=1ikargmaxlogPL(Di)+j=1mlogσij2σ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。

OutlookTemperatureHumidityWindyPlay
SunnyHotHighFALSEN
OvercastHotHighFALSEP
RainMildHighTRUEN
①初始化全局变量,读取数据文件,并设置要处理的数据类型。
	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算法的分类准确度较高,算法速度快,能够处理多分类任务。
但是,朴素贝叶斯也是十分的“朴素”,因为它假设条件属性之间相互独立,但这种情况在现实生活中往往是不成立的。因此,当条件属性较多或属性之间关联性较大时,分类效果不好。

2、除了可以处理字符型数据与数值型数据,NB算法还能处理布尔型数据吗?

朴素贝叶斯算法可以通过伯努利模型来对布尔型数据进行处理。如:用true和false来表示正常言论与不良言论,通过NB算法构建一个过滤器来屏蔽网站中不当言论。

3、NB算法既然对缺失数据不太敏感,那么它对异常数据敏感吗?

朴素贝叶斯算法对异常值不敏感,因此在进行数据处理时,可以不用去除异常值,同时也可以保持NB算法的整体精度。

欢迎分享,转载请注明来源:内存溢出

原文地址: https://outofmemory.cn/langs/904582.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-05-15
下一篇 2022-05-15

发表评论

登录后才能评论

评论列表(0条)

保存