机器学习实战之树回归

1. 两种树构建算法比较

ID3:

每次选取当前最优的特征来分割数据,并按照该特征所有可能取值来切分;一旦按某特征切分后,该特征在之后的算法执行过程中不会再起作用.

缺点: 不能处理连续型特征,除非事先将连续型特征转换成离散型,但转换过程破坏了连续型变量的内在性质.

CART;

二元切分法,每次把数据切成两份;若特征值大于给定值,就走左子树,反之,右子树.

2. 树构建

2.1 树存储

     采用字典来存储树的数据结构,该字典包含以下4个元素:待切分的特征;待切分的特征值;右子树(当不再需要继续切分时,是单个值);左子树。

2.2 创建树

流程如下:找到最佳的待切分特征:

                           如果该节点不能再分,将该节点存为叶节点

                          执行二元切分

                          在左、右子树分别调用创建树的函数

<span style="font-size:18px;">#负责产生叶节点
def regLeaf(dataset):
	return mean(dataset[:,-1])

#误差估计函数.计算目标变量的平均误差 总方差=均方差*样本数
def regErr(dataset):
	return var(dataset[:,-1])*shape(dataset)[0]

#
def createTree(dataset,leaftype=regLeaf,errType=regErr,ops=(1,4)):
	feat,val = chooseBestSplit(dataset,leaftype,errType,ops)
	if feat == None:
		return val
	
	retTree = {}
	retTree['spInd'] = feat
	retTree['spVal'] = val
	lset,rset = binarySplitData(dataset,feat,val)

	retTree['left'] = createTree(lset,leaftype,errType,ops)
	retTree['right'] = createTree(rset,leaftype,errType,ops)

	return retTree</span>


2.3 创建回归树

(1)回归树:其叶节点是常数值

(2)选择最佳特征的依据:计算数据的混乱度:计算所有数据的平均值,然后计算每条数据到均值的差值的平方,这里需要的是总方差,即均方差乘以数据个数。

(3)如何寻找当前最佳切分特征和特征值:函数choosebest。其思想是:遍历所有的特征及其可能的取值来找到误差最小化的切分阈值。

在此函数中,有三种情况不会切分数据集:

  • 如果数据集的值全部相等
  • 若找到最优划分之后,但是划分后的数据集的误差相比划分前的数据集的误差减少不大时,即效果提升不明显时,不执行划分操作
  • 若找到的最优划分切分得到的数据集很小时,不执行划分

(4)其伪代码如下:

对每个特征:

对当前特征可能的每个取值:

将数据分成两部分

  计算切分误差

若当前误差小于当前最小误差,更新当前最小误差,及其最佳切分方式

返回最佳切分特征和特征值

实现代码如下:

<span style="font-size:18px;">#找到数据的最佳二元划分方式
def chooseBestSplit(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 = shape(dataset)
	S = errtype(dataset)
	bestS = inf;bestindex = 0;bestvalue = 0
	
	#寻找最好的切分方式,最佳切分就是使得切分后能够达到最低误差的切分
	for featindex in range(n-1):
		for splitVal in set(dataset[:,featindex]):
			mat0,mat1 = binarySplitData(dataset,featindex,splitVal)
			if (shape(mat0)[0] < toln) or (shape(mat1)[0] < toln):
				continue
			newS = errtype(mat0) + errtype(mat1)
			if newS < bestS:
				bestS = newS
				bestindex = featindex
				bestvalue = splitVal

	#切分数据集后,效果提升不大,则不应进行切分操作而直接创建叶节点
	if (S- bestS) < tols:
		return None,leaftype(dataset)

	mat0,mat1 = binarySplitData(dataset,bestindex,bestvalue)
	if (shape(mat0)[0] < toln) or (shape(mat1)[0] < toln):
		return None,leaftype(dataset)

	return bestindex,bestvalue</span>


2.4 创建模型树

(1)模型树: 其叶节点是分段线性函数;分段线性指的是模型由多个线性片段组成

(2)如何找到最佳切分?对于给定的数据集,先用线性模型对它进行拟合,然后计算真实的目标值与模型预测值间的差值,最后将这些差值的平方求和作为所需的误差值。

其计算过程如下所示:

<span style="font-size:18px;">def linearSolve(dataSet):   #helper function used in two places
    m,n = shape(dataSet)
    X = mat(ones((m,n))); Y = mat(ones((m,1)))#create a copy of data with 1 in 0th postion
    X[:,1:n] = dataSet[:,0:n-1]; Y = dataSet[:,-1]#and strip out Y
    xTx = X.T*X
    if linalg.det(xTx) == 0.0:
        raise NameError('This matrix is singular, cannot do inverse,\n\
        try increasing the second value of ops')
    ws = xTx.I * (X.T * Y)
    return ws,X,Y

def modelLeaf(dataSet):#create linear model and return coeficients
    ws,X,Y = linearSolve(dataSet)
    return ws

def modelErr(dataSet):
    ws,X,Y = linearSolve(dataSet)
    yHat = X * ws
    return sum(power(Y - yHat,2))</span>

3. 树剪枝(tree pruning)

(1)基本概念

过拟合:一棵树节点过多,且使用测试集验证过的。

剪枝:通过降低决策树的复杂度来避免过拟合的过程。

  • 预剪枝(prepruning):即函数chooseBestSplit()中的不切分的那三个条件
  • 后剪枝(postpruning):需要使用测试集和训练集
(2)后剪枝

过程:首先指定参数,使得构建得到的树足够大,足够复杂,便于剪枝。然后从上向下找到叶节点,用测试集来判断将这些叶节点合并是否能够降低测试误差,若是的话就合并。

其伪代码如下:

基于已有的树切分测试数据:

如果存在任一子集是一棵树,则在该子集中地柜剪枝过程

计算将当前两个叶节点合并后的误差

计算不合并的误差

如果合并会降低误差的话,就将叶节点合并

其实现代码如下:

<span style="font-size:18px;"> 
def getMean(tree): 
    if isTree(tree['right']): tree['right'] = getMean(tree['right']) 
    if isTree(tree['left']): tree['left'] = getMean(tree['left']) 
    return (tree['left']+tree['right'])/2.0

#input:待剪枝的树与剪枝所需的测试数据
def prune(tree,testdata):
	if shape(testdata)[0] == 0:
		return getMean(tree)
	if (isTree(tree['right']) or isTree(tree['left'])):
		lset,rset = binarySplitData(testdata,tree['spInd'],tree['spVal'])

	if isTree(tree['left']) :
		tree['left'] = prune(tree['left'],lset)
	if isTree(tree['right']) :
		tree['right'] = prune(tree['right'],rset)

	if not isTree(tree['left']) and not isTree(tree['right']):
		lset,rset = binarySplitData(testdata,tree['spInd'],tree['spVal'])
		errorNmerge = sum(power(lset[:,-1]-tree['left'],2)) +\
			sum(power(rset[:,-1]-tree['right'],2))
		treeMean = (tree['left'] + tree['right'])/2.0
		errormerge = sum(power(testdata[:,-1]-treeMean,2))
		if errormerge < errorNmerge:
			print "merging"
			return treeMean
		else:
			return tree
	else:
		return tree</span>


4. 模型评价

如何评价回归树、模型树、线性回归等模型的好坏呢? 

比较客观的方法是计算相关系数。numpy.corrcoef()函数实现

5. 使用回归树和模型树进行预测

其实现代码如下:

<span style="font-size:18px;">def regTreeEval(model, inDat):
    return float(model)

def modelTreeEval(model, inDat):
    n = shape(inDat)[1]
    X = mat(ones((1,n+1)))
    X[:,1:n+1]=inDat
    return float(X*model)

def treeForeCast(tree, inData, modelEval=regTreeEval):
    if not isTree(tree): return modelEval(tree, inData)
    if inData[tree['spInd']] > tree['spVal']:
        if isTree(tree['left']): return treeForeCast(tree['left'], inData, modelEval)
        else: return modelEval(tree['left'], inData)
    else:
        if isTree(tree['right']): return treeForeCast(tree['right'], inData, modelEval)
        else: return modelEval(tree['right'], inData)
        
def createForeCast(tree, testData, modelEval=regTreeEval):
    m=len(testData)
    yHat = mat(zeros((m,1)))
    for i in range(m):
        yHat[i,0] = treeForeCast(tree, mat(testData[i]), modelEval)
    return yHat</span>




  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值