PS:该系列数据都可以在图灵社区(点击此链接)中随书下载中下载(如下)
1 复杂数据的局部性建模
树回归
优点:可以对复杂和非线性的数据建模。
缺点:结果不易理解。
适用数据类型:数值型和标称型数据。
之前决策树适用的算法是ID3,其做法是每次选取当前最佳的特征来分割数据,并按照该特征的所有可能取值来切分。也就是说,如果一个特征有4中取值,那么数据将被切成4份。一旦按某特征切分后,该特征在之后的算法执行过程中将不会再起作用,这种切分方式过于迅速。ID3算法也不能直接处理连续型变量。
另一种方法是二元切分法,即每次把数据集切成两份。易于对树构建过程进行调整以处理连续型特征,具体做法是:如果特征值大于给定值就走左子树,否则就走右子树。
CART(Classification And Regression Trees,分类回归树) 是十分著名的树构建算法,它使用二元切分来处理连续型变量。
2 连续和离散型特征的树的构建
python中可以直接使用字典来存储树结构而无需另外自定义一个类,从而有效地减少代码量。下面会构建两种树:回归树(regression tree),其每个叶节点包含单个值;模型树(model tree),其每个叶节点包含一个线性方程。
共用函数createTree()伪代码:
找到最佳的待切分特征:
如果该节点不能再分,将该节点存为叶节点
执行二元切分
在右子树调用createTree()方法
在左子树调用createTree()方法
创建文件regTrees.py,并在python命令行进行测试:
import numpy as np
import matplotlib.pyplot as plt
def loadDataSet(fileName):
dataMat = []
with open(fileName, 'r') as fileObject:
for line in fileObject.readlines():
curLine = line.strip().split('\t')
fltLine = list(map(float, curLine))
dataMat.append(fltLine)
return dataMat
def binSplitDataSet(dataSet, feature, value):
'''根据待切分的feature和值value执行二元切分'''
mat0 = dataSet[np.nonzero(dataSet[:, feature] > value)[0], :]
mat1 = dataSet[np.nonzero(dataSet[:, feature] <= value)[0], :]
return mat0, mat1
3 将CART算法用于回归
数据集混乱度计算:首先计算所有数据的均值,然后计算每条数据的值到均值的差值。为了对正负差值同等看待,一般使用绝对值或者平方值来代替上述差值,所以这里使用平方误差的总值(总方差)。
3.1 构建树
需要编写函数chooseBestSplit(),该函数的目标是找到数据集切分的最佳位置。它遍历所有的特征及其可能的取值来找到使误差最小化的切分阈值。伪代码如下:
对每个特征:
对每个特征值:
将数据集切分成两份
计算切分的误差
如果当前误差小于当前最小误差,那么将当前切分设定为最佳切分并更新最小误差
返回最佳切分的特征和阈值
添加代码,把数据文件拷贝到regTrees.py所在文件夹,并进行测试:
def regLeaf(dataSet):
'''返回叶节点'''
return np.mean(dataSet[:, -1])
def regErr(dataSet):
'''返回平方误差'''
return np.var(dataSet[:, -1]) * np.shape(dataSet)[0]
def choseBestSplit(dataSet, leafType=regLeaf, errType=regErr, ops=(1, 4)):
'''找到数据的最佳二元切分方式,生成相应的叶节点'''
tolS = ops[0] #容许的误差下降值
tolN = ops[1] #切分的最少样本数
if len(set(dataSet[:, -1].T.tolist()[0])) == 1:
#如果所有特征值相等则退出
return None, leafType(dataSet)
m, n = np.shape(dataSet)
S = errType(dataSet)
bestS = float('inf')
bestIndex = 0
bestValue = 0
#所有可能的特征和可能的取值上遍历
for featIndex in range(n-1):
for splitVal in set((dataSet[:, featIndex].T.A.tolist())[0]):
mat0, mat1 = binSplitDataSet(dataSet, featIndex, splitVal)
if np.shape(mat0)[0] < tolN or np.shape(mat1)[0] < tolN:
continue
newS = errType(mat0) + errType(mat1)
if newS < bestS:
bestIndex = featIndex
bestValue = splitVal
bestS = newS
if (S - bestS) < tolS:
#如果误差减少不大则退出
return None, leafType(dataSet)
mat0, mat1 = binSpl