《统计学习方法》 决策树 ID3和C4.5 生成算法 Python实现
来源:互联网 发布:凌波城静脉不动的算法 编辑:程序博客网 时间:2024/05/20 21:47
终于搞定了决策树的生成算法,决策树看起来简单,实现超复杂。楼主用书本64页例5.3作为本次的数据集。
定义5.1(决策树) 分类决策树模型是一种描述对实例进行分类的树形结点。
先说一下熵和条件熵的公式。
熵:
条件熵:
好了,如果这两个公式的概率是由数据估计得到时,所对应的熵与条件熵又称为经验熵(empirical entropy)和经验条件熵(empirical conditional entropy)。
经验熵:
条件经验熵:
信息增益比:
,其中
先说下经验熵的一些符号的含义,不然你可能会有点懵。D是数据集,K是最后一列的标签类别(“是”,“否”)的个数,这里K对应着2,|D|代表数据集,这里是15。
然后说下条件经验熵,条件经验熵就是根据A的这个特征进行数据划分,比如我们现在可以根据第一维进行划分,数据集D会分成3个数据子集
dataSet = [['青年', '否', '否', '一般', '否'], ['青年', '否', '否', '好', '否'], ['青年', '是', '否', '好', '是'], ['青年', '是', '是', '一般', '是'], ['青年', '否', '否', '一般', '否'], ['中年', '否', '否', '一般', '否'], ['中年', '否', '否', '好', '否'], ['中年', '是', '是', '好', '是'], ['中年', '否', '是', '非常好', '是'], ['中年', '否', '是', '非常好', '是'], ['老年', '否', '是', '非常好', '是'], ['老年', '否', '是', '好', '是'], ['老年', '是', '否', '好', '是'], ['老年', '是', '否', '非常好', '是'], ['老年', '否', '否', '一般', '否']]
好了,再看下下面的式子,是否会清晰一点?
经验熵:
条件经验熵:
这里我用了2个函数来实现上面2个式子的计算。
def entropy(self, dataSet, featureKey = -1): #经验熵,默认选最后一列作为特征 classLabel = [row[featureKey] for row in dataSet] #读取数据集中的最后一列,也就是类标签 labelSet = set(classLabel) #类别的集合 k = len(labelSet) #有k个类别,此次数据集中只有2个类别,“是”和“否” entropy = 0 for x in labelSet: #此为遍历类标签的类别,计算熵 p = classLabel.count(x) / float(len(classLabel)) #计算概率 entropy -= p * math.log(p, 2) #按照经验熵公式来的 return entropy
这个就是经验熵的计算,第一个参数是数据集,第二个参数是经验熵根据第几维的特征进行划分的。这里写-1,是代表最后一列的意思,也就是默认是按标签类别作为特征来计算经验熵的。
def conditionalEntropy(self, dataSet, i, featureSet): #条件经验熵 column = [row[i] for row in dataSet] #读取i列 entropy = 0 for x in featureSet[i]: #划分数据集,并计算 subDataSet = [row for row in dataSet if row[i] == x] #按i维的特征划分数据子集 entropy += (column.count(x) / float(len(column))) * self.entropy(subDataSet) #按条件经验熵公式来的 return entropy
这个就是条件经验熵的计算,你会看到这个经验熵最后我乘以了self.entropy(subDataSet),这个就是
你还记得吧,生成决策树是需要一个特征子集的。来看一下特征子集的代码。
def createFeatureSet(self, dataSet): #创建特征集 featureSet = {} m, n = np.shape(dataSet) for i in range(n - 1): #按列来遍历,n-1代表不存入类别的特征 column = list(set([row[i] for row in dataSet])) #按列提取数据 featureSet[i] = column #每一行就是每一维的特征值 return featureSet
楼主用了字典来保存(本来是用列表来保存的,后面发现计算(已分类的特征子集要去除掉)有些不便)。特征子集会是这样的。
{"0": ["青年", "中年", "老年"], "1": ["否", "是"], "2": ["否", "是"], "3": ["好", "非常好", "一般"]}
好了,铺垫了很多。我们来看一下建树。首先给你们看下结点,结点因为决策树呀,不一定是二叉树,楼主用了字典的形式来保存子结点,key代表着决策树的权值(或者说”边的值“会不会更贴切点?),value就是子结点了。
class Node: #结点 def __init__(self, data = None, lchild = None, rchild = None): self.data = data self.child = {}
下面是建树的代码。
def create(self, dataSet, labels): #ID3算法和C4.5算法 featureSet = self.createFeatureSet(dataSet) #特征集 def createBranch(dataSet, featureSet): label = [row[-1] for row in dataSet] #按列读取标签 node = Node() #TODO:算法(2) if (len(set(label)) == 1): #说明已经没有需要划分的了 node.data = label[0] node.child = None return node HD = self.entropy(dataSet) #数据集的熵 maxGa = 0 #最大信息增益 maxGaKey = 0 #最大信息增益的索引 for key in featureSet: #计算最大信息增益 gDA = HD - self.conditionalEntropy(dataSet, key, featureSet) #当前按i维特征划分的信息增益 gDA = gDA / float(self.entropy(dataSet, key)) #这行注释掉,就是用ID3算法了,否则就是C4.5算法 print gDA if (maxGa < gDA): maxGa = gDA maxGaKey = key #TODO:算法(4) node.data = labels[maxGaKey] subFeatureSet = featureSet.copy() del subFeatureSet[maxGaKey]#删除特征集(不再作为划分依据) for x in featureSet[maxGaKey]: #这里是计算出信息增益后,知道了需要按哪一维进行划分集合 subDataSet = [row for row in dataSet if row[maxGaKey] == x] #这个可以得出数据子集 print json.dumps(subDataSet,encoding='UTF-8',ensure_ascii=False) node.child[x] = createBranch(subDataSet, subFeatureSet) return node return createBranch(dataSet, featureSet)
这段代码其实ID3和C4.5都有实现,两者唯一的区别就在于一个用信息增益判断,一个用信息增益比判断。
复述下信息增益比:
,其中
注:在这里楼主并未实现书中的算法(2)和(4),因为没有数据集测试呀,不过你如果理解了楼主这个做法,你应该能想出来的。我用了TODO来表示。
这就是建树的过程了,后面楼主用了先序遍历进行简单的验证,以及分类函数来分类。
下面贴出完整的代码。
#-*-coding:utf-8-*-import numpy as npimport mathimport jsonclass Node: #结点 def __init__(self, data = None, lchild = None, rchild = None): self.data = data self.child = {}class DecisionTree: #决策树 def create(self, dataSet, labels): #ID3算法 featureSet = self.createFeatureSet(dataSet) #特征集 def createBranch(dataSet, featureSet): label = [row[-1] for row in dataSet] #按列读取标签 node = Node() #TODO:算法(2) if (len(set(label)) == 1): #说明已经没有需要划分的了 node.data = label[0] node.child = None return node HD = self.entropy(dataSet) #数据集的熵 maxGa = 0 #最大信息增益 maxGaKey = 0 #最大信息增益的索引 for key in featureSet: #计算最大信息增益 gDA = HD - self.conditionalEntropy(dataSet, key, featureSet) #当前按i维特征划分的信息增益 gDA = gDA / float(self.entropy(dataSet, key)) #这行注释掉,就是用ID3算法了,否则就是C4.5算法 print gDA if (maxGa < gDA): maxGa = gDA maxGaKey = key #TODO:算法(4) node.data = labels[maxGaKey] subFeatureSet = featureSet.copy() del subFeatureSet[maxGaKey]#删除特征集(不再作为划分依据) for x in featureSet[maxGaKey]: #这里是计算出信息增益后,知道了需要按哪一维进行划分集合 subDataSet = [row for row in dataSet if row[maxGaKey] == x] #这个可以得出数据子集 print json.dumps(subDataSet,encoding='UTF-8',ensure_ascii=False) node.child[x] = createBranch(subDataSet, subFeatureSet) return node return createBranch(dataSet, featureSet) def classify(self, node, labels, testVec): while node != None: if (node.data in labels): index = labels.index(node.data) x = testVec[index] for key in node.child: if x == key: node = node.child[key] else: # print node.data break return node.data def createFeatureSet(self, dataSet): #创建特征集 featureSet = {} m, n = np.shape(dataSet) for i in range(n - 1): #按列来遍历,n-1代表不存入类别的特征 column = list(set([row[i] for row in dataSet])) #按列提取数据 featureSet[i] = column #每一行就是每一维的特征值 return featureSet def conditionalEntropy(self, dataSet, i, featureSet): #条件经验熵 column = [row[i] for row in dataSet] #读取i列 entropy = 0 for x in featureSet[i]: #划分数据集,并计算 subDataSet = [row for row in dataSet if row[i] == x] #按i维的特征划分数据子集 entropy += (column.count(x) / float(len(column))) * self.entropy(subDataSet) #按公式来的 return entropy def entropy(self, dataSet, featureKey = -1): #经验熵,默认选最后一列作为特征 classLabel = [row[featureKey] for row in dataSet] #读取数据集中的最后一列,也就是类标签 labelSet = set(classLabel) #类别的集合 k = len(labelSet) #有k个类别,此次数据集中只有2个类别,“是”和“否” entropy = 0 for x in labelSet: #此为遍历类标签的类别,计算熵 p = classLabel.count(x) / float(len(classLabel)) #计算概率 entropy -= p * math.log(p, 2) #按照公式来的 return entropy def preOrder(self, node, depth = 0): if (node != None): print node.data, depth if (node.child != None): for key in node.child: print key self.preOrder(node.child[key], depth + 1)dataSet = [['青年', '否', '否', '一般', '否'], ['青年', '否', '否', '好', '否'], ['青年', '是', '否', '好', '是'], ['青年', '是', '是', '一般', '是'], ['青年', '否', '否', '一般', '否'], ['中年', '否', '否', '一般', '否'], ['中年', '否', '否', '好', '否'], ['中年', '是', '是', '好', '是'], ['中年', '否', '是', '非常好', '是'], ['中年', '否', '是', '非常好', '是'], ['老年', '否', '是', '非常好', '是'], ['老年', '否', '是', '好', '是'], ['老年', '是', '否', '好', '是'], ['老年', '是', '否', '非常好', '是'], ['老年', '否', '否', '一般', '否']]labels = ['年龄', '有工作', '有自己的房子', '信贷情况']# print dataSettree = DecisionTree()node = tree.create(dataSet, labels)for line in dataSet: print tree.classify(node, labels, line)
注:本文中用了一些json.dumps(subDataSet,encoding=’UTF-8’,ensure_ascii=False)来打印,这是因为如果直接打印中文会出错的。
- 《统计学习方法》 决策树 ID3和C4.5 生成算法 Python实现
- Python实现决策树算法 C4.5和ID3算法
- 决策树ID3和C4.5算法Python实现源码
- 决策树ID3和C4.5算法Python实现源码
- 决策树ID3和C4.5算法Python实现源码
- 机器学习算法-决策树生成算法ID3和C4.5
- python实现决策树C4.5算法(在ID3基础上改进)
- python实现决策树C4.5算法(ID3基础上改进)
- Python 决策树算法(ID3 & C4.5)
- 决策树(ID3,C4.5)Python实现
- Python实现决策树(ID3、C4.5)
- 整理--决策树算法:ID3和C4.5
- 整理--决策树算法:ID3和C4.5
- 决策树算法:ID3和C4.5
- ID3和C4.5决策树算法总结
- 《统计学习方法》 决策树 CART生成算法 分类树 Python实现
- 《统计学习方法》 决策树 CART生成算法 回归树 Python实现
- Python 实现决策树 ID3 C4.5 悲观剪枝
- JavaScript中的正则表达式
- 数位dp
- Nexus6p device debug
- iOS 常用排序算法~集合
- python numpy学习笔记(8) 多项式
- 《统计学习方法》 决策树 ID3和C4.5 生成算法 Python实现
- java final 关键字使用
- leetcode 3. Longest Substring Without Repeating Characters
- AAPT err(Facade for): libpng error: Not a PNG file
- HTTP个人总结(一)
- KMP算法
- scrollview+listview 会出现只显示一行和滑动问题
- Java 集合框架综述
- 购物车增加删除