决策树
概念
决策树是一种有监督的机器学习算法,该方法可以用于解决分类和回归问题。决策树可以简单地理解为达到某一特定结果的一系列决策。
优点
- 易于理解和解释: 决策树的结构类似于人类的决策过程,因此容易理解和解释。它们可以可视化为树状图,便于展示和沟通。
- 不需要大量数据预处理: 决策树模型对于数据的缺失值或异常值具有相对较好的容忍度,通常不需要大量的数据预处理。
- 适用于离散型和连续型数据: 决策树可以处理离散型和连续型特征,不需要对数据进行特殊的转换。
- 计算复杂度相对较低: 在训练阶段,决策树的计算复杂度相对较低,使得它们在处理大型数据集时效率较高。
- 能够处理多输出问题: 决策树可以自然地处理多输出问题,而无需进行复杂的修改。
- 对于特征的缩放不敏感: 决策树的算法不受特征缩放的影响,即使特征具有不同的尺度也能够很好地工作。
缺点
- 容易过拟合: 决策树很容易在训练数据上过拟合,特别是当树的深度较大时。为了缓解过拟合,通常需要使用剪枝技术。决策树的过拟合是指模型在训练数据上表现很好,但在未见过的测试数据上表现较差的现象。具体来说,过拟合发生在决策树过度匹配训练数据的特点和噪声,而忽略了数据中的真实模式。
- 不稳定性: 输入数据的微小变化可能导致生成完全不同的树结构,这使得决策树在一些情况下比较不稳定。
- 忽略特征之间的关联性: 决策树通常假设特征之间是独立的,因此可能忽略了特征之间的相关性,这在某些情况下可能影响模型的性能。
- 不适合处理复杂的关系: 决策树可能无法捕捉一些复杂的关系,特别是涉及到多个特征之间复杂交互的情况。
- 类别不平衡问题: 当类别的分布不平衡时,即某个类别的样本数量远远超过其他类别时,决策树可能偏向于支持样本数量更多的类别。
- 对噪声敏感: 决策树对于数据中的噪声和异常值相当敏感,这可能导致产生不稳定的树结构。
包含的概念
信息量
一个事件发生的概率越大,包含的信息量越小。信息量公式:
− log 2 p i -\log_{2}{p_i} −log2pi
信息熵
信息熵是对事件中不确定的信息的度量,不确定信息越大,信息熵越大。
一个系统的信息熵
E
(
P
)
=
−
∑
i
p
i
log
2
p
i
E(P)=-\sum_{i}^{} p_i\log_{2}{p_i}
E(P)=−i∑pilog2pi
其中p_i为事件发生的概率 (相当于加权)
信息增益
信息增益 = 信息熵 - 条件熵
条件熵:在每一个小类里面,都计算一个小熵,然后每一个小熵乘以各个类别的概率,然后求和。
通俗一点就是每一类中不同属性,计算不同属性的信息熵,最后用占比、加和来求最终的条件熵
流程图
步骤
一、求出维度条件下的条件熵和自然熵
二、算出信息增益,求解最优特征值
三、根据特征值的大小排序,创建决策树
决策树创建实战示例:公司老板发红包
例题
解决问题流程图
Python代码
算法思路
1.首先如何处理原始数据?
2.如何根据现有数据集计算熵值?
3.如何找去掉最佳特征后的数据集?
4.信息增益怎么计算?(计算每一个条件熵和信息熵的差值,返回最好的信息增益的索引)
5.信息增益相同怎么划分结点?(用sort排序)
6.递归构建决策树(前面的函数找到最佳特征,和值,删掉特征标签,)
决策树代码
#计算熵值
from math import log
def calcEntroy(datas):
datalen=len(datas) #数据总数
labels={}
#统计每个标签的出现次数
for feat in datas:
current= feat[-1] #数据的最后一个元素,标签
if current not in labels.keys(): #遇见的标签不在 字典的键中
labels[current]=0 #初始化
labels[current]+=1
entroy=0.0 #初始化熵 表示有浮点数
#计算熵
for key in labels:
prob=float(labels[key])/datalen
entroy-=prob*log(prob,2)
return entroy
#原始数据集
def createDatas():
datas=[[1,1,'发'],[1,1,'发'],[1,0,'不发'],[0,1,'不发'],[0,1,'不发']]
labels=["是否属于某种节日","是否是该员工的生日"]
return datas, labels
#划分数据集
def splitDatas(datas,axis,value): #需要划分的数据集 ,指定的特征, 特征对应的返回值
returnDatas=[]
for feat in datas:
if feat[axis]==value:
featcopy=feat[:axis]
featcopy.extend(feat[axis+1:])
returnDatas.append(featcopy)
return returnDatas
#信息增益计算
def chooseBest(datas):
numFlag=len(datas[0])-1 #获取凷目标变量列的数量
base=calcEntroy(datas)
bestGain=0.0 #初始化最佳信息熵
bestFlag=-1 #最佳标志 数据集中哪一列最好
for i in range(numFlag):
# 获取当前特征的所有取值
lists=[feat[i] for feat in datas]
#因为要划分子数据集,所以要集合去重,比如好几个数据,而性别只有男女
simpleVals=set(lists)
#计算基于当前特征的新熵值
entroy_new=0.0
for value in simpleVals:
# 根据当前特征的取值划分数据集
subDatas=splitDatas(datas,i,value)
#计算占比
prob=len(subDatas)/float(len(datas))
#计算子数据集的熵值 并求和
entroy_new+=prob*calcEntroy(subDatas)
gain=base-entroy_new
if gain>bestGain:
bestGain=gain
bestFlag=i
return bestFlag
#信息增益相同 采用多数表决法决定叶子节点分类 表决函数
import operator
def majority(classes):
#统计每个类别的数量
class_num={}
for cls in classes:
if cls not in class_num.keys():
class_num[cls]=0
class_num[cls]+=1
sorted_class_num=sorted(class_num.items(),key=operator.itemgetter(1),reverse=True)
return sorted_class_num
def createTree(datas, labels):
# 获取数据集中的所有类别
classes = [example[-1] for example in datas]
# 如果所有数据属于同一类别,返回该类别作为叶子节点
if classes.count(classes[0]) == len(classes):
return classes[0]
# 如果数据集中的特征只剩下一个,返回多数表决的结果作为叶子节点
if len(datas[0]) == 1:
return majorityNode(classes)
# 选择最佳的特征进行划分 由于是递归,每一次都会计算新的最佳特征值 去掉前一个最佳
bestFlag = chooseBest(datas)
bestFlagLabel = labels[bestFlag]
result_tree = {bestFlagLabel: {}}
# 删除已经使用的特征标签
del labels[bestFlag]
# 获取最佳特征的所有取值
flag_values = [example[bestFlag] for example in datas]
unique_values = set(flag_values)
# 对每个取值递归构建子树
for mValue in unique_values:
sub_labels = labels[:]
result_tree[bestFlagLabel][mValue] = createTree(splitDatas(datas, bestFlag, mValue), sub_labels)
# 返回构建好的决策树
return result_tree
#测试是否分类成功
datas,labels=createDatas()
print(createTree(datas,labels))