1、介绍
决策树(decision tree),每个分支都是需要通过条件判断进行划分的树,解决分类和回归问题的方法。
- 策略
正则化的极大似然函数。
此外,从所有可能的决策树选取最优决策树是NP完全问题,实际中学习算法采用启发式方法,近似解决最优化问题。 - 学习算法三要素:
特征选择、决策树生成、剪枝。
决策树可看作 if-then 规则的集合。把决策树看作对特征空间的划分,那么它表示了给定特征条件下类的条件概率分布。
2、特征选择
特征选择的准则是信息增益或是信息增益比。
2.1 概念
几个术语:熵、条件熵、信息增益、信息增益比。
熵(entropy):随机变量不确定性度量,熵越大,不确定性越大。
随机变量概率分布:
定义 。
补充:
交叉熵衡量两个分布的差异信息。当两个分布相同时,相对熵为0,交叉熵最小。这也是交叉熵用于分类模型损失函数的原理。
条件熵:
已知一个随机变量下,另一个随机变量的不确定性。
注:如果熵和条件熵是由数据估计(特别是极大似然估计法)得到时,对应的熵和条件熵分别为经验熵(empirical entropy)和经验条件熵(empirical conditional entropy).
信息增益:
特征 A 对数据集 D 的信息增益 g(D, A),定义为
其中,经验熵计算为:
其中, 代表类.
条件熵计算为:
其中,是根据特征A的取值划分的子集。
一般,类熵与特征下类条件熵之差称为互信息。决策树中的信息增益等价于类与特征的互信息。
先来说说优点:
1.信息增益考虑了特征出现与不出现的两种情况,比较全面,一般而言效果不错。
2.使用了所有样例的统计属性,减小了对噪声的敏感度。
3.容易理解,计算简单。主要的缺陷:
1.信息增益考察的是特征对整个系统的贡献,没有到具体的类别上,所以一般只能用来做全局的特征选择,而没法针对单个类别做特征选择。
2.只能处理连续型的属性值,没法处理连续值的特征。
3.算法天生偏向选择分支多的属性,容易导致overfitting。
信息增益比
基尼指数:
gini越大,数据D不确定性越大。
信息增益和基尼系数的区别:
信息增益是衡量样本纯度的指标;基尼系数是衡量不纯度的指标。
3、决策树生成
常用决策树生成算法:ID3、C4.5、CART。
3.1 ID3算法
使用信息增益进行决策树的生成。
ID3 算法 |
输入:训练集D,特征集A,阈值 |
(1)计算A中各特征对D的信息增益,选择最大的特征 |
优点:
1.可能出现每个分支节点只包含一个样本;
缺点:
1.不具备泛化性;
2.容易产生过拟合可取值数据较多的属性(ID3只有生成,没有剪枝);
3.不能处理连续数值型特征;
3.2 C4.5算法
采用信息增益比作为决策树划分的判断,选择信息增益比最大的分裂点作为该特征的最佳分裂点。
C4.5处理连续数值型特征的方法:
先把连续属性转换成离散属性。把特征排序,按照大于、小于等于某个值进行划分到左树、右树,然后,计算这些情况下最大的信息增益率。
此外,C4.5算法倾向于选择连续特征作为最佳树分裂点,具体方法是,连续特征最佳分裂点的信息增益减去
- 优点:
1.偏向取值较少的特征;
2.可以处理连续数值型特征; - 缺点:
1.只能分类;
3.3 CART算法
CART,classification and regression tree,采用基尼指数作为划分判断的二叉树。
特点:
1.可以用来分类和回归;
2.生成的决策树为二叉树,而ID3,C4.5允许决策树为多分支的。
回归树的生成
在训练集的某个特征进行划分成两个数据集,分别计算两个数据集与相应输出平均值差的平方和(均方差),取使得数据集均方差最小的分割点,并且以分割后样本的平均目标值作为预测值;重复这一过程,完成回归树的生成。
分类树的生成
算法:
1)分别计算各个特征的基尼指数;
2)基尼指数最小的选为最佳特征,如min{a1,a2,a3,b1,b2}总选择a1,并且以该类特征中,如a类,最小的作为子节点min{a1,a2,a3},进行展开.
3)重复1、2步骤。
总而言,决策树生成步骤为:
生成步骤:
1.将所有特征看成一个一个节点;
2.遍历每个特征的每一种分割方式,找出最好的分割点;将数据划成不同的子节点;计算划分后所有节点的信息不确定性;
3.根据2计算结果,选择最优的划分方式,得出最终的子节点N1,N2,…,Nm;
4.对子节点分别继续执行2-3步,直到最终的子节点满足要求。
5、决策树的剪枝
目的:防止过拟合;
实现:采用极小化决策树的代价函数;
计算每个节点及其父节点的损失函数,若该节点的损失函数比父节点的损失函数还大(包括等于),那么把这个节点剪掉。
CART除了生成决策树,也可以用于剪枝:
CART剪枝
算法:
1)初始值α=+∞,k=0,T=T0;
2)从底到顶,分别计算各个节点的g(t)(含义为损失函数)
3)损失函数较大的,剪掉,得到修建后的Tk;
4)重复2、3步骤;
5)使用交叉验证,从Tk,k=0,1,2,n,中选出最优子树Tα。
6.决策树细节
决策树如何处理缺失值?
- 1.当决策树开始决定选择哪个属性来进行分支时:
比如这里,选择色泽,还是根蒂还是其他。
决策时会先忽略这些缺失值,计算信息增益,然后给信息增益/信息增益率 乘于 , 这里的 是 非缺失值个数/总样本数。
然后,我们计算出分支属性。 - 2.当选择分支属性(比如色泽)后,如何确定分支属性缺失值样本处于哪个分支:
把这些缺失值样本都按权值赋予各个分支,这里的权值是根据各分支样本所占的比例,比如色泽乌黑占6/14,那么缺失值1、5、13就给予6/14的权值划分到色泽乌黑分支。其他非缺失值样本的权值都是1。 - 3.决策树生成后,如何推理待预测样本?
这个比较复杂,C4.5的处理方法是,缺失值样本会按照一定权值进入各个分支,这个权值也是按照先验概率(该分支样本数/总样本数),然后计算样本属于各个类别的概率,选择概率最大的类别当做样本的类别。
当然,上面处理方法不是唯一的,决策数有很多种处理缺失值的方法,比如 ,忽略缺失值样本,填充缺失值(均值,众数等)等。
决策树最耗时的步骤:
确定最佳分割点,这个步骤对特征的值进行排序。
针对此,xgboost和lightgbm都进行改进。
决策树选择特征进行分类时,一个特征被选择过后,是否还会选择这个特征?
会,比如特征是离散特征,会对分类的子空间继续划分;若为连续特征,则可能在决策树多次被选择。
决策树的特点:
- 优点:
1.可解释性强; - 缺点:
1.容易过拟合;
2.不适合处理高维数据;
3.对异常值敏感;
4.泛化能力(generalization)差,没出现过的值几乎处理不了。
7、决策树可视化
#python实现决策树2
from math import log
import operator
def calShannonEnt(dataSet):
numEntries = len(dataSet)
labelCounts = {}
for featVect in dataSet:
currentLabel = featVect[-1]
if currentLabel not in labelCounts.keys():
labelCounts[currentLabel] = 0
labelCounts[currentLabel] += 1
shannonEnt = 0.0
for key in labelCounts:
prob = 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 createDataSet():
dataSet = [['youth', 'no', 'no', 'just so-so', 'no'],
['youth', 'no', 'no', 'good', 'no'],
['youth', 'yes', 'no', 'good', 'yes'],
['youth', 'yes', 'yes', 'just so-so', 'yes'],
['youth', 'no', 'no', 'just so-so', 'no'],
['midlife', 'no', 'no', 'just so-so', 'no'],
['midlife', 'no', 'no', 'good', 'no'],
['midlife', 'yes', 'yes', 'good', 'yes'],
['midlife', 'no', 'yes', 'great', 'yes'],
['midlife', 'no', 'yes', 'great', 'yes'],
['geriatric', 'no', 'yes', 'great', 'yes'],
['geriatric', 'no', 'yes', 'good', 'yes'],
['geriatric', 'yes', 'no', 'good', 'yes'],
['geriatric', 'yes', 'no', 'great', 'yes'],
['geriatric', 'no', 'no', 'just so-so', 'no']]
labels = ['age', 'work', 'house', 'credit']
return dataSet, labels
def chooseBestFeatureToSplit(dataSet):
numFeatures = len(dataSet[0]) - 1
baseEntropy = calShannonEnt(dataSet)
bestInfoGain = 0.0
bestFeature = -1
for i in range(numFeatures):
featList = [example[i] for example in dataSet]
uniqueValue = set(featList)
newEntropy = 0.0
for value in uniqueValue:
subDataSet = splitDataSet(dataSet, i, value)
prob = len(subDataSet) / len(dataSet)
newEntropy += prob * calShannonEnt(subDataSet)
infoGain = baseEntropy - newEntropy
if infoGain > bestInfoGain:
bestInfoGain = infoGain
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.iteritems(), key=operator.itemgetter(1), reverse=True)
return sortedClassCount[0][0]
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]
uniqueVals = set(featValues)
for value in uniqueVals:
subLabels = labels[:]
myTree[bestFeatLabel][value] = createTree(splitDataSet(dataSet, bestFeat, value), subLabels)
return myTree
myDat, labels = createDataSet()
myTree = createTree(myDat, labels)
print(myTree)
结果:
{'house': {'yes': 'yes', 'no':
{'work': {'yes': 'yes', 'no': 'no'}}}}
参考: