树回归
来源:互联网 发布:湖南才能网络 编辑:程序博客网 时间: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]
阅读全文
0 0
- 线性回归 逻辑回归 树回归
- 回归树
- 树回归
- 树回归
- 树回归
- 树回归
- 树回归
- 树回归
- 树回归
- 回归树
- 树回归
- 树回归
- 树回归
- 树回归
- 树回归
- 树回归
- 树回归
- 回归树-----生成回归树
- 【启发】leetcode
- hdu 2147 kiki's game
- [数位DP]Hdu 2089——不要62
- EK算法
- 【思维-链表】hdu 6058
- 树回归
- 又是毕业季II
- java泛型出现原因分析
- 容器对路径的处理、servlet特性
- 路径最优问题
- mysql 修改密码的四个方法
- hdu1142(最短路+DFS)
- 陶陶摘苹果(noi 1.6-02)
- 【SSLGZ 2671】2017年8月8日提高组T2 呀!回文串