决策树(Decision Tree)是在已知各种情况发生概率的基础上,通过构成决策树来求取净现值的期望值大于等于零的概率,评价项目风险,判断其可行性的决策分析方法,是直观运用概率分析的一种图解法。由于这种决策分支画成图形很像一棵树的枝干,故称决策树。在机器学习中,决策树是一个预测模型,他代表的是对象属性与对象值之间的一种映射关系。Entropy = 系统的凌乱程度,使用算法ID3, C4.5和C5.0生成树算法使用熵。这一度量是基于信息学理论中熵的概念


sklearn 决策树

决策树超参数

1,criterion(标准)可选信息熵和基尼系数,观察公式 gini 系数相比信息熵计算量要小一点。sklearn 默认的 criterion 也是 gini 系数。效果方面信息熵 entropy 和 gini 系数差不多。entropy 在模型出现欠拟合的时候可以用一用,过拟合的时候少用。

\(Entropy(t) = -\sum_{i=0}^{c-1}p(i|t)log_2^{p(i|t)}\)

\(Gini(t)=1-\sum_{i=0}^{c-1}p(i|t)^2\)

2,random_state 控制随机性,sklearn 版本的决策树不是遍历所有特征求不纯度,而是随机筛选一些特征进行计算(决策树本身具有的随机性)。所以这个设定会导致我们每次训练的结果都不相同。通过设定 random_state=0 (随便写个数字),控制决策树的随机性,便于我们分析其他的参数。random_state 默认是 None。

3,splitter 也是控制决策时随机性的参数。共有两个可选值:'best','random'。'best' 会选着 model.feature_importances_ 最大的几个进行分支。'ramdom' 决策树分支会更随机、树会更深,对训练集的拟合程度会降低。('random' 模式也是防止过拟合的一种方式

4, 剪枝参数(决策树的核心超参数):在不加限制的情况下,一棵决策树会生长到衡量不纯度指标最优,或者没有更多的特征可用为止。这往往就会发生过拟合(训练集表现很好,但是测试集表现很差)。

参数

含义

max_depth

限制树的最大深度,超过设定深度的树枝全部剪掉。这是最广泛的剪枝参数,在高维度低样本量时非常有效。在集成算法中也非常使用。实际使用时,建议从 3 开始尝试。

min_samples_leaf & min_samples_split

针对叶子节点。可视化图中有一个 samples 参数(分出来的样本个数)设定 min_samples_leaf 之后如果分出来的样本数没有达到,该分枝就会被剪掉。min_samples_leaf(建议从 5 开始) 一般和 max_depth 搭配使用。对于类别不多的分类问题 min_samples_leaf 设置为1,通常就是最佳选择。min_samples_split 限定,一个节点必须包含至少 min_samples_split 个训练样本,这个节点才允许被分枝。

max_features & min_impurity_decrease

限制高维度数据过拟合,暴力限制选择的 feature 数量。强行设置可能会导致模型学习不足。如果希望通过降维的方式防止过拟合,可以使用 PCA ICA 等降维算法。 min_impurity_decrease 限制信息增益的大小,信息增益小于设定数值的分支不会发生。信息增益:父节点和子节点的信息熵之差

5, 目标权重参数(很重要)

参数

含义

class_weight & min_weight_fraction_leaf

针对不平等的数据集调整权重。比如在一个二分类的情况下一类占了9份,另一类占了1分,这种分类状况下,即便模型什么也不做,全把结果预测成了占9份的分类,正确率也能有99%。因此我们要使用class_weight 参数对样本标签进行一定的均衡,给少量的标签更多的权重,让模型更偏向少数类,向捕获少数类的方向建模。该参数默认 None,此模式表示自动给与数据集中的所有标签相同的权重。有了权重之后,样本量就不再是单纯地记录数目,而是受输入的权重影响了,因此这时候剪枝,就需要搭配min_weight_fraction_leaf这个基于权重的剪枝参数来使用。另请注意,基于权重的剪枝参数(例如min_weight_fraction_leaf)将比不知道样本权重的标准(比如min_samples_leaf)更少偏向主导类。如果样本是加权的,则使用基于权重的预修剪标准来更容易优化树结构,这确保叶节点至少包含样本权重的总和的一小部分。


代码演示

from sklearn import tree
from sklearn.datasets import load_wine
from sklearn.model_selection import train_test_split
import graphviz
import pydotplus


def data_load():
    """ 导入红酒数据集 """
    data = load_wine()
    x = data.data
    y = data.target
    return x, y, data


def decison_tree_demo():
    """ 决策树 Demo """
    x, y, m = data_load()
    # 划分数据集
    x_train, x_test, y_train, y_test = train_test_split(x, y, test_size=0.3)
    # 实例化
    clf = tree.DecisionTreeClassifier(criterion='entropy', random_state=30)
    # 训练
    clf = clf.fit(x_train, y_train)
    # 评分
    score = clf.score(x_test, y_test)
    print(score)
    return clf 


def vue_tree(model, feature_names, class_names, path='./vue_tree.svg'):
    """ 决策树可视化 """
    dot_data = tree.export_graphviz(model,
                                    feature_names=feature_names,
                                    class_names=class_names,
                                    filled=True,
                                    rounded=True
                                    )   
    graph = graphviz.Source(dot_data)
    graph = pydotplus.graph_from_dot_data(dot_data)
    # write_pdf 中文可能会乱码
    graph.write_svg(path)


if __name__ == "__main__":
    model = decison_tree_demo()
    feature_names = ['酒精', '苹果酸', '灰', '灰的碱性', '镁', '酚', '类黄酮', '非黄烷类分', '花青素', '颜色强度', '色调', 'od280/od315稀释葡>萄酒','脯氨酸']
    class_names = ['佳美', '仙粉黛', '黑比诺']
    vue_tree(model, feature_names, class_names)

可视化结果

如何实现决策树对连续变量自动分箱python 决策树 random_state_决策树


决策树可视化

超参数学习曲线(确定最优参数)

控制变量法,forloop 画超参数。

def max_depth_learning_curve(path='./learning_curve.png'):
    x, y, m = data_load()
    x_train, x_test, y_train, y_test = train_test_split(x, y, test_size=0.3)
    scores = []
    for i in range(10):
        clf = tree.DecisionTreeClassifier(criterion='entropy'
                                        , max_depth = i + 1 
                                        ,splitter='random'
                                        ,random_state=30
                                        )   
        clf = clf.fit(x_train, y_train)
        score = clf.score(x_test, y_test)
        scores.append(score)
    fig, ax1 = plt.subplots(1)
    ax1.plot(range(1, 11), scores, c='red', label="max_depth")
    ax1.legend()
    ax1.set_xlabel("Decision tree max depth")
    ax1.set_ylabel("Score")
    plt.savefig(path)

如何实现决策树对连续变量自动分箱python 决策树 random_state_决策树_02

训练集、测试集评分观察

过拟合:训练集表现良好,测试集表现很差。
欠拟合:测试集表现很差。
这里我们通过观察图像的方式来判断是否时过拟合。

train = []
test = []
for i in range(1, 11):
    clf = tree.DecisionTreeClassifier(random_state=25
                                      ,criterion='entropy' # entropy 一般在模型欠拟合的时候使用  
                                      ,max_depth=i
                                      ,splitter='random'
                                      ,min_samples_split=10
                                     )                               
    clf = clf.fit(x_train, y_train)
    # 训练集上的模型表现
    score_tr = clf.score(x_train, y_train)
    # 测试集上的模型表现
    score_te = cross_val_score(clf, x, y, cv=10).mean()
    train.append(score_tr)
    test.append(score_te)
    
    # 训练集表现好,测试集上表现差,过拟合
    # 训练集上表现差,测试集上表现好,欠拟合
print(max(test))
import matplotlib.pyplot as plt 
plt.plot(range(1, 11), train, c='red', label='train')
plt.plot(range(1, 11), test, color='blue', label='test')
plt.xticks(range(1, 11))
plt.legend()
plt.show()

如何实现决策树对连续变量自动分箱python 决策树 random_state_权重_03


观察上图可知,随着 max_depth 的增大,模型训练集表现良好,测试集表现变差。这是典型的过拟合。

多参数调参

进行多参数调参,sklearn 给出了网格搜索的解决方案。 GridSearchCV 使用枚举技术,所以计算时间较慢。GridSearchCV 集成了 fit、cross_val_score 的功能。使用方式如下:

import numpy as np
from sklearn.model_selection import GridSearchCV
parameters = {
    'criterion': ('gini', 'entropy'),
    'splitter': ('random',),
    'max_depth': [3],
    'min_samples_leaf': [*range(1, 50, 5)],
    'min_impurity_decrease': [*np.linspace(0, 0.5, 50)]# 信息增益,不在网格搜索中很难取使用这个参数
}

clf = tree.DecisonTreeClassifier(random_state=25)
GS = GridSearchCV(clf, parameters, cv=10)
GS = GS.fit(x_train, y_train))

# 输出最好的参数,返回一个字典
print(GS.best_params_)
# 输出评分
print(GS.best_score_)