Naive Bayes就不做具体介绍了,很简单的一个模型。直接看代码
BuildClassify:
// can classifier handle the data?
getCapabilities().testWithFail(instances);
// remove instances with missing class
instances = new Instances(instances);
instances.deleteWithMissingClass();
m_NumClasses = instances.numClasses();
// Copy the instances
m_Instances = new Instances(instances);
// Discretize instances if required
if (m_UseDiscretization) {
m_Disc = new weka.filters.supervised.attribute.Discretize();
m_Disc.setInputFormat(m_Instances);
m_Instances = weka.filters.Filter.useFilter(m_Instances, m_Disc);
} else {
m_Disc = null;
}
前面还是老样子,预处理下。后面就是把样本属性值离散化一下。具体方法参考
http://ijcai.org/Past%20Proceedings/IJCAI-93-VOL2/PDF/022.pdf
// Reserve space for the distributions
m_Distributions = new Estimator[m_Instances.numAttributes() - 1]
[m_Instances.numClasses()];
m_ClassDistribution = new DiscreteEstimator(m_Instances.numClasses(),
true);
m_Distributions存放的是P(C|x), m_Distributions存放的是P(C).
int attIndex = 0;
Enumeration enu = m_Instances.enumerateAttributes();
while (enu.hasMoreElements()) {
Attribute attribute = (Attribute) enu.nextElement();
// If the attribute is numeric, determine the estimator
// numeric precision from differences between adjacent values
double numPrecision = DEFAULT_NUM_PRECISION;
if (attribute.type() == Attribute.NUMERIC) {
m_Instances.sort(attribute);//按attribute将数据进行排序(从小到大)
if ((m_Instances.numInstances() > 0)
&& !m_Instances.instance(0).isMissing(attribute)) {
double lastVal = m_Instances.instance(0).value(attribute);//该属性的最小值
double currentVal, deltaSum = 0;
int distinct = 0;
for (int i = 1; i < m_Instances.numInstances(); i++) {
Instance currentInst = m_Instances.instance(i);
if (currentInst.isMissing(attribute)) {
break;//已经排好序,缺失放最后,意味着后面的数据该属性都缺失
}
currentVal = currentInst.value(attribute);//当前样本对应属性的值
//如果不是最小-lastVal
if (currentVal != lastVal) {
deltaSum += currentVal - lastVal;//deltaSum记录了最大值和最小值的差
lastVal = currentVal;
distinct++;//numeric的时候,该属性的不同取值个数
}
}
if (distinct > 0) {
numPrecision = deltaSum / distinct;
}
}
}
上面这一段是为了得到numPrecision, 参考代码中的注释,可以知道,上面的deltaSum是该属性的最大差值。
这个numPrecision会在后面构造estimator的时候用到,如果是连续的属性,可以理解成分区的一个参数。
接着往下看
for (int j = 0; j < m_Instances.numClasses(); j++) {
switch (attribute.type()) {
case Attribute.NUMERIC:
if (m_UseKernelEstimator) {
m_Distributions[attIndex][j] =
new KernelEstimator(numPrecision);
} else {
m_Distributions[attIndex][j] =
new NormalEstimator(numPrecision);
}
break;
case Attribute.NOMINAL:
m_Distributions[attIndex][j] =
new DiscreteEstimator(attribute.numValues(), true);
break;
default:
throw new Exception("Attribute type unknown to NaiveBayes");
}
}
attIndex++;
如果属性是连续的,再选择用kernel的还是normal的。 这里的构造需要用到numPrecision. 具体的这个estimator如何构造,参考weka.estimator这个package。
如果属性是离散的,直接调用离散的estimator。
这里的这个attIndex,对应的是最外层while循环的。
// Compute counts
Enumeration enumInsts = m_Instances.enumerateInstances();
while (enumInsts.hasMoreElements()) {
Instance instance =
(Instance) enumInsts.nextElement();
updateClassifier(instance);
}
下面就是增量式学习了,每个样本调用updataClassifier更新结果。
public void updateClassifier(Instance instance) throws Exception {
if (!instance.classIsMissing()) {
Enumeration enumAtts = m_Instances.enumerateAttributes();
int attIndex = 0;
while (enumAtts.hasMoreElements()) {
Attribute attribute = (Attribute) enumAtts.nextElement();
if (!instance.isMissing(attribute)) {
m_Distributions[attIndex][(int)instance.classValue()].
addValue(instance.value(attribute), instance.weight());
}
attIndex++;
}
m_ClassDistribution.addValue(instance.classValue(),
instance.weight());
}
}
这里很清楚,我们每次更新的是
1.m_Distribution: 对应的是P(x|C)
2.m_ClassDistribution: 对应的是P(C)
最后,作为总结的一句话,Naive Bayes要求每个属性独立,这个条件太强,导致他的效果不是很好。下一篇看看Bayes Network。