日撸 Java 三百行: DAY59 数值型数据的 NB 算法

1. 数值型数据的处理

1.1 用概率密度函数取代条件概率

数值型数据是连续的,这就需要对贝叶斯分类器中条件概率的那部分表达式进行修改。已知符号型数据的贝叶斯分类器如下
y = arg ⁡ max ⁡ c k P ( Y = c k ) ∏ j P ( X ( j ) = x ( j ) ∣ Y = c k ) y=\arg\underset{c_k}{\max}P(Y=c_k)\underset{j}{\prod}P(X^{(j)}=x^{(j)}|Y=c_k) y=argckmaxP(Y=ck)jP(X(j)=x(j)Y=ck)
对于数值型数据,其 P ( X ( j ) = x ( j ) ) P(X^{(j)}=x^{(j)}) P(X(j)=x(j)) 的概率是 0 0 0,因此不能直接使用,而要对其进行离散化,计算在一段区间内的概率。但是这样离散化会很麻烦,又因为进行分类是基于对概率大小的比较,因此可以进一步简化,直接使用概率密度函数来替换掉条件概率。
这显然是可行的,考虑将区间缩小到一个极小范围内 ( 区间大小趋近于 0 0 0 ),此时比较条件属性在各个取值点邻域范围内的概率大小和比较它们在概率密度函数中的取值大小是一致的。因此就可以概率密度函数的取值来替换掉条件概率。

1.2 概率密度函数的选取

正态分布在实际中是最常见的,也可以采用其他的分布。正态分布的概率密度函数如下:
p ( x ) = 1 2 π σ exp ⁡ ( − ( x − μ ) 2 2 σ 2 ) p(x)=\frac{1}{\sqrt{2\pi}\sigma}\exp^{(-\frac{(x-\mu)^2}{2\sigma^2})} p(x)=2π σ1exp(2σ2(xμ)2)
要确定一个正态分布,需要确定两个参数,均值 μ \mu μ 和方差 σ \sigma σ,通过训练集数据,我们可以计算出这两个参数,从而得到正态分布,并假设测试集中的数据也同样符合此正态分布。

1.3 符号型数据的贝叶斯分类器

确定分布之后,我们直接用概率密度函数替代条件概率,即得
y = arg ⁡ max ⁡ c k P ( Y = c k ) ∏ j p ( x ( j ) ) = arg ⁡ max ⁡ c k P ( Y = c k ) ∏ j 1 2 π σ exp ⁡ ( − ( x ( j ) − μ ) 2 2 σ 2 ) \begin{aligned}y&=\arg\max_{c_k}P(Y=c_k)\prod_jp(x^{(j)})\\&=\arg\max_{c_k}P(Y=c_k)\prod_j\frac{1}{\sqrt{2\pi}\sigma}\exp^{(-\frac{(x^{(j)}-\mu)^2}{2\sigma^2})}\end{aligned} y=argckmaxP(Y=ck)jp(x(j))=argckmaxP(Y=ck)j2π σ1exp(2σ2(x(j)μ)2)
因为 2 π \sqrt{2\pi} 2π 并不影响最终的分类,所有可以将它忽略掉,同时为了防止溢出,可以取对数,由此得到最终的贝叶斯分类器。
y = arg ⁡ max ⁡ c k log ⁡ P ( Y = c k ) + ∑ j − log ⁡ σ − ( x ( j ) − μ ) 2 2 σ 2 y=\arg\max_{c_k}\log P(Y=c_k)+\sum_j-\log\sigma-\frac{(x^{(j)}-\mu)^2}{2\sigma^2} y=argckmaxlogP(Y=ck)+jlogσ2σ2(x(j)μ)2

2. 程序

只给出相较于符号型数据的 N a i v e   B a y e s Naive\ Bayes Naive Bayes 算法所增加的内容。

2.1 内部类 GaussianParameters

用于处理高斯分布 ( 即正态分布 ) 的两个参数,均值 μ \mu μ 和 方程 σ \sigma σ

	/**
	 ***************************
	 * An inner class to store parameters.
	 ***************************
	 */
	private class GaussianParameters {
		double mu;
		double sigma;
		
		public GaussianParameters(double paraMu, double paraSigma) {
			mu = paraMu;
			sigma = paraSigma;
		} // Of the constructor
		
		public String toString() {
			return "(" + mu + "," + sigma + ")";
		} // Of toString
	} // Of GaussianParameters

2.2 计算高斯分布参数

这里用一个二维矩阵 gaussianParameters 来存储高斯分布参数,该矩阵的第 i i i 行第 j j j 列表示,第 i i i 类的第 j j j 个属性的概率密度分布。
对于每一个分布的获取,分为以下几步

  1. 统计属于该类的数据的数量,以及每个属性的取值总和
  2. 计算均值和方差
  3. 将分布保存到 gaussianParameters
	/**
	 ***************************
	 * Calculate the conditional probabilities with Laplacian smooth.
	 ***************************
	 */
	public void calculateGaussianParameters() {
		gaussianParameters = new GaussianParameters[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;
				
				// Obtain values for this class.
				tempNumValues = 0;
				for(int k = 0; k < numInstances; ++k) {
					if((int) dataset.instance(k).classValue() != i) {
						continue;
					} // Of if
					
					tempValuesArray[tempNumValues] = dataset.instance(k).value(j);
					tempSum += tempValuesArray[tempNumValues];
					tempNumValues++;
				} // Of for k
				
				// Obtain parameters.
				double tempMu = tempSum / tempNumValues;
				double tempSigma = 0;
				for(int k = 0; k < tempNumValues; ++k) {
					tempSigma += (tempValuesArray[k] - tempMu) * (tempValuesArray[k] - tempMu);
				} // Of for k
				tempSigma /= tempNumValues;
				tempSigma = Math.sqrt(tempSigma);
				
				gaussianParameters[i][j] = new GaussianParameters(tempMu, tempSigma);
			} // Of for j
		} // Of for i
		
		System.out.println(Arrays.deepToString(gaussianParameters));
	} // Of calculateGaussianParameters

2.3 选择数据类型

根据给定的类型调用给定的方法,包含符号型和数值型两种,给出其他输入返回 − 1 -1 1

	/**
	 ***************************
	 * Classify an instances.
	 ***************************
	 */
	public int classify(Instance paraInstance) {
		if(dataType == NOMINAL) {
			return classifyNominal(paraInstance);
		} else if (dataType == NUMERICAL) {
			return classifyNumerical(paraInstance);
		} // Of if
		
		return -1;
	} // Of classify

2.4 分类

根据前文的公式,将每条数据分别指派到各类,属于哪一类的概率最高,则将数据分为此类。

	/**
	 ***************************
	 * Classify an instances with numerical data.
	 ***************************
	 */
	public int classifyNumerical(Instance paraInstance) {
		//Find the biggest one
		double tempBiggest = -10000;
		int resultBestIndex = 0;
		
		for(int i = 0; i < numClasses; ++i) {
			double tempPseudoProbability = Math.log(classDistributionLaplacian[i]);
			for(int j = 0; j < numConditions; ++j) {
				double tempAttributeValue = paraInstance.value(j);
				double tempSigma = gaussianParameters[i][j].sigma;
				double tempMu = gaussianParameters[i][j].mu;
				
				tempPseudoProbability += - Math.log(tempSigma) - (tempAttributeValue - tempMu) * (tempAttributeValue - tempMu) / (2 * tempSigma * tempSigma);
			} // Of for j
			
			if(tempBiggest < tempPseudoProbability) {
				tempBiggest = tempPseudoProbability;
				resultBestIndex = i;
			} // Of if
		} // Of for i
		
		return resultBestIndex;
	} // Of classifyNumerical

2.5 数据测试

iris 数据集来进行测试

	/**
	 ***************************
	 * Test numerical data.
	 ***************************
	 */
	public static void testNumerical() {
		System.out.println("Hello, Naive Bayes. I only want to test the numerical data with Gaussian assumption.");
		String tempFilename = "G:/Program Files/Weka-3-8-6/data/iris.arff";
		
		NaiveBayes tempLearner = new NaiveBayes(tempFilename);
		tempLearner.setDataType(NUMERICAL);
		tempLearner.calculateClassDistribution();
		tempLearner.calculateGaussianParameters();
		tempLearner.classify();
		
		System.out.println("The accuracy is: " + tempLearner.computeAccuracy());
	} // Of testNumerical
	
	/**
	 ***************************
	 * Test this class.
	 *
	 * @param args Not used now.
	 ***************************
	 */
	public static void main(String[] args) {
		testNumerical();
	} // Of main
} // Of class NaiveBayes

测试结果如下,准确度 0.96 0.96 0.96
在这里插入图片描述

你可能感兴趣的:(算法,java,概率论)