决策树(学习笔记)

算法学习

算法理解

  • 决策树的本质就是从数据集中归纳出一组分类规则,也称‘树归纳’,对于给定数据集,存在许多对他无错编码的树,我们感兴趣的是从中选出最小的树(树的结点数和决策结点的复杂性度量)。(一个if-then规则的集合)
  • 从另一个角度看,决策树学习是根据训练数据集估计条件概率模型。基于特征空间划分的类的条件概率模型有无数个,我们选择的数据应该是不仅能对训练数据有很好的拟合,而且对未知数据也有很好的预测。
  • 从所有可能的树中选取最优的树是NP完全问题,所以我们必须使用基于启发式的局部搜索过程,在合理的时间内找到合理的树。
  • 树的学习是‘贪婪算法’,从包含全部训练数据的根开始,每一步的选择都是最佳划分。
    NP完全问题

决策树生成步骤

  • 特征选择
  • 决策树的生成(考虑局部最优)
  • 决策树的剪枝(考虑全局最优)

分类树手写代码实现

如何分支(不纯度的度量指标)

  • 分类树:基尼系数;信息熵
  • 回归树:MSE均方误差

构造数据集的最佳切分函数并返回结果(特征选择)

关于香农熵的问题

# 定义熵函数(Entropy function熵函数)
def calEnt(dataset):
    n=dataset.shape[0]
    i=dataset.iloc[:,-1].value_counts()
    p=i/n
    ent=(-p*np.log2(p)).sum()
    return ent
# 手写数据
row_data = {'accompany':[0,0,0,1,1],
             'game':[1,1,0,1,1],
             'bad boy':['yes','yes','no','no','no']}
data = pd.DataFrame(row_data)
data
calEnt(data)
# 第0列的信息增益
a = 3/5*(-2/3*np.log2(2/3)-1/3*np.log2(1/3))
calEnt(data)-a
# 第1列的信息增益
b = 4/5*(-2/4*np.log2(2/4)-2/4*np.log2(2/4))
calEnt(data)-b
  • 第0列的信息增益大于第1列的信息增益,所以应该选择第0列切分数据集
""" 
函数功能:根据信息增益选择出最佳数据集切分的列
参数说明:
    dataSet:原始数据集
返回:
    axis:数据集最佳切分列的索引
"""
def bestSplit(dataSet):
    baseEnt = calEnt(dataSet)                                # 计算原始熵
    bestGain = 0                                             # 初始化信息增益
    axis = -1                                                # 初始化最佳切分列,标签列
    for i in range(dataSet.shape[1]-1):                      # 对特征的每一列进行循环
        levels= dataSet.iloc[:,i].value_counts().index       # 提取出当前列的所有取值
        ents = 0                                             # 初始化子节点的信息熵
        for j in levels:                                     # 对当前列的每一个取值进行循环
            childSet = dataSet[dataSet.iloc[:,i]==j]         # 某一个子节点的dataframe
            ent = calEnt(childSet)                           # 计算某一个子节点的信息熵
            ents += (childSet.shape[0]/dataSet.shape[0])*ent # 计算当前列的信息熵
            print(f'第{i}列的信息熵为{ents}')
        infoGain = baseEnt-ents                              # 计算当前列的信息增益
        print(f'第{i}列的信息增益为{infoGain}')
        if (infoGain > bestGain):
            bestGain = infoGain                              # 选择最大信息增益
            axis = i                                         # 最大信息增益所在列的索引
    return axis
# 输出结果:0
bestSplit(data)
col = data.columns[0]
col
data.loc[data['accompany']==1,:].drop('accompany',axis=1)
'''
函数功能:按照给定的列划分数据集
参数说明:
dataSet:原始数据集
axis:指定的列索引
value:指定的属性值
返回:
redateset:按照指定索引和属性值切分后的数据集
'''
def mySplit(dataSet,axis,value):
    col = dataSet.columns[axis]
    redateset = dataSet.loc[dataSet[col]==value,:].drop(col,axis=1)
    return redateset
# 例子
mySplit(data,0,1)
data.iloc[:,-1].value_counts()

决策树生成

递归结束的条件:

  • 程序遍历完所有划分数据集的属性
  • 每个分支下的所有实例都具有相同的分类
  • 当前结点包含的样本集和为空,不能划分

ID3算法

ID3算法:在决策树的各个结点应用信息增益准则选择特征,递归的构建决策树。
具体方法:

  • 从根结点开始,对结点计算所有可能的特征的信息增益
  • 选择信息增益最大的特征作为结点特征,由该特征的不同取值建立子结点
  • 再对子结点调用以上方法构建决策树
  • 直到所有的信息增益均很小或没有特征可以选择为止,最后得到一个决策树
'''
函数说明:基于最大信息增益切分数据集,递归构建决策树
参数说明:
    dataSet:原始数据集(最后一列是标签)
返回:
    myTree:字典形式的树
'''
def createTree(dataSet):
    featlist = list(dataSet.columns)                        # 提取出数据集所有的列
    classlist = dataSet.iloc[:,-1].value_counts()           # 获取最后一列类标签
    # 判断最多标签数目是否等于数据集行数,或者数据集是否只有一列
    if classlist[0]==dataSet.shape[0] or dataSet.shape[1] == 1:
        return classlist.index[0]                           # 如果是,返回类标签
    axis = bestSplit(dataSet)                               # 确定出当前最佳切分列的索引
    bestfeat = featlist[axis]                               # 获取该索引对应的特征
    myTree = {bestfeat:{}}                                  # 采用字典嵌套的方式存储树信息
    del featlist[axis]                                      # 删除当前特征
    valuelist = set(dataSet.iloc[:,axis])                   # 提取最佳切分列所有属性值
    for value in valuelist:                                 # 对每一个属性值递归建树
        myTree[bestfeat][value] = createTree(mySplit(dataSet,axis,value))
    return myTree
# 生成字典形式的树
mytree = createTree(data)
mytree

ID3算法的局限性:

  • 分支度越高离散变量子节点的总信息熵越小,容易出现极端情况
  • 不能直接处理连续性变量,先要对连续性变量离散化
  • 对缺失值敏感,使用ID3之前先要处理缺失值
  • 没有剪枝的设置,很容易导致过拟合

C4.5

  • 修改局部最优化条件:用信息增益比准则来选择特征(信息增益比:信息增益/训练数据关于某一特征值的熵)
  • 增加连续变量处理手段:为连续变量分箱提供了模型方法,这也是决策树的最常见用途之一

决策树剪枝

  • 预剪枝:降低了过拟合风险,由于贪婪算法的本质是禁止后续分支展开,带来了欠拟合风险;训练开销和测试开销小
  • 后剪枝:先生成决策树,自上而下逐一考察,欠拟合风险小,泛化能力更强;训练开销大

CART算法

CART:分类回归树

  • 分裂过程是一个二叉递归划分过程
  • 预测变量x的类型既可以是连续性变量,也可以是分类型变量
  • 数据应以其原始形式处理,不需要离散化
  • 用于数值型预测时,并没有使用回归,而是基于到达叶结点的案例的平均值做出预测

属性不同的预测变量y分裂准则不同:

  • 分类树:Gini准则,与信息增益类似,Gini系数度量一个结点的不纯度。
  • 回归树:常见分割标准是标准偏差减少(SDR),类似于最小均方误差(LS准则)。

sklearn实现

from sklearn.datasets import load_wine
from sklearn.model_selection import train_test_split
import matplotlib.pyplot as plt
plt.rcParams['font.sans-serif']=['Simhei'] #显示中文
plt.rcParams['axes.unicode_minus']=False   #显示负号
data=load_wine()
X=data.data
y=data.target
Xtrain,Xtest,Ytrain,Ytest=train_test_split(X,y,test_size=0.2,random_state=0)
from sklearn.tree import DecisionTreeClassifier
clf=DecisionTreeClassifier()
clf.fit(Xtrain,Ytrain)
clf.score(Xtest,Ytest)

DecisionTreeClassifier参数:
- criterion: 属性选择算法,默认:“gini"表示基尼不纯度,可用"entropy"表示信息熵,两种算法类似,信息熵运算效率略低
- splitter:结点选择属性的策略,默认"best”:选择最优分割属性;"random"从排名靠前的属性中随机选择
- max_depth:树的最大深度,即层数,默认为None,也可以给整数
- min_samples_split:内部结点包含的观测数最小值(即能创建分支的节点包含的最小观测数),默认为2,可以是整数表示最少包含几行数据,也可以是浮点数表示占比
- min_samples_leaf:叶节点包含的最小观测数(即分支后的子节点包含的最小观测数),默认为1,可以是整数和浮点数,表示含义同上
- max_leaf_node:节点个数的最大值
- min_impurity_decrease:不纯度(信息熵)下降的阈值,只有当下降值大于阈值的时候才分支,用于前剪枝策略.版本0.19的新参数,对应的旧参数名为:min_impurity_split
- presort:是否对数据排序以提高寻找最优分割点的效率,默认为False,大数据集设置为True会降低训练速度,小数据集设置为True会提速
- 更多其他参数查看帮助文件(主要是样本加权相关参数)
函数返回结果包含的属性:
- classes_:y的类别标签
- n_classes_:类别标签的个数
- feature_importances:属性重要性的列表
- n_features_:属性个数
- n_outputs_:输出个数
- tree_:存储整个树结构,可以抽出如下属性,详情参考,其中-2(或-1)表示叶节点
- node_count:树的结点个数
- children_left:所在位置节点对应左节点的id值,所有节点从0开始计数,每个节点对应一个id,计数规则从左分支到右分支,-1表示叶节点
- children_right:所在位置节点对应右节点的id值,-1表示叶节点
- feature:每个结点对应的属性索引值,-2表示叶节点
- threshold:每个结点对应的分割值(-2表示叶节点)

feature_name = ['酒精','苹果酸','灰','灰的碱性','镁','总酚','类黄酮','非黄烷类酚类','花青素','颜色强度','色调','od280/od315稀释葡萄酒','脯氨酸']

决策树的可视化

import graphviz
from sklearn import tree
dot_data = tree.export_graphviz(clf,feature_names=feature_name,
                               class_names=['琴酒','雪莉','贝尔摩德'],
                               filled=True,rounded=True)
graph = graphviz.Source(dot_data)
#graph.render('wine')
graph

重要参数:certerion

# 默认criterion='gini'
clf = tree.DecisionTreeClassifier(criterion="entropy") 
clf = clf.fit(Xtrain, Ytrain) 
score = clf.score(Xtest, Ytest) 
score

重要参数:random_state & splitter

# 默认splitter='best'
clf = tree.DecisionTreeClassifier(criterion="entropy",splitter="random",random_state=420) 
clf = clf.fit(Xtrain, Ytrain) 
score = clf.score(Xtest, Ytest) 
score 

网格搜索

from sklearn.model_selection import GridSearchCV
Xtrain,Xtest,Ytrain,Ytest=train_test_split(X,y,test_size=0.2,random_state=0)

# 设置参数可取值
param_grid = {'criterion':['entropy','gini'],
             'max_depth':range(2,11),
             'min_samples_split':range(2,20,2),
             'min_samples_leaf':range(1,10)}

# 设置参数网格
reg = GridSearchCV(DecisionTreeClassifier(),param_grid,cv=5)

# 建模
reg.fit(Xtrain,Ytrain)
# 最优参数
reg.best_params_
# 最优分数
reg.best_score_
clf = DecisionTreeClassifier(criterion='entropy',max_depth=10,
                            min_samples_split=2,min_samples_leaf=4)
clf.fit(Xtrain,Ytrain)
clf.score(Xtest,Ytest)

剪枝参数

# 模型对训练集的拟合程度,这是一课没有修建的树
train_score=clf.score(Xtrain,Ytrain)
train_score
  • max_depth:限制树的最大深度
  • min_samples_leaf:限制子结点的最小训练样本数
  • min_samples_split:限制每个内部结点包含的最小训练样本数
  • max_features:方法暴力,直接限制可以拟合的特征数量;建议使用PCA,ICA或特征选择模块中的降维算法
  • min_impurity_decrease:限制信息增益的大小
# 学习曲线确定最优参数
score=[]
for i in range(10):
    clf = DecisionTreeClassifier(max_depth=i+1,
                                criterion='gini',
                                random_state=30)
    clf = clf.fit(Xtrain,Ytrain)
    score.append(clf.score(Xtest,Ytest))
    
plt.plot(range(1,11),score,c='red',label='max depth')
plt.legend()
plt.show()
# 返回max(score)对应的值
score.index(max(score))+1

在这里插入图片描述

模型的其他属性接口

# 特征重要性clf.faeture_importances_
[*zip(feature_name,clf.feature_importances_)]
# 结点个数
clf.tree_.node_count

分类模型的评估

二分类决策树中的模型不均衡问题

from sklearn.datasets import make_blobs
class_1 = 1000 #类别1有1000个样本
class_2 = 100 #类别2只有100个
centers = [[0,0], [2.0, 2.0]] #设定两个类别的中心
clusters_std = [2.5, 0.5] #设定两个类别的方差,通常来说,样本量比较大的类别会更加松散
X, y = make_blobs(n_samples=[class_1, class_2],
                  centers=centers,
                  cluster_std=clusters_std,
                  random_state=420,shuffle=False)
#看看数据集长什么样
plt.scatter(X[:, 0], X[:, 1], c=y, cmap="rainbow",s=10);
Xtrain,Xtest,Ytrain,Ytest = train_test_split(X,y,test_size=0.2,random_state=420)
clf = DecisionTreeClassifier()
clf.fit(Xtrain,Ytrain)
#clf.predict(Xtest)
clf.score(Xtest,Ytest)
wclf = DecisionTreeClassifier(class_weight='balanced')
wclf.fit(Xtrain,Ytrain)
#wclf.predict(Xtest)
wclf.score(Xtest,Ytest)

混淆矩阵

二分类模型评估

from sklearn import metrics

ypred = wclf.predict(Xtest)

# 混淆矩阵
metrics.confusion_matrix(Ytest,ypred) 

# 准确率accuracy
metrics.accuracy_score(Ytest,ypred)

# 精确度precision
metrics.precision_score(Ytest,ypred)

# 召回率recall
metrics.recall_score(Ytest,ypred)

# F1 measure
metrics.f1_score(Ytest,ypred)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值