机器学习实战—树回归

线性回归包含了一些强大的方法,但这些方法创建的模型需要拟合所有的样本点(局部加权线性回归除外)。当数据拥有众多特征并且特征之间关系十分复杂时,构建全局模型的想法就太难了。一种可行的办法是将数据集切分成很多份易建模的数据,然后利用线性回归建模,如果首次切分后仍然难以拟合线性模型就继续切分,在这种切分方式下,树结构和回归法就很有用。

这里介绍一种即可用于分类还可以用于回归的CART树构建算法。之后引入一个更加高级的模型树算法,与回归树不同,该算法需要在每个叶节点上都构建出一个线性模型。

一、复杂数据的局部性建模
决策树是一种贪心算法,它要在给定时间内做出最佳选择,但是并不关心能否达到全局最优。

之前使用的树构建算法是ID3,ID3的做法是每次选取当前最佳的特征来分割数据,并按照该特征的所有可能取值来切分,一旦按某特征切分后,该特征在之后的算法执行过程中将不再起作用,这种切分算法过于迅速。另外一种方法是二元切分法。即每次把数据集切分成两份。如果数据的某特征值等于切分所要求的值,那么这些数据就进入树的左子树,反之则进入树的右子树。

二元切分法易于对树构建过程进行调整以处理连续型特征。具体处理方法是:如果特征值大于给定值就进入左子树,否则就进入右子树。

二、连续和离散型特征的树的构建
这里使用一部字典来存储树的数据结构,该字典包含以下4个元素:
待切分的特征、待切分的特征值、右子树(当不再需要切分的时候,也可以是单个值)、左子树(与右子树类似)

CART算法只做二元切分,所以这里可以固定树的数据结构。树包含左键和右键,可以存储另一颗子树或者单个值。字典还包含特征和特征值这两个键,它们给出切分算法所有的特征和特征值。

本文将构建两种树:第一种是回归树,其每个叶节点包含单个值;第二种是模型树,其每个叶节点包含一个线性方程。

构建树函数伪代码:
找到最佳的切分特征:
如果该节点不能再分,将该节点存为叶节点
执行二元切分
在右子树调用createTree()方法
在左子树调用createTree()方法

CART算法的实现代码:

import numpy as np

#加载数据集
def loadDataSet(filename):
    dataSet = []
    fp = open(filename)
    for line in fp.readlines():
        curLine = line.strip().split('\t')
        #将每行中的数据映射为浮点数
        fltLine = list(map(float,curLine))
        dataSet.append(fltLine)
    return dataSet

#二值切分
def binSplitDataSet(dataSet, feature, value):
    #np.nonzero()函数返回数组中非零数据的索引值数组
    #利用数组过滤得到左子树集
    #返回左子树集的第一个样本,构建回归树时每次只需要返回集合中的第一条样本
    leftSet = dataSet[np.nonzero(dataSet[:,feature]>value)[0],:]
    #返回左子树矩阵
    # leftSet = dataSet[np.nonzero(dataSet[:, feature] > value)[0], :][0]
    # 利用数组过滤得到右子树集
    # 返回右子树集的第一个样本
    rightSet = dataSet[np.nonzero(dataSet[:, feature] <= value)[0],:]
    # 返回右子树矩阵
    # rightSet = dataSet[np.nonzero(dataSet[:, feature] <= value)[0], :][0]
    return leftSet,rightSet

三、将CART算法用于回归
为成功构建以分段常数为叶节点的树,需要度量出数据的一致性,事实上,在数据集上计算混乱度是非常简单的,首先计算所有数据的均值,然后计算每条数据的值到均值的差值。为了对正负值同等看待,一般使用绝对值或平方值来代替上述差值,上述做法有点类似于统计学中常用的方差计算。唯一不同的是,方差是平方误差的均值(均方差),二而这里需要的是平方误差的总值,总方差可以通过均方差乘以数据集中样本点的个数来得到。

1.构建树
回归树的切分函数:

#因为在进行二元划分时,最后一次划分的每个子集不一定就只含有一个数据样本,大多数情况都是子集中有多个样本
#但是叶节点值却只能为一个,所以在创建叶节点时需要使用函数对叶节点值进行计算并指定,回归树中的叶节点值为最终子节样本目标值的均值
#计算叶节点值的函数
def regLeaf(dataSet):
    return np.mean(dataSet[:,-1])

def regErr(dataSet):
    #返回此数据集中目标值列的总方差和(均方误差和*集合样本数)
    return np.var(dataSet[:,-1])*np.shape(dataSet)[0]

#寻找最佳切分特征及切分值函数
def chooseBestSplit(dataSet,leafType=regLeaf,errType=regErr,ops=(1,4)):
    #获取参数中的误差减少限度
    tolS = ops[0]
    #获取划分子集最小样本数量
    tolN = ops[1]
    #数据集划分停止条件:
    #1.划分子集中的目标值相同,则不需要继续划分
    if (len(set(dataSet[:,-1].T.tolist()[0]))==1):
        return None,leafType(dataSet)
    m,n = np.shape(dataSet)
    #计算为划分数据集的总方差和
    S = errType(dataSet)
    #定义最小方差和为无穷
    bestS = np.inf
    #初始化最佳特征索引值和最佳划分值
    bestIndex = 0
    bestValue = 0
    #遍历所有特征,数据集中最后一列是目标值,不是特征列
    for featIndex in range(n-1):
        #遍历每一个特征的所有取值
        #for splitValue in set(dataSet[:,featIndex]):   #python2.x用法
        for splitValue in set(dataSet[:,featIndex].T.A.tolist()[0]):  #python3.x用法
            #进行二值划分
            matLeft,matRight = binSplitDataSet(dataSet,featIndex,splitValue)
            #如果划分的数据集样本数小于指定数,则重新划分
            if ((np.shape(matLeft)[0
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值