7. 建立决策树三种算法
建立决策树的主要是以下三种算法
- ID3
- C4.5
- CART(Classification And Regression Tree)
8. 参数与实现说明
- sklearn中提供的决策树是CART模型。因此构建的树为二叉树结构。
- API中的参数spliter指的是分类器,默认为best,即按照最优的标准进行划分,但是,划分是基于训练集的,有可能会存在过拟合。
- 参数spliter还有一个值为random,指的是随机划分,随机划分在训练集中的表现可能不太好,但是,可能会带来模型泛化能力的提升,从而在一定程度上缓解过拟合的情况。
- 参数max_feature,值切分时,选择的最大特征的数量。因为如果随机选择特征作为切分标准,则效果可能较差。这时,我们可以指定max_feature,即从原有的所有特征中,最多选择max_feature个特征,然后在这些特征中选择一个最好的,缓解了因为随机选择特征,而导致分类不好的情况。尽管在选择的max_feature特征中,挑选出来的最好的效果的特征,不一定是所有全局中最好的,但已经是局部(max_feature)特征中最好的。
- 在训练集上,随着树的深度提升,训练效果越来越好(解决欠拟合)。但是,在测试集上,效果是先好,然后又下降(存在一定的过拟合)
- 在决策树API中,已经进行了减枝操作,使用的是前向剪枝,因为后向剪枝操作较多,对性能影响较大。相关参数min_sample_split控制剪枝。节点中包含的样本数量<min_sample_split时,停止分裂。
# import numpy as np
# a = np.array([1, 2, 3, 4, 5])
# b = np.array([1, 10, 3, 40, 50])
# np.where(a == b)
from sklearn.datasets import load_iris
load_iris().target_names
import numpy as np
import matplotlib as mpl
import matplotlib.pyplot as plt
from matplotlib.colors import ListedColormap
from sklearn.datasets import load_iris
# scikit提供的决策树分类的class。
from sklearn.tree import DecisionTreeClassifier
mpl.rcParams["font.family"] = "SimHei"
mpl.rcParams["axes.unicode_minus"] = False
iris = load_iris()
# 将特征名称设置为中文,方便可视化中展示。(不是必要的)
iris.feature_names = ["花萼长度", "花萼宽度", "花瓣长度", "花瓣宽度"]
cmap = ListedColormap(["r", "g", "b"])
plt.figure(figsize=(18, 9))
# 处于可视化角度考虑,我们只考虑其中的两个特征。
# pair中存储的是每两个特征的组合,分别进行分类与可视化。
for pairidx, pair in enumerate([[0, 1], [0, 2], [0, 3],
[1, 2], [1, 3], [2, 3]]):
X = iris.data[:, pair]
y = iris.target
# 创建决策树分类对象,并进行训练。
clf = DecisionTreeClassifier(max_depth=2).fit(X, y)
# 每个子绘图区域绘制一组(两个特征)的可视化效果。
plt.subplot(2, 3, pairidx + 1)
plt.title(f"正确率:{clf.score(X, y):.3f}")
x1_min, x2_min = np.min(X, axis=0)
x1_max, x2_max = np.max(X, axis=0)
x1 = np.linspace(x1_min - 1, x1_max + 1, 100)
x2 = np.linspace(x2_min - 1, x2_max + 1, 100)
X1, X2 = np.meshgrid(x1, x2)
Z = clf.predict(np.c_[X1.ravel(), X2.ravel()])
Z = Z.reshape(X1.shape)
cs = plt.contourf(X1, X2, Z, cmap=cmap, alpha=0.5)
plt.xlabel(iris.feature_names[pair[0]], fontsize=16)
plt.ylabel(iris.feature_names[pair[1]], fontsize=16)
# 绘制每个类别鸢尾花数据的散点图。
for i in range(3):
# np.where 当只有一个参数时,返回条件为True的索引值。
# 这里会依次返回每个类别的鸢尾花样本索引。
idx = np.where(y == i)
plt.scatter(X[idx, 0], X[idx, 1], c=cmap(i), label=iris.target_names[i],
cmap=cmap, edgecolor="black")
# 定义父标题。
plt.suptitle("决策树决策边界", fontsize=16)
plt.legend(loc="lower right", fontsize=16)
plt.subplots_adjust(top=0.9, hspace=0.25)
plt.show()
9. 决策树回归
- 分类树采用信息增益、信息增益率、基尼系数来评价树的效果,都是基于概率值进行判断的;而分类树的叶子节点的预测值一般为叶子节点中概率最大的类别作为当前叶子的预测值。
- 在回归树中,叶子节点的预测值一般为叶子节点中所有值的均值来作为当前叶子节点的预测值。所以在回归树中一般采用MSE作为树的评价指标,即均方差。
- 一般情况下,只会使用CART算法构建回归树。
9.1 数据标准化
决策树因为使用按照属性值划分样本,因此,该算法不要求数据必须进行标准化操作。同时,决策树在解决非线性分类问题上,要好于逻辑回归。不过,在回归问题上,其输出的区间不如线性回归平滑。
from sklearn.datasets import load_boston
# scikit learn提供的决策树回归类。
from sklearn.tree import DecisionTreeRegressor
from sklearn.model_selection import train_test_split
from sklearn.linear_model import SGDRegressor
X, y = load_boston(return_X_y=True)
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.25, random_state=0)
# 很多算法依赖于数据标准化(梯度下降就是一个典型)。理论上上,数据标准化对梯度下降来说,不是必须的。我们可以将学习率
# 调的足够小,将迭代次数调的足够多,这样也能够接近最佳解。但是,这样,会浪费很多时间,严重降低性能。因此,对数据集进行
# 标准化处理,可以使梯度下降算法更快的进行学习(更快的接近于极值点)。
sgd = SGDRegressor(max_iter=5000, eta0=0.00001)
sgd.fit(X_train, y_train)
print("随机梯度下降R^2:")
print(sgd.score(X_train, y_train))
print(sgd.score(X_test, y_test))
# 没有限制决策树的深度,训练集完美,但是测试集一般,发生过拟合。
# tree = DecisionTreeRegressor()
# 为了缓解过拟合的情况,我们可以设置树的最大深度,或者设置最小节点分类样本数。
tree = DecisionTreeRegressor(max_depth=3)
tree.fit(X_train, y_train)
print("决策树R^2")
# 从运行结果可知,决策树不需要对数据进行标准化数量。
print(tree.score(X_train, y_train))
print(tree.score(X_test, y_test))
9.2 过拟合与欠拟合
无论是分类还是回归,如果决策树的深度过小,则容易发生欠拟合,反之,如果树的深度过大,则容易发生过拟合。因此,合理控制树的深度是重要的。
import numpy as np
from sklearn.tree import DecisionTreeRegressor
import matplotlib as mpl
import matplotlib.pyplot as plt
mpl.rcParams["font.family"] = "SimHei"
mpl.rcParams["axes.unicode_minus"] = False
# 创建随机种子的类。之前一直使用的参数random_state,底层就是使用RandomState对象来实现的。
# 参数random_state可以传递一个整数,其实,也可以传递一个RandomState对象。当我们传递一个整数
# 的时候,底层也是使用RandomState对象来实现的。
rng = np.random.RandomState(1)
# axis = 0,以行作为单位进行排序。
X = np.sort(5 * rng.rand(80, 1), axis=0)
y = np.sin(X).ravel()
# 增加噪声。
y[::5] += 3 * (0.5 - rng.rand(16))
X_test = np.arange(0.0, 5.0, 0.01)[:, np.newaxis]
y_test = np.sin(X_test)
plt.figure(figsize=(18, 15))
# 最大深度从1到6,依次来观察拟合效果,从而展示欠拟合与过拟合。
for i in range(1, 7):
plt.subplot(3, 2, i)
plt.scatter(X, y, s=20, edgecolor="black", c="r", label="样本数据")
tree = DecisionTreeRegressor(max_depth=i)
tree.fit(X, y)
y_hat = tree.predict(X_test)
plt.plot(X_test, y_hat, color="g", label="拟合线")
plt.plot(X_test, y_test, color="b", label="真实线")
plt.xlabel("数据")
plt.ylabel("目标值")
train_score = tree.score(X, y)
test_score = tree.score(X_test, y_test)
plt.title(f"最大深度{i},训练集:{train_score:.3f} 测试集:{test_score:.3f}")
plt.legend()
plt.show()
10. 决策树可视化
当决策树拟合后,为了能够观察到决策树节点是如何进行划分的,我们可以对决策树进行可视化。
决策树可视化需要graphviz模块的支持,我们使用命令:conda install python-graphviz
进行安装(在安装上述模块时,会有一些依赖模块一并进行安装)。
10.1 可视化方式
- 可以通过tree模块的export_graphviz导出.dot文件,然后使用如下的命令转换成需要的文件类型:
$ dot -Tps tree.dot -o tree.ps(PostScript format)$ dot -Tpng tree.dot -o tree.png(PNG format) - 可以通过graphviz模块的Source类返回Source类型,然后通过render方法生成pdf文件。
graph = graphviz.Source(dot_data)graph.render("filename") - 也可以直接展示执行graph在jupyter notebook中显示。
10.2 程序展示
# 决策树可视化的必要模块,用于进行决策树的导出。
import graphviz
from sklearn.tree import DecisionTreeClassifier, export_graphviz
from sklearn.datasets import load_iris
iris = load_iris()
X, y = iris.data, iris.target
# tree = DecisionTreeClassifier(max_depth=3)
tree = DecisionTreeClassifier()
tree.fit(X, y)
# 当调用fit方法之后,就可以对决策树进行可视化。
# tree 决策树对象
# out_file: 决策树可视化文件输出的路径。
# feature_names:指定在可视化图像上显示的特征名字。如果不指定,默认使用X[0], X[1]
# class_names:指定目标类别的名字
# filled:指定是否使用颜色进行填充,默认为False。
# rounded:可视化展示时,每个节点是否使用圆角矩形。默认为False。
# dot_data = export_graphviz(tree, out_file="iris.dot", feature_names=iris.feature_names,
# class_names=iris.target_names, filled=True, rounded=True)
# 我们也可以将out_file参数指定为None,此时,不会输出文件,而是会将可视化结构信息以字符串的形式返回。
dot_data = export_graphviz(tree, out_file=None, feature_names=iris.feature_names,
class_names=iris.target_names, filled=True, rounded=True)
# 创建graphviz.Source类对象,该独享接受决策树可视化字符串对象,
graph = graphviz.Source(dot_data)
# 生成可视化文件(pdf文件)。
# graph.render("iris")
# 也可以直接对graph求值,此时可以在jupyter notebook下直接显示决策树的可视化效果。
graph