学习内容
(决策树、信息熵与最优划分、基尼系数、CART)
一、决策树的概念和构建
1.什么是决策树
类似流程图的结构,其中每个内部节点表示一个测试功能,即类似做出决策的过程(动作),每个叶节点都表示一个类标签,即在计算所有特征之后做出的决定(结果)。标签和分支表示导致这些类标签的功能的连接。从根到叶的路径表示分类规则
2.决策树与条件概率
决策树表示给定特征条件下,类的条件概率分布,这个条件概率分布表示在特征空间的划分上,将特征空间根据各个特征值不断进行划分,就将特征空间分为了多个不相交的单元,在每个单元定义了一个类的概率分布,这样,这条由根节点到达叶节点的路径就成了一个条件概率分布
3.决策树的本质
从训练数据集中归纳出一组分类规则,找到一个与训练数据矛盾较小的决策树,同时具有很好的泛化能力,决策树学习是由训练数据集估计条件概率模型,不仅对训练数据有很好地拟合,而且对未知数据有很好地预测
4.决策树损失函数
决策树学习的损失函数通常是正则化的极大似然函数。以损失函数为目标函数的最小化。极大似然估计方法的原理:用使概率达到最大的那个概率值w来估计真实参数w。决策树生成的过程可以理解成对决策树模型的参数估计(就是基于特征空间划分的类的概率模型),根据训练数据的特征分布,选择使得模型最契合当前样本分布空间时的条件概率模型。当损失函数确定以后,学习问题就变为在损失函数意义下选择最优决策树的问题
5.决策树的构建
特征选择-决策树的生成-决策树的修剪
决策树学习的算法通常是一个递归地选择最优特征,并根据该特征对训练数据进行分割,使得对各个子数据集有一个最好的分类的过程。这一过程对应着对特征空间的划分,也对应着决策树的构建
这一过程对应着对特征空间的划分,也对应着决策树的构建。
决策树生成和决策树剪枝是个相对的过程,决策树生成旨在得到对于当前子数据集最好的分类效果(局部最优),而决策树剪枝则是考虑全局最优,增强泛化能力。
import numpy as np
import matplotlib.pyplot as plt
from sklearn import datasets
iris = datasets.load_iris()
X = iris.data[:,2:] # iris有四个特征,这里取后两个,形成一个坐标点
y = iris.target
# 绘图
plt.scatter(X[y==0,0],X[y==0,1])
plt.scatter(X[y==1,0],X[y==1,1])
plt.scatter(X[y==2,0],X[y==2,1])
plt.show()
from sklearn.tree import DecisionTreeClassifier
# 创建决策树对象,最大深度max_depth为2层,criterion评判标准为entropy(熵)
dt_clt = DecisionTreeClassifier(max_depth=2,criterion='entropy')
# 将训练数据送给模型
dt_clt.fit(X,y)
# 绘制决策边界
def plot_decision_boundary(model, axis): # model是模型,axis是范围
x0, x1 = np.meshgrid(
np.linspace(axis[0], axis[1],int((axis[1]-axis[0])*100)).reshape(-1,1),
np.linspace(axis[2], axis[3],int((axis[3]-axis[2])*100)).reshape(-1,1),
)
X_new = np.c_[x0.ravel(), x1.ravel()]
y_predict = model.predict(X_new)
zz = y_predict.reshape(x0.shape)
from matplotlib.colors import ListedColormap
custom_cmap = ListedColormap(['#EF9A9A','#FFF59D','#90CAF9'])
plt.contourf(x0, x1, zz, linewidth=5, cmap=custom_cmap)
# 数据可视化
plot_decision_boundary(dt_clt, axis=[0.5,7.5,0,3])
plt.scatter(X[y==0,0],X[y==0,1])
plt.scatter(X[y==1,0],X[y==1,1])
plt.scatter(X[y==2,0],X[y==2,1])
plt.show()
6.决策树总结
决策树是一个非参数的决策算法,决策树可以解决分类问题,且天然支持多分类问题。决策树也可以解决回归问题,按照树的路径追踪到叶子结点,最终叶子节点对应一个数值,且回归问题的结果是一个具体的数值,就可以落在叶子结点的所有样本的平均值,作为回归的预测结果。确定选择特征的准则。直观上,如果一个特征具有更好的分类能力,或者说,按照这一特征将训练数据集分割成子集,使得各个子集在当前条件下有最好的分类,那么就更应该选择这个特征
二、决策树的特征选择概念
1.信息熵(information entropy)
熵是热力学中的概念,表示混乱程度。熵越大,热力系统中粒子无规则的运动越剧烈;熵越小,粒子越趋近于静止的状态。信息熵表示随机变量的不确定度。对于一组数据来说,越随机、不确定性越高,信息熵越大;不确定性越低,信息熵越小。香农公式:
import numpy as np
import matplotlib.pyplot as plt
# p可以传递数值,也可以传递向量。因此使用np.log
def entropy(p):
return -p * np.log(p) - (1-p) * np.log(1-p)
# linspace生成向量x,从0到1均匀取值,绘制出x在不同值时对应的信息熵
x = np.linspace(0.01,0.99,100)
plt.plot(x,entropy(x))
plt.show()
形似抛物线,以0.5为对称轴。当x=0.5时,曲线取到最大值,也就是说对于信息熵来说,只有两个类别,其中一个类别是0.5,另一个类别是1-0.5时,此时信息熵是最大的,也就是最不确定的。如果x偏向于某一类,确定性变高了,信息熵变低了。
2.条件熵(conditional entropy)
所谓小类,就是不包含当前所选特征的其他维度,即当前的特征是给定的条件,在其他维度下求熵,是条件下的。各类别的概率,是当前这个小类别(年龄>30)下的样本量除以总的样本量。
3.信息增益(information gain)
在划分数据集前后信息发生的变化称为信息增益,获得信息增益最高的特征就是最好的选择。
条件熵H(D|A)越小,说明使用此特征划分得到的子集的不确定性越小(也就是纯度越高),因为得到的信息增益就越大。说明在决策树构建的过程中我们总是希望集合往最快到达纯度更高的子集合方向发展,因此我们总是选择使得信息增益最大的特征来划分当前数据集D。
信息增益偏向取值较多的特征
原因:当特征的取值较多时,根据此特征划分更容易得到纯度更高的子集,因此划分之后的熵更低,由于划分前的熵是一定的,因此信息增益更大,因此信息增益比较偏向取值较多的特征。
4.信息增益率(information gain ratio)
对于增益信息:g(D|信用级别) > g(D|工资级别)。很明显,信息增益偏向于选择取值较多的特征,但是根据熵的公式可知,特征越多,熵越大。
那么有什么办法呢?是在信息增益的基础之上乘上一个惩罚参数,对树分支过多的情况进行惩罚,抵消了特征变量的复杂程度,避免了过拟合的存在。
信息增益比 = 惩罚参数 * 信息增益
所谓惩罚参数,是数据集D以特征A作为随机变量的熵的倒数,即:将特征A取值相同的样本划分到同一个子集中(之前所说数据集的熵是依据类别进行划分的)。
信息增益比的缺点是:偏向取值较少的特征。原因:当特征取值较少时HA(D)的值较小,因此其倒数较大,因而信息增益比较大。因而偏向取值较少的特征。
基于以上特点,在使用增益信息比时,并不是直接选择信息增益率最大的特征,而是现在候选特征中找出信息增益高于平均水平的特征,然后在这些特征中再选择信息增益率最高的特征。
5.基尼指数(Gini index)
基尼系数(Gini),也被称为基尼不纯度,表示在样本集合中一个随机选中的样本被分错的概率。Gini系数越小表示集合中被选中的样本被分错的概率越小,也就是说集合的纯度越高,反之,基尼指数集合越不纯。
三、特征选择之寻找最优划分(代码干货)
1.信息熵的最优划分
import numpy as np
from collections import Counter
from math import log
# 每列:['年龄','有工作','有自己的房子','信贷情况','是否申请贷款']
dataSet=np.array([[0, 0, 0, 0, 0],
[0, 0, 0, 1, 0],
[0, 1, 0, 1, 1],
[0, 1, 1, 0, 1],
[0, 0, 0, 0, 0],
[1, 0, 0, 0, 0],
[1, 0, 0, 1, 0],
[1, 1, 1, 1, 1],
[1, 0, 1, 2, 1],
[1, 0, 1, 2, 1],
[2, 0, 1, 2, 1],
[2, 0, 1, 1, 1],
[2, 1, 0, 1, 1],
[2, 1, 0, 2, 1],
[2, 0, 0, 0, 0]])
featList = ['年龄','有工作','有自己的房子','信贷情况']
"""
函数说明:计算给定标签的经验熵(信息熵)
Parameters:
y:使用标签y计算信息熵,,此时传递y是多维数组
计算信息熵需要每种类别出现的概率p,因此传入包含分类信息的标签y
Returns:
entropy:经验熵
"""
def calEntropy(y):
# 计数器,统计y中所有类别出现的次数
# 扁平化,将嵌套的多维数组变成一维数组
counter = Counter(y.flatten())
entropy = 0
for num in counter.values():
p = num / len(y)
entropy += -p * log(p)
return entropy
"""
函数说明:根据传递进来的特征维度及值,将数据划分为2类
Parameters:
X,y,featVec,value:特征向量、标签、特征维度、值
Returns:
返回划分为两类的后的数据
"""
def split(X, y, featVec, value):
# 使用维度featVect上的value,将数据划分成左右两部分
# 得到的布尔向量,传入array中做索引,即可找出满足条件的相应数据(布尔屏蔽)
index_a = (X[:,featVec] <= value)
index_b = (X[:,featVec] > value)
return X[index_a], X[index_b], y[index_a], y[index_b]
"""
函数说明:寻找最优划分
Parameters:
X,y:特征向量、标签
Returns:
返回最优熵,以及在哪个维度、哪个值进行划分
"""
def try_split(X, y):
# 搞一个熵的初始值:正无穷
best_entropy = float('inf')
best_featVec = -1 # 特征向量
best_value = -1
# 遍历每一个特征维度(列)
for featVec in range(X.shape[1]):
# 然后需要找到每个特征维度上的划分点。
# 找出该维度上的每个两个样本点的中间值,作为候选划分点。
# 为了方便寻找候选划分点,可以对该维度上的数值进行排序,
# argsort函数返回的是数组值从小到大的索引值(不打乱原来的顺序)
sort_index = np.argsort(X[:,featVec])
for i in range(1, len(X)):
if X[sort_index[i-1], featVec] != X[sort_index[i], featVec]:
value = (X[sort_index[i-1], featVec] + X[sort_index[i], featVec]) / 2
X_l, X_r, y_l, y_r = split(X, y, featVec, value)
# 要求最优划分,需要看在此划分下得到的两个分类数据集的熵之和是否是最小的
entropy = calEntropy(y_l) + calEntropy(y_r)
if entropy < best_entropy:
best_entropy, best_featVec, best_value = entropy, featVec, value
return best_entropy, best_featVec, best_value
best_entropy, best_featVec, best_value = try_split(X, y)
print("最优熵:", best_featVec)
print("在哪个维度熵进行划分:", best_featVec)
print("在哪个值上进行划分:", best_value)
2.信息增益的最优划分
import numpy as np
from collections import Counter
from math import log
# 每列:['年龄','有工作','有自己的房子','信贷情况','是否申请贷款'],其中'是否申请贷款'是label
dataSet=np.array([[0, 0, 0, 0, 0],
[0, 0, 0, 1, 0],
[0, 1, 0, 1, 1],
[0, 1, 1, 0, 1],
[0, 0, 0, 0, 0],
[1, 0, 0, 0, 0],
[1, 0, 0, 1, 0],
[1, 1, 1, 1, 1],
[1, 0, 1, 2, 1],
[1, 0, 1, 2, 1],
[2, 0, 1, 2, 1],
[2, 0, 1, 1, 1],
[2, 1, 0, 1, 1],
[2, 1, 0, 2, 1],
[2, 0, 0, 0, 0]])
X = dataSet[:,:4]
y = dataSet[:,-1:]
strs = ['年龄','有工作','有自己的房子','信贷情况','是否申请贷款']
"""
函数说明:计算经验熵
Parameters:
dataSet:样本数据集D
Returns:
entory:经验熵
"""
def calEntropy(dataSet):
#返回数据集行数
numEntries=len(dataSet)
#保存每个标签(label)出现次数的字典:<label:出现次数>
labelCounts={}
#对每组特征向量进行统计
for featVec in dataSet:
#提取标签信息
currentLabel=featVec[-1]
#如果标签没有放入统计次数的字典,添加进去
if currentLabel not in labelCounts.keys():
labelCounts[currentLabel]=0
#label计数
labelCounts[currentLabel]+=1
entory=0.0
#计算经验熵
for key in labelCounts:
#选择该标签的概率
prob=float(labelCounts[key])/numEntries
#利用公式计算
entory-=prob*log(prob,2)
return entory
"""
函数说明:得到当前特征条件下的小类的所有样本集合(即不包含当前特征的特征样本集)
Parameters:
dataSet:样本数据集D
curtFeatIndex:当前用来划分数据集的特征A的位置
categories:特征A所有可能分类的集合
Returns:
otherFeatSets:不包含当前特征的特征样本集
"""
def currentConditionSet(dataSet, curtFeatIndex, categroy):
otherFeatSets = []
# 对于数据集中的所有特征向量,抛去当前特征后拼接好的集合
for featVec in dataSet:
if featVec[curtFeatIndex] == categroy:
otherFeatSet = np.append(featVec[:curtFeatIndex],featVec[curtFeatIndex+1:])
otherFeatSets.append(otherFeatSet)
return otherFeatSets
"""
函数说明:在选择当前特征的条件下,计算熵,即条件熵
Parameters:
dataSet:样本数据集D
curtFeatIndex:当前用来划分数据集的特征A的位置
categories:特征A所有可能分类的集合
Returns:
conditionalEnt:返回条件熵
"""
def calConditionalEnt(dataSet, curtFeatIndex, categories):
conditionalEnt = 0
# 对于每一个分类,计算选择当前特征的条件下条件熵
# 比如在选择“年龄”这一特征下,共有“老中青”三个小分类
for categroy in categories:
# 得到当前特征条件下的小类的所有样本集合,即不包含当前特征的特征样本集
# 如得到在选择“青年”这个小类下一共有5个样本,且不包含“年龄”这一特征
cdtSetCategroy = currentConditionSet(dataSet, curtFeatIndex, categroy)
# 计算当前特征条件下的小分类,占总分类的比例
prob = len(cdtSetCategroy) / float(dataSet.shape[0])
# 累加得到条件熵
conditionalEnt += prob * calEntropy(cdtSetCategroy)
return conditionalEnt
"""
函数说明:计算信息增益
Parameters:
baseEntropy:划分样本集合D的熵是为H(D),即基本熵
dataSet:样本数据集D
curtFeatIndex:当前用来划分数据集的特征A的位置
Returns:
infoGain:信息增益值
"""
def calInfoGain(baseEntropy,dataSet,curtFeatIndex):
conditionalEnt = 0.0
# categories是所有特征向量中当前特征的对应值的set集合(去重复)
# 相当于该特征一共有几种分类,如“年龄”这一特征,分为“老中青”三类
categories = set(dataSet[:,curtFeatIndex])
# 计算划分后的数据子集(给定特征A的情况下,数据集D)的条件熵(经验条件熵)H(D|A)
conditionalEnt = calConditionalEnt(dataSet,curtFeatIndex,categories)
# 计算信息增益:g(D,A)=H(D)−H(D|A)
infoGain = baseEntropy - conditionalEnt
#打印每个特征的信息增益
print("第%d个特征的增益为%.3f" % (curtFeatIndex, infoGain))
return infoGain
"""
函数说明:寻找最优划分
Parameters:
dataSet:数据集
Returns:
打印最优划分结果
"""
def optimalPartition(dataSet):
bestInfoGain = -1 # 最佳信息增益初始值
bestFeatVec = -1 # 最佳划分的特征向量
# 划分前样本集合D的熵H(D),即基本熵
baseEntropy = calEntropy(dataSet)
# 遍历每一个特征维度(列),得到基于当前特征划分的信息增益
for curtFeatIndex in range(dataSet.shape[1]-1):
# 计算信息增益
infoGain = calInfoGain(baseEntropy, dataSet, curtFeatIndex)
# 选取最优信息增益的划分
if (infoGain > bestInfoGain):
#更新信息增益,找到最大的信息增益
bestInfoGain = infoGain
#记录信息增益最大的特征的索引值
bestFeatVec = curtFeatIndex
print("最佳的划分为第%d个特征,是”%s“,信息增益为%.3f" % (bestFeatVec,featList[bestFeatVec],bestInfoGain))
return bestFeatVec
optimalPartition(dataSet)
3.信息增益率的最优划分
"""
函数说明:计算惩罚参数,信息增益g(D,A)与训练数据集D关于特征A的值的熵HA(D)之比
Parameters:
dataSet:样本数据集D
curtFeatIndex:当前用来划分数据集的特征A的位置
categories:特征A所有可能分类的集合
Returns:
conditionalEnt:惩罚参数
"""
def calPenaltyPara(dataSet, curtFeatIndex, categories):
penaltyItem = 1
# 对于每一个分类,计算选择当前特征的条件下条件熵
# 比如在选择“年龄”这一特征下,共有“老中青”三个小分类
for categroy in categories:
# 得到当前特征条件下的小类的所有样本集合,即不包含当前特征的特征样本集
# 如得到在选择“青年”这个小类下一共有5个样本,且不包含“年龄”这一特征
cdtSetCategroy = currentConditionSet(dataSet, curtFeatIndex, categroy)
# 计算当前特征条件下的小分类,占总分类的比例
prob = len(cdtSetCategroy) / float(dataSet.shape[0])
# 累加得到惩罚项
penaltyItem += -prob * log(prob,2)
return penaltyItem
"""
函数说明:计算信息增益率(惩罚参数 * 信息增益)
Parameters:
baseEntropy:划分样本集合D的熵是为H(D),即基本熵
dataSet:样本数据集D
curtFeatIndex:当前用来划分数据集的特征A的位置
Returns:
infoGain:信息增益值
"""
def calInfoGainRate(baseEntropy,dataSet,curtFeatIndex):
infoGainRate = 0.0
# 计算信息增益
infoGain = calInfoGain(baseEntropy,dataSet,curtFeatIndex)
# 得到该特征的所有分类
categories = set(dataSet[:,curtFeatIndex])
# 计算惩罚项
penaltyItem = calPenaltyPara(dataSet, curtFeatIndex, categories)
# 计算信息增益率
infoGainRatio = infoGain / penaltyItem
#打印每个特征的信息增益率
print("第%d个特征的增益率为%.3f" % (curtFeatIndex, infoGainRatio))
return infoGainRatio
"""
函数说明:寻找最优划分
Parameters:
dataSet:数据集
Returns:
打印最优划分结果
"""
def optimalPartition(dataSet):
bestInfoGainRatio = 0.0 # 最佳信息增益率初始值
bestFeatVec = -1 # 最佳划分的特征向量
# 划分前样本集合D的熵H(D),即基本熵
baseEntropy = calEntropy(dataSet)
# 遍历每一个特征维度(列),得到基于当前特征划分的信息增益
for curtFeatIndex in range(dataSet.shape[1]-1):
# categories是所有特征向量中当前特征的对应值的set集合(去重复)
# 相当于该特征一共有几种分类,如“年龄”这一特征,分为“老中青”三类
#categories = set(dataSet[:,curtFeatIndex])
# 计算信息增益率
infoGainRatio = calInfoGainRate(baseEntropy, dataSet, curtFeatIndex)
# 选取最优信息增益率的划分
if (infoGainRatio > bestInfoGainRatio):
#更新信息增益率,找到最大的信息增益率
bestInfoGainRatio = infoGainRatio
#记录信息增益率最大的特征的索引值
bestFeatVec = curtFeatIndex
print("最佳的划分为第%d个特征,是”%s“,信息增益率为%.3f" % (bestFeatVec,strs[bestFeatVec],bestInfoGainRatio))
return
optimalPartition(dataSet)
4.基尼系数的最优划分
"""
函数说明:计算基尼系数
Parameters:
y:使用标签y计算信息熵,此时传递y是多维数组
Returns:
entropy:经验熵
"""
def calGini(y):
# 计数器,统计y中所有类别出现的次数
# 扁平化,将嵌套的多维数组变成一维数组
counter = Counter(y.flatten())
gini = 1
for num in counter.values():
p = num / len(y)
gini -= p ** 2
return gini
"""
函数说明:寻找最优划分
Parameters:
X,y:特征向量、标签
Returns:
返回最优熵,以及在哪个维度、哪个值进行划分
"""
def try_split(X, y):
# 搞一个基尼系数的初始值:正无穷
bestGini = float('inf')
bestFeatVec = -1 # 特征向量
bestValue = -1
# 遍历每一个特征维度(列)
for featVec in range(X.shape[1]):
# 然后需要找到每个特征维度上的划分点。
# 找出该维度上的每个两个样本点的中间值,作为候选划分点。
# 为了方便寻找候选划分点,可以对该维度上的数值进行排序,
# argsort函数返回的是数组值从小到大的索引值(不打乱原来的顺序)
sort_index = np.argsort(X[:,featVec])
for i in range(1, len(X)):
if X[sort_index[i-1], featVec] != X[sort_index[i], featVec]:
value = (X[sort_index[i-1], featVec] + X[sort_index[i], featVec]) / 2
X_l, X_r, y_l, y_r = split(X, y, featVec, value)
# 要求最优划分,需要看在此划分下得到的两个分类数据集的熵之和是否是最小的
gini = calGini(y_l) + calGini(y_r)
if gini < bestGini:
bestGini, bestFeatVec, bestValue = gini, featVec, value
return bestGini, bestFeatVec, bestValue
bestGini, bestFeatVec, bestValue = try_split(X, y)
print("最优基尼系数:", bestGini)
print("在哪个维度上进行划分:", bestFeatVec)
print("在哪个值上进行划分:", bestValue)
四、构建算法之ID3、C4.5
1.ID3
ID3算法是一种分类预测算法,算法以信息论中的“信息增益”为基础。核心是通过计算每个特征的信息增益,每次划分选取信息增益最高的属性为划分标准,递归地构建决策树。ID3相当于用极大似然法进行概率模型的选择。
具体方法是:
- 从根结点(root node)开始,对结点计算所有可能的特征的信息增益,选择信息增益最大的特征作为结点的特征。
- 由该特征的不同取值建立子节点,再对子结点递归地调用以上方法,构建决策树;直到所有特征的信息增益均很小或没有特征可以选择为止;
- 最后得到一个决策树。
从ID3的构建树过程而言,它可以看成使用贪心算法得到近似最优的一颗决策树,它无法保证是最优的
算法:createTree(dataSet,featList,bestFeatLists)。由给定的训练数据产生一棵判定树。
输入:
dataSet:训练数据集
featList:分类属性标签
bestFeatLists:存储选择的最优特征标签
输出:
myTree:一棵判定树。
方法:
createTree(dataSet,featList,bestFeatLists)
1)从传入的数据集dataSet中切割出分类标签,yList
2)如果yList中只有同一种标签,说明已经递归到分类边界了,则返回该标签
3)如果已经处理了dataSet中所有属性(列),但是类标签依然不是唯一的,采用多数判决的方法决定该子节点的分类
4)找出dataSet最优划分(信息增益最大)的特征所在位置bestFeatVec
5)在分类属性标签featList找出该位置所对应的特征值bestFeatLabel,并将该特征值存储到bestFeatLists中
6)将最优划分特征值作为当前(子)树的根节点,生成初始决策树myTree(用字典表示一个树结构)
7)在featList中删除当前已经使用过的特征标签(因为每次选择特征作为条件,dataSet会删掉这一列,形成新的子类,因此对应的featList中的值也要删掉)
8)确定子树分支:获取已选择的最优划分特征所对应的值分类categories(如“年龄”是最优特征,则“老”“中”“青”三个子类)
9)遍历每一个当前特征下的子类,在每个子类中,递归地调用创建决策树的方法,将递归调用的结果作为当前树节点的一个分支(构建树的方法是:特征作为字典的key,所得到的分类结果作为value;子树进行嵌套)
2.ID3优缺点
相对于其他数据挖掘算法,决策树在以下几个方面拥有优势:
- 决策树易于理解和实现. 人们在通过解释后都有能力去理解决策树所表达的意义。
- 对于决策树,数据的准备往往是简单或者是不必要的 . 其他的技术往往要求先把数据一般化,比如去掉多余的或者空白的属性。能够同时处理数据型和常规型属性。其他的技术往往要求数据属性的单一。
- 是一个白盒模型如果给定一个观察的模型,那么根据所产生的决策树很容易推出相应的逻辑表达式。
- 易于通过静态测试来对模型进行评测。表示有可能测量该模型的可信度。
- 在相对短的时间内能够对大型数据源做出可行且效果良好的结果
ID3算法可用于划分标准称型数据,但存在一些问题: - 没有剪枝过程,为了去除过渡数据匹配的问题,可通过裁剪合并相邻的无法产生大量信息增益的叶子节点;
- 信息增益的方法偏向选择具有大量值的属性,也就是说某个属性特征索取的不同值越多,那么越有可能作为分裂属性,这样是不合理的;
- 只可以处理离散分布的数据特征
- ID3算法只考虑了树的生成,即尽可能的是模型拟合当前训练数据集,所以该算法生成的树容易过拟合
注意:该算法使用了贪婪搜索,从不回溯重新考虑之前的选择情况。
3.C4.5
C4.5算法是数据挖掘十大算法之一,它是对ID3算法的改进,相对于ID3算法主要有以下几个改进
- 用信息增益比来选择属性
- 在决策树的构造过程中对树进行剪枝
- 对非离散数据也能处理
- 能够对不完整数据进行处理
C4.5算法与ID3算法过程相似,仅在特征选择时,使用信息增益比作为特征选择准则
4.总结
一、ID3:
熵表示的是数据中包含的信息量大小。熵越小,数据的纯度越高,也就是说数据越趋于一致,这是我们希望的划分之后每个子节点的样子。
信息增益 = 划分前熵 - 划分后熵。信息增益越大,则意味着使用属性 a 来进行划分所获得的 “纯度提升” 越大 **。也就是说,用属性 a 来划分训练集,得到的结果中纯度比较高。
ID3 仅仅适用于二分类问题。ID3 仅仅能够处理离散属性。
二、C4.5:
C4.5 克服了 ID3 仅仅能够处理离散属性的问题,以及信息增益偏向选择取值较多特征的问题,使用信息增益比来选择特征。信息增益比 = 信息增益 / 划分前熵 选择信息增益比最大的作为最优特征。
C4.5 处理连续特征是先将特征取值排序,以连续两个值中间值作为划分标准。尝试每一种划分,并计算修正后的信息增益,选择信息增益最大的分裂点作为该属性的分裂点。
三、信息增益 vs 信息增益比:
之所以引入了信息增益比,是由于信息增益的一个缺点。那就是:信息增益总是偏向于选择取值较多的属性。信息增益比在此基础上增加了一个罚项,解决了这个问题。
五、剪枝与sklearn中的决策树
决策树是依据训练集进行构建的,为了尽可能正确地分类训练样本,结点划分过程将不断重复,有时会造成决策树分支过多。这就可能会把训练样本学的“太好”了,以至于把训练集自身的一些特点当作所有数据都具有的一般性质而导致过拟合。因此可主动去掉一些分支来降低过拟合风险。
决策树非常容易产生过拟合,实际所有非参数学习算法,都非常容易产生过拟合。
因此,对于决策树的构建还需要最后一步,即决策树的修剪。两个目的:降低复杂度,解决过拟合。
决策树的修剪,也就是剪枝操作,主要分为两种:
- 预剪枝(Pre-Pruning)
- 后剪枝(Post-Pruning)
1.预剪枝
对每个节点在划分前先进行估计,若当前节点的划分不能带来决策树泛化性能的提升,则停止划分并将当前节点标记为叶节点。
对比未剪枝的决策树和经过预剪枝的决策树可以看出:预剪枝使得决策树的很多分支都没有“展开”,这不仅降低了过拟合的风险,还显著减少了决策树的训练时间开销和测试时间开销。但是,另一方面,因为预剪枝是基于“贪心”的,所以,虽然当前划分不能提升泛化性能,但是基于该划分的后续划分却有可能导致性能提升,因此预剪枝决策树有可能带来欠拟合的风险。
2.后剪枝
后剪枝是先从训练集生成一颗完整的决策树,然后自底向上地对非叶节点进行考察,若将该节点对应的子树完全替换为叶节点能带来决策树繁花性的提升,则将该子树替换为叶节点。
3.总结
对比预剪枝和后剪枝,能够发现,后剪枝决策树通常比预剪枝决策树保留了更多的分支,一般情形下,后剪枝决策树的欠拟合风险小,泛华性能往往也要优于预剪枝决策树。但后剪枝过程是在构建完全决策树之后进行的,并且要自底向上的对树中的所有非叶结点进行逐一考察,因此其训练时间开销要比未剪枝决策树和预剪枝决策树都大得多。
六、分类与回归树CART
1.CART算法
CART算法:Classification And Regression Tree。顾名思义,CART算法既可以用于创建分类树(Classification Tree),也可以用于创建回归树(Regression Tree)、模型树(Model Tree),两者在建树的过程稍有差异。既可以解决分类问题,也可以解决回归问题。根据某一个维度d和某一个阈值v进行二分,得到的决策树是二叉树。
ID3中使用了信息增益选择特征,增益大优先选择。C4.5中,采用信息增益比选择特征,减少因特征值多导致信息增益大的问题。CART分类树算法使用基尼系数来代替信息增益比,基尼系数代表了模型的不纯度,基尼系数越小,不纯度越低,特征越好。这和信息增益(比)相反。
CART作为分类树时,特征属性可以是连续类型也可以是离散类型,但观察属性(即标签属性或者分类属性)必须是离散类型
- 离散特征
CART采用的是不停的二分。会考虑把特征A分成{A1}和{A2,A3}、{A2}和{A1,A3}、{A3}和{A1,A2}三种情况,找到基尼系数最小的组合 - 连续特征
对于连续属性先进行排序(升序),只有在决策属性(即分类发生了变化)发生改变的地方才需要切开,这可以显著减少运算量。
注意的是,与ID3、C4.5处理离散属性不同的是,如果当前节点为连续属性,则该属性在后面还可以参与子节点的产生选择过程。
CART分类树建立算法流程,之所以加上建立,是因为CART分类树算法有剪枝:
算法从根节点开始,用训练集递归建立CART分类树。
输入:训练集D,基尼系数的阈值,样本个数阈值。
输出:决策树T。
1)对于当前节点的数据集为D,如果样本个数小于阈值或没有特征,则返回决策子树,当前节点停止递归。
2)计算样本集D的基尼系数,如果基尼系数小于阈值,则返回决策树子树,当前节点停止递归。
3)计算当前节点现有的各个特征的各个特征值对数据集D的基尼系数。
4)在计算出来的各个特征的各个特征值对数据集D的基尼系数中,选择基尼系数最小的特征A和对应的特征值a。根据这个最优特征和最优特征值,把数据集划分成两部分D1和D2,同时建立当前节点的左右节点,做节点的数据集D为D1,右节点的数据集D为D2。
5)对左右的子节点递归的调用1-4步,生成决策树。
2.回归树
CART回归树和CART分类树的建立类似,区别在于样本的输出,如果样本输出是离散值,这是分类树;样本输出是连续值,这是回归树。分类树的输出是样本的类别,回归树的输出是一个实数。
并且分类树采用基尼系数的大小度量特征各个划分点的优劣。而回归树采用最小化均方差和进行最优划分特征的选择,对于划分特征A,划分点s两边的数据集D1和D2,求出使D1和D2各自集合的均方差最小,同时D1和D2的均方差之和最小,对应的特征和特征值划分点
3.CART剪枝
import numpy as np
import matplotlib.pyplot as plt
from sklearn import datasets
from sklearn.model_selection import train_test_split
boston = datasets.load_boston()
X = boston.data
y = boston.target
X_train, X_test, y_train, y_test = train_test_split(X, y, random_state=666)
from sklearn.tree import DecisionTreeRegressor
dt_reg = DecisionTreeRegressor()
dt_reg.fit(X_train, y_train)
dt_reg.score(X_test, y_test)
该算法既可以做分类,又可以做回归。在分类和回归时,其算法流程大致相同,但是其特征划分、输出预测结果等步骤是不同的,大家要多加对比和注意
#############################################################################################################
@ 2019.12.21 木居居士的机器学习小组 第七周 打卡
安利公益监督学习组织 - 【公众号】数据科学家联盟
https://mp.weixin.qq.com/s/1WWmbLZucz9vIp-4tKKQ5Q
感谢木东大佬、饼干大佬、南头大佬、星空妹砸、Desitiny、DD的无私付出,抱拳ing~