之前的文章里写到过Classififer的基本作用,它是Weka中分类器都需要实现的抽象类。
这次的AbstractClassifier即实现了Classifier,但并没有实现全部方法,因此仍为抽象类。
此外该类还实现了若干个接口,简要介绍如下:
// Cloneable:提供了一个复制特定类下实例的方法,实现该接口需重写Object.clone
//Serializable:保证了类的可序列化,需实现java.io.Serializable 接口
//OptionHandler:对界面选项的描述
//CapabilotiesHandler:查看对象的适用范围
//RevisionHandler:返回修订信息
//CapabilitiesIgnorer:设定或取得适用范围信息
public abstract class AbstractClassifier implements Classifier, Cloneable,
Serializable, OptionHandler,
CapabilitiesHandler, RevisionHandler,
CapabilitiesIgnorer
//显式声明了序列化,java对象序列化不仅保留一个对象的数据,而且递归保存对象引用的每个对象的数据。序列化运行时使用一个称为 serialVersionUID 的版本号与每个可序列化类相关联。学习自:http://blog.csdn.net/applehoney/article/details/2278086
private static final long serialVersionUID = 6502780192411755341L;
//是否在调试模式下运行
protected boolean m_Debug = false;
//是否预先检查分类器适用范围
protected boolean m_DoNotCheckCapabilities = false;
//作用:对测试实例进行分类
//参数:被分类的实例 返回值:实例被预测的分类或Utils.missingValue()(分类不成功)
//抛出异常:如果预测时发生错误
@Override
public double classifyInstance(Instance instance) throws Exception {
//调用distributionForInstance()方法
double[] dist = distributionForInstance(instance);
if (dist == null) {
throw new Exception("Null distribution predicted");
}
switch (instance.classAttribute().type()) {
case Attribute.NOMINAL:
double max = 0;
int maxIndex = 0;
//得到最大的概率,即最可能的分类类别
for (int i = 0; i < dist.length; i++) {
if (dist[i] > max) {
maxIndex = i;
max = dist[i];
}
}
if (max > 0) {
return maxIndex;
} else {
return Utils.missingValue();
}
case Attribute.NUMERIC:
case Attribute.DATE:
return dist[0];//dist[0]中放置了分类信息
default:
return Utils.missingValue();
}
}
//作用:预测实例的类关系
//返回值:一个包含测试实例在每一个可能分类上的分类概率的数组或者一个数值型的预测
//抛出异常:如果概率不能被成功计算
@Override
public double[] distributionForInstance(Instance instance) throws Exception {
double[] dist = new double[instance.numClasses()];//得到instance类标号的数目并作为数组长度
switch (instance.classAttribute().type()) {//以整形来判断属性的类型
case Attribute.NOMINAL://枚举型
double classification = classifyInstance(instance);//返回instance的分类
if (Utils.isMissingValue(classification)) {
return dist;//全0数组
} else {
dist[(int) classification] = 1.0;//最可能分类位为1,其余为0的数组
}
return dist;
case Attribute.NUMERIC:
case Attribute.DATE:
dist[0] = classifyInstance(instance);//数字或日期型,直接将分类号放置在数组0位
return dist;
default:
return dist;
}
}
//设立一个自定义的分类器,给出名字及操作名称
//参数:分类器名称
//参数:操作名称
//返回:可以使用的分类器
//抛出异常:分类器名称非法,或操作名格式不符不能被接收
public static Classifier forName(String classifierName, String[] options)
throws Exception {
return ((AbstractClassifier) Utils.forName(Classifier.class,
classifierName, options));
}
//使用序列化对分类器进行深拷贝(深、浅拷贝的区别学习自http://www.cnblogs.com/haiyang1985/archive/2009/01/13/1375017.html)
//参数model:将要拷贝的分类器
//返回:特定分类器的深拷贝
//抛出异常:如果发生错误
public static Classifier makeCopy(Classifier model) throws Exception {
return (Classifier) new SerializedObject(model).getObject();
}
//作用:用序列化的方法对给定的分类器进行给定数量的深拷贝。
//参数 model:被拷贝的分类器
//参数 num:拷贝的数量
//返回:分类器的数组
//异常:错误发生时
public static Classifier[] makeCopies(Classifier model, int num)
throws Exception {
if (model == null) {
throw new Exception("No model classifier set");
}
Classifier[] classifiers = new Classifier[num];
SerializedObject so = new SerializedObject(model);
for (int i = 0; i < classifiers.length; i++) {
classifiers[i] = (Classifier) so.getObject();
}
return classifiers;
}
//作用:返回对操作描述的枚举
//返回:可用操作的枚举
@Override
public Enumeration<Option> listOptions() {
Vector<Option> newVector = new Vector<Option>(2);
newVector.addElement(new Option(
"\tIf set, classifier is run in debug mode and\n"
+ "\tmay output additional info to the console", "output-debug-info",
0, "-output-debug-info"));
newVector
.addElement(new Option(
"\tIf set, classifier capabilities are not checked before classifier is built\n"
+ "\t(use with caution).", "-do-not-check-capabilities", 0,
"-do-not-check-capabilities"));
return newVector.elements();
}
//作用:依照给定的操作运行分类实例
//参数:分类器名
//参数:操作名称
public static void runClassifier(Classifier classifier, String[] options) {
try {
System.out.println(Evaluation.evaluateModel(classifier, options));//输出验证后的结果
} catch (Exception e) {
if (((e.getMessage() != null) && (e.getMessage().indexOf(
"General options") == -1))
|| (e.getMessage() == null)) {
e.printStackTrace();
} else {
System.err.println(e.getMessage());
}
}
}
}
public static String evaluateModel()函数比较复杂,含有大量的判断语句,涉及到模型验证的许多参数,在这里我们暂且搁置。
AbstractClassifier类中其余的是一些简单的getter和setter,还有一些提示语句,就不列出来了。虽然还没有接触到具体的分类功能,程序已经变得非常复杂,学习的过程中,也不能忘了向Weka的作者们致敬,感谢你们无私的付出!