CART算法的树回归:

返回的每个节点最后是一个最终确定的平均值。
#coding:utf-8
import numpy as np
# 加载文件数据
def loadDataSet(fileName): #general function to parse tab -delimited floats
dataMat = [] #assume last column is target value
fr = open(fileName)
for line in fr.readlines():
curLine = line.strip().split('\t')
fltLine = map(float,curLine) #map all elements to float()
dataMat.append(fltLine)
return dataMat
#在dataset中选择特征为feature的这一列,以value值分成两部分
def binSplitDataSet(dataSet, feature, value):
mat0 = dataSet[np.nonzero(dataSet[:,feature] > value)[0],:][0]
mat1 = dataSet[np.nonzero(dataSet[:,feature] <= value)[0],:][0]
return mat0,mat1
#计算此矩阵的最后一列结果的平均值,用平均值来当做最后的返回结果,后面的模型树返回的是一个 线性模型
def regLeaf(dataSet):
return np.mean(dataSet[:,-1])
#计算dataset结果的混乱程度,用方差反应,因为是连续数据
def regErr(dataSet):
return np.var(dataSet[:,-1]) * np.shape(dataSet)[0]
#选择最佳的分离特征和该特征的分离点
#这里的ops是预先的给定值,1是差别太小就不分了,4是分开后的各自样本数,太小就舍去,这是一 种预剪枝方法
def chooseBestSplit(dataSet, leafType=regLeaf, errType=regErr, ops=(1,4)):
tolS = ops[0]; tolN = ops[1]
#if all the target variables are the same value: quit and return value
if len(set(dataSet[:,-1].T.tolist()[0])) == 1: #exit cond 1
return None, leafType(dataSet)
m,n = np.shape(dataSet)
#the choice of the best feature is driven by Reduction in RSS error from mean
S = errType(dataSet)
bestS = np.inf; bestIndex = 0; bestValue = 0
#循环所有的特征
for featIndex in range(n-1):
#循环该特征下的所有特征值
for splitVal in set(dataSet[:,featIndex]):
mat0, mat1 = binSplitDataSet(dataSet, featIndex, splitVal)
#如果更具这个特征值分成的两类有一个小与预先给定值,说明分类太偏,则不考虑
if (np.shape(mat0)[0] < tolN) or (np.shape(mat1)[0] < tolN): continue
newS = errType(mat0) + errType(mat1)
if newS < bestS:
bestIndex = featIndex
bestValue = splitVal
bestS = newS
#if the decrease (S-bestS) is less than a threshold don't do the split
if (S - bestS) < tolS:
return None, leafType(dataSet) #exit cond 2
mat0, mat1 = binSplitDataSet(dataSet, bestIndex, bestValue)
if (np.shape(mat0)[0] < tolN) or (np.shape(mat1)[0] < tolN): #exit cond 3
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
myDat = loadDataSet('ex0.txt')
myMat = np.mat(myDat)
result = createTree(myMat)
print result结果:
{'spInd': 1, 'spVal': matrix(` 0`.`39435`), 'right': {'spInd': 1, 'spVal': matrix(` 0`.`197834`), 'right': -0.023838155555555553, 'left': 1.0289583666666666}, 'left': {'spInd': 1, 'spVal': matrix(` 0`.`582002`), 'right': 1.980035071428571, 'left': {'spInd': 1, 'spVal': matrix(` 0`.`797583`), 'right': 2.9836209534883724, 'left': 3.9871631999999999}}}
结果的意思是:第几个特征,以多大作为特征值分开,分成左右,依次分下去。
这个算法很好,但是对数据的分类太过于高,容易造成过拟合。因此要采用剪枝技术。
通过降低决策树的复杂度来避免过拟合的过程称为剪枝。
#判断obj是否是一个子树 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 np.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 they are now both leafs, see if we can merge them #如果左右都是节点,则计算节点误差 if not isTree(tree['left']) and not isTree(tree['right']): lSet, rSet = binSplitDataSet(testData, tree['spInd'], tree['spVal']) #计算不合并的误差 errorNoMerge = sum(np.power(lSet[:,-1] - tree['left'],2)) + sum(np.power(rSet[:,-1] - tree['right'],2)) treeMean = (tree['left']+tree['right'])/2.0 #计算将当前两个叶子节点合并后的误差 errorMerge = sum(np.power(testData[:,-1] - treeMean,2)) if errorMerge < errorNoMerge: print "merging" #可以合并就返回平均值 return treeMean #不可以合并就返回树,不变 else: return tree else: return tree
一般来说都是预剪枝和后剪枝合并使用
模型树
每个节点是一个线性模型
其他基本一样:
#对数据集进行线性回归
def linearSolve(dataSet):
m,n = np.shape(dataSet)
X = np.mat(np.ones((m,n))); Y = np.mat(np.ones((m,1)))
#有一列是常数项,因此要多出一列放置常数项
X[:,1:n] = dataSet[:,0:n-1]; Y = dataSet[:,-1]
xTx = X.T*X
if np.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
#产生针对该数据集的线性模型
#相当于上面的regLeaf函数
def modelLeaf(dataSet):
ws,X,Y = linearSolve(dataSet)
return ws
#产生针对该数据集的线性模型,并计算误差返回
#相当于上面的regErr函数,计算模型的误差,如果分后和不分的误差差不多则选择不分
def modelErr(dataSet):
ws,X,Y = linearSolve(dataSet)
yHat = X * ws
return sum(np.power(Y - yHat,2))模型树回归很好,而且可以用作预测
另外有需要云服务器可以了解下创新互联scvps.cn,海内外云服务器15元起步,三天无理由+7*72小时售后在线,公司持有idc许可证,提供“云服务器、裸金属服务器、高防服务器、香港服务器、美国服务器、虚拟主机、免备案服务器”等云主机租用服务以及企业上云的综合解决方案,具有“安全稳定、简单易用、服务可用性高、性价比高”等特点与优势,专为企业上云打造定制,能够满足用户丰富、多元化的应用场景需求。
文章题目:学习日志---树回归(回归树,模型树)-创新互联
URL地址:http://www.jxjierui.cn/article/eiicp.html


咨询
建站咨询
