决策回归树
- 1. 决策回归树原理
- 2. 决策回归树算例
- 3. 手动计算MSE和最优划分属性
- 4. 决策回归树 VS 线性回归
1. 决策回归树原理
决策回归树,虽然叫做“回归”树,但是它的本质还是分类算法,只是分的类别多一点。
1. 回归树的裂分指标
- 回归树种,节点的裂分指标是
MSE
或MAE
,但是最常用的是MSE
:
MSE ( y , y ^ ) = 1 n ∑ i = 1 n ( y i − y ^ i ) 2 \text{MSE}(y, \hat{y}) = \frac{1}{n} \sum\limits_{i=1}^{n} (y_i - \hat{y}_i)^2 MSE(y,y^)=n1i=1∑n(yi−y^i)2
MAE ( y , y ^ ) = 1 n ∑ i = 1 n ∣ y i − y ^ i ∣ \text{MAE}(y, \hat{y}) = \frac{1}{n} \sum\limits_{i=1}^{n} \left| y_i - \hat{y}_i \right| MAE(y,y^)=n1i=1∑n∣yi−y^i∣ - 其中 y i y_i yi 表示目标值, y ^ i \hat{y}_i y^i 是预测值,也是所有样本的目标值的均值。
2. 筛选最优划分属性的步骤
- 就像之前学习的分类树一样,每一个节点,都有自己的MSE(熵),根据某一个属性进行划分,得到的子节点也有自己的MSE(熵),进而可以求得根据这一属性划分的子节点MSE带权加和(信息增益)。
- 最优的划分属性,就是子节点MSE带权加和最小的那一个。
3. 叶子结点的值(预测值)
- 对于回归树,叶子节点的值通常是该叶子节点内所有样本的目标变量值的平均值。
2. 决策回归树算例
1. 准备数据
import numpy as np
import matplotlib.pyplot as plt
# 造40个范围0到2pi的训练数据,并转化为列向量
X_train = np.linspace(0, 2 * np.pi, 40).reshape(-1, 1) # 训练数据
X_test = np.linspace(0, 2 * np.pi, 256).reshape(-1, 1) # 测试数据
# y 一个正弦波,余弦波,圆,目标值是二维的,y1 = sinx,y2 = cosx
y_train = np.concatenate([np.sin(X_train), np.cos(X_train)], axis=1)
plt.figure(figsize=(4, 4)) # 横纵坐标比例相同
plt.scatter(y_train[:,0], y_train[:,1])
- 我们希望习得一个模型,来拟合这个圆轨迹。上面的数据中,特征只有一个,二目标值有两个,是二维的。
2. 使用普通线性回归拟合
# 使用线性回归拟合
from sklearn.linear_model import LinearRegression
model = LinearRegression()
model.fit(X_train, y_train)
y_pred = model.predict(X_test)
plt.scatter(y_pred[:, 0], y_pred[:, 1])
- 效果非常不好,普通线性回归就是需要拟合出一条直线,但是我们希望得到是一个类圆的曲线。
3. 使用回归树拟合
from sklearn.tree import DecisionTreeRegressor
# 决策回归树
model = DecisionTreeRegressor(max_depth=3) # 深度为3
model.fit(X_train, y_train)
y_pred = model.predict(X_test)
plt.scatter(y_pred[:, 0], y_pred[:, 1])
plt.axis('equal') # 表示各轴横纵坐标相等
- 可以看到,虽然拟合出的只有几个簇点,但是似乎已经有圆的样子了,我们试着再将深度放开点。
from sklearn.tree import DecisionTreeRegressor
# 决策回归树
model = DecisionTreeRegressor()
model.fit(X_train, y_train)
y_pred = model.predict(X_test)
plt.scatter(y_pred[:, 0], y_pred[:, 1])
plt.axis('equal') # 表示各轴横纵坐标相等
print(model.get_depth()) # 显示深度
"""
输出:
6
"""
- 可以看到,这次拟合出的图形更像圆了,决策树的深度是6。
3. 将深度为3的决策树可视化
from sklearn import tree
import graphviz
dot_data = tree.export_graphviz(model, filled=True, rounded=True)
graph = graphviz.Source(dot_data)
graph.render('Account', format='png')
- 上图回归树深度为3,叶子结点最多有23=8个,二上图得到的又恰巧是一颗满二叉树,所以习得的图像才有会8个簇点。
- 如果我们默认将回归树分到底,可以看到得到的回归树深度是6,也就是说最多可能会有2^6个叶子结点,而我们的训练数据只有40个,这意味着回归数会将所有的训练数据分纯(这样一定会过拟合)。
3. 手动计算MSE和最优划分属性
1. 根节点MSE
# 求未分裂时的mse
((y_train - y_train.mean(axis=0))**2).mean() # 注意y是二维的,有两列
"""
输出:
0.4996875
"""
2. 计算最优划分属性
- 由于本算例中属性只有一个,所以计算最优划分属性实际上就是计算该属性的最优划分点:
# 最佳裂分条件
lower_mse = 1 # 根节点的mse是0.5,这个初始值只要比0.5大就可以
best_split = {} # 最佳划分点:mse带权加和
for i in range(len(X_train) - 1):
split = X_train[i:i+2].mean()
cond = X_train <= split
# 根据条件,得到左侧和右侧数据划分
y_left = y_train[cond.reshape(-1)] # 左节点
y_right = y_train[(~cond).reshape(-1)] # 右节点
# 子节点MSE
mse_left = ((y_left - y_left.mean(axis=0))**2).mean()
mse_right = ((y_right - y_right.mean(axis=0))**2).mean()
# 计算划分整体的mse
p_left = cond.sum()/cond.size # 左节点所占比重
p_right = 1 - p_left # 右节点所占比重
mse = mse_left * p_left + mse_right * p_right
if mse < lower_mse:
lower_mse = mse
best_split.clear()
best_split[np.round(split, 3)] = mse
print('最佳划分点是:', best_split)
"""
输出:
最佳划分点是: {3.142: 0.3072588991283228}
"""
4. 决策回归树 VS 线性回归
1. 准备数据
import numpy as np
from sklearn.tree import DecisionTreeRegressor
from sklearn import tree
from sklearn import datasets
from sklearn.model_selection import train_test_split
from sklearn.linear_model import LinearRegression
diabetes = datasets.load_diabetes() # 糖尿病数据
X = diabetes['data']
y = diabetes['target']
X_train,X_test,y_train,y_test = train_test_split(X,y,random_state = 911)
2. 线性回归
linear = LinearRegression()
linear.fit(X_train,y_train)
print('训练数据得分:', linear.score(X_train, y_train))
print('测试数据得分:', linear.score(X_test,y_test))
- 输出:
训练数据得分: 0.5400326830930334
测试数据得分: 0.4139431540141
3. 决策树回归
import matplotlib.pyplot as plt
plt.rcParams['font.family'] = 'STKaiti'
max_depth = np.arange(1,16)
score_train = [] # 训练集得分
score_test = [] # 测试集得分
# 遍历所有深度
for d in max_depth:
model = DecisionTreeRegressor(max_depth = d)
model.fit(X_train,y_train)
score_train.append(model.score(X_train, y_train))
score_test.append(model.score(X_test, y_test))
plt.plot(max_depth, score_train, 'g*-') # 绿线,随着深度增加,训练集的情况
plt.plot(max_depth,score_test,'ro-') # 红线,随着深度增加,测试集的情况
plt.xlabel('max_depth',fontsize = 18)
plt.ylabel('Score',fontsize = 18)
plt.title('决策树准确率随着深度变化',pad = 20,fontsize = 20)
plt.legend(['训练数据得分', '测试数据得分'], fontsize = 18)
plt.savefig('./6-决策树回归糖尿病.png',dpi = 200)
- 可以看到,决策回归树测试数据得分最高不过才2点几,在这个数据集上没有线性回归效果好。
- 并且随着深度的提高,训练集的得分接近满分,但是测试集得分越来越差,这就是典型的过拟合。
此处的得分是R2值。