分析决策树:
分析决策树是利用tree模块的export_graphviz函数来将树可视化,从而便于深入理解对应的算法是如何进行的(该函数会生成一个.dot格式文件,这是一种用于保存图像的文本文件格式)。示例代码如下:
from sklearn.tree import export_graphviz
export_graphviz(tree, out_file="tree.dot", class_names=["malignant", "benign"], feature_names=cancer.feature_names,impurity=False, filled=True)
from sklearn.tree import export_graphviz
export_graphviz(tree,out_file="tree.dot",class_names=["malignant","benign"], feature_names=cancer.feature_names,impurity=False, filled=True)
import graphviz
with open("tree.dot") as f:
dot_graph = f.read()
graphviz.Source(dot_graph)
树的特征重要性:
我们要查看整个树可能非常费劲,除此之外,我们还意义利用一些有用的属性来书的工作原理。其中最常用的是特征重要性,他为每个特征对树的重要性进行排序。对于每个特征来说,他都是结语0和1之间的数字,其中0表示该特征没用到,1表示完美的预测目标值。特征重要性求和始终为1,打印出上述书的特征重要性的代码如下:
print("Feature importances:\n{}".format(tree.feature_importances_))
运行后的结果如下:
Feature importances:
[0. 0. 0. 0. 0. 0.
0. 0. 0. 0. 0.01019737 0.04839825
0. 0. 0.0024156 0. 0. 0.
0. 0. 0.72682851 0.0458159 0. 0.
0.0141577 0. 0.018188 0.1221132 0.01188548 0. ]
我们将其特征数的重要性进行可视化,对应代码如下:
def plot_feature_importances_cancer(model):
n_features = cancer.data.shape[1]
plt.barh(range(n_features), model.feature_importances_, align='center')
plt.yticks(np.arange(n_features), cancer.feature_names)
plt.xlabel("Feature importance")
plt.ylabel("Feature")
plot_feature_importances_cancer(tree)
运行后结果如下图:
乳腺癌数据集上学到的决策树的特征重要性
从这里我们可以看到,顶部划分用到的特征(“worst radius”)是最重要特征。符合我们的预期。不过,如果某个特征的feature_importances很小,这并不能说明这个特征没有提供任何信息,只能说明该特征没有被树选中,可能因为另一种特征也包含了同样的信息。
与线性模型不同,特征的重要性始终为正数,也不能说明该特征对应哪个类别,特征重要性告诉我们“worst randius”(最大半径)特征很重要,但是并没有告诉我们半径达表示的是良性的还是恶性的。事实上,特征和类别之间可能没有这样简单的关系,可以通过下述例子看出这一点:
对应代码如下:
from IPython.display import display
tree = mglearn.plots.plot_tree_not_monotone()
display(tree)
运行后结果如下:
二维数据与决策树给出的决策边界
和上述讨论的决策树分类类用法似,决策树用于回归在DecisionTreeeRegression中实现。但是在将给予书的模型用于回归时,我们想要指出他的一个特殊性。那就是DecisionTreeeRegression及其他所有基于树的回归模型不能外推(extapolate),也不能在训练集之外进行预测。我们可以利用计算机内存离世价格数据和波斯顿房价数据集来说明这一点。代码如下所示:
import pandas as pd
ram_prices=pd.read_csv("D:\\ProgramData\\Anaconda3\\Lib\\site-packages\\mglearn\\data\\ram_price.csv")
plt.semilogx(ram_prices.date, ram_prices.price)
plt.xlabel("year")
plt.ylabel("Price in $/mbyte")
from sklearn.tree import DecisionTreeRegressor
#利用历史数据预测2000年后房价、
data_train = ram_prices[ram_prices.date < 2000]
data_test = ram_prices[ram_prices >= 2000]
x_train = data_train.date[:, np.newaxis]
y_train = np.log(data_train.price)
tree = DecisionTreeRegressor().fit(x_train, y_train)
linear_reg = LinearRegression().fit(x_train, y_train)
x_all = ram_prices.date[:, np.newaxis]
pred_tree = tree.predict(x_all)
pred_lr = linear_reg.predict(x_all)
price_tree = np.exp(pred_tree)
price_lr = np.exp(pred_lr)
plt.semilogy(data_train.date, data_train.price, label="Training data")
plt.semilogy(data_test.date, data_test.price, label="test data")
plt.semilogy(ram_prices.date, price_tree, label="Tee predicition")
plt.semilogy(ram_prices.date, price_lr, label="Linear prediction")
plt.legend()
运行后结果如下图:
线性模型和回归树对RAM价格数据的预测结果比对
由运行结果可知,两个模型之间的差异非常明显。线性模型用一条直线对数据做近似,这是我们所知道的。这条线对测试数据(20000年后的价格)给出了相当好的预测,不过忽略了训练数据和测试数据之间一些更加细微的变化。与之相反,树模型完美预测了训练数据,由于我们没有限制树的复杂度,因此它引用的是整个数据集。但是,一旦输入超出了模型训练数据范围,模型就只能预测最后一个一直数据点,而不能预测之后的数据点。
控制决策树的优缺点以及参数:控制决策树模型复杂度的参数是预剪枝参数,他在树完全展开之前停止树的构造。通常来说,选择一种预剪枝策略(设置max_depth、max_leaf_nodes或min_samples_leaf)足以放置过拟合。
其优点有两个,一是得到的模型很容易可视化,非专家也很容易理解;而是算法完全不受数据缩放影响(因为每个数都被单独处理,而且数据的划分也不依赖缩放),因此决策树算法不需要特征预处理,比如归一化或标准化。特别是特征的尺度完全不一样是或者二元特征和连续特征同时存在是,决策树效果很好。
决策树的缺点在于:即使做了预剪枝,他也经常会过拟合,同时泛化性能差。因此,在大多数应用中往往会使用我们下节介绍的集成方法来代替单棵局册数。
大家对此有什么意见或建议,欢迎留言讨论。
注意:本文主要参照《python机器学习基础教程》,根据个人情况,做了相应的改动