1. 决策树原理
数据挖掘中的分类主要包括基于决策树的分类、基于规则的分类、基于神经网络的分类、基于支持向量机的分类、基
于朴素贝叶斯的分类等。
机器学习中,决策树是一个预测模型,他代表的是对象属性与对象值之间的一种映射关系。树中每个节点表示某个对
象,而每个分叉路径则代表的某个可能的属性值,而每个叶结点则对应从根节点到该叶节点所经历的路径所表示的对
象的值。决策树仅有单一输出,若欲有复数输出,可以建立独立的决策树以处理不同输出。数据挖掘中决策树是一种
经常要用到的技术,可以用于分析数据,同样也可以用来作预测。
(1)Hunt算法
Hunt算法是许多决策树算法的基础,包括ID3、C4.5和CART。
(2)ID3算法
ID3算法的核心是在决策树各级结点上选择属性时,用信息增益(information gain)作为属性的选择标准,以使得在
每一个非叶结点进行测试时,能获得关于被测试记录最大的类别信息。
(3)C4.5算法
C4.5算法继承了ID3(Iterative Dichotomiser 3)算法的优点,并在以下几个方面对ID3算法进行了改进:
(4)CART算法
CART(Classification And Regression Tree)算法采用一种二分递归分割的技术,将当前的样本集分为两个子样本集,
使得生成的的每个非叶子节点都有两个分支。因此,CART算法生成的决策树是结构简洁的二叉树。采用一种二分递
归分割的技术,将当前的样本集分为两个子样本集,使得生成的的每个非叶子节点都有两个分支。因此,CART算法
生成的决策树是结构简洁的二叉树。
(5)SLIQ算法
SLIQ(super-vised learning in quest)算法对C4.5决策树分类算法的实现方法进行了改进,在决策树的构造过程中采用
了“预排序”和“广度优先策略”两种技术。
(6)SPRINT算法
为了减少驻留于内存的数据量,SPRINT算法(scalable parallelizable induction of decision trees)进一步改进了决策树
算法的数据结构,去掉了在SLIQ中需要驻留于内存的类别列表,将它的类别列合并到每个属性列表中。这样,在遍历
每个属性列表寻找当前结点的最优分裂标准时,不必参照其他信息,将对结点的分裂表现在对属性列表的分裂,即将
每个属性列表分成两个,分别存放属于各个结点的记录。
总结:除此之外,常见的决策树算法还有CHAID、Quest和C5.0等。ID3、C4.5和CART都采用贪心(即非回溯的)方
法,其中决策树以自顶向下递归的分治方式构造。大多数决策树归纳算法都沿用这种自顶向下方法,从训练元组集和
它们相关联的类标号开始构造决策树。随着树的构建,训练集递归地划分成较小的子集。
2. 实验数据weather.nominal.arff
@relation weather.symbolic
@attribute outlook {sunny, overcast, rainy}
@attribute temperature {hot, mild, cool}
@attribute humidity {high, normal}
@attribute windy {TRUE, FALSE}
@attribute play {yes, no}
@data
sunny,hot,high,FALSE,no
sunny,hot,high,TRUE,no
overcast,hot,high,FALSE,yes
rainy,mild,high,FALSE,yes
rainy,cool,normal,FALSE,yes
rainy,cool,normal,TRUE,no
overcast,cool,normal,TRUE,yes
sunny,mild,high,FALSE,no
sunny,cool,normal,FALSE,yes
rainy,mild,normal,FALSE,yes
sunny,mild,normal,TRUE,yes
overcast,mild,high,TRUE,yes
overcast,hot,normal,FALSE,yes
rainy,mild,high,TRUE,no
3. Weka实现
(1)Preprocess选项
(2)Classify选项
(3)Classifier output选项
=== Run information ===
Scheme:weka.classifiers.trees.Id3
Relation: weather.symbolic
Instances: 14
Attributes: 5
outlook
temperature
humidity
windy
play
Test mode:10-fold cross-validation
=== Classifier model (full training set) ===
Id3
outlook = sunny
| humidity = high: no
| humidity = normal: yes
outlook = overcast: yes
outlook = rainy
| windy = TRUE: no
| windy = FALSE: yes
Time taken to build model: 0.01 seconds
=== Stratified cross-validation ===
=== Summary ===
Correctly Classified Instances 12 85.7143 %
Incorrectly Classified Instances 2 14.2857 %
Kappa statistic 0.6889
Mean absolute error 0.1429
Root mean squared error 0.378
Relative absolute error 30 %
Root relative squared error 76.6097 %
Total Number of Instances 14
=== Detailed Accuracy By Class ===
TP Rate FP Rate Precision Recall F-Measure ROC Area Class
0.889 0.2 0.889 0.889 0.889 0.844 yes
0.8 0.111 0.8 0.8 0.8 0.844 no
Weighted Avg. 0.857 0.168 0.857 0.857 0.857 0.844
=== Confusion Matrix ===
a b <-- classified as
8 1 | a = yes
1 4 | b = no
解析:
(1)统计量
(2)相关术语
4. 信息熵概念
解析:信息熵方程,如下所示:
Entropy = 系统的凌乱程度,使用算法ID3,C4.5和C5.0生成树算法使用熵,这一度量是基于信息学理论中熵的概念。
5. ID3决策树算法伪代码
算法:Generate_decision_tree(samples, attribute)。由给定的训练数据产生一棵判定树。
输入:训练样本samples,由离散值属性表示;候选属性的集合attribute_list。
输出:一棵判定树。
方法:
Generate_decision_tree(samples, attribute_list)
(1)创建结点N;
(2)if samples都在同一个类C then // 类标号属性的值均为C,其候选属性值不考虑
(3)return N作为叶结点,以类C标记;
(4)if attribut_list为空 then
(5)return N作为叶结点,标记为samples中最普通的类; // 类标号属性值数量最大的那个
(6)选择attribute_list中具有最高信息增益的属性best_attribute; // 找出最好的划分属性
(7)标记结点N为best_attribute;
(8)for each best_attribute中的未知值ai // 将样本samples按照best_attribute进行划分
(9)由结点N长出一个条件为best_attribute = ai的分枝;
(10)设si是samples中best_attribute = ai的样本的集合; // 一个分区
(11)if si为空 then
(12)加上一个树叶,标记为samples中最普通的类; // 从样本中找出类标号数量最多的,作为此节点的标记
(13)else加上一个由Generate_decision_tree(si, attribute_list–best_attribute)返回的结点;
// 对数据子集si递归调用,此时候选属性已删除best_attribute
6. 计算过程
(1)在没有给定任何天气信息时,根据历史数据,我们只知道新的一天打球的概率是9/14,不打的概率是5/14。此
时的熵为:
(2)下面我们计算当已知变量outlook的值时,信息熵为多少。
outlook = sunny时,2/5的概率打球,3/5的概率不打球,则entropy = 0.971
outlook = overcast时,4/4的概率打球,0/4的概率不打球,则entropy = 0
outlook = rainy时,3/5的概率打球,2/5的概率不打球,则entropy = 0.971
根据历史统计数据,outlook取值为sunny、overcast、rainy的概率分别是5/14、4/14、5/14,所以当已知变量outlook
的值时,信息熵为:5/14 × 0.971 + 4/14 × 0 + 5/14 × 0.971 = 0.693。信息增溢gain(outlook)为0.940 - 0.693 =
0.247,故系统熵就从0.940下降到了0.693。
同理,gain(temperature) = 0.029,gain(humidity) = 0.152,gain(windy) = 0.048。gain(outlook)最大(即outlook在第
一步使系统的信息熵下降得最快),所以决策树的根节点就取outlook。
(3)确定N1取temperature、humidity还是windy?
在已知outlook = sunny的情况,根据历史数据,我们作出一张类似上表的表,分别计算gain(temperature)、
gain(humidity)和gain(windy),选最大者为N1。其余计算,依次类推。
7. 代码实现
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.dom4j.Document;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;
import org.dom4j.io.OutputFormat;
import org.dom4j.io.XMLWriter;
public class ID3 {
private ArrayList attribute = new ArrayList(); // 存储属性的名称
private ArrayList> attributevalue = new ArrayList>(); // 存储每个属性的取值
private ArrayList data = new ArrayList();; // 原始数据
int decatt; // 决策变量在属性集中的索引
public static final String patternString = "@attribute(.*)[{](.*?)[}]";
Document xmldoc;
Element root;
public ID3() {
xmldoc = DocumentHelper.createDocument();
root = xmldoc.addElement("root");
root.addElement("DecisionTree").addAttribute("value", "null");
}
public static void main(String[] args) {
ID3 inst = new ID3();
inst.readARFF(new File("/home/wss/weather.nominal.arff"));
inst.setDec("play");
LinkedList ll=new LinkedList();
for(int i=0;i al=new ArrayList();
for(int i=0;i al = new ArrayList(values.length);
for (String value : values) {
al.add(value.trim());
}
attributevalue.add(al);
} else if (line.startsWith("@data")) {
while ((line = br.readLine()) != null) {
if(line=="")
continue;
String[] row = line.split(",");
data.add(row);
}
} else {
continue;
}
}
br.close();
} catch (IOException e1) {
e1.printStackTrace();
}
}
// 设置决策变量
public void setDec(int n) {
if (n < 0 || n >= attribute.size()) {
System.err.println("决策变量指定错误。");
System.exit(2);
}
decatt = n;
}
public void setDec(String name) {
int n = attribute.indexOf(name);
setDec(n);
}
// 给一个样本(数组中是各种情况的计数),计算它的熵
public double getEntropy(int[] arr) {
double entropy = 0.0;
int sum = 0;
for (int i = 0; i < arr.length; i++) {
entropy -= arr[i] * Math.log(arr[i]+Double.MIN_VALUE)/Math.log(2);
sum += arr[i];
}
entropy += sum * Math.log(sum+Double.MIN_VALUE)/Math.log(2);
entropy /= sum;
return entropy;
}
// 给一个样本数组及样本的算术和,计算它的熵
public double getEntropy(int[] arr, int sum) {
double entropy = 0.0;
for (int i = 0; i < arr.length; i++) {
entropy -= arr[i] * Math.log(arr[i]+Double.MIN_VALUE)/Math.log(2);
}
entropy += sum * Math.log(sum+Double.MIN_VALUE)/Math.log(2);
entropy /= sum;
return entropy;
}
public boolean infoPure(ArrayList subset) {
String value = data.get(subset.get(0))[decatt];
for (int i = 1; i < subset.size(); i++) {
String next=data.get(subset.get(i))[decatt];
// equals表示对象内容相同,==表示两个对象指向的是同一片内存
if (!value.equals(next))
return false;
}
return true;
}
// 给定原始数据的子集(subset中存储行号),当以第index个属性为节点时计算它的信息熵
public double calNodeEntropy(ArrayList subset, int index) {
int sum = subset.size();
double entropy = 0.0;
int[][] info = new int[attributevalue.get(index).size()][];
for (int i = 0; i < info.length; i++)
info[i] = new int[attributevalue.get(decatt).size()];
int[] count = new int[attributevalue.get(index).size()];
for (int i = 0; i < sum; i++) {
int n = subset.get(i);
String nodevalue = data.get(n)[index];
int nodeind = attributevalue.get(index).indexOf(nodevalue);
count[nodeind]++;
String decvalue = data.get(n)[decatt];
int decind = attributevalue.get(decatt).indexOf(decvalue);
info[nodeind][decind]++;
}
for (int i = 0; i < info.length; i++) {
entropy += getEntropy(info[i]) * count[i] / sum;
}
return entropy;
}
// 构建决策树
public void buildDT(String name, String value, ArrayList subset,
LinkedList selatt) {
Element ele = null;
@SuppressWarnings("unchecked")
List list = root.selectNodes("//"+name);
Iterator iter=list.iterator();
while(iter.hasNext()){
ele=iter.next();
if(ele.attributeValue("value").equals(value))
break;
}
if (infoPure(subset)) {
ele.setText(data.get(subset.get(0))[decatt]);
return;
}
int minIndex = -1;
double minEntropy = Double.MAX_VALUE;
for (int i = 0; i < selatt.size(); i++) {
if (i == decatt)
continue;
double entropy = calNodeEntropy(subset, selatt.get(i));
if (entropy < minEntropy) {
minIndex = selatt.get(i);
minEntropy = entropy;
}
}
String nodeName = attribute.get(minIndex);
selatt.remove(new Integer(minIndex));
ArrayList attvalues = attributevalue.get(minIndex);
for (String val : attvalues) {
ele.addElement(nodeName).addAttribute("value", val);
ArrayList al = new ArrayList();
for (int i = 0; i < subset.size(); i++) {
if (data.get(subset.get(i))[minIndex].equals(val)) {
al.add(subset.get(i));
}
}
buildDT(nodeName, val, al, selatt);
}
}
// 把xml写入文件
public void writeXML(String filename) {
try {
File file = new File(filename);
if (!file.exists())
file.createNewFile();
FileWriter fw = new FileWriter(file);
OutputFormat format = OutputFormat.createPrettyPrint();
XMLWriter output = new XMLWriter(fw, format);
output.write(xmldoc);
output.close();
} catch (IOException e) {
System.out.println(e.getMessage());
}
}
}
结果输出,如下所示:
no
yes
yes
no
yes
结果输出,如下所示:
参考文献:
[1] 决策树:http://zh.wikipedia.org/zh-cn/%E5%86%B3%E7%AD%96%E6%A0%91[2] 《数据挖掘:概念与技术》
[3] 决策树分类算法:http://hi.baidu.com/kakashare/item/b353ce1f6a38def686ad4e64
[4] 归纳决策树ID3:http://www.cnblogs.com/zhangchaoyang/articles/2196631.html
[5] ID3决策树算法伪代码及注解:http://blog.csdn.net/liema2000/article/details/6118384