机器学习实战之树回归

引入

  线性回归包含一些强大的方法,但是这些方法创建的模型需要拟合所有的样本点(局部线性回归除外)。当数据拥有众多特征,且特征之间关系十分复杂时,构建全局模型的想法显得困难。并且,生活中很多问题都是非线性的,不可能使用全局线性模型来拟合任何模型。
  一种可行的方法是将数据集切分成很多份易建模的数据,然后利用线性回归技术来建模。如果首次切分后仍然难以拟合线性模型就继续切分。这种切分方式下,数结构回归法就相当有用。
  接下来将介绍一个新的叫做CART(Classification and Regression Trees)的数建构算法。该算法既可用于分类,亦可用于回归。

1 复杂数据的局部性建模

树回归

  优点:可以对复杂和非线性的数据建模;
  缺点:结果不易理解;
  适用数据类型:数值型和标称型数据

  回顾决策树:
  1)决策树不断将数据切分为小数据集,直到所有目标变量完全相同,或者数据不能再切分为止。决策树是一种贪心算法,它要在给定时间内做出最佳选择,但并不关心能否达到全局最优。
  2)使用的数构建算法是ID3。
  2.1)ID3的做法是每次选取当前最佳的特征来分隔数据,并按照该特征的所有取值来切分。
  2.2)例:如果一个特征有四种取值,那么数据将被切成4份。一旦按照某特征切分后,该特征在之后的算法执行中将不会起作用,所以有观点认为该切分方式过于迅速。
  2.3)另一种切分方法是二分切分法:每次把数据集切分两份。如果数据的某特征值等于切分要求的值,那么这些数据就进入数的左子树,反之进入数的右子树。
  3)ID3算法的问题与二分切分法的优势:
  3.1)由于切分过于迅速,不能直接处理连续型特征。只有事先将连续型特征转换成离散型,才能在ID3算法中使用。但是这种转换过程会破坏连续型变量的内在性质。
  3.2)二分切分法易于对树构建过程进行调整以处理连续型特征。另外能节省树的构建时间,但是这点的意义不是特别大,因为树构建一般是离线完成。

  CART使用二元切分来处理连续型变量。决策树中使用香农熵来度量集合的无组织程度,如果选用其他方法来代替香农熵,则可以使用树构建算法来完成回归。

2 连续和离散型特征的树的构建

  在树的构建过程中,需要解决多种类型数据的存储问题。这里将使用一部字典来存储树的数据结构,该字典将包含以下4个元素:

  • 待切分的特征
  • 待切分的特征值
  • 左子树:当不再需要切分时,也可以是单个值。
  • 右子树:与左子树类似。

  这与决策树的树结构有一点不同。
  1)决策树中用一部字典来存储每个切分,但该字典可以包含两个或者以上的值。
  2)CART算法只做二元切分,所以这里固定树的数据结构。树包含左键和右键,可以存储另一颗子树或者单个值。字典还包含特征和特征值两个键,它们给出切分算法所有的特征和特征值。
  当然也可以使用面向对象的编程模式来建立这个数据结构,如下:

**程序清单2-1:**建立树节点

class TreeNode():
    def __init__(self, feat, val, right, left):
        feature_to_split_on = feat    #特征
        value_of_split = val    #特征值
        right_branch = right    #左键
        left_branch = left    #右键

  随后将构建两种树:
  1)回归树(regression tree):每个叶节点包含单个值;
  2)模型树(model tree):每个节点包含一个线性方程。
  创建者两种树时,为了使得代码复用,以下先给出构建两种树的共用代码。其伪代码如下:
  找到最佳的待切分特征:
    如果该节点不能再分:
      将该节点存为叶节点
    执行二元分类
    在右子树调用create_tree()方法
    在左子树调用create_tree()方法

  创建regtrees.py文件并添加以下代码:

程序清单2-2: CART算法的实现代码

from numpy import *

"""数据载入,但是返回映射后的数据"""
def load_data_set(file_name):
    with open(file_name) as fd:
        fd_data = fd.readlines()

    data_set = []
    for data in fd_data:
        data = data.strip().split('\t')
        data = map(float, data)    #将每行映射为一组浮点数
        data_set.append(data)
    return data_set

"""划分数据集合"""
def bin_split_data_set(data_set, feature, value):    #输入参数:数据集合、待切分特征、待切分特征的某个值
    mat0 = data_set[nonzero(data_set[:, feature] > value)[0], :][0]
    mat1 = data_set[nonzero(data_set[:, feature] <= value)[0], :][0]
    return mat0, mat1

"""创建叶节点"""
def reg_leaf():
    pass

"""计算总方差"""
def reg_err():
    pass

"""构建树"""
def create_tree(data_set, leaf_type=reg_leaf, err_type=reg_err, ops=(1, 4)):    #ops包含树构建所需其他参数的元组
    feat, val = choose_best_split(data_set, leaf_type, err_type, ops)
    if feat == None:
        return val
    ret_tree = {}
    ret_tree['sp_ind'] = feat
    ret_tree['sp_val'] = val
    l_tree, r_tree = bin_split_data_set(data_set, feat, val)
    ret_tree['left'] = create_tree(l_tree, leaf_type, err_type, ops)
    ret_tree['right'] = create_tree(r_tree, leaf_type, err_type, ops)
    return ret_tree

def choose_best_split(data_set, leaf_type, err_type, ops):
    return 0, 0

def test1():
    data_set = load_data_set('E:\Machine Learing\myMachineLearning\data\ex0.txt')
    print(data_set)

if __name__ == '__main__':
    test1()

待续…

原创文章 35 获赞 44 访问量 8640

猜你喜欢

转载自blog.csdn.net/weixin_44575152/article/details/100775586