树回归

来源:互联网 发布:湖南才能网络 编辑:程序博客网 时间:2024/06/11 06:28

1、分类与回归树

分类与回归树(Classification and Regression Trees, CART),既可用于分类也可用于回归。

本文基于机器学习实战这本书,将构建两种树,回归树-其每个叶节点包含单值
模型树-其每个叶节点是一个线性方程。这也就说CART既能分类又能回归的原因。

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

回顾前面的决策树进行分裂,决策树不断江苏局切分小数据集,知道目标变量完全相同或者数据不能再切分为止。决策树是一种贪心算法,他要在给定时间内做出最佳选择。但并不关心达到全局最优。ID3算法是每次选取当前最佳的特征来切割数据,并按照该特征所有可能取值来切分,ID3算法存在两个问题,一个是切分过于迅速(一旦某个特征切分后,该特征在算法执行过程中以后将不再执行),另一种是,他不能切分连续性特征。只有事先将连续特征转化为离散才能使用。但这种转化过程会破坏连续型变量的内在性质。
而是用二元切分则易于对数构建过程进行调整以处理连续型特征。具体方法是:
如果特征值大于给定值就进入左子树,否则进入右子树。卧槽…………好像二叉排序树……有木有…………

首先设计一个字典来存储树的结构1、待切分的特征2、待切分的特征值3、右子树。当不再需要切分的时候,也可以是单值4、左子树与右子树类似找到最佳的待切分特征:   如果该节点不能再分,将该节点存为叶节点   执行二元切分   在右子树调用creatTree()方法…………就是递归啦   在左子树调用creatTree()方法

3、构建树

4、树剪枝

5、模型树

#encoding:utf-8import numpy as npfrom numpy import *#导入文件def loadDataSet(fileName) :     dataMat = []    fr = open(fileName)    for line in fr.readlines() :        curLine = line.strip().split('\t')        fltLine = map(float, curLine)        dataMat.append(fltLine)    return dataMat# dataSet: 数据集合# feature: 待切分的特征# value: 该特征的某个值    # 该函数有三个参数, 数据集合、待切分的特征和该特征的某个值def binSplitDataSet(dataSet, feature, value) :    mat0 = dataSet[nonzero(dataSet[:,feature] > value)[0],:]    mat1 = dataSet[nonzero(dataSet[:,feature] <= value)[0],:]    return mat0, mat1#回归树用于负责生成叶节点。该函数的叶节点代表的是目标变量的均值def regLeaf(dataSet):    return mean(dataSet[:,-1])#误差估计函数'''该函数在给定数据计算目标变量的平方误差,'''def regErr(dataSet):    return var(dataSet[:,-1])*shape(dataSet)[0]'''模型树中负责生成的叶节点。 叶节点中存储的是该分类的回归系数'''def modelLeaf(dataSet):    ws,X, Y= linerSlove(dataSet)    return ws'''模型树中计算yHat与Y之间的平方误差'''def modelErr(dataSet):    ws,X,Y = linerSlove(dataSet)    yHat = X*ws    return sum(power(Y-yHat, 2))'''树回归中预测返回的叶节点'''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)'''最好的切分方式,既能切分回归树又能切分模型树'''#dataSet数据集,leafType 选择生成叶子节点的函数 errType选择计算误差类型的函数#ops[0]代表的容许误差下降值,ops[1]代表切分最少的样本数#其实ops是用户用于控制函数停止的时机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:#set([..,..,..])里面接收列表        return None,leafType(dataSet)    m, n = shape(dataSet)    S = errType(dataSet)#当前的误差值    bestS = inf; bestIndex = 0.0;bestValue = 0.0    for i in range(n - 1):#遍历所有的特征值         for splitVal in set(dataSet[:,i].T.tolist()[0]):#遍历特征中的 不同类型,寻找最优            lSet, rSet = binSplitDataSet(dataSet, i, splitVal)            if (shape(lSet)[0] < tolN) or (shape(rSet)[0] < tolN):#如果样本数少于用户指定的数目,则不切分,一定程度上防止过拟合                continue            newS = errType(lSet) + errType(rSet)#切分后的误差值            if newS < bestS:#寻找最优解                bestS = newS                bestIndex = i                 bestValue = splitVal    if (S - bestS) < tolS:#如果最优解的变化的误差值都小于规定的容忍度,那么不用继续划分直接生成叶节点        return None, leafType(dataSet)    lSet, rSet = binSplitDataSet(dataSet, bestIndex, bestValue)#如果最优解切分的数目过小也是直接生成叶节点    if (shape(lSet)[0] < tolN) or (shape(rSet)[0] < tolN):        return None, leafType(dataSet)    return bestIndex,bestValue#如果符合用户指定要求那么返回最佳的特征值,和特征值中最佳的切割点'''创建回归树或者模型树'''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')'''函数getMean()是一个递归函数,它从上往下遍历树直到叶节点为止。如果找到两个叶节点则计算他们的平均值。该函数对树进行“塌陷处理”(返回树的平均值),prunce中调用函数时候应该明确'''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#tree待剪树,testData测试数据  '''注意  树的剪枝是用测试集来协助剪枝的,这里的剪枝是在回归树中的应用'''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['left']) and not isTree(tree['right']):#如果左右都是叶节点那么比较合并和不合并的误差值,选择最优        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 linerSlove(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:        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    '''用树回归进行预测'''#tree:建立好的树模型#indata:预测的向量#modelEval对节点进行预测的引用,根据回归树和模型树的不同调用不同的函数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 createForCast(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#------------树回归与树剪枝----------------------# data1 = mat(loadDataSet('ex2.txt'))# print shape(data1)# data = mat(loadDataSet('ex2test.txt'))# print shape(data)# mytree = createTree(data1,ops = (0,1))# print mytree# print prune(mytree, data)# import matplotlib.pyplot as plt# plt.plot(data[:,0],data[:,1],'o')# plt.plot(data1[:,0],data1[:,1],'o')# plt.show()#--------------模型树-----------------------------# data = mat(loadDataSet('exp2.txt'))# mytree = createTree(data,leafType = modelLeaf,errType = modelErr, ops = (1, 10))# print mytree#--------------比较回归树、模型树、标准的线性回归--------#回归树的预测# tainSet = mat(loadDataSet('bikeSpeedVsIq_train.txt'))# testSet = mat(loadDataSet('bikeSpeedVsIq_test.txt'))# myTree = createTree(tainSet,ops=(1,20))# yHat = createForCast(myTree, testSet[:,0])# print '树回归',corrcoef(yHat, testSet[:,-1], rowvar=0)[0,1]# #模型树的预测# myTree = createTree(tainSet,leafType =  modelLeaf,errType = modelErr,ops=(1,20))# yHat = createForCast(myTree, testSet[:,0],modelEval = modelTreeEval)# print '模型树',corrcoef(yHat, testSet[:,-1], rowvar=0)[0,1]# #标准线性回归预测# ws,x,y = linerSlove(tainSet)# yHat[:,0] = testSet[:,0]*ws[1, 0]+ws[0,0]# print '标准线性回归',corrcoef(yHat, testSet[:,-1], rowvar=0)[0,1] 
原创粉丝点击