核心内容翻译自:https://www.saedsayad.com/decision_tree_reg.htm
对其进行了修改,并增加了代码,但原创内容不多,因此仍作为翻译文章。
我在之前的文章中介绍过决策树用在分类问题中的应用,例如,通过选择信息增益作为分裂准则,我们就得到了著名的ID3决策树。如果将信息增益换为标准差减少量(Standard Deviation Reduction,SDR),那么就可以利用决策树解决回归问题了。
本文用到的样例数据集如下(下载):
Outlook | Temp | Humidity | Windy | Hours Played |
---|---|---|---|---|
Rainy | Hot | High | False | 25 |
Rainy | Hot | High | True | 30 |
Overcast | Hot | High | False | 46 |
Sunny | Mild | High | False | 45 |
Sunny | Cool | Normal | False | 52 |
Sunny | Cool | Normal | True | 23 |
Overcast | Cool | Normal | True | 43 |
Rainy | Mild | High | False | 35 |
Rainy | Cool | Normal | False | 38 |
Sunny | Mild | Normal | False | 46 |
Rainy | Mild | Normal | True | 48 |
Overcast | Mild | High | True | 52 |
Overcast | Hot | Normal | False | 44 |
Sunny | Mild | High | True | 30 |
其中,Hours Played是目标列,其余是特征列。
1、什么是标准差减少量
我们已经知道,分类决策树中的每个结点(叶子结点除外)表达了按照某个属性对样本点进行分类的方式。被分到同一类别中的样本具有一定的相似性(或者说它们在一定程度上是同质的),我们可以使用特征列取值的标准差来衡量这种同质性的大小。如果标准差为0,说明这些样本是完全同质的。
也就是说,通过计算按照某个属性对目标进行分类前后的标准差的减少量,可以判断是否将该属性作为分裂准则。
根据这个观点,我们来看一下如何将标准差减少量应用到回归模型中。
2、回归模型中的应用
我们的目标是基于特征列来预测Hours Played的值,因此,最终的叶子节点一定是Hours Played的不同取值。
1)在分裂之前,我们可以计算全部Hours Played值的标准差:
import pandas as pd
df = pd.read_csv('data.csv')
print(df['Hours Played'].std())
# 9.672948571450904
2)对于某个特征
T
T
T,我们可以按照如下公式计算它相对于目标列的“标准差”:
S
(
T
,
X
)
=
∑
c
∈
X
P
(
c
)
S
t
d
(
c
)
S(T,X)=\sum_{c\in X}P(c)Std(c)
S(T,X)=c∈X∑P(c)Std(c)
其中,
X
X
X表示该特征所有可能的取值,
P
(
c
)
P(c)
P(c)表示
T
T
T取值为
c
c
c的样本占样本总数的比例,
S
t
d
(
c
)
Std(c)
Std(c)表示
T
T
T取值为
c
c
c的样本的目标列取值的标准差。
例如,如果 T T T是Outlook,那么得到如下计算结果:
Outlook’s value | Hours Played’s std | # samples |
---|---|---|
Overcast | 4.03 | 4 |
Rainy | 8.7 | 5 |
Sunny | 12.15 | 5 |
S ( H o u r s P l a y e d , O u t l o o k ) = 4 / 14 ∗ 4.03 + 5 / 14 ∗ 8.7 + 5 / 14 ∗ 12.15 = 8.60 S(HoursPlayed,Outlook)=4/14*4.03+5/14*8.7+5/14*12.15=8.60 S(HoursPlayed,Outlook)=4/14∗4.03+5/14∗8.7+5/14∗12.15=8.60
print(df[df['Outlook'] == 'Overcast']['Hours Played'].std() * 4/14 + \
df[df['Outlook'] == 'Rainy']['Hours Played'].std() * 5/14 + \
df[df['Outlook'] == 'Sunny']['Hours Played'].std() * 5/14)
# 8.59952381247049
这可以理解成一种加权标准差,权重就是某一属性的不同取值。
于是,所谓「标准差减少量」,指的就是加权标准差相较于分类前总的标准差的减少量。
在第一步中,计算得到了目标列的标准差为9.67;在第二步中,计算得到了Outlook属性相对于目标列的标准差为8.6;因此,按Outlook属性分类后,目标列标准差的减少量为 9.67 − 8.6 = 1.07 9.67-8.6=1.07 9.67−8.6=1.07。
类似地,可以计算出所有特征属性相对于目标列的标准差减少量:
def calculate_SDR(df, fea_name, original_std=9.67):
vc = dict(df[fea_name].value_counts())
new_std = 0
for key in vc:
_std = df[df[fea_name] == key]['Hours Played'].std()
if pd.isna(_std): # 如果该类别中只有一个样本了,则标准差按0计算
continue
else:
new_std += _std * vc[key] / df.shape[0]
return original_std - new_std
print(calculate_SDR(df, 'Outlook')) # 1.0704761875295095
print(calculate_SDR(df, 'Temp')) # -0.34483660877697453
print(calculate_SDR(df, 'Humidity')) # -0.10381057186483211
print(calculate_SDR(df, 'Windy')) # -0.1128887570057504
3)你已经猜到了,具有最大SDR的属性,即Outlook,将作为当前节点的分裂属性。
3、提前终止条件
理论上,按照选定的分裂属性将样本分类后,递归地执行上述过程,直到所有的数据都被处理完成,就可以得到最终的决策树。但在实际应用过程中,我们需要设置一些终止条件,使得在满足这些条件时不再继续分裂而是返回叶子节点。
终止条件可以有多种,这里介绍一种名为偏差系数(coefficient of deviation)的条件,它的计算公式如下:
c
v
=
s
t
d
m
e
a
n
∗
100
%
cv=\frac{std}{mean}*100\%
cv=meanstd∗100%
即标准差与均值的比值。
可以想见,样本经过某个特征进行分裂后形成不同的子样本组,同组样本的cv值越小,说明该组样本彼此间的差异越不明显。于是,当cv低于一个设定值(如10%),我们有理由认为该组样本已经具有了较高的同质性而不再对其进行分裂。
还可以设置多个终止条件,满足任一条件则终止分裂。
例如,当按照某个特征进行分裂后,各个分支得到的样本数量小于某个设定值(如4),则不再继续分裂。这种终止条件的合理性依据是:当样本数量足够小时,已经很难继续从其中找出有价值的分裂规则。
继续上面的例子,当按照Outlook进行分裂后,取值为Overcast的样本一共有4个,计算它们的标准差和均值,进而发现其cv值为8%,如下表所示:
Outlook取值 | 目标列的std | 目标列的mean | # samples | cv |
---|---|---|---|---|
Overcast | 4.03 | 46.25 | 4 | 8.7% |
Rainy | 8.7 | 35.2 | 5 | 24.7% |
Sunny | 12.15 | 39.2 | 5 | 31% |
因此,按照cv小于10%则终止分裂的理论,不再对该4个样本进行分类,而是直接返回样本的目标列均值作为叶子节点。
这也就是说,如果一个待预测的样本的Outlook属性的值为Overcast,则预测的Hours Played的值为46.25。
4、完成一颗回归树
以Outlook取值为Sunny的样本为例,其cv值为31%,远大于10%的阈值,因此我们需要对这些样本再进行细分。即,将Outlook取值为Sunny的5个样本视为初始样本,其std为12.15,然后按照上面的方法计算剩余的三个特征对这5个样本的SDR值。
经过计算可以发现,Temp、Humidity、Windy的SDR值分别为-1.43、-1.27、7.89。可知,应该按照Windy进行分裂。但上述5个样本按照Windy分裂后取各个值的样本的数量都小于4(取值为False的有3个,True的有2个),满足了第二个终止条件,因此,不再继续分裂,而是计算各组样本目标列的均值作为叶子节点返回。
同理,可以处理Outlook为Rainy的那5个样本。首先计算剩余特征的SDR后发现应该按照Temp进行分裂(SDR为3.61)。分裂后各个取值的样本的数量均小于4,满足终止条件,因此返回目标列的均值作为叶子节点。
至此,一颗用于回归的决策树就训练完成了,它的结构如下: