基于Weka平台,对基于差分演化算法的属性加权朴素贝叶斯算法的研究
个人兴趣,希望对大家有所帮助
文章目录
前言
朴素贝叶斯算法因其分类精度高、模型简单等优点而被得到普遍应用,但因为它需要具备很强的属性之间的条件独立性假设,使得其在实际分类学习中很难实现。针对这个缺点,提出了一种基于差分演化算法的加权朴素贝叶斯分类算法 (DE_AWNB) 。该算法将差分演化算法将差分演化算法(DE)与属性加权的朴素贝叶斯算法相结合,以随机的方式产生初始种群,以分类正确率为适应度函数,进行逐代的优选,最终将总的迭代过程中适应度函数值最高的一组权值,应用到属性加权中,构建朴素贝叶斯分类器。实验表明,基于差分演化算法的属性加权朴素贝叶斯的分类能力略高于朴素贝叶斯分类器。
一、引言
在模式识别中各种分类算法中,朴素贝叶斯算法因模型简单,计算高效以及计算精度高等特点被广泛应用。然而,由于朴素贝叶斯的条件独立假设在实际应用中比较难满足,针对朴素贝叶斯的缺点,众多学者通过研究学习贝叶斯网络来改进其性能。国外学者Zhang Harry中提出了5种加权朴素贝叶斯算法( weighted native bayes,简称 WNB) ,分别针对不同的方向,评价每个类属性对分类的影响程度,给类属性赋予不同的权重,该算法不仅保留了朴素贝叶斯的分类精度高的优点,又削弱了类属性条件独立性假设,在一定程度上优化了朴素贝叶斯算法的性能,实验证明爬山法和 Monte Carlo 相结合的权值求解很大程度上提高了分类器的性能。
事实上,根据经验可知,基于演化算法的方式来求解最优的属性权值,是一种理论上有效、可行的方式。因此,我将在本文中对基于差分演化算法的属性加权的朴素贝叶斯分类器进行研究。我的大致思想为,将n个属性权值(范围为0-1)构成的n维向量作为个体,随机产生100个个体构成初始种群,分类的正确率作为适应度函数,采用差分演化算法,不断迭代,最终将后代中适应度函数最高的个体作为得到的最优权值,用以提高模型的准确率。并利用weka平台的数据集进行实验。
二、理论模型与构建思路
1.差分演化算法
差分演化算法是演化算法的一种,是一类基于群体的随机优化算法,主要采用实数编码,用于求解实数优化问题。差分演化算法的主要算子为差分变异算子,该算子利用不同个体间的差分信息,在搜索方向和搜索步长上具有自适应性,将同一群体中两个个体向量进行差分和缩放,并与该群体中第三个个体向量相加得到一个变异个体向量;然后变异个体向量与父个体向量进行适应值比较,较优者保存在下一代群体中。这样,差分演化算法利用差分变异、杂交和选择等算子对群体不断进行演化,直到达到终止条件退出。该算法具有结构简单、容易使用、收敛快和鲁棒性强等优点。这些优点使算法得到了广泛的应用,如模式识别、数据挖掘等。
2.朴素贝叶斯分类算法
(上述图片,为本人参考西瓜书整理所编,由于公式不容易编辑,直接给大家奉献截图)
三、基于差分演化算法的属性加权算法
1.属性加权朴素贝叶斯
由于朴素贝叶斯条件独立性的假设在实际应用,有学者为了弱化其属性条件独立性假设的影响,根据属性对分类的重要程度大小给属性赋予相应的权重,并提出了属性加权朴素贝叶斯模型(AWNB):
2.基于差分演化算法的属性加权朴素贝叶斯算法
改进的加权朴素贝叶斯分类器 AWNB 放松了朴素贝叶斯的条件独立性假设,在实际运用中得以满足。属性加权算法在某些数据集上表现很好,但却在另外数据集上的表现差强人意。目前学者 J Liu 将混合模拟退火和遗传算法相结合对属性集进行优化,提出基于演化算法的朴素贝叶斯算法,众多学者将演化算法与贝叶斯算法结合并取得较好成果。所以我们提出基于差分演化算法算法的加权朴素贝叶斯分类算法。
2.2.1编码方式
采用实数编码的方式,每条染色体有一组实数组成,每条染色体对应属性的权值,长度为数据集中属性的个数,每个实数位依次与每个属性的权值相对应。
2.2.2初始种群
采用随机的方式,以0到1之间的随机数为编码作为初始种群进行搜索。这种方式的优点在于种群的多样性比较强,防止陷入局部最优。
2.2.3适应度函数
适应度函数为贝叶斯分类器的分类正确率,程序中体现为函数public double Performance(Instances instances,double[] weight),其中instances指测试集实例,weight指我们的一个个体(染色体),即属性权值。函数将对所有实例的类标记进行预测,返回正确率。
2.2.4算法参数设置
差分演化算法的参数有种群规模、算法执行的最大代数、交叉概率、缩放因子等。使用的参数如下:种群规模NP=50,最大代数generation=100,交叉概率CR=0.7,缩放因子F随算法的代数进行自适应改变,自适应算子为指数变化,使得F从0.7指数降低至0.35,防止早熟。
2.2.5算法原型
本文通过参考Jiang给出的基于演化算法的属性选择算法的原型,得到了如表2.2所示的基于差分演化算法的属性加权算法。
3.实验结果
将提出的基于差分演化算法的属性加权朴素贝叶斯分类器应用到6个Weka自带的数据集中,在weka平台中验证其改进效果。6个数据集分别如下:breast-cancer、contact-lenses、vote、weather、label。对其中的数值型属性用weka平台中的有导师过滤器Discretize进行离散化。并设置训练集占66%,测试集占34%。
运用我们编写的基于差分演化算法属性加权的朴素贝叶斯分类器,对每个数据集分别进行5次实验(weka平台的运行结果截图在附录中展示;实际上,为了实验的准确性,应进行数十次实验以保证避免结果的偶然性;这里为了方便展示数据,仅记录五次)。记录下分类正确率并计算平均值,以及weka自带的朴素贝叶斯分类器得到的结果,如表3.1所示:
为了能够直观的展示基于差分演化算法属性加权的朴素贝叶斯分类器与朴素贝叶斯的分类能力,我们将两种分类器在各数据集上的分类正确率进行比较,如图3.1所示:
由上图可知,DE_AWNB的分类能力只是略高于NB,其中在breast-cancer、contact-lenses、vote、weather四个数据集的分类正确率高于NB,在iris-2d和label数据集中低于NB。总体上来说,基于差分演化算法的朴素贝叶斯分类器的分类能力还是略优于朴素贝叶斯。
附录
代码如下:
package weka.classifiers.mfh;
import weka.core.*;
import java.util.Iterator;
import java.util.Random;
import org.netlib.util.doubleW;
import java_cup.internal_error;
import weka.classifiers.*;
public class EAB extends AbstractClassifier {
private double [][] m_ClassAttCounts;
private double [] m_ClassCounts;
private int [] m_NumAttValues;
private int [] m_StartAttIndex;
private int m_TotalAttValues;
private int m_NumClasses;
private int m_NumAttributes;
private int m_NumInstances;
private int m_ClassIndex;
private double [][] Weights; //权重种群矩阵
public double [] fbest; //最优个体
private double fbest_fitness; //最优适应值
public int NP =50; /*种群个体数量*/
public int generation =100;//迭代次数
public double F=0.5;//缩放因子
public double CR=0.5;//交叉概率
/*变异函数,参数为实例与属性权值构成的种群*/
public double[][] mutation(Instances instances,double[][] iter1) throws Exception{
double[][] v=new double[iter1.length][iter1[1].length];
/*选取三个不同的个体,构造差分向量*/
for(int i=0;i<NP;i++) {
int r1=(int)(Math.random()*NP);
while(r1==i)
r1=(int)(Math.random()*NP);//保证r1不与i相同
int r2=(int)(Math.random()*NP);
while(r2==r1||r2==i)
r2=(int)(Math.random()*NP);//保证r2不于r1、i相同
int r3=(int)(Math.random()*NP);
while(r3==r2||r3==r1||r3==i)
r3=(int)(Math.random()*NP);//保证r3不于前面三个向量相同
/*使差分向量始终指向适应值高的区域*/
if(Performance(instances,iter1[r2])>Performance(instances, iter1[r3]))
//在权值为r2时,分类准确率大于权值为r3的时候
for(int j=0;j<v[1].length;j++) {
v[i][j]=iter1[r1][j] + F*(iter1[r2][j]-iter1[r3][j]);
if(v[i][j]<0)v[i][j]=Math.random();
if(v[i][j]>1)v[i][j]=Math.random();
}
else //在权值为r3时,分类准确率大于权值为r2的时候
for(int j=0;j<v[1].length;j++) {
v[i][j]=iter1[r1][j] + F*(iter1[r3][j]-iter1[r2][j]);
if(v[i][j]<0)v[i][j]=Math.random();
if(v[i][j]>1)v[i][j]=Math.random();
}
}
return v;//返回差分向量
}
/*交叉函数,参数为属性权值的种群和差分向量*/
public double[][] cross(double[][] weight,double[][] v) throws Exception{
double[][] child = new double[v.length][v[1].length];//用于存储交叉后得到的子群
for(int i=0;i<NP;i++) {
for(int j=0;j<v[1].length;j++) {
/*如果得到的随机数小于0.7或随机交叉点与j相等,则进行交叉*/
if(Math.random()<=CR||(j==(int)(Math.random()*(v.length+1))))
child[i][j]=v[i][j];
else //否则,不交叉
child[i][j]=weight[i][j];
}
}
return child;
}
/*选择函数,输入为交叉得到的子群,初始种群和实例*/
public double[][] selection(double[][] child,double[][] weight,Instances instances) throws Exception{
/*一对一锦标赛,将分类准确率高的个体加入新的种群*/
for(int i=0;i<NP;i++)
if(Performance(instances,weight[i])<Performance(instances, child[i]))
weight[i]=child[i];
return weight;
}
/*计算分类准确率,输入为实例及属性权值*/
public double Performance(Instances instances,double[] weight) throws Exception
{
int realnum =0;//用于记录分类正确的个数
for(int num=0; num < instances.numInstances();num++) {
double [] probs = new double[instances.numClasses()];
Instance ins = instances.instance(num);
probs = predict_tmp(ins,weight);//记录ins在每个类的概率
int max=0;
for(int i1=0;i1<instances.numClasses();i1++)
{
if(probs[i1]>probs[max])max=i1;//记录可能性最大的类
}
int real_class = (int)ins.classValue();//读取ins真实的类标记
if(real_class==max)realnum++;//预测与实际相同,realnum++
}
return 1.0*realnum/instances.numInstances();
}
public void buildClassifier(Instances instances) throws Exception {
m_NumClasses = instances.numClasses();//类的数目
m_ClassIndex = instances.classIndex();//类在数组中的下标,定位用的
m_NumAttributes = instances.numAttributes();//属性的数目,使用时需要减1,因为里面包括类
m_NumInstances = instances.numInstances();//数据集的个数
m_TotalAttValues = 0;//用来计算每个属性的相对下标
m_StartAttIndex = new int[m_NumAttributes];//属性对应的下标
m_NumAttValues = new int[m_NumAttributes];//属性下的属性值的个数
/*计算每个属性开始的下标*/
for(int i = 0; i < m_NumAttributes; i++) {
if(i != m_ClassIndex) {
m_StartAttIndex[i] = m_TotalAttValues;
m_NumAttValues[i] = instances.attribute(i).numValues();
m_TotalAttValues += m_NumAttValues[i];
}
else {
m_StartAttIndex[i] = -1;
m_NumAttValues[i] = m_NumClasses;
}
}
m_ClassCounts = new double[m_NumClasses];//类标记的个数
m_ClassAttCounts = new double[m_NumClasses][10000];//每个类标记出现的次数
/*计算每个类标记在每个属性下的次数*/
for(int k = 0; k < m_NumInstances; k++) {
int classVal=(int)instances.instance(k).classValue();
m_ClassCounts[classVal] ++;
int[] attIndex = new int[m_NumAttributes];
for(int i = 0; i < m_NumAttributes; i++) {
if(i == m_ClassIndex){
attIndex[i] = -1;
}
else{
attIndex[i] = m_StartAttIndex[i] + (int)instances.instance(k).value(i);
m_ClassAttCounts[classVal][attIndex[i]]++;
}
}
}
/*数组,用来生成权值种群*/
double[] norm_array = new double[m_NumAttributes-1];
Weights = new double[NP][m_NumAttributes-1];//初始种群
/*构建初始种群*/
for(int num=0;num< NP;num++) {
for(int num2=0;num2<m_NumAttributes-1;num2++) {
double i = Math.random();
norm_array[num2] = i;
}
for(int num3=0;num3<m_NumAttributes-1;num3++) {
Weights[num][num3] = norm_array[num3];
}
}
fbest = new double[m_NumAttributes-1];
fbest_fitness = Double.NEGATIVE_INFINITY;
int n=0;
/*差分烟花算法求解最优解过程*/
for(int i=0;i<generation;i++) {
n=1;
double [][]v =new double[NP][m_NumAttributes-1];//用来存差分向量
v= mutation(instances, Weights);//变异
double [][]weight = new double[NP][m_NumAttributes-1];//用来存交叉后得到的种群
weight = cross(Weights, v);//交叉
Weights = selection(weight, Weights, instances);//一对一锦标赛选择,生成新种群
/*计算当前种群中最好的个体*/
double best_fitness=Performance(instances, Weights[0]);
double []best = Weights[0];
for(int j=1;j<NP;j++) {
if(Performance(instances, Weights[j])>best_fitness) {
best_fitness = Performance(instances, Weights[j]);
best=Weights[i];
}
}
/*如果当前种群的最好个体比全局最好个体好,则替换之*/
if(best_fitness>fbest_fitness) {
fbest_fitness=best_fitness;
fbest=best;
}
/*自适应算子,用来自适应改变缩放因子F*/
double suanzi = Math.exp(1 - generation / (generation + 1 - n));
F = 0.35 * Math.pow(2,suanzi);
}
}
public double [] distributionForInstance(Instance instance) throws Exception {
double [] probs = new double[m_NumClasses];
int[] attIndex = new int[m_NumAttributes];
for(int att = 0; att < m_NumAttributes; att++) {
if(att == m_ClassIndex)
attIndex[att] = -1;
else
attIndex[att] = m_StartAttIndex[att] + (int)instance.value(att);
}
for(int classVal = 0; classVal < m_NumClasses; classVal++) {
/*利用计算实例属于当前类的概率,运用拉普拉斯平滑,运用属性加权的公式计算概率*/
probs[classVal]=(m_ClassCounts[classVal]+1.0)/(m_NumInstances+m_NumClasses);
for(int att = 0; att < m_NumAttributes; att++) {
if(attIndex[att]==-1) continue;
probs[classVal]*=Math.pow((m_ClassAttCounts[classVal][attIndex[att]]+1.0)/(m_ClassCounts[classVal]+m_NumAttValues[att]),(fbest[att]));
}
}
Utils.normalize(probs); //将得到属于各个类标记的概率,进行标准化
return probs;
}
/*计算一个实例在当前权值下预测出的类标记*/
public double [] predict_tmp(Instance instance,double[] weight) throws Exception {
double [] probs = new double[m_NumClasses];
int[] attIndex = new int[m_NumAttributes];
for(int att = 0; att < m_NumAttributes; att++) {
if(att == m_ClassIndex)
attIndex[att] = -1;
else
attIndex[att] = m_StartAttIndex[att] + (int)instance.value(att);
}
for(int classVal = 0; classVal < m_NumClasses; classVal++) {
/*利用计算实例属于当前类的概率,运用拉普拉斯平滑,运用属性加权的公式计算概率,*/
probs[classVal]=(m_ClassCounts[classVal]+1.0)/(m_NumInstances+m_NumClasses);
for(int att = 0; att < m_NumAttributes; att++) {
if(attIndex[att]==-1) continue;
probs[classVal]*=Math.pow((m_ClassAttCounts[classVal][attIndex[att]]+1.0)/(m_ClassCounts[classVal]+m_NumAttValues[att]),(weight[att]));
}
}
return probs;
}
public static void main(String [] argv) {
runClassifier(new GAB(), argv);
}
}
2.实验截图
篇幅过大,仅列出breast-cancer数据集,大家可以自己去weka平台运行代码,希望对大家有所帮助