
机器学习之五:决策树(ID3、C4.5)
则随机变量X的熵定义为:
熵只依赖与X的分布,与X的取值无关,所以X的熵记做H(p),即
设随机变量(X,Y)的联合概率分布为:
条件熵H(Y|X)表示在已知随机变量X的条件下随机变量Y的不确定性,随机变量X给定的条件下随机变量Y的条件熵H(Y|X)定义为X给定条件下Y的条件概率分布的熵对X的数学期望:
当熵和条件熵中的概率有数据估计得到时,对应的熵与条件熵分别称为经验熵和经验条件熵。
2、信息增益
特征A对训练数据集D的信息增益g(D,A)定义为集合D的经验熵H(D)与特征A给定条件下D的经验条件熵H(D|A)之差,即:
给定训练数据集D和特征A,经验熵H(D)表示对数据集D进行分类时的不确定性,而经验条件熵H(D|A)表示在特征A给定的条件下对数据集D进行分类的不确定性,那么它们的差,即信息增益,就表示由于特征A而使得对数据集D的分类的不确定性减少的程度。决策树进行特征选择时,选择信息增益最大的特征作为划分依据。
3、信息增益比:
特征A对训练数据集D的信息增益比 gR(D,A) 定义为其信息增益 g(D,A) 与训练数据集D关于特征A的值的熵 HA(D)之比 ,即
其中, HA(D)=−∑ni=1|Di||D|log2|Di||D| ,n是特征A的取值个数。
4、决策树生成
决策树的生成有三种算法,分别是ID3、C4.5和CART算法,ID3算法使用信息增益来选择特征,而C4.5使用信息增益比来选择特征。ID3的做法是每次选取当前最佳的特征来分割数据(选择信息增益最大的特征作为划分依据),并按照该特征的所有可能取值来切分。也就是说,如果一个特征有4种取值,那么数据将被切成4份。某个特征的信息增益计算如下:
输入:训练数据D和特征A
输出:特征A对训练数据集D的信息增益 g(D,A) 或者信息增益比 gR(D,A)
(1)计算数据集D的经验熵:
其中训练数据集为 D , |D| 表示其样本容量,设有K个类 Ck ,k=1,2,3,…,K, |Ck| 为属于类 Ck 的样本个数 ∑kk=1|Ck|=|D| 。
(2)计算特征A对数据集D的经验条件熵 H(D|A) :
其中特征A有n个不同的取值 { a1,a2,...,an} ,根据A的取值将D划分为n个子集 D1,D2,...,Dn,|Di| 为 Di 的样本个数, ∑ni=1|Di|=|D| 。子集 Di 中属于类 Ck 的样本集合为 Dik , |Dik| 为 Dik 的样本个数。
(3)计算信息增益或者信息增益比:
5、决策树剪枝
决策树生成算法递归产生决策树,知道不能继续下去为止,这样产生的树往往对训练数据的分类很准确,但对未知的测试数据的分类却没那么准确,即出现过拟合现象。解决这个问题的办法是考虑决策树的复杂度,对已生成的决策树进行简化。将已生成的树进行简化的过程称为剪枝,剪枝从已生成的树上减掉一些子树或者叶节点,并将其根节点或者父节点作为新的叶节点。
决策树的剪枝往往通过极小化树整体的损失函数来实现。设树 T 的叶节点个数为 |T| ,t是树 T 的叶节点,该叶节点有 Nt 个样本点,其中k类的样本点有 Ntk 个, k=1,2,3,...,K . Ht(T) 为叶节点t的经验熵, α>=0 为参数,决策树的损失函数可以定义为:
其中经验熵为:
可以把损失函数写为:
其中C(T)表示模型对训练数据的预测误差,即模型与训练数据的拟合程度,|T|表示模型的复杂度。 α 控制两者间的影响,较大的 α 促使选择较简单的模型树,较小的 α 促使选择较复杂的模型树。剪枝就是 α 确定时,选择损失函数最小的模型。
6、代码实现ID3算法生成决策树
发布日期:2021-05-07 13:29:22
浏览次数:18
分类:原创文章
本文共 7106 字,大约阅读时间需要 23 分钟。
1、信息熵和条件熵
在信息论中,熵是表示随机变量不确定性的度量,熵越大,则随机变量的不确定性越大。设X是一个离散随机变量,X的概率分布为:
P(X=xi)=pi,i=1,2,3...,n
则随机变量X的熵定义为:
H(X)=−∑i=1npilogpi
熵只依赖与X的分布,与X的取值无关,所以X的熵记做H(p),即
H(p)=−∑i=1npilogpi
设随机变量(X,Y)的联合概率分布为:
P(X=xi,Y=yi)=pij,i=1,2,3...,n
条件熵H(Y|X)表示在已知随机变量X的条件下随机变量Y的不确定性,随机变量X给定的条件下随机变量Y的条件熵H(Y|X)定义为X给定条件下Y的条件概率分布的熵对X的数学期望:
H(Y|X)=∑i=1npiH(Y|X=xi)
当熵和条件熵中的概率有数据估计得到时,对应的熵与条件熵分别称为经验熵和经验条件熵。
2、信息增益
特征A对训练数据集D的信息增益g(D,A)定义为集合D的经验熵H(D)与特征A给定条件下D的经验条件熵H(D|A)之差,即:
g(D,A)=H(D)−H(D|A)
给定训练数据集D和特征A,经验熵H(D)表示对数据集D进行分类时的不确定性,而经验条件熵H(D|A)表示在特征A给定的条件下对数据集D进行分类的不确定性,那么它们的差,即信息增益,就表示由于特征A而使得对数据集D的分类的不确定性减少的程度。决策树进行特征选择时,选择信息增益最大的特征作为划分依据。
3、信息增益比:
特征A对训练数据集D的信息增益比 gR(D,A) 定义为其信息增益 g(D,A) 与训练数据集D关于特征A的值的熵 HA(D)之比 ,即
gR(D,A)=g(D,A)HA(D)
其中, HA(D)=−∑ni=1|Di||D|log2|Di||D| ,n是特征A的取值个数。
4、决策树生成
决策树的生成有三种算法,分别是ID3、C4.5和CART算法,ID3算法使用信息增益来选择特征,而C4.5使用信息增益比来选择特征。ID3的做法是每次选取当前最佳的特征来分割数据(选择信息增益最大的特征作为划分依据),并按照该特征的所有可能取值来切分。也就是说,如果一个特征有4种取值,那么数据将被切成4份。某个特征的信息增益计算如下:
输入:训练数据D和特征A
输出:特征A对训练数据集D的信息增益 g(D,A) 或者信息增益比 gR(D,A)
(1)计算数据集D的经验熵:
H(D)=−∑k=1k|Ck||D|log2|Ck||D|
其中训练数据集为 D ,
(2)计算特征A对数据集D的经验条件熵 H(D|A) :
H(D|A)=∑i=1n|Di||D|H(Di)=−∑i=1n|Di||D|∑k=1K|Dik||Di|log2|Dik||Di|
其中特征A有n个不同的取值 { a1,a2,...,an} ,根据A的取值将D划分为n个子集 D1,D2,...,Dn,|Di| 为 Di 的样本个数, ∑ni=1|Di|=|D| 。子集 Di 中属于类 Ck 的样本集合为 Dik , |Dik| 为 Dik 的样本个数。
(3)计算信息增益或者信息增益比:
g(D,A)=H(D)−H(D|A)
gR(D,A)=g(D,A)HA(D)
5、决策树剪枝
决策树生成算法递归产生决策树,知道不能继续下去为止,这样产生的树往往对训练数据的分类很准确,但对未知的测试数据的分类却没那么准确,即出现过拟合现象。解决这个问题的办法是考虑决策树的复杂度,对已生成的决策树进行简化。将已生成的树进行简化的过程称为剪枝,剪枝从已生成的树上减掉一些子树或者叶节点,并将其根节点或者父节点作为新的叶节点。
决策树的剪枝往往通过极小化树整体的损失函数来实现。设树 T 的叶节点个数为
Cα(T)=∑t=1|T|NtHt(T)+α|T|
其中经验熵为:
Ht(T)=−∑kNtkNtlogNtkNt
可以把损失函数写为:
Cα(T)=C(T)+α|T|
其中C(T)表示模型对训练数据的预测误差,即模型与训练数据的拟合程度,|T|表示模型的复杂度。 α 控制两者间的影响,较大的 α 促使选择较简单的模型树,较小的 α 促使选择较复杂的模型树。剪枝就是 α 确定时,选择损失函数最小的模型。
6、代码实现ID3算法生成决策树
#coding=utf-8import osimport sysimport numpy as npfrom math import *import matplotlib.pyplot as plt#创建测试数据集' def createDataSet(): dataSet = [[1, 1, 'yes'], [1, 1, 'yes'], [1, 0, 'no'], [0, 1, 'no'], [0, 1, 'no']] return dataSet#'计算香农熵'def calcShannonEnt(dataSet): # 数据集个数 numEntries = len(dataSet) # 标签集合 labelCounts = {} for featVec in dataSet: # 行遍历数据集 # 当前标签 currentLabel = featVec[-1] # 加入标签集合 if currentLabel not in labelCounts.keys(): labelCounts[currentLabel] = 0 labelCounts[currentLabel] += 1 # 计算当前数据集的香农熵并返回 shannonEnt = 0.0 for key in labelCounts: prob = float(labelCounts[key])/numEntries shannonEnt -= prob * log(prob,2) return shannonEnt#'数据集划分'# ==============================================# 输入:# dataSet: 训练集文件名(含路径)# axis: 用于划分的特征的列数,为 0 | 1# value: 划分值,根据特征的 n个不同的取值来划分# 输出:# retDataSet: 划分后的子列表# ==============================================def splitDataSet(dataSet, axis, value): # 划分结果 retDataSet = [] for featVec in dataSet: # 逐行遍历数据集 if featVec[axis] == value: # 如果目标特征值等于value # 抽取掉数据集中的目标特征值列 reducedFeatVec = featVec[:axis] reducedFeatVec.extend(featVec[axis+1:]) # 将抽取后的数据加入到划分结果列表中 retDataSet.append(reducedFeatVec) return retDataSet#'选择最佳划分方案'# ===============================================# 输入:# dataSet: 数据集# 输出:# bestFeature: 和原数据集熵差最大划分对应的特征的列号# ===============================================def chooseBestFeatureToSplit(dataSet): # 特征个数,也是数据集列数-1 numFeatures = len(dataSet[0]) - 1 # 原数据集香农熵 baseEntropy = calcShannonEnt(dataSet) # 暂存最大熵增量 bestInfoGain = 0.0; # 和原数据集熵差最大的划分对应的特征的列号 bestFeature = -1 for i in range(numFeatures): # 逐列遍历数据集 # 获取该列所有特征值 featList = [example[i] for example in dataSet] # 得到一列的数据,也就是某个特征所有的取值 # 将特征值列featList的值唯一化并保存到集合uniqueVals uniqueVals = set(featList) # 新划分法香农熵 newEntropy = 0.0 # 计算该特征划分下所有划分子集的香农熵,并叠加。 for value in uniqueVals: # 遍历该特征列所有特征值 subDataSet = splitDataSet(dataSet, i, value) prob = len(subDataSet)/float(len(dataSet)) # 每个子集占得比率 newEntropy += prob * calcShannonEnt(subDataSet) # 保存所有划分法中,和原数据集熵差最大划分对应的特征的列号。 infoGain = baseEntropy - newEntropy # 计算信息增益 if (infoGain > bestInfoGain): bestInfoGain = infoGain bestFeature = i return bestFeature#'采用多数表决的方式求出classList中出现次数最多的类标签' # ===============================================# 输入:# classList: 类标签集# 输出:# sortedClassCount[0][0]: 出现次数最多的标签# ===============================================def majorityCnt(classList): classCount={} for vote in classList: if vote not in classCount.keys(): classCount[vote] = 0 classCount[vote] += 1 sortedClassCount = sorted(classCount.iteritems(), key=operator.itemgetter(1), reverse=True) return sortedClassCount[0][0]# '创建决策树'#在Python中,可以用字典来具体实现树:字典的键存放节点信息,值存放分支及子树/叶子节点信息。# ===============================================# 输入:# dataSet: 数据集# labels: 划分标签集# 输出:# myTree: 生成的决策树# ===============================================def createTree(dataSet,labels): # 获得类标签列表 classList = [example[-1] for example in dataSet] # 递归终止条件一:如果数据集内所有分类一致 if classList.count(classList[0]) == len(classList): return classList[0] # 递归终止条件二:如果所有特征都划分完毕 if len(dataSet[0]) == 1: # 将它们都归为一类并返回 return majorityCnt(classList) # 选择最佳划分特征 bestFeat = chooseBestFeatureToSplit(dataSet) # 最佳划分对应的划分标签。注意不是分类标签 bestFeatLabel = labels[bestFeat] # 构建字典空树 myTree = {bestFeatLabel:{}} # 从划分标签列表中删掉划分后的元素 del(labels[bestFeat]) # 获取最佳划分对应特征的所有特征值 featValues = [example[bestFeat] for example in dataSet] # 对特征值列表featValues唯一化,结果存于uniqueVals。 uniqueVals = set(featValues) for value in uniqueVals: # 逐行遍历特征值集合 # 保存所有划分标签信息并将其伙同划分后的数据集传递进下一次递归 subLabels = labels[:] myTree[bestFeatLabel][value] = createTree(splitDataSet(dataSet, bestFeat, value),subLabels) return myTree#'使用决策树分类'# ========================# 输入:# inputTree: 决策树文件名# featLabels: 分类标签集# testVec: 待分类对象# 输出:# classLabel: 分类结果# ======================== def classify(inputTree,featLabels,testVec): # 当前分类标签 firstStr = inputTree.keys()[0] secondDict = inputTree[firstStr] # 找到当前分类标签在分类标签集中的下标 featIndex = featLabels.index(firstStr) # 获取待分类对象中当前分类的特征值 key = testVec[featIndex] # 遍历 valueOfFeat = secondDict[key] # 子树分支则递归 if isinstance(valueOfFeat, dict): classLabel = classify(valueOfFeat, featLabels, testVec) # 叶子分支则返回结果 else: classLabel = valueOfFeat return classLabel myDat = createDataSet()labels = ['no surfacing', 'flippers']myTree = createTree(myDat, labels)print classify(myTree, labels, [1,1])
发表评论
最新留言
初次前来,多多关照!
[***.217.46.12]2025年04月02日 02时24分15秒
关于作者

喝酒易醉,品茶养心,人生如梦,品茶悟道,何以解忧?唯有杜康!
-- 愿君每日到此一游!
推荐文章
1062 Talent and Virtue
2019-03-06
1045 Favorite Color Stripe
2019-03-06
B. Spreadsheets(进制转换,数学)
2019-03-06
等和的分隔子集(DP)
2019-03-06
基础练习 十六进制转八进制(模拟)
2019-03-06
L - Large Division (大数, 同余)
2019-03-06
39. Combination Sum
2019-03-06
41. First Missing Positive
2019-03-06
80. Remove Duplicates from Sorted Array II
2019-03-06
83. Remove Duplicates from Sorted List
2019-03-06
410. Split Array Largest Sum
2019-03-06
开源项目在闲鱼、b 站上被倒卖?这是什么骚操作?
2019-03-06
Vue3发布半年我不学,摸鱼爽歪歪,哎~就是玩儿
2019-03-06
《实战java高并发程序设计》源码整理及读书笔记
2019-03-06
Java开源博客My-Blog(SpringBoot+Docker)系列文章
2019-03-06
程序员视角:鹿晗公布恋情是如何把微博搞炸的?
2019-03-06
【JavaScript】动态原型模式创建对象 ||为何不能用字面量创建原型对象?
2019-03-06
Linux应用-线程操作
2019-03-06