决策树(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)
可视化结果
决策树可视化
超参数学习曲线(确定最优参数)
控制变量法,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)
训练集、测试集评分观察
过拟合:训练集表现良好,测试集表现很差。
欠拟合:测试集表现很差。
这里我们通过观察图像的方式来判断是否时过拟合。
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()
观察上图可知,随着 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_)