数值型数据是连续的,这就需要对贝叶斯分类器中条件概率的那部分表达式进行修改。已知符号型数据的贝叶斯分类器如下
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)j∏P(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 ),此时比较条件属性在各个取值点邻域范围内的概率大小和比较它们在概率密度函数中的取值大小是一致的。因此就可以概率密度函数的取值来替换掉条件概率。
正态分布在实际中是最常见的,也可以采用其他的分布。正态分布的概率密度函数如下:
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 σ,通过训练集数据,我们可以计算出这两个参数,从而得到正态分布,并假设测试集中的数据也同样符合此正态分布。
确定分布之后,我们直接用概率密度函数替代条件概率,即得
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)j∏p(x(j))=argckmaxP(Y=ck)j∏2πσ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)+j∑−logσ−2σ2(x(j)−μ)2
只给出相较于符号型数据的 N a i v e B a y e s Naive\ Bayes Naive Bayes 算法所增加的内容。
用于处理高斯分布 ( 即正态分布 ) 的两个参数,均值 μ \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
这里用一个二维矩阵 gaussianParameters 来存储高斯分布参数,该矩阵的第 i i i 行第 j j j 列表示,第 i i i 类的第 j j j 个属性的概率密度分布。
对于每一个分布的获取,分为以下几步
/**
***************************
* 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
根据给定的类型调用给定的方法,包含符号型和数值型两种,给出其他输入返回 − 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
根据前文的公式,将每条数据分别指派到各类,属于哪一类的概率最高,则将数据分为此类。
/**
***************************
* 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
用 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