监督学习-分类-决策树
决策树使用树形分支结构分类事物
例:
- 小丽找对象,要求:高、帅、富
- 小明找对象,要求:美美美
if height >= 172: if hansom = '帅': if rich >= : print('小哥哥我晚上有空!') else: print('加个微信吧!') else: print('孩子放学了,我该做饭去了!') else: print('呵呵,洗澡去了!')
讯享网
构造二叉树结构的两个指标:
- 在什么地方分支(特征的先后次序,执行效率不同)
- 分支的阈值定多数合适?
所谓的决策树算法,就是通过数据自动学习出二叉树的分支维度顺序和每个分支的阈值
与一般的分支结构不同,决策树的分支条件(特征维度、阈值)通过训练得到,而非手动构造
讯享网import numpy as np import matplotlib.pyplot as plt
from sklearn.datasets import load_iris
决策树skicit-learn实现
1:获取数据,特征工程,样本数据(特征和标签)
讯享网iris = load_iris() # iris
X = iris['data'][:, 2:] y = iris['target'] # X
2:将样本数据分为训练集和测试集
为方便可视化理解,只取第2和第3列两维特征
讯享网from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=.3, random_state=3)
3:算法:创建分类器
- 调包,调用不同分类器
- 调参,调节超参数
讯享网from sklearn.tree import DecisionTreeClassifier # 创建空分类器 # my_classifier = DecisionTreeClassifier() my_classifier = DecisionTreeClassifier(max_depth=2, criterion='entropy', random_state=42) # # max_depth最大深度,entropy信息熵算法,随机种子 my_classifier
DecisionTreeClassifier(class_weight=None, criterion='entropy', max_depth=2, max_features=None, max_leaf_nodes=None, min_impurity_decrease=0.0, min_impurity_split=None, min_samples_leaf=1, min_samples_split=2, min_weight_fraction_leaf=0.0, presort=False, random_state=42, splitter='best')
4:训练:用训练集(特征和标签)训练分类器
讯享网my_classifier.fit(X_train, y_train)
DecisionTreeClassifier(class_weight=None, criterion='entropy', max_depth=2, max_features=None, max_leaf_nodes=None, min_impurity_decrease=0.0, min_impurity_split=None, min_samples_leaf=1, min_samples_split=2, min_weight_fraction_leaf=0.0, presort=False, random_state=42, splitter='best')
5:预测:使用测试集(特征)预测标签
讯享网predictions = my_classifier.predict(X_test) predictions
array([0, 0, 0, 0, 0, 2, 1, 0, 2, 1, 1, 0, 1, 1, 2, 0, 1, 2, 2, 0, 2, 2, 2, 1, 0, 2, 1, 1, 1, 1, 0, 0, 2, 1, 0, 0, 2, 0, 2, 1, 2, 1, 0, 0, 2])
讯享网predictions.shape
(45,)
6:用测试集自带的标签和预测的标签比较,评价分类器的准确率
讯享网from sklearn.metrics import accuracy_score
accuracy_score(predictions, y_test) # 正确率
讯享网0.55556
决策树原理可视化
# 绘图函数,不用写,直接调用 def plot_decision_boundary(model, axis): x0, x1 = np.meshgrid( np.linspace(axis[0], axis[1], int((axis[1]-axis[0])*100)).reshape(-1, 1), np.linspace(axis[2], axis[3], int((axis[3]-axis[2])*100)).reshape(-1, 1), ) X_new = np.c_[x0.ravel(), x1.ravel()] y_predict = model.predict(X_new) zz = y_predict.reshape(x0.shape) from matplotlib.colors import ListedColormap custom_cmap = ListedColormap(['#EF9A9A','#FFF59D','#90CAF9']) plt.contourf(x0, x1, zz, cmap=custom_cmap)
讯享网plot_decision_boundary(my_classifier, axis=[0.5, 7.5, 0, 3]) plt.scatter(X[y==0, 0], X[y==0, 1]) plt.scatter(X[y==1, 0], X[y==1, 1]) plt.scatter(X[y==2, 0], X[y==2, 1])
<matplotlib.collections.PathCollection at 0x134be1d0>
[外链图片转存失败(img-RkKTIih7-76)(output_21_1.png)]
下面代码,生成可视化文件的库:graphviz需要安装,不同环境安装方式不同
- 可以使用 export_graphviz 导出器以 Graphviz 格式导出决策树
- 如果你是用 conda 来管理包,那么安装 graphviz 二进制文件和 python 包可以用以下指令安装:
讯享网# Conda安装库命令 conda install python-graphviz
注:网络问题可能需要翻墙
pdf文件是在整个 iris 数据集上训练的决策树树的 graphviz 导出示例,其结果被保存在 iris_xxx.pdf 中:
- iris_entropy.pdf
- iris_gini.pdf
生成文档中:
- gini值为当前节点系统基尼系数
- entropy值为当前节点系统熵值
- samples值为此分支节点下还有多少值待拆分
- values值为待拆分值的分类
import graphviz
讯享网from sklearn.tree import export_graphviz
# # 基尼系数算法 # dot_data = export_graphviz(my_classifier, out_file=None) # graph = graphviz.Source(dot_data) # graph.render("iris_gini")
讯享网# # 信息熵算法 # dot_data = export_graphviz(my_classifier, out_file=None) # graph = graphviz.Source(dot_data) # graph.render("iris_entropy")
用下面测试数据人工模拟决策树分类过程
模拟时,data的下标是0,1,2,3,target的结果是0,1,2
X[0], X[51], X[102]
讯享网(array([1.4, 0.2]), array([4.5, 1.5]), array([5.9, 2.1]))
y[0], y[51], y[102]
讯享网(0, 1, 2)
决策树原理
- 每个分支节点在哪个特征维度划分?
- 某个特征维度在什么值划分?
根据某一特征维度下的某一阈值进行二分,划分后使得整个系统信息熵降低
信息熵
信息熵:不确定性的度量
- 封闭系统,不确定性越高,熵值越大,越混乱
- 封闭系统,不确定性越低,熵值越小,越规律
信息熵公式:$ H=-\sum ^{k}{i=1}p{i}\log \left( p_{i}\right) $
- 解释:一个封闭系统中,有k类信息,每类信息的比例为p
信息、能量、物质三者是可以相互转换的
例:鸢尾花数据集,共有3类鸢尾花,每类鸢尾花所占比例为1/3,p1/p2/p3都等于1/3,本系统熵值为:
$ H= -\dfrac {1}{3}\cdot \log \left( \dfrac {1}{3}\right) -\dfrac {1}{3}\cdot \log \left( \dfrac {1}{3}\right) -\dfrac {1}{3}\cdot \log \left( \dfrac {1}{3}\right) =1.0986 $
-(1/3) * np.log2(1/3) - (1/3) * np.log2(1/3) - (1/3) * np.log2(1/3) # byte 比特
讯享网1.1156
例:一个封闭系统,三种花所占比例为:1/10、2/10、7/10,本系统熵值为:
-(1/10) * np.log2(1/10) - (2/10) * np.log2(2/10) - (7/10) * np.log2(7/10)
讯享网1.70395
例:一个封闭系统,三种花所占比例为:1, 0, 0,本系统熵值为:
-1/1 * np.log2(1/1) - 0 - 0 # 完全确定的系统,没有不确定性
讯享网-0.0
1.58 - 1.15 # 信息增益,传入一条信息到封闭空间,导致空间熵值下降,下降数就是信息量 # 原封闭系统,熵值最大就是1.58,最小是0
讯享网0.000016
信息熵图像
- 设系统分两类,一类比例为x,则另一类比例为1-x
- 代入信息熵公式:H = -x*log(x)-(1-x)*log(1-x)
def en(x): return -x * np.log2(x) - (1-x) * np.log2(1-x) x = np.linspace(0.01, 0.99, 200) # 对数的底大于0且不等于1 x plt.plot(x, en(x)) # x轴为分类的比例,y轴为熵值 plt.xticks(np.arange(10)*0.1) plt.grid(linewidth=0.2) plt.show()
[外链图片转存失败(img-112FJKKi-84)(output_38_0.png)]
结论:2分类系统中,类别比例0.5的,熵值最大,不确定性最高
使用信息熵寻找最优划分
- 遍历特征数据下所有可划分特征维度和所有取值
- 当划分一种特征一个值后,系统熵值降到最低,此特征下的此值就是本分支节点
- 继续用分割后熵值不为0的特征数据划分下一分支节点,循环往复
- 直到整个系统熵降到最低
手写决策树算法
使用信息熵作为指标划分数据为不同类别
讯享网from collections import Counter
# 计算系统总信息熵的流程 y Counter(y) res = 0 for i in Counter(y).values(): print(i) p = i / len(y) # 每类值出现的概率 res += -p * np.log2(p) res
讯享网50 50 50 1.1156
讯享网# 划分类别函数 def split(X, y, d, value): # 特征,标签,维度,阈值 index_a = X[:, d] <= value # 布尔索引 index_b = X[:, d] > value # 返回被划分后的两类数据(特征和标签) return X[index_a], X[index_b], y[index_a], y[index_b] # 计算信息熵的函数 def entropy(y): counter = Counter(y) res = 0.0 for i in counter.values(): p = i / len(y) # 每类值出现的概率 res += -p * np.log2(p) return res # 寻找**维度和阈值,让每次划分后系统信息熵下降最大(信息熵最低) def try_split(X, y): best_entropy = float('inf') # 系统拆分后的总信息熵,设为正无穷大 best_d, best_v = -1, -1 # 划分维度和本维度下的划分阈值 # 遍历特征列 # d的取值为0, 1,总共只有2两列,0代表花瓣长1代表花瓣宽 for d in range(X.shape[1]): # 遍历特征数据X最外维的值(遍历所有特征列) # 遍历特征行(取每两行 特征值的中间值) sorted_index = np.argsort(X[:, d]) # 排序特征行(d列的所有行) for i in range(1, len(X)): # x1 = X[sorted_index[i - 1], d] # 本列特征值i- 1 x2 = X[sorted_index[i], d] if x1 != x2: # 如果相等则没有划分价值,跳过代码 v = (x1 + x2) / 2 # 候选阈值 # 数据用本维度和阈值划分为left和right两部分 X_l, X_r, y_l, y_r = split(X, y, d, v) # 本阈值划分后的系统熵值 e = entropy(y_l) + entropy(y_r) if e < best_entropy: # 如果切分后的新系统熵值更小,更新参数 best_entropy, best_d, best_v = e, d, v return best_entropy, best_d, best_v try_split(X, y)
(1.0, 0, 2.45)
讯享网best_entropy, best_d, best_v = try_split(X_train, y_train) print('最低系统信息熵为:', best_entropy) print('**划分维度为:', best_d) print('本维度**划分阈值为:', best_v)
最低系统信息熵为: 1.0 **划分维度为: 0 本维度**划分阈值为: 2.45
讯享网# 第一次划分后的结果 X1_l, X1_r, y1_l, y1_r = split(X_train, y_train, best_d, best_v) X1_l.shape, X1_r.shape, y1_l.shape, y1_r.shape
((33, 2), (72, 2), (33,), (72,))
讯享网# 查看划分后的左侧标签,信息熵为0,说明完全划分完毕 entropy(y1_l), entropy(y1_r) # 右侧熵不为0,有待继续划分
(0.0, 1.0)
讯享网# 继续划分右侧数据 best_entropy2, best_d2, best_v2 = try_split(X1_r, y1_r) print('最低系统信息熵为:', best_entropy2) print('**划分维度为:', best_d2) print('本维度**划分阈值为:', best_v2)
最低系统信息熵为: 0.94291 **划分维度为: 0 本维度**划分阈值为: 4.75
讯享网# 第二次划分 X2_l, X2_r, y2_l, y2_r = split(X1_r, y1_r, best_d2, best_v2) X2_l.shape, X2_r.shape, y2_l.shape, y2_r.shape
((34, 2), (38, 2), (34,), (38,))
讯享网entropy(y2_l), entropy(y2_r)
(0., 0.52357)
系统分支熵值仍不为0,还可继续划分,略
刚才的运算构造的决策树为:
讯享网if X[0] <= 2.45: X_left_en = 0.0 print('第一种鸢尾花,分类完毕') else: if X[0] <= 4.75: left_en = 0.191 print('第二种鸢尾花') else: right_en = 0.398 print('第三种鸢尾花')
基尼系数
基尼系数公式: G = 1 − ∑ i = 1 k p i 2 G=1-\sum_{i=1}^{k} p_{i} 2 G=1−∑i=1kpi2
例:三种鸢尾花,比例都为1/3,基尼系数为: G = 1 − ( 1 3 ) 2 − ( 1 3 ) 2 − ( 1 3 ) 2 = 0.6666 G=1-\left(\frac{1}{3}\right)^{2}-\left(\frac{1}{3}\right)^{2}-\left(\frac{1}{3}\right)^{2}=0.6666 G=1−(31)2−(31)2−(31)2=0.6666
1 - (1/3) 2 - (1/3) 2 - (1/3) 2
讯享网0.66665
例:三种花,比例为:1/10,2/10,7/10,基尼系数为:
1 - ((1/10)2 + (2/10)2 + (7/10)2)
讯享网0.00001
例:三种花,比例为:1,0,0,基尼系数为:
1 - (12 + 0 + 0)
讯享网0
基尼系数图像
- 设系统分两类,一类比例为x,则另一类比例为1-x
- 代入信息熵公式: G = 1 − ( x 2 + ( 1 − x ) ∗ 2 ) = − 2 x 2 + 2 x G=1-\left(x^2+(1-x)* 2\right)=-2x^2+2x G=1−(x2+(1−x)∗2)=−2x2+2x# 抛物线
def gini(x): return 1 - (x 2 + (1-x) 2) x = np.linspace(0.01, 0.99, 200) # 对数的底大于0且不等于1 x plt.plot(x, gini(x)) # x轴为分类的比例,y轴为熵值 plt.xticks(np.arange(10)*0.1) plt.grid(linewidth=0.2) plt.show()
[外链图片转存失败(img-lOdBZ28k-87)(output_61_0.png)]
讯享网def en(x): return -x * np.log2(x) - (1-x) * np.log2(1-x) def gini(x): return 1 - (x 2 + (1-x) 2) x = np.linspace(0.01, 0.99, 200) # 对数的底大于0且不等于1 plt.plot(x, en(x), color='r', alpha=0.8, label='en') # x轴为分类的比例,y轴为熵值 plt.plot(x, gini(x)+0.5, color='g', alpha=0.8, label='gini') plt.xticks(np.arange(10)*0.1) plt.grid(linewidth=0.2) plt.legend()
<matplotlib.legend.Legend at 0x531c748>
[外链图片转存失败(img-Yh21dqip-91)(output_62_1.png)]
基尼系数和信息熵比较
- 信息熵计算比基尼系数更慢(对数运算)
- sklearn默认基尼系数
- 二者差异较小,大部分数据下优劣相当
决策树作为一个算法类别,有很多不同实现
- ID3
- 只能做分类,分支为二分或多分
- 使用信息增益做分隔
- C4.5
- 只能做分类,分支为二分或多分
- 使用信息增益比来分特征(惩罚了值类别特别多的特征)
- C5.0
- C4.5的性能提升版
- CART
- Classification And Regression Tree,分类和回归树
- 能做分类也能做回归(可以用分类后值的概率作为回归结果)
- 分支为二叉树
- 使用基尼系数来分特征
sklearn内的决策树算法实现为CART算法的最优版本
决策树剪枝:对一些参数进行平衡
剪去枝干,让树变小,目的是:
- 降低复杂度
- 解决过拟合
过拟合和模型精确度是一对互相矛盾的指标:
- 拟合程度越低,模型预测越不精确
- 精确度越高,越容易过拟合
- 二者需要取得平衡,在适度拟合的前提下,模型预测达到最精确
选择模型的标准:如果两个模型精度一样,效率也一样,选择更简单那个
- 奥卡姆剃刀原理
- 如无必要,勿增实体
科学思维:车库里的龙
讯享网import numpy as np import matplotlib.pyplot as plt # 生成非线性测试数据 from sklearn import datasets X, y = datasets.make_moons(noise=0.25, random_state=666) # 噪点参数0.25 X[:5]
array([[ 1.0, 0.], [-0.0, 0.0], [-0., 0.], [-0., 0.], [-0., 0.]])
讯享网y
array([1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 0, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 0, 0, 1, 0, 1, 1, 1, 0, 0, 0, 1, 0, 0, 1, 1, 1, 0, 1, 1, 1, 1, 0, 0, 0], dtype=int64)
讯享网plt.scatter(X[y==0, 0], X[y==0, 1]) plt.scatter(X[y==1, 0], X[y==1, 1])
<matplotlib.collections.PathCollection at 0x53864e0>
[外链图片转存失败(img-Lp7ACHtZ-96)(output_67_1.png)]
默认参数训练模型
讯享网from sklearn.tree import DecisionTreeClassifier dt_clf = DecisionTreeClassifier() dt_clf.fit(X, y)
DecisionTreeClassifier(class_weight=None, criterion='gini', max_depth=None, max_features=None, max_leaf_nodes=None, min_impurity_decrease=0.0, min_impurity_split=None, min_samples_leaf=1, min_samples_split=2, min_weight_fraction_leaf=0.0, presort=False, random_state=None, splitter='best')
讯享网# 默认参数训练模型可视化 plot_decision_boundary(dt_clf, axis=[-1.5, 2.5, -1.0, 1.5]) # axis为横轴和众轴的范围 plt.scatter(X[y==0,0], X[y==0,1]) plt.scatter(X[y==1,0], X[y==1,1]) # 分支规则太细,过拟合明显
<matplotlib.collections.PathCollection at 0x>
[外链图片转存失败(img-8LB320kK-08)(output_70_1.png)]
讯享网# 设分支最大深度为2(值越小越不容易过拟合) # 分割两次,每次都限制最大和最小范围 dt_clf2 = DecisionTreeClassifier(max_depth=2) dt_clf2.fit(X, y) plot_decision_boundary(dt_clf2, axis=[-1.5, 2.5, -1.0, 1.5]) plt.scatter(X[y==0,0], X[y==0,1]) plt.scatter(X[y==1,0], X[y==1,1])
<matplotlib.collections.PathCollection at 0xfd38be0>
[外链图片转存失败(img-7iaWO4DJ-15)(output_71_1.png)]
讯享网# 节点至少有x个样本数据才会继续分支(值越高越不容易过拟合) dt_clf3 = DecisionTreeClassifier(min_samples_split=10) dt_clf3.fit(X, y) plot_decision_boundary(dt_clf3, axis=[-1.5, 2.5, -1.0, 1.5]) plt.scatter(X[y==0,0], X[y==0,1]) plt.scatter(X[y==1,0], X[y==1,1])
<matplotlib.collections.PathCollection at 0xfbe3198>
[外链图片转存失败(img-wBIJ2scR-18)(output_72_1.png)]
讯享网# 叶子节点至少应该有x个样本数据(最终节点内最少样本数,越大越不容易过拟合) dt_clf4 = DecisionTreeClassifier(min_samples_leaf=6) dt_clf4.fit(X, y) plot_decision_boundary(dt_clf4, axis=[-1.5, 2.5, -1.0, 1.5]) plt.scatter(X[y==0,0], X[y==0,1]) plt.scatter(X[y==1,0], X[y==1,1])
<matplotlib.collections.PathCollection at 0xfc56710>
[外链图片转存失败(img-KcplM0NA-23)(output_73_1.png)]
讯享网# 最多有多少个叶子节点(越少越不容易过拟合) dt_clf5 = DecisionTreeClassifier(max_leaf_nodes=4) dt_clf5.fit(X, y) plot_decision_boundary(dt_clf5, axis=[-1.5, 2.5, -1.0, 1.5]) plt.scatter(X[y==0,0], X[y==0,1]) plt.scatter(X[y==1,0], X[y==1,1])
<matplotlib.collections.PathCollection at 0xfcc6c88>
[外链图片转存失败(img-B1ps3gwm-28)(output_74_1.png)]
讯享网# # 更多参数见文档 # help(DecisionTreeClassifier)
决策树解决回归问题
from sklearn import datasets
讯享网boston = datasets.load_boston()
X = boston.data y = boston.target # X
讯享网X.shape
(506, 13)
讯享网y[:10]
array([24. , 21.6, 34.7, 33.4, 36.2, 28.7, 22.9, 27.1, 16.5, 18.9])
讯享网from sklearn.model_selection import train_test_split X_train, X_test, y_train, y_test = train_test_split(X, y, random_state=666)
from sklearn.tree import DecisionTreeRegressor dt_reg = DecisionTreeRegressor() dt_reg.fit(X_train, y_train) # 训练
讯享网DecisionTreeRegressor(criterion='mse', max_depth=None, max_features=None, max_leaf_nodes=None, min_impurity_decrease=0.0, min_impurity_split=None, min_samples_leaf=1, min_samples_split=2, min_weight_fraction_leaf=0.0, presort=False, random_state=None, splitter='best')
dt_reg.predict(X_test) # 预测
讯享网array([27.1, 50. , 20.4, 34.9, 21.6, 21.1, 17.8, 20.5, 28.5, 16.7, 8.8, 23.9, 22.9, 16.7, 22.9, 26.4, 32.5, 18.4, 14.1, 30.1, 23.3, 10.4, 10.2, 48.3, 27.5, 15. , 21.2, 13.4, 36.1, 32.2, 43.1, 9.5, 31.5, 17.8, 22.6, 22.4, 36. , 32.5, 23.1, 13.3, 24. , 20.6, 26.6, 16.8, 24.1, 10.2, 30.1, 34.9, 24.4, 22.7, 24.6, 23.3, 50. , 16.8, 21.4, 18.8, 21.7, 15.2, 14.1, 15.6, 20.9, 20.4, 28. , 14.9, 14.3, 10.4, 22.4, 24. , 23.8, 20.3, 5.6, 31.5, 18.2, 14.9, 46. , 22.4, 5.6, 23.8, 19. , 32.7, 28.4, 22. , 10.2, 30.1, 36.1, 19.4, 23.3, 24.3, 19.7, 50. , 15.6, 34.6, 24.6, 32.5, 19.5, 13.3, 29.8, 23.3, 13.3, 16.3, 17.8, 16.1, 24.8, 14.1, 17.7, 23.1, 16.1, 21.7, 22.6, 23.1, 48.3, 20.5, 22.4, 5.6, 22.2, 24.4, 17.8, 13.3, 33.2, 16.8, 21.9, 20.3, 25.1, 12.3, 10.4, 33.4, 12.7])
dt_reg.score(X_train, y_train) # 训练集特征预测效果100%
讯享网1.0
dt_reg.score(X_test, y_test) # 测试集特征预测过小(58%),说明模型过拟合比较严重,需要调参
讯享网0.72887
# help(DecisionTreeRegressor)
讯享网
决策树的局限
- 分类边界横平竖直,不能如实反映数据真实分隔情况
- 对某些边界值很敏感,数据一点小改变会导致结果发生很大变化
决策树的优势:
- 结果具有可解释性
- 决策树模型之前的差异性大,模型多样化,最适合做集成学习
实际工作中,决策树一般不单独使用,而是和转为集成算法中的随机森林,更好的应用
讯享网
讯享网

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容,请联系我们,一经查实,本站将立刻删除。
如需转载请保留出处:https://51itzy.com/kjqy/124691.html