决策树的引入
决策树的结构
根节点
决策节点(内部节点)
叶节点
决策树构建最关键的一点是判断谁来当根节点,就像这个相亲问题,能一个问题判断出来就不需要问其他的问题了,所以选那个为根节点就显得尤为重要,这样不仅能增加我们的运算效率,还能减少错误的产生
构建决策树
1.计算熵值
构建决策树的第一点,就是计算根节点的熵值,选取熵值小的,能有效的减少数的深度,查看哪个特征对结果产生最直接的影响,方便后面的计算,熵值的计算公式为:Ent(D)= -
∑
k
=
1
∣
y
∣
\sum_{k=1}^{\mid{y}\mid}
∑k=1∣y∣pklog2pk
Ent(D)的值越小,代表D的纯度越高
2.计算信息增益
计算信息增益率是为了确定哪个特征作为根节点,信息增益 = Ent(D) -
∑
u
y
∣
D
u
∣
∣
D
∣
\sum_u^y\frac{\mid{D~u~}\mid}{\mid{D}\mid}
∑uy∣D∣∣D u ∣Ent(Du)
3.根节点确定好后需要递归来确定其他的节点
当熵值为0时,就可以停止这一操作了,此时的叶节点都是纯的,当然这是最理想的状态,最坏的情况是决策树的高度等于了决策变量的个数,此时的叶节点不纯,意味着我们要以一定的概率来做出决策了
过拟合问题
使用决策树最容易出现的就是过拟合的问题了,遇到这个问题我们有剪枝的方式来处理:
预剪枝
结点划分前,若划分不能带来泛化性能提高,就停止划分标为叶结点。性能提高的判断由划分前后验证集的精度决定。
优点:分支较少,减小过拟合风险,也减少了训练时间开销和测试时间开销
缺点:带来了欠拟合的风险(当前划分虽不能提升泛化能力,但继续划分呢?)
后剪枝
生成完整的决策树后自底向上对非叶结点考察,若替换为叶结点能带来泛化性能提升,则替换。
优点:比预剪枝分支多,欠拟合风险小,泛化性能往往优于预剪枝
缺点:训练时间开销大得多
如何增加决策树的准确率
决策树是建立在已知的历史数据及概率上的,一课决策树的预测可能会不太准确,提高准确率最好的方法是构建随机森林(Random Forest)。
所谓随机森林就是通过随机抽样的方式从历史数据表中生成多张抽样的历史表,对每个抽样的历史表生成一棵决策树。由于每次生成抽样表后数据都会放回到总表中,因此每一棵决策树之间都是独立的没有关联。将多颗决策树组成一个随机森林。当有一条新的数据产生时,让森林里的每一颗决策树分别进行判断,以投票最多的结果作为最终的判断结果。以此来提高正确的概率。
决策树的优缺点
决策树算法的优点:
1)简单直观,生成的决策树很直观。
2)既可以处理离散值也可以处理连续值。很多算法只是专注于离散值或者连续值。
3)可以处理多维度输出的分类问题。
4)相比于神经网络之类的黑盒分类模型,决策树在逻辑上可以得到很好的解释
5)可以交叉验证的剪枝来选择模型,从而提高泛化能力。
6)对于异常点的容错能力好,健壮性高。
决策树算法的缺点:
1)决策树算法容易过拟合:通过设置节点最少样本数量、不纯度/熵阈值、限制决策树深度来改进。
2)决策树会因为样本发生一点点的改动,就会导致树结构的剧烈改变:通过随机森林等算法。
3)寻找最优的决策树是一个NP难题,通过启发式方法,容易陷入局部最优:通过随机森林算法等方法改善。
4)有些比较复杂的关系,决策树很难学习,一般通过神经网络等方法解决。
5)如果某些特征的样本比例过大,生成决策树容易偏向于这些特征:可通过调节样本权重来改善。
代码实现
import numpy as np
import matplotlib.pyplot as plt
from sklearn.tree import DecisionTreeRegressor
plt.rcParams['font.sans-serif'] = ['SimHei']
# 随机数产生器
rng = np.random.RandomState(1)
# 随机产生400个数的数据集X,并排序
X = np.sort(5 * rng.rand(400, 1), axis=0)
# 计算X的sin函数值
y = np.sin(X).ravel()
# 调用回归树,并设置最大深度为2
r_tree_2 = DecisionTreeRegressor(max_depth=2)
r_tree_5 = DecisionTreeRegressor(max_depth=5)
# 训练模型
r_tree_2.fit(X, y)
r_tree_5.fit(X, y)
# 计算准确率
print('最大深度为2的准确率为:',r_tree_2.score(X, y))
print('最大深度为5的准确率为:',r_tree_5.score(X, y))
# 预测结果(深度=2)
test_X_2 = np.c_[np.arange(0.0, 5.0, 0.01)]
y_2 = r_tree_2.predict(test_X_2)
# 预测结果(深度=5)
test_X_5 = np.c_[np.arange(0.0, 5.0, 0.01)]
y_5 = r_tree_5.predict(test_X_5)
# 画图(深度=2)
plt.scatter(X, y, label='原数据')
plt.plot(test_X_2, y_2, color='red', label='深度=2')
plt.legend()
plt.show()
# 深度=5
plt.scatter(X, y, label='原数据')
plt.plot(test_X_5, y_5, color='red', label='深度=5')
plt.legend()
plt.show()
效果展示
总结
从图像可以清晰的看出深度为2的准确率上不如准确率为5的