机器学习实战之树回归

来源:互联网 发布:动态寄生虫 猫云seo 编辑:程序博客网 时间:2024/05/22 17:51

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 valretTree = {}retTree['spInd'] = featretTree['spVal'] = vallset,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):continuenewS = errtype(mat0) + errtype(mat1)if newS < bestS:bestS = newSbestindex = featindexbestvalue = 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,Ydef modelLeaf(dataSet):#create linear model and return coeficients    ws,X,Y = linearSolve(dataSet)    return wsdef 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.0errormerge = sum(power(testdata[:,-1]-treeMean,2))if errormerge < errorNmerge:print "merging"return treeMeanelse:return treeelse: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