机器学习经典算法8-树回归

1.简单介绍

        线性回归方法可以有效的拟合所有样本点(局部加权线性回归除外)。当数据拥有众多特征并且特征之间关系十分复杂时,构建全局模型的想法一个是困难一个是笨拙。此外,实际中很多问题为非线性的,例如常见到的分段函数,不可能用全局线性模型来进行拟合。

树回归将数据集切分成多份易建模的数据,然后利用线性回归进行建模和拟合。这里介绍较为经典的树回归CART(classification and regression trees,分类回归树)算法。

2.分类回归树基本流程

    构建树:

           1.找到[最佳待切分特征]

            2.若不能再切分,则将该节点存为[叶子节点]并返回

            3.按照最佳待切分特征将数据集切分成左右子树(这里为了方便,假设大于特征值则为左,小于则归为右)

            4.对左子树进行[构建树]

            5.对右子树进行[构建树]

   最佳待切分特征:

           1.遍历特征

               1.1遍历特征所有特征值

                    1.1.1计算按该特征值进行数据集切分的[误差]

           2.选择误差最小的特征及其相应值作为最佳待切分特征并返回

   基于回归树的预测:

           1.判断当前回归树是否为叶子节点,如果是则[预测],如果不是则执行2

            2.将测试数据相应特征上的特征值与当前回归树进行比较,如果测试数据特征值大,则判别当前回归树的左子树是否为叶子节点,如果不是叶子节点则进行[基于回归树的预测],如果是叶子节点,则[预测];反之,判别当前回归树的右子树是否为叶子节点,如果不是叶子节点则进行[基于回归树的预测],如果是叶子节点,则[预测]

3.分类回归树的实践说明

  

        误差、叶子节点和预测三者有相关的关联关系,一种相对简单的是误差采用的是y值均方差,叶子节点相应的建立为该节点下所有样本的y值平均值,预测的时候根据判断返回该叶子节点下y值平均值即可。

        在进行最佳待切分特征选取的时候,一般还有两个参数,一个是允许的误差下降值,一个是切分最小样本数。对于允许误差下降值,在实际过程中,需要在分割之后其误差减少应该至少大于该bound;对于切分最小样本数,也就是说切分后的子树中包含的样本数应该多于该bound。其实这两种策略都是为了避免过拟合。

4树剪枝

       

        通过在最佳待切分特征选取时进行参数设定来避免过拟合,这其实是一种预剪枝的行为;而在回归树建立后,再进行剪枝,则是一种后剪枝的行为。

        后剪枝的过程如下:

               如果存在任一子集是一棵树,则在该子集中递归剪枝

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

               计算不合并的误差

              比较合并前后误差,如果合并后的误差降低,则对叶子节点进行合并

5模型树

        之前讲到误差、叶子节点和预测三者具备关联关系,当建立叶子节点是基于模型的,则构建了相应的模型树。这里可以使用之前的线性回归模型,建立相应的叶子节点。这样误差计算采用的将是线性回归中的误差,而预测则是基于该叶子节点拟合其样本后的参数。

6编程实现

         这里createTree负责进行树的构建;chooseBestSplit函数负责进行最佳带切特征的选取,而ops参数则是进行了两个bound的设定;prune进行了相关后剪枝。
         这里regErr、regLeaf、regTreeEval是基于简单均值计算的误差、叶子节点和预测;而modelErr、modelLeaf和modelTreeEval(+linearSolve)则是基于线性回顾模型的误差、叶子节点和预测。
         数据集链接:http://pan.baidu.com/share/link?shareid=3744521160&uk=973467359 密码:9ivd
from numpy import *
def loadDataSet(filename):
    dataMat = []
    fr = open(filename)
    for line in fr.readlines():
        curLine = line.strip('\n').split('\t')
        fltLine = map(float, curLine)
        dataMat.append(fltLine)
    fr.close()
    return dataMat
def regLeaf(dataSet):
    return mean(dataSet[:,-1])
def regErr(dataSet):
    return var(dataSet[:,-1])*shape(dataSet)[0]
def regTreeEval(model, inDat):
    return float(model)
def linearSolve(dataSet):
    m,n=shape(dataSet)
    X = mat(ones((m,n)))
    Y = mat(ones((m,1)))
    X[:,1:n]=dataSet[:,0:n-1]
    Y=dataSet[:,-1]
    xTx = X.T*X
    if linalg.det(xTx)==0.0:
        raise NameError('This matrix is singular, cannot do inverse, \
               try increasing the second value of ops')
    ws = xTx.T*(X.T*Y)
    return ws, X, Y
def modelLeaf(dataSet):
    ws, X, Y = linearSolve(dataSet)
    return ws
def modelErr(dataSet):
    ws,X,Y = linearSolve(dataSet)
    yHat = X*ws
    return sum(power(Y-yHat,2))
def modelTreeEval(model, inDat):
    n=shape(inDat)[1]
    X = mat(ones((1,n+1)))
    X[:,1:n+1]=inDat
    return float(X*model)
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 = binSplitDataSet(dataSet, featIndex, splitVal)
            if(shape(mat0)[0]<tolN) or (shape(mat1)[0]<tolN):
                continue
            newS = errType(mat0)+errType(mat1)
            if newS < bestS:
                bestIndex = featIndex
                bestValue = splitVal
                bestS = newS
    if (S-bestS)<tolS:
        return None, leafType(dataSet)
    mat0, mat1 = binSplitDataSet(dataSet, bestIndex, bestValue)
    if(shape(mat0)[0]<tolN) or (shape(mat1)[0]<tolN):
        print "Not enough nums"
        return None, leafType(dataSet)
    return bestIndex, bestValue
def binSplitDataSet(dataSet, feature, value):
    mat0 = dataSet[nonzero(dataSet[:, feature]>value)[0],:][0]
    mat1 = dataSet[nonzero(dataSet[:, feature]<=value)[0],:][0]
    return mat0, mat1
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 = binSplitDataSet(dataSet, feat, val)
    retTree['left']=createTree(lSet, leafType, errType, ops)
    retTree['right']=createTree(rSet, leafType, errType, ops)
    return retTree
def isTree(obj):
    return (type(obj).__name__=='dict')
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
def prune(tree, testData):
    if shape(testData)[0] == 0:
        return getMean(tree)
    if(isTree(tree['right']) or isTree(tree['left'])):
        lSet, rSet = binSplitDataSet(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['right']) and not isTree(tree['left']):
        lSet, rSet = binSplitDataSet(testData, tree['spInd'], tree['spVal'])
        errorNoMerge = 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 < errorNoMerge:
            print "Merging"
            return treeMean
        else:
            return tree
    else:
        return tree
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
'''
myData2 = loadDataSet(r"ex2.txt")
myMat2 = mat(myData2)
tree2 = createTree(myMat2, ops=(0,1))
print tree2
myData2Test = loadDataSet(r"ex2test.txt")
myMat2Test = mat(myData2Test)
print prune(tree2, myMat2Test)
'''
trainMat = mat(loadDataSet('bikeSpeedVsIq_train.txt'))
testMat = mat(loadDataSet('bikeSpeedVsIq_test.txt'))
myregTree=createTree(trainMat, ops=(1,20))
mymodTree=createTree(trainMat, modelLeaf, modelErr, (1,20))
yregHat=createForeCast(myregTree, testMat[:,0])
ymodHat=createForeCast(mymodTree, testMat[:,0], modelTreeEval)
regCo = corrcoef(yregHat, testMat[:,1], rowvar=0)[0,1]
modCo = corrcoef(ymodHat, testMat[:,1], rowvar=0)[0,1]
print "reg", regCo
print "model", modCo


阅读更多
想对作者说点什么? 我来说一句

没有更多推荐了,返回首页

关闭
关闭
关闭