一、决策树简介
决策树(Decision Tree)是一种决策分析方法,它基于已知的各种情况发生概率来构建,旨在求取净现值的期望值大于等于零的概率,进而评价项目风险并判断其可行性。这种方法以图形的方式直观运用概率分析,由于决策分支的图形表现类似于树的枝干,因此得名决策树。
在机器学习中,决策树是一个预测模型,它代表对象属性与对象值之间的一种映射关系。决策树是一种树形结构,其中每个内部节点表示一个属性上的测试,每个分支代表一个测试输出,每个叶节点则代表一种类别。在分类问题中,决策树是一种常用的分类方法,它属于监督学习范畴。监督学习是通过给定带有属性和类别的样本,学习得到一个能够对新对象进行分类的分类器。
二、构建决策树的流程
1.构建决策树的一般流程包括以下步骤:
(1)收集并预处理数据:首先,需要准备包含特征和标签的数据集。树构造算法通常适用于标称型数据,因此如果数据是数值型的,必须进行离散化处理。
(2)特征选择:基于某种准则(如信息增益或基尼不纯度)来选择最佳特征作为根节点。这个选择的目的是找到一个能够最好地划分数据集的特征。
(3)划分数据集:根据所选的特征,将数据集划分为多个子集。对于离散型特征,可以直接根据特征值进行划分;对于连续型特征,可能需要使用二分法等方法将其转化为离散型特征。
(4)递归构建子树:对于每个划分后的子集,重复进行特征选择和划分数据集的步骤,递归地构建子树。这个过程一直持续到满足停止条件,例如子集中的所有实例都属于同一类别,或者没有更多特征可用。
(5)剪枝:在构建完整个决策树后,进行剪枝操作以去除一些不必要的节点,避免过拟合。剪枝策略分为预剪枝和后剪枝。预剪枝是在构造决策树的过程中,对每个结点在划分前进行估计,如果当前结点的划分不能提升决策树的泛化性能,则不进行划分并将该结点标记为叶结点。后剪枝则是先构造完整的决策树,然后从底向上对非叶结点进行考察,如果替换某子树为叶结点能提升泛化性能,则进行替换。
(6)测试与评估:使用测试数据集来评估构建的决策树的性能,计算错误率等指标,以确保其具有良好的泛化能力。
通过以上步骤,可以构建出一个能够用于分类或回归任务的决策树模型。在实际应用中,可能还需要对决策树进行进一步的优化和调整,以适应特定的数据集和任务需求。
2.决策树停止划分的条件
(1)当前结点包含的样本属于同一类别,无需划分;
(2)当前属性集为空,或是所有样本在所有属性上取值相同无法划分,简单理解就是当分到这一节点时,所有的属性特征都用完了,没有特征可用了,就根据label数量多的给这一节点打标签使其变成叶节点(其实是在用样本出现的后验概率做先验概率);
(3)当前结点包含的样本集合为空,不能划分。这种情况出现是因为该样本数据缺少这个属性取值,根据父结点的label情况为该结点打标记(其实是在用父结点出现的后验概率做该结点的先验概率)。
三、构建决策树的三种算法
1.ID3算法:
D3算法是一种基于熵的算法,它通过计算每个属性对应的信息增益来选择最优的属性作为节点。
从信息论的知识中我们知道:信息熵越大,样本的纯度越低。ID3 算法的核心思想就是以信息增益来度量特征选择,选择信息增益最大的特征进行分裂。
信息增益 = 信息熵 - 条件熵:
信息熵:
信息增益:
其中,D表示数据集,A表示属性,P(i)表示样本属于某一类别的概率。
2.C4.5算法:
C4.5算法与ID3相似,在ID3的基础上进行了改进,采用信息增益比来选择属性。ID3选择属性用的是子树的信息增益,ID3使用的是熵(entropy, 熵是一种不纯度度量准则),也就是熵的变化值,而C4.5用的是信息增益率
信息增益率:增益率是用前面的信息增益Gain(D, a)和属性a对应的"固有值"的比值来共同定义的。属性 a 的可能取值数目越多(即 V 越大),则 IV(a) 的值通常会越大.
信息增益比:
其中,IV(A)表示属性A的固有值,即所有可能取值所对应的信息熵。
3.CART算法:
CART算法是一种基于基尼指数的算法,它用于分类和回归树。在分类树中,基尼指数表示样本被错误分类的概率;在回归树中,基尼指数表示样本的方差。基尼系数代表了模型得不纯度,基尼系数越小,则不纯度越低,特征越好。这点和信息增益是相反的。
基尼指数Gini(D) = 1 - Σ(pi)^2
基尼指数增益GiniIndex(D, A) = Σ(Dv/D)*Gini(Dv)、
在分类问题中,假设有K各类别,第k个类别概率为,则基尼系数的表达式为:
若给定样本D,如果根据特征A的某个值a,把D分为D1和D2两个部分,则在特征条件A下,D的基尼系数表达式为:
四、剪枝
剪枝是决策树学习中一种重要的技术,旨在避免过拟合,简化决策树模型。剪枝处理是决策树对付过拟合的主要手段。下面详细介绍剪枝的两种主要类型:预剪枝和后剪枝。
1.预剪枝(Pre-pruning):
(1)定义:预剪枝是自上而下的剪枝,指在决策树生成过程中,对每个结点进行事先估计,如果当前结点的划分不能带来决策树泛化性能的提升,则停止划分并将当前节点标记为叶结点。
(2)优点:预剪枝使很多分支都没有展开,降低了过拟合的风险,并显著减少了决策树的时间开销(包括训练时间和测试时间)。
(3)缺点:预剪枝的贪心本质禁止这些分支展开,可能会带来欠拟合风险。因为当前结点的划分虽不能提高模型的泛化性能,但此结点之后的结点划分有可能提高模型的泛化性能,这样直接一刀切未免太过武断。
2.后剪枝(Post-pruning):
(1)定义:后剪枝是自下而上的剪枝,指对一棵已经生成的完整决策树自下而上地对非叶节点进行估计,如果将该结点对应的子树替换成叶结点能够带来决策树泛化性能的提升,则将该子树替换成叶结点。后剪枝是一种更常用的方法,因为在预剪枝中精确估计何时停止树增长很困难。
(2)优点:后剪枝的欠拟合风险小,泛化性能往往优于预剪枝。
(3)缺点:要对树中所有非叶结点进行逐一考察,时间开销很大。
3.剪枝的方法:
REP(错误率降低剪枝):根据错误率进行剪枝,如果一棵子树修剪前后错误率没有下降,就可以认为该子树是可以修剪的。REP剪枝需要用新的数据集,以降低训练数据的影响,降低过拟合的程度,提高预测的准确率。
PEP(悲观剪枝):如果剪枝后的误差小于剪枝前精度的上限,则进行剪枝。这种方法通过给每一个节点都加上误差修正因子,使得在计算误差时,子节点的总误差可能高于其父节点,从而更倾向于剪枝。
4.剪枝的目的:
剪枝的目的是简化决策树模型,通过减少叶结点和层数来降低决策树的复杂度,从而避免过拟合现象。过拟合是指决策树在训练数据上表现良好,但在测试数据上表现不佳的情况。剪枝可以帮助提高决策树在测试数据上的性能。
五、ID3和C4.5案例和对比
5.1 ID3
from math import log
def createDataSet(): ## 自定义创建数据集
dataSet = [[1, 1, 'yes'],
[1, 1, 'yes'],
[1, 0, 'no'],
[0, 1, 'no'],
[0, 1, 'no']]
labels = ['no surfacing','flippers']
#change to discrete values
return dataSet, labels
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
##print(labelCounts)
shannonEnt = 0.0
for key in labelCounts:
prob = float(labelCounts[key])/numEntries
shannonEnt -= prob * log(prob,2) #log base 2
return shannonEnt
Mydata, labels = createDataSet() ## 调用
calcShannonEnt(Mydata)
按给定特征划分数据集
def splitDataSet(dataSet, axis, value):
retDataSet = []
for featVec in dataSet:
if featVec[axis] == value:
reducedFeatVec = featVec[:axis]
reducedFeatVec.extend(featVec[axis+1:])
retDataSet.append(reducedFeatVec)
return retDataSet
## 选择最好的数据集划分方式
def chooseBestFeatureToSplit(dataSet):
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]#create a list of all the examples of this feature(建个列表把第i个特征值和所有可能值存进去)(相当于按特征值的一列存)
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
chooseBestFeatureToSplit(Mydata)
递归构建决策树
## 创建树
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: #stop splitting when there are no more features in dataSet
return majorityCnt(classList)
## 开始创建树
bestFeat = chooseBestFeatureToSplit(dataSet)
bestFeatLabel = labels[bestFeat]
myTree = {bestFeatLabel:{}}
del(labels[bestFeat])
featValues = [example[bestFeat] for example in dataSet]
uniqueVals = set(featValues)
for value in uniqueVals:
subLabels = labels[:]
myTree[bestFeatLabel][value] = createTree(splitDataSet(dataSet, bestFeat, value),subLabels)
return myTree
该案例源自决策树创建_createtree函数-CSDN博客
5.2 C4.5
将上述ID3算法的Python实现转换成C4.5算法,主要的修改包括使用信息增益率(而非纯信息增益)来选择最好的数据集划分方式。信息增益率是信息增益与数据集D关于特征A的值的熵的比值。这种方法可以减少因特征值数目过多而倾向于选择这些特征的问题。
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
def splitDataSet(dataSet, axis, value):
retDataSet = []
for featVec in dataSet:
if featVec[axis] == value:
reducedFeatVec = featVec[:axis]
reducedFeatVec.extend(featVec[axis+1:])
retDataSet.append(reducedFeatVec)
return retDataSet
def calcConditionalEntropy(dataSet, axis):
numEntries = len(dataSet)
featureValues = [example[axis] for example in dataSet]
uniqueVals = set(featureValues)
conditionalEntropy = 0.0
for value in uniqueVals:
subDataSet = splitDataSet(dataSet, axis, value)
prob = len(subDataSet) / float(numEntries)
conditionalEntropy += (prob * calcShannonEnt(subDataSet))
return conditionalEntropy
def calcInfoGainRatio(dataSet, axis):
baseEntropy = calcShannonEnt(dataSet)
conditionalEntropy = calcConditionalEntropy(dataSet, axis)
featureValues = [example[axis] for example in dataSet]
uniqueVals = set(featureValues)
IV = -sum([(featureValues.count(v) / float(len(dataSet))) * log(featureValues.count(v) / float(len(dataSet)), 2) for v in uniqueVals if featureValues.count(v) > 0])
infoGain = baseEntropy - conditionalEntropy
return infoGain / IV if IV != 0 else 0
def chooseBestFeatureToSplit(dataSet):
numFeatures = len(dataSet[0]) - 1
bestInfoGainRatio = 0.0
bestFeature = -1
for i in range(numFeatures):
infoGainRatio = calcInfoGainRatio(dataSet, i)
if (infoGainRatio > bestInfoGainRatio):
bestInfoGainRatio = infoGainRatio
bestFeature = i
return bestFeature
def majorityCnt(classList):
classCount = {}
for vote in classList:
if vote not in classCount.keys():
classCount[vote] = 0
classCount[vote] += 1
sortedClassCount = sorted(classCount.items(), key=lambda item: item[1], reverse=True)
return sortedClassCount[0][0]
5.3 ID3和C4.5对比
ID3算法和C4.5算法都是用于构建决策树的算法,主要用于分类问题。尽管这两种算法有相似的目标它们在处理节和特性上有一些不同,这可能会导致在某些数据集上得到不同的结果。以下是ID3算法和C4.算法的一些主要区别,这些区别可能影响它们在具体案例中的表现和:
-
属性的准则:
- ID 使用信息增益(Information Gain)作选择属性的标准。
- C4.5 改进了ID3,使用增益率( Ratio)来选择属性,以解决ID3倾向选择具有更多值的属性的问题。
-
处理连续:
- ID3 通不直接处理连续属性,需要将连续属性离散化。
- C4.5 可以直接处理连续属性,它会为连续属性自动找到一个最优切分点。
-
处理缺失值:
- **ID3 没有直接处理缺失值的机制。
- C4.5 提供了缺失值的策略。
-
剪枝策略:
- ID3 不包括剪枝过程,可能导致过拟合。
- C4.5 包括后剪枝略,帮助防止过拟合和提高模型的泛化能力。
因此,尽管某些情况下ID3和C4.5可能得出似或相同的结果,它们在复杂、有失数据或连续属性的数据集时可能产生不同的决策树。选择一个算法也取决于具体的集特性和需要解的问题。如果提供具体的案例信息,我可以提供更精确的分析。您提到的ID3和C4.5是两种决策树算法,用于数据挖掘和机器学习中分类问题的解决。理解您的问题之前,我们需要明确一下这两种算法的主要区别,然后才能判断在特定案例中它们是否可能得到相同的结果。
ID3算法:
- 属性选择标准:使用信息增益作为选择属性的标准。
- 分支方法:通常对离散属性进行分支,每个属性的每个值都可以成为一个分支。
- 剪枝:ID3算法本身不包括剪枝策略,容易过拟合。
C4.5算法:
- 属性选择标准:使用信息增益率来选择属性,以克服ID3算法倾向于选择具有更多值的属性的问题。
- 分支方法: 不仅支持离散数据,也支持连续数据。对于连续属性,C4.5将查找一个最佳分割点。
- 剪枝: C4.5加入了剪枝步骤,减少过拟合风险,提高模型的泛化能力。
结果的相似性:
- 如果所使用的数据集主要包含离散属性,并且属性值的分布不是异常倾斜的,ID3和C4.5可能会得到相似的结果。
- 如果数据集包含连续数据或者某些属性有很多值,C4.5通常会比ID3表现得更好,因为它通过信息增益率和剪枝来优化决策树结构。