决策树
决策树是广泛用于分类和回归任务的模型。
- 它从一层层的if/else问题(尽可能少的问题)中进行学习,并得出结论
1、构造决策树
- 1)two_moons数据集
- 这是个二分类数据集(每个类别50个数据点),样本点在坐标图中的分布像两个半月牙,因此叫做two_moons
- 2)学习决策树,就是学习一系列的if/else问题(这些问题被叫做测试),使得能够以最快速度得到正确答案
- 测试的形式有
- 二元特征(是/否):“是否有是翅膀?”
- 连续数据的测试形式:“特征i的值是否大于a?”
- 测试的形式有
- 3)构造决策树,算法搜遍所有可能的测试,找出对目标变量来说信息量最大的那一个。
- 信息量?熵增理论
- 构造决策树就是不断地搜寻最优测试,在特征空间上能最大程度上对数据实现划分,直到划分后的每个区域(决策树的每个叶节点)只包含单一目标值
- 如果书中的某个叶节点所包含数据点的目标值都相同,则该叶节点是纯的。(纯叶节点太多容易过拟合)
- 4)对新数据点进行预测
- 查看这个点位于特征空间划分的哪个区域,然后将该区域的多数目标值作为预测结果
2、控制决策树的复杂度
1)为什么要控制决策树的复杂度?
-
防止过拟合。
-
构造决策树直到所有叶结点都是纯的叶结点,这会导致模型非常复杂,并且对训练数据高度过拟合。
- 纯叶结点的存在说明这棵树在训练集上的精度是 100%。有的决策边界过于关注远离同类别其他点的单个异常点。
2)防止过拟合有两种常见的策略:
-
预剪枝(pre-pruning),及早停止树的生长
-
后剪枝(post-pruning)或剪枝(pruning),先构造树,但随后删除或折叠信息量很少的结点
-
剪枝条件:限制树的最大深度、叶节点的最大数目、规定一个节点中数据点的最小数目
-
scikit-learn 只实现了预剪枝,没有实现后剪枝。
3)scikit-learn 的决策树在 DecisionTreeRegressor 类和 DecisionTreeClassifier 类中实现。
-
DecisionTreeClassifier 类
from sklearn.tree import DecisionTreeClassifier from sklearn.datasets import load_breast_cancer from sklearn.model_selection import train_test_split #加载数据 cancer = load_breast_cancer() #分离数据,stratify作用为以分层方式分割数据,保持测试集与整个数据集里cancer.target的数据分类比例一致 #随机数种子为42 X_train, X_test, y_train, y_test = train_test_split( cancer.data, cancer.target, stratify=cancer.target, random_state=42) #调用函数 tree = DecisionTreeClassifier(random_state=0) #训练模型 tree.fit(X_train, y_train) print("Accuracy on training set: {:.3f}".format(tree.score(X_train, y_train))) print("Accuracy on test set: {:.3f}".format(tree.score(X_test, y_test)))
-
输出
Accuracy on training set: 1.000 Accuracy on test set: 0.937
-
DecisionTreeRegressor 类
price = load_boston() X1_train, X1_test, y1_train, y1_test = train_test_split(price.data,price.target,random_state = 42) DTR = DecisionTreeRegressor().fit(X1_train,y1_train) print("DTR taining score:{}".format(DTR.score(X1_train,y1_train))) print("DTR test score:{}".format(DTR.score(X1_test,y1_test)))
-
输出
DTR taining score:1.0 DTR test score:0.8113048906750595
-
📣
- 训练集上的精度是 100%,
- 这是因为叶结点都是纯的,树的深度很大,足以完美地记住训练数据的所有标签。
- 如果我们不进行剪枝,树的深度和复杂度都可以变得特别大,容易过拟合,对新数据的泛化性能不佳。
- 剪枝可以通过参数max_depth,max_features,max_leaf_nodes来实现比如,DTC = DecisionTreeClassifier(max_depth = 5)
- 训练集上的精度是 100%,
3、分析决策树
在分析决策树前,需要安装一下grahviz,而且不能用pip直接安装,需要配置环境
-
按照上网搜的教程:给小白准备的graphviz图文安装教程(2021最新)
-
接着,我们就可以使用tree.export_graphviz将树可视化
from sklearn.tree import export_graphviz import graphviz export_graphviz(DTR,out_file="DTR.dot",feature_names=price.feature_names,impurity=False,filled=True) with open("DTR.dot") as f: g = f.read() graphviz.Source(g)
-
看看结果~
4、树的特征重要度
由上面的图片可以发现,查看整个树可能非常费劲,图太大啦...
- 因此利用一些有用的属性来总结树的工作原理
-
其中最常用的是特征重要性(feature importance),它为每个特征对树的决策的重要性进行排序。
def plot_feature_importance_cancer(model): #.shape[1]表示矩阵的第二维的长度,在这里就是特征的长度 n_features = cancer.data.shape[1] plt.barh(range(n_features), model.feature_importances_, align='center') #前者代表y坐标轴的各个刻度,后者代表各个刻度位置的显示的lable plt.yticks(np.arange(n_features), cancer.feature_names) plt.xlabel("Feature importance") plt.ylabel("Feature") plt.show()
-
📣
从图中我们可以看到,特征worst radius是最重要的特征。
- 即第一层划分已经将两个类别区分得很好。
- 但是,如果某个特征的 feature_importance_ 很小,并不能说明这个特征没有提供任何信息。这只能说明该特征没有被树选中,可能是因为另一个特征也包含了同样的信息。
- 与线性模型的系数不同,特征重要性始终为正数,也不能说明该特征对应哪个类别。
- 特征重要性告诉我们“worst radius”(最大半径)特征很重要,但并没有告诉我们半径大表示样本是良性还是恶性。
- 事实上,在特征和类别之间可能没有这样简单的关系。
5、优点、缺点和参数
-
优点:一是得到的模型很容易可视化,非专家也很容易理解(至少对于较小的树而言);二是算法完全不受数据缩放的影响,不需要特征预处理。
-
缺点:即使做了预剪枝,它也经常会过拟合,泛化性能很差。因此,在大多数应用中,往往使用下面介绍的集成方法来替代单棵决策树。
-
参数:控制决策树模型复杂度的参数是预剪枝参数,它在树完全展开之前停止树的构造。通常来说,选择一种预剪枝策略(设置 max_depth 、 max_leaf_nodes 或 min_samples_leaf )足以防止过拟合。
6、参考文献
《Python机器学习基础教程》P54-P62