3.3.4 数据测试类
数据测试类接受一个模型 obj,一个属性名列表 Attr_Name,一个测试数据(属性值)列表 TestData,这三个参数用于测试数据;另外由于需要对外开放测试结果,还接受一个字符串变量 line 用于递归返回最终结果。下面是 TestData.java 的定义:
public static String TestData(Object obj, Object[] Attr_Name, Object[] TestData,String line) {
基于既定的决策树模型,从根节点出发,根据传入的测试数据选择分支,一路向下,直到抵达叶节点,或者无法找到向下的分支为止。流程解释如下:
判断传入的 obj 类型是否为 Tree,如果不为 Tree,代表着已经到了叶节点,可以根据当前节点的值来找到对应的分类;
如果 obj 是 Tree 类型,找到 obj 对应的属性名,然后根据属性名找到测试数据的值,同时新建立一个未曾使用的属性名列表和对应的测试数据列表(长度等于原来的列表减一);
遍历当前节点的每个分支,找到属性值等于测试数据的那一条;
将分支对应的子树以及(2)中建立的两个新列表,还有 line 传入新的 TestData()中,递归调用,返回 line;
数据测试类被定义为静态类,所以可以直接在 GUI 后台程序中调用而不用考虑是否定义实例。在 GUI 中的命令处理环节,有两个部分运用到了 TestData()这个方法,一个是 test,这个命令可以单独对一组数据进行测试;而另外一个 autotest 则是对已经加载好的数据测试文件进行解读后,直接批量测试,最后得出准确率,结合 SVM 下的准确率一起显示在消息提示栏。每一次测试数据的整体流程图如下:
图 15 数据测试流程
3.4 支持向量机实现
SVM 由于其复杂性较高,而且网络上已经有了相当成熟的软件包可以直接取用,所以我最后借鉴了台湾大学林智仁教授的 LibSVM 包中的 Java 部分。
这个包的对外内容由四个,分别是 svm_toy,svm_scale,svm_train,svm_predict。我用到的是后面两个 svm_train 和 svm_predict。其中 svm_train 是训练模型用到的实现代码,svm_predict 是在实际使用过程中使用模型进行数据分类的实现代码。
为了配合 GUI 的显示,将这些内容整合到了一个类:ZZB_SVM.java 中,确保可以全面的调用这些包内的方法并且返回需要的数值。代码如下:
import java.io.IOException; import java.text.NumberFormat; public class ZZB_SVM { public static Float main() throws IOException { SVMReadData sr = new SVMReadData(); Parameter par = new Parameter(); String trainFileName = sr.readTrainData(par); String testFileName = sr.readTestData(par); //训练使用的数据以及训练得出生成的模型文件名。 String[] trainFile = { trainFileName, "model.txt" }; //测试数据文件,模型文件,结果存放文件 String[] predictFile = { testFileName, "model.txt","predict.txt" }; System.**out**.println("........SVM Start.........."); long start=System.**currentTimeMillis**(); svm_train.**main**(trainFile); //训练 System.**out**.println("Usage of Time : "+(System.**currentTimeMillis**()-start)); //预测 float x = svm_predict.**main**(predictFile); return x; }
该类的 main()方法最终将返回一个测试正确率的浮点数。静态方法 main()的调用位置在人机交互界面类 GUI.java 中调用并且显示。
在对 LibSVM 的使用过程中,均使用默认的选项进行模型训练。下面是对于 SVM 分类建模过程有较大影响的参数解释:
SVM 类型设置(SVM type):默认为 C-支持向量分类机,参数 C 是惩罚系数,C 的数值与对错误分类的惩罚成正比例关系。因此,惩罚系数对于模型预测的精度有较大的影响;
核函数设置(kernel type):默认为径向基函数 (Radial Basis Function,简称为 RBF 函数),在本次试验中采用高斯核函数,见表 2-1 中第三条;
gamma:核函数中的 gamma 函数设置,此背景下默认为 0.25;
eps:允许的终止判据,类似迭代精度(默认 0.001)
shrinking:是否使用启发式,0 或 1(默认为 1)
3.5 人机交互界面设计
人机交互界面采用一个名为GUI.java的类来实现,其采用Java的awt和Swing两个专用于GUI编程的自带库来实现各个组件。在ZZB_JCS.java的main方法中定义一个GUI.java的实例化对象后,其以线程的方式独立于主线程存在,不过在主线程运行完毕之前会将构建的决策树模型以及其他一些会在人机交互中用到的变量,通过定义的静态方法传入到GUI的实例对象中,从而实现从后台到前台的过渡。
GUI 整体由一个大框架,12 个 Label 文字标签、1 个文字输入栏,3 个按钮组成。初步效果如下:
图 15 人机交互界面展示
另外配有两个菜单栏提供功能显示:
图 16 菜单栏
3.5.1 界面组件介绍
在主程序的后半段,会将决策树算法处理数据后生成的决策树模型传入到 GUI 线程实例中,然后在此基础上,将决策树模型分段在 GUI 上展示。Line1-Line10 就是用于展示决策树模型的。最上方的“This is the Code Line for Command!”作为提示,表示下面的输入框的作用。在文本输入框中键入对应的指令即可调用不同的系统命令执行包括测试数据,自动加载测试数据,退出,决策树展示换行等不同的功能。部分命令按钮会在首次加载 GUI 的时候以弹窗形式告知。
图 16 初始化人机交互指令提示弹窗
按照提示,在命令框中输入“HELP”会显示所有的命令:
图 17 help 命令弹窗
底部的两个按钮则是用于查看决策树模型的辅助按钮。Button-CLEAR 用于直接清空 Label 的输出,恢复到模型展示初始状态,也就是初始化的时候的样子,效果等同于在命令输入框输入 clear 这条指令。而 Button-NEXT 则是等同于在命令框输入 next 这条指令的效果,调取下一个十行决策树模型展示。
在最底部还有一个Label,这个Label用于响应如test,Load等无法直接在界面组件上展示效果的各种命令。如果命令错误,那么在底部会显示Error报错;如果是进行数据测试,那么会在最后这一条Label上展示出结果;如果是如加载数据的“Load”指令,则会在底部显示加载结果。
3.5.2 操作命令介绍
下面解释下输入框能够接受的几条定义的命令:
init:初始化决策树输出,内部加载数据,在 init 执行前所有的其他命令都无法操作;
clear:清屏效果,将 Line1-Line10 以及底部提示栏还原为初始状态;
next:读取下一个十行决策树内容显示在界面上;
last: 读取上一个十行决策树内容显示在界面上;
test [data(value1 value2 value3 …)]:调用决策树模型测试输入的数据:数据格式如上。如果没有在命令框中输入数据,那么系统会查询是否已经加载了外部数据,如果有,则自动读取外部数据,否则按照预设的数据(固定的示例)进行计算演示;其结果输出到底部消息提示栏的为分类结果;
load: 效果类似于菜单栏的 open
autoload:自动加载测试数据;搜索当前目录下的测试数据文本,如果要跨越目录可以使用左上角的菜单栏->Open,选择文件读入或者在命令框中输入“LOAD”加载文件读取窗口;
autotest:自动将目前存储的所有测试数据进行分类预测,并且统计正确率。同时调用 ZZB_SVM.main()测试 SVM 下的结果,将两种模型准确度输出到底部消息提示栏;
save:将当前的决策树存入到指定文件夹,效果等同于菜单栏中的 save 选项;
help:输入后可以跳出弹窗显示所有命令提示;
showinfo:显示当先使用者的信息,包括工作目录、使用者、操作系统、训练集大小、测试集大小、模型输出行数、页面数等;
exit:退出 GUI 界面,效果等同于默认的退出按钮或菜单栏的 exit 选项;
settrainnum:设置训练集大小,后必须跟一个整形参数,另外重置后必须再次进行初始化,否则会报错;
settestnum:设置测试集大小,后必须跟一个整形参数,重置后也必须进行初始化操作。
3.5.3 适用场景
此 GUI 界面适用于智能工厂环境,,因为需要预装数据库,Java8 软件,所以需要电脑的性能较好,才能保证我们能够迅速得到模型并且初始化 GUI 界面。另外 GUI 界面需要标准键盘输入才能与之交互,如果可以使用一些实物按钮来封装命令或者对应着 GUI 界面上的某些按钮将会有较好的效果。
每一次运行都是从主程序开始运行。因为该模型属于数据驱动方式,所以每一次模型的建立都依赖于历史运行数据。同时测试数据的时候也需要在本地读取一份需要测试的数据集,而且要根据相应的格式存储。所以我们需要用 PLC、Arduino 或者树莓派等控制器接受来自传感器的数据之后,进行简单的处理再存储到本地或者是串口发送到 PC 端。当然也可以将这份数据处理的工作放到运行程序的 PC 上,不过这样的话负载不够均衡。
在我的设计中,由于无法进行实物制作,所以 PC 需要从 MySQL 动态读取数据,如果直接使用的话,中间还存在一个数据转储的问题,这对于网络资源、运算能力都是极大地浪费。所以在实际的操作中完全可以用串口或者是局域网将控制器和 PC 进行连接,然后由 PC 端接收后直接使用即可。
图 18 应用场景示意图
另外,我们也可以将所有程序打包发布为 EXE 可执行文件,安装到本地后直接使用而不用去部署环境,而且可以根据工厂里面的设备导出不同类型的可执行文件,适配于大部分的生产场合。
4 性能分析
4.1 性能度量
本次设计采用了两套方案,我们就需要评估方法来对性能进行评估以便我们选择更好的模型构建方式。性能测试采用“测试集”来测试模型对于新样本的分类能力。最后以测试集上的误差来作为实际误差的近似。
测试集的获取的方式有几种,本次采用“留出法”。即将一个样本集 D 划分为两个,一个作为模型训练集 S,一个作为模型测试集 T,满足 D=S∪T 且 S∩T=,常见的划分方式为分层抽样数据后取出三分之一到五分之一作为测试集。这一点模型通过 Parameter 这个类中的静态变量来定义。
private static int rate = 2;
private static int trainNum = 20000;
private static int testNum = trainNum/rate;
所有的数据数量都来自于这个类中的静态变量。
在 GUI 界面中提供了 autotest 进行批量的数据测试,最终结果显示在消息栏。里面有 DecisionTree 的精度和 SVM 的精度表示。而由【错误率 + 精度=1】就可以知道错误率。
另外,还引入查准率(precision)P 和查全率(recall)R 的概念。对于二分类问题,分类结果的混淆矩阵和上述两种概念的定义如下:
表 3-1 混淆矩阵
查全率 P 定义:
查准率 R 定义:
4.2 决策树性能度量
决策树的精度对数据数量很敏感,数据量少的时候精度较低,数据量提升之后精度会随之提高。而 SVM 则对数据的数量要求较低,在连续属性离散化的部分曾介绍过。下面是用各种大小的训练数据量下得出来的“数据量-精度”图:
图 20 决策树“数据量-精度”关系图
下图中分别显示了精度(Accuracy)、查全率(Precision)、查准率(Recall)在各种数据下的对应值。
图 21 三种度量与数据量的关系图
从两图分析可知:
整体上决策树的预测结果精度与数据量有关,据趋势可以看出来,数据量越大,预测精度就会越高;
数据量与查全率的关系不大,查全率是尽可能将更多的情况考虑到的数值度量;
可以看到查准率随着数据量的提高甚至略有下降趋势,查准率表示对预测结果正确性要求的度量;
在数据量较大的时候,查全率与查准率二者之间会产生矛盾。如果希望将大部分的正例都选出来,那么可以通过提高选取的样本数量提高来实现,但是这样的话就会使得查准率降低,如我们上面图形中所示;而如果希望查准率高,那么就尽量让选中正例的范围变大,因为我们有连续属性离散化的过程,所以当数据较多时,离散化区间较多,区间细分程度更高,以至于查准率会稍有降低。
4.3 支持向量机性能度量
支持向量机的性能度量主要是精度,在运行时间上 SVM 比决策树同等数据量规模下所花费的时间更多,所以主要对比二者之间的精度。
相比于决策树,SVM 显然受数据量的规模影响小得多,而且 SVM 由于其本身性质,对于错误数据的敏感程度取决于惩罚系数的大小。所以哪怕错误的数据不是很多,在很多情况下也会造成较大的影响。
下面是 SVM 的精度与数据量的关系图:
图 21 SVM “数据量-精度”图
从图中分析,对比决策树从 30%~60% 的波动,SVM 50%~60% 的精度波动范围显然受数据量的影响较小,但是从整体趋势上看,SVM 的精度也会随着数据量的增大微弱的提高。