4.1 基于scikit lean实现决策树
基于scikit-lean实现决策树
在上一节我们介绍了决策树的一些知识。如果对决策树还不是很了解的话,建议先阅读上一节,在来学习本节。
加载数据集
首先我们还是需要先加载数据集,数据集来自scikit
自带的iris
数据集,数据集的内容可以参考以前的博客,这里就不在赘述。
首先让我们从scikit-learn
中加载数据集。
from sklearn.datasets import load_iris dataset = load_iris() data = dataset.data target = dataset.target
然后我们再使用pandas
将数据进行格式化以下,添加Iris
的属性到数据集中。
import numpy as np import pandas as pd data = pd.DataFrame(data,columns=["sepal_length","sepal_width","petal_length","petal_width"]) data["class"] = target
data的数据如下所示:
class代表类别。其他的就是Iris的属性了。
数据特征
这里我们主要是用画图来看一看Iris
数据集的特征。本来以为画图就matpotlib
就行了,但是没想到有seaborn
这个好使用的库,来自B站up主的提示。使用的库如下:
- matplotlib
- seaborn
首先我们画散点图:
import matplotlib.pyplot as plt import seaborn as sb # data.dropna()去除里面的none元素 sb.pairplot(data.dropna(),hue="class")
图像如下所示:
上面的这幅图展示了在四个属性中的类别的分别情况。
同时我们还可以画小提琴图:
plt.figure(figsize=(20, 20)) for column_index, column in enumerate(data.columns): if column == 'class': continue plt.subplot(2, 2, column_index + 1) sb.violinplot(x='class', y=column, data=data)
画出的图如下:
通过上面的这幅图我们可以直观的比较出哪一个变量更具有代表性。比如说petal_width
对类别0更加的友好。
接下来就是进行训练了。
训练
首先,我们还是需要从数据集中抽出训练集和测试集。这个内容在前面讲过了,就不多讲了。
from sklearn.model_selection import train_test_split input_data = data[["sepal_length","sepal_width","petal_length","petal_width"]] input_class = data["class"] train_data,test_data,train_class,test_class = train_test_split(input_data,input_class,random_state = 14)
then,让我们来开始进行训练吧,在scikit-learn
中实现了决策树,和前面的K
近邻算法一样我们直接引用就行,调用fit
(训练)和predict
(预测)函数。使用如下所示:
from sklearn.tree import DecisionTreeClassifier decision_tree = DecisionTreeClassifier(random_state=14) decision_tree.fit(train_data,train_class) predict_class = decision_tree.predict(test_data) predict_score = np.mean(predict_class == test_class) print("预测的准确度为{}".format(predict_score))
DecisionTreeClassifier
其他的参数在后面说,这里主要说一下random_state
参数。为什么决策树还需要random_state
这个参数,以下知乎上面的两位博主的说法。
说法一:
正如我们之前提到的决策树模型是靠优化节点来建造一棵优化的树,但最优的节点不一定可以得到最优树, 于是sklearn选择建不同的树,然后从中取最好的,在每次分枝时不使用全部特征,随机选取一部分特征,从中选取 不纯度相关指标最优的作为分枝的节点,这样每次生成的树也就不同了。 当sklearn随机选择特征,尤其是当spliter在分枝时多个特征的不纯度相关指标一样时,就会随机选择一个作为分枝点, 于是官方设计了random_state参数决定特征选择的随机性,类似random.seed(),保证同一个random_state参数得到的 决策树模型相同,实验结果可以复现,便于模型参数调优。
说法二:
决策树的生成过程中,往往引入随机数,这是为了得到更好的分类间隔。如使用的经典鸢尾花数据, 它本身的特征是连续的(姑且看作连续特征),所以,计算分割点就需要随机。
至于哪个说法是正确的,我暂时也不知道,如果有知道的,可以在评论区留言哦!
最后得到的预测结果如下所示:
0.9736842105263158
这里值得注意的是DecisionTreeClassifier()
函数,里面可以添加很多参数。官方文档在这里。
这里还是稍微的说一下参数。
# criterion gini(默认)/tropy:这里对应的就是之前的熵增益和Gini系数 # splitter best(默认)/random 每个结点选择的拆分策略 # max_depth 树的最大深度。 # min_samples_split int类型或者float(默认2) 如果某节点的样本数少于min_samples_split,则不会进行拆分了。浮点值表示分数,代表所占比例 # min_samples_leaf 默认=1 这个值限制了叶子节点最少的样本数,如果某叶子节点数目小于样本数,则会和兄弟节点一起被剪枝。 # min_weight_fraction_leaf float(默认0.0) 这个值限制了叶子节点所有样本权重,如果小于这个值,则会和兄弟节点一起被剪枝。一般来说,如果我们有较多样本有缺失值,或者分类树样本的分布类别偏差很大,就会引入样本权重,这时我们就要注意这个值了。 # max_features int, float or {“auto”, “sqrt”, “log2”}(默认0.0) # max_leaf_nodes 通过限制最大叶子节点数,可以防止过拟合,默认是"None”,即不限制最大的叶子节点数。如果加了限制,算法会建立在最大叶子节点数内最优的决策树。 # class_weight dict/balanced 指定样本各类别的的权重,主要是为了防止训练集某些类别的样本过多导致训练的决策树过于偏向这些类别。这里可以自己指定各个样本的权重。“balanced”,则算法会自己计算权重,样本量少的类别所对应的样本权重会高。 # min_impurity_split 这个值限制了决策树的增长,如果某节点的不纯度(基尼系数,信息增益,均方差,绝对差)小于这个阈值则该节点不再生成子节点。即为叶子节点 。
更多的可以去看官网细节。
然后我们可以将这个树的结构可视化,将文件保存在“tree.dot”中:
from sklearn.tree import export_graphviz with open("tree.dot",'w') as f: export_graphviz(decision_tree, feature_names =['sepal_length', 'sepal_width', 'petal_length', 'petal_width'], out_file = f)
这个是决策树的图:
同样,我们还可以使用交叉验证:
from sklearn.model_selection import cross_val_score decision_tree = DecisionTreeClassifier() scores = cross_val_score(decision_tree,input_data,input_class,scoring='accuracy') print("交叉验证结果: {0:.2f}%".format(np.mean(scores) * 100))
通过交叉验证得到的准确度如下:
96.67%
比上面的结果略低,不过这个是正常的。
随机森林
前面的博客介绍了随机树,这里不多做介绍,直接看使用吧。我们通过导入RandomForestClassifier
模块,并指令森林中树的个数为30,具体的参数看官网
from sklearn.ensemble import RandomForestClassifier rft = RandomForestClassifier(n_estimators=20,random_state=14) rft.fit(train_data,train_class) predict_class = rft.predict(test_data) predict_score = np.mean(predict_class == test_class) print("随机森林预测的准确度为{}".format(predict_score))
最后的结果如下图:
随机森林预测的准确度为0.976842105263158
然后进行交叉验证
scores = cross_val_score(rft,input_data,input_class,scoring='accuracy') print("Accuracy: {0:.2f}%".format(np.mean(scores) * 100))
emm,好像和上面的结果一样,因为这个数据集很小,可能会有这种情况。
调参工程师
首先,我们可以对决策树的max_feature
和max_depth
进行调参,改变其值,最终的结果如下
在随机森林中,我们可以对树的个数进行调参,结果如下图
结尾
这次并没有使用《 Python数据挖掘入门与实践 》书上的例子,实在是它打篮球的数据找不到,emm。相比较与oneR算法的70%左右的正确率,决策树95%正确率已经算足够优秀了。
尽管代码写起来很简单,也很容易实现得到结果,但是我们真正应该了解的是里面的内涵:决策树是什么?里面是怎样工作的?以及所蕴含的含义……