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