Weka中贝叶斯网络学习情况小结.docx
《Weka中贝叶斯网络学习情况小结.docx》由会员分享,可在线阅读,更多相关《Weka中贝叶斯网络学习情况小结.docx(33页珍藏版)》请在冰豆网上搜索。
Weka中贝叶斯网络学习情况小结
Weka中贝叶斯网络学习情况小结
Weka中对于贝叶斯网络的学习,仅仅看相关的几个包几乎是不可能的,结果还是一叶障目不见泰山。
后来发现就那些代码死磕根本不行,还得采取灵活的方式方法。
一方面采用学习人家博客的总结,逐步梳理weka中那些类的组织和功能函数的情况。
一方面下载了一个weka源代码分析的中文翻译包,能够从更加宽的领域理解BN的相关重要函数以及操作。
随便找个类,比如instance和instances,比如evaluation,都是几百行,或者千余行,通读一遍都很费力,而且许多类有继承,许多地方用的是接口,很少有地方用抽象类。
各个类之间的关联较多,随便引用一下,在某个函数中出现一下,都是关系,本来以为UML图能够方便的解决问题,但是拖动以后发现,事情越高越多,线条也越来越多,实际上展示效力快速下降,到最后还是不能够说明问题。
刚开始研究原理,其实朴素贝叶斯和贝叶斯网络的基本原理并不复杂,小型网络手算是可以的,有点像矩阵,小矩阵加减乘除都没问题,但是大型矩阵就得想方设法用计算机、写代码来实现了。
数学上涉及的东西,写道计算机上就需要一些辅助的东西了。
特别是程序实现上。
事实上,学习概论的东西本身也要小心,一不留神还是会犯下各种错误。
在软件实现上,读入arff数据,构建ADTree,然后训练分类器,最后进行测试。
Analternatingdecisiontree(ADTree)isamachinelearningmethodforclassification.Itgeneralizesdecisiontreesandhasconnectionstoboosting.
Analternatingdecisiontreeconsistsofdecisionnodesandpredictionnodes.Decisionnodesspecifyapredicatecondition.Predictionnodescontainasinglenumber.ADTreesalwayshavepredictionnodesasbothrootandleaves.AninstanceisclassifiedbyanADTreebyfollowingallpathsforwhichalldecisionnodesaretrueandsumminganypredictionnodesthataretraversed.ThisisdifferentfrombinaryclassificationtreessuchasCART(Classificationandregressiontree)orC4.5inwhichaninstancefollowsonlyonepaththroughthetree.
http:
//en.wikipedia.org/wiki/ADTree这个网站给的例子可以好好理解一下、
weka在进行实例测试的时候也有许多术语和内容需要继续认识,比如recall,precision,confusionmatrix,以及其他指标。
评分函数(score),在软件中归在evaluation中。
CPT,也就是conditionalprobabilitytable也是建立网络的关键。
有一个说法:
Bays=Bs(DAG)+Bp(CPT)weka中的Beiyes有两个要求,一个是离散化的数据,另一个是数据的值不能是null整个学习的过程是先构建DAG在学习出CPT。
记录一点代码分析:
在buildClassifier函数中,重要的几行是:
//buildthenetworkstructure
initStructure();
//buildthenetworkstructure
buildStructure();
//buildthesetofCPTs
estimateCPTs();
函数initStructure为初始化网络结构,buildStructure为构造网络结构,estimateCPTs为计算条件概率表(conditionalprobabilitytable)。
/**
* Init structure initializes the structure to an empty graph
* or a Naive Bayes graph (depending on the -N flag).
*/
public void initStructure() throws Exception{
//reservememory
m_ParentSets = new ParentSet[m_Instances.numAttributes()];
for (int iAttribute=0;iAttribute m_Instances.numAttributes();
iAttribute++){
m_ParentSets[iAttribute]= new ParentSet(m_Instances
.numAttributes());
}
} //initStructure
m_ParentSets是记录下第i个属性(iAttribute)的父结点,ParentSet初始函数为:
public ParentSet(int nMaxNrOfParents){
m_nParents = new int[nMaxNrOfParents];
m_nNrOfParents =0;
m_nCardinalityOfParents =1;
} //ParentSet
不做什么,也就是一个空图。
接下来看buildStructure,它会调用SearchAlgorithm中的buildStructure:
/**
* buildStructure determines the network structure/graph of the network.
* The default behavior is creating a network where all nodes have
* the first node as its parent (i.e., a BayesNet that behaves like
* a naive Bayes classifier). This method can be overridden by derived
* classes to restrict the class of network structures that are acceptable.
*/
public void buildStructure(BayesNetbayesNet,Instancesinstances) throws Exception{
if (m_bInitAsNaiveBayes){
int iClass=instances.classIndex();
//initializeparentsetstohavearrowfromclassifiernodeto
//eachoftheothernodes
for (int iAttribute=0;iAttributeiAttribute++){
if (iAttribute!
=iClass){
bayesNet.getParentSet(iAttribute).addParent(iClass,
instances);
}
}
}
search(bayesNet,instances);
if (m_bMarkovBlanketClassifier){
doMarkovBlanketCorrection(bayesNet,instances);
}
} //buildStructure
这里会判断是不是初始化成朴素贝叶斯,如果不初始化为朴素贝叶斯,那么就还是空图,如果初始为朴素贝叶斯,则对于每个属性将类别属性加为父结点。
addParent的代码如下:
public void addParent(int nParent,Instances_Instances){
if (m_nNrOfParents ==10){
//reservemorememory
int[]nParents= new int[50];
for (int i=0;i< m_nNrOfParents;i++){
nParents[i]= m_nParents[i];
}
m_nParents =nParents;
}
m_nParents[m_nNrOfParents]=nParent;
m_nNrOfParents++;
m_nCardinalityOfParents *=_Instances.attribute(nParent)
.numValues();
} //AddParent
前面的if是预保留内存的代码,后面的是保存哪个属性是它的父结点,m_NrOfParent是父结点数,CardinalityOfParents是父结点所能取的所有属性值之和。
search函数的实现有很多,这里看K2的代码实现:
int nOrder[]= new int[instances.numAttributes()];
nOrder[0]=instances.classIndex();
int nAttribute=0;
for (int iOrder=1;iOrder if (nAttribute==instances.classIndex()){
nAttribute++;
}
nOrder[iOrder]=nAttribute++;
}
nOrder中类别属性下标为0,其实它属性顺序还是一样的。
//determinebasescores
double[]fBaseScores= new double[instances.numAttributes()];
for (int iOrder=0;iOrder int iAttribute=nOrder[iOrder];
fBaseScores[iAttribute]=calcNodeScore(iAttribute);
}
计算basescores,调用calcNodeScore函数:
public double calcNodeScore(int nNode){
if (m_BayesNet.getUseADTree()&& m_BayesNet.getADTree()!
= null){
return calcNodeScoreADTree(nNode);
} else {
return calcNodeScorePlain(nNode);
}
}
ADTree就暂时不去理会了,看calcNodeScorePlain函数:
//estimatedistributions
EnumerationenumInsts=instances.enumerateInstances();
while (enumInsts.hasMoreElements()){
Instanceinstance=(Instance)enumInsts.nextElement();
//updateClassifier;
double iCPT=0;
for (int iParent=0;iParent iParent++){
int nParent=oParentSet.getParent(iParent);
iCPT=iCPT*instances.attribute(nParent).numValues()
+instance.value(nParent);
}
nCounts[numValues*((int)iCPT)+(int)instance.value(nNode)]++;
}
这里的nCounts是文章BayesianNetworkClassifiersinWeka中第4页所提到的Nijk,这里是将i,j,k三维放到了一些,类别值是最后的instance.value(nNode)。
在calcNodeScorePlain函数中最后调用了calcScoreOfCount函数:
for (int iParent=0;iParent switch (m_nScoreType){
case (Scoreable.BAYES):
{
double nSumOfCounts=0;
for (int iSymbol=0;iSymbol if (m_fAlpha +nCounts[iParent*numValues+iSymbol]!
=0){
fLogScore+=Statistics.lnGamma(m_fAlpha
+nCounts[iParent*numValues+iSymbol]);
nSumOfCounts+= m_fAlpha
+nCounts[iParent*numValues+iSymbol];
}
}
if (nSumOfCounts!
=0){
fLogScore-=Statistics.lnGamma(nSumOfCounts);
}
if (m_fAlpha !
=0){
fLogScore-=numValues*Statistics.lnGamma(m_fAlpha);
fLogScore+=Statistics.lnGamma(numValues* m_fAlpha);
}
}
可以看BayesianNetworkClassifiersinWeka第6页中的Bayesianmetric中的公式,第一个for是计算Gamma(Nijk prime+Nijk)。
接下来是计算Gamma(Nijk prime+Nij),再将下来是计算Gamma(Nij prime)/Gamma(Nij prime+Nij)。
case (Scoreable.MDL):
case (Scoreable.AIC):
case (Scoreable.ENTROPY):
{
double nSumOfCounts=0;
for (int iSymbol=0;iSymbol nSumOfCounts+=nCounts[iParent*numValues+iSymbol];
}
for (int iSymbol=0;iSymbol if (nCounts[iParent*numValues+iSymbol]>0){
fLogScore+=nCounts[iParent*numValues+iSymbol]
*Math.log(nCounts[iParent*numValues
+iSymbol] /nSumOfCounts);
}
}
}
这里相应于BayesianNetworkClassifiersinWeka第5页的公式
(2)不同之处是没有N,因为它可以消掉。
switch (m_nScoreType){
case (Scoreable.MDL):
{
fLogScore-=0.5*nCardinality*(numValues-1)
*Math.log(instances.numInstances());
}
break;
case (Scoreable.AIC):
{
fLogScore-=nCardinality*(numValues-1);
}
break;
}
公式中的K=nCardinality*(numValues-1),N=instances.numInstances()。
见公式(3),MDL和AIC的计算见公式(5)。
// K2algorithm:
greedysearchrestrictedbyordering
for (int iOrder=1;iOrder int iAttribute=nOrder[iOrder];
double fBestScore=fBaseScores[iAttribute];
boolean bProgress=(bayesNet.getParentSet(iAttribute)
.getNrOfParents() while (bProgress){
int nBestAttribute=-1;
for (int iOrder2=0;iOrder2 int iAttribute2=nOrder[iOrder2];
double fScore=calcScoreWithExtraParent(iAttribute,
iAttribute2);
if (fScore>fBestScore){
fBestScore=fScore;
nBestAttribute=iAttribute2;
}
}
if (nBestAttribute!
=-1){
bayesNet.getParentSet(iAttribute).addParent(
nBestAttribute, instances);
fBaseScores[iAttribute]=fBestScore;
bProgress=(bayesNet.getParentSet(iAttribute)
.getNrOfParents() } else {
bProgress= false;
}
}
}
bProgress是判断iAttribute结点的父结点是否超过最大父结点数,代码逻辑大致是:
对每个属性(iOrder)得到它的父结点,找它的父结点是在[0-iOrder]中找,不然就循环了。
对于每个属性调用calcScoreWithExtraParent函数计算得到,如果它比以前的结分高,那它成为最好的属性,调用addParent加入。
public double calcScoreWithExtraParent(int nNode, int nCandidateParent){
ParentSetoParentSet= m_BayesNet.getParentSet(nNode);
//sanitycheck:
nCandidateParentshouldnotbeinparentsetalready
if (oParentSet.contains(nCandidateParent)){
return -1e100;
}
//setupcandidateparent
oParentSet.addParent(nCandidateParent, m_BayesNet.m_Instances);
//calculatethescore
double logScore=calcNodeScore(nNode);
//deletetemporarilyaddedparent
oParentSet.deleteLastParent(m_BayesNet.m_Instances);
return logScore;
} //Ca