树回归问题

原创 2017年08月10日 16:03:01

1. 树回归

基于之前的线性回归,树回归归根结底也是回归,但不同的是,树回归可以更好的处理多特征的非线性回归问题,其基本思想就是切分数据集,切分至易拟合的数据集后进行线性回归建模。(复杂数据的局部建模)

回归树
节点为数值型/标称型
模型树
节点为线性模型

2.优缺点

优点: 可以对复杂的非线性数据建模
缺点: 结果不易理解,抽象化

3.伪代码

'''
部分核心代码伪代码

1.建树creatTree伪代码:
找到最佳切分特征(值)
if 节点不能继续切分
    返回节点值
else
    切分数据集
    左右子树递归调用creatTree

2.最佳切分特征值函数chooseBestSplit伪代码

预剪枝1判断数据集是否为一个元素
设立最大值标记(又是打擂台)
计算初始误差
for 每个特征:
    for 每个特征值:
        切分数据集
        预剪枝2 数据量限制
        计算拆分后误差
        打擂台记录最小误差
预剪枝3 误差变动限制
拆分数据集
预剪枝4 数据量限制
返回最佳切分数据特征(值)

3.后剪枝prune函数伪代码

if 数据集空 合并子树
if 左/右有子树
    拆分数据集
if 左子树不是叶子
    递归调用prune函数
if 右子树不是叶子
    递归调用prune函数
if 都是叶子节点
    拆分数据集
    比较拆分误差 与 原始误差
    记录最优
返回tree

'''

3.1 其它函数列表

'''
modelTree
modelErr modelLeaf linerSolve modelTreeEval (计算误差 节点生成2 模型预测)

regTree
regErr regLeaf regTreeEval (同上)

prune
isTree getMean (判断是否节点 返回平均值)

详情见代码注释
'''

4.代码

# coding:utf-8

from numpy import *

# 加载数据集
def loadData(fileName):
    dataMat = []
    with open(fileName) as txtFile:
        for line in txtFile.readlines():
            dataMat.append(map(float, line.split()))
    return dataMat  # 200*2

# 线性回归 最小二乘法 modelTree也会用到
def linerSolve(data):
    m, n = shape(data)
    X = mat(ones((m, n)))
    Y = mat(ones((n, 1)))
    X[:, 1:n] = data[:, 0:n - 1]  # x从1取数据的前n-1列 第0列是常数1
    Y = data[:, -1]
    xtx = X.T * X  # xTx.I * XTy
    if linalg.det(xtx) == 0:
        print "error 0!"
        return
    ws = xtx.I * X.T * Y
    return ws, X, Y

# modelTree 误差 是平方加和
def modelErr(errData):
    ws, X, Y = linerSolve(errData)
    yPre = X * ws
    return sum(power(yPre - Y, 2))

# modelTree 不用数据 用ws生成叶节点的模型
def modelLeaf(data):
    ws, X, Y = linerSolve(data)
    return ws

# regTree 计算误差 var均方差*数量=总方差
def regErr(errData):
    return var(errData[:, -1]) * shape(errData)[0]

# regTree 返回叶子节点的值,用了均值 所以说 叶子节点是一个值
def regLeaf(data):
    return mean(data[:, -1])

# 根据特征与阀值 分成两份数据
def binSplitTree(data, feat, value):
    mat0 = data[nonzero(data[:, feat] > value)[0]]
    mat1 = data[nonzero(data[:, feat] <= value)[0]]
    return mat0, mat1

# 判断是否是树 (上面说到 叶子节点是数值)
def isTree(obj):
    return type(obj).__name__ == 'dict'

# 利用getMean获取节点均值 剪枝
def getMean(tree):
    # print 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, data):
    if shape(data)[0] == 0:  # 数据集空 合并子树
        return getMean(tree)
    # 左右有子树 进行剪枝
    if isTree(tree['Left']) or isTree(tree['Right']):
        lSet, rSet = binSplitTree(data, tree['Feat'], tree['Value'])
    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']):
        # print "not tree", tree
        lSet, rSet = binSplitTree(data, tree['Feat'], tree['Value'])
        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(data[:, -1] - treeMean, 2))  # 合并误差
        # print errorMerge,errorNoMerge
        if errorMerge < errorNoMerge:  # 判断
            print "merging"
            return treeMean  # 合并
        else:
            return tree  # 不合并
    else:
        return tree  # 完成 返回‘根’

# 选择最佳分配方案
def chooseBestSplit(data, leafType, errType, ops=(1, 4)):
    if len(set(data[:, -1].T.tolist()[0])) == 1:  # 预剪枝 1:只有一个元素 set 判断重复
        # print "len is:", len(set(data[:, -1].T.tolist()[0]))
        return None, leafType(data)
    bestFeat = 0  # 最佳特征
    bestValue = 0  # 最佳特征值
    numLim = ops[1]  # 节点数目限制
    errorLim = ops[0]  # 误差限制
    errorFirst = errType(data)  # 开始时的误差
    bestError = inf  # 最佳误差
    m, n = shape(data)
    for featLoop in range(n - 1):  # 每个特征
        for valLoop in set(data[:, featLoop].T.tolist()[0]):  # 每个特征值
            mat0, mat1 = binSplitTree(data, featLoop, valLoop)  # 分割
            if shape(mat0)[0] < numLim or shape(mat1)[0] < numLim:  # 预剪枝 2:节点个数少于限制 continue
                continue
            newError = errType(mat0) + errType(mat1)  # 拆分后误差
            if newError < bestError:  # 更新最小误差 并且记录特征(值)
                bestError = newError
                bestFeat = featLoop
                bestValue = valLoop
    if errorFirst - bestError < errorLim:  # 预剪枝 3:小于误差限制 返回节点均值
        return None, leafType(data)
    mat0, mat1 = binSplitTree(data, bestFeat, bestValue)
    if shape(mat0)[0] < numLim or shape(mat1)[0] < numLim:  # 预剪枝 4:小于点数目限制 返回节点均值
        return None, leafType(data)
    return bestFeat, bestValue

# 递归建树 (叶子生成方法 长度/误差限制
def creatTree(dataSet, leafType=regLeaf, errType=regErr, ops=(1, 4)):
    feat, value = chooseBestSplit(dataSet, leafType, errType, ops)
    if feat is None:  #
        return value  # 此时 叶子节点是一个数值
    tree = {}
    lSet, rSet = binSplitTree(dataSet, feat, value)
    tree['Feat'] = feat
    tree['Value'] = value
    tree['Left'] = creatTree(lSet, leafType, errType, ops)  #
    tree['Right'] = creatTree(rSet, leafType, errType, ops)  # 左右子树递归建树
    return tree

# 回归树 估计方法
def regTreeEval(tree, data):
    return float(tree)

# 模型树 估计方法
def modelTreeEval(tree, data):
    m, n = shape(data)
    X = mat(ones((1, n + 1)))  # 增加了一列(第零列)1
    X[:, 1:n + 1] = data
    # print shape(X)
    # print shape(tree)
    return float(X * tree)

# 树预测 (树节点+数据+估计方法
def treeForecast(tree, data, Eval=regTreeEval):
    if not isTree(tree):
        return Eval(tree, data)
    # 数值 左大右小
    # 左子树
    if data[tree['Feat']] > tree['Value']:
        if isTree(tree['Left']):
            return treeForecast(tree['Left'], data, Eval)
        else:
            return Eval(tree['Left'], data)
    # 右子树
    else:
        if isTree(tree['Right']):
            return treeForecast(tree['Right'], data, Eval)
        else:
            return Eval(tree['Right'], data)

# 创建预测值 (树节点+测试集+预估方法
def creatForecast(tree, testData, Eval=regTreeEval):
    m = len(testData)
    yPre = mat(zeros((m, 1)))
    for i in range(m):
        yPre[i, 0] = treeForecast(tree, testData[i], Eval)  # 第i个点 进行预测
    return yPre

# 打印图片
def outPic(x, y, yPre, Type):
    import pylab as pl
    pl.scatter(x.tolist(), y.tolist(), s=10, c='green', marker='+')
    pl.scatter(x.tolist(), yPre.tolist(), s=15, c='red')
    pl.xlabel(Type)
    pl.show()

#
if __name__ == '__main__':
    # 加载数据
    trainMat = mat(loadData("bike_train.txt"))
    testMat = mat(loadData("bike_test.txt"))
    # 模型树
    tree = creatTree(trainMat, leafType=modelLeaf, errType=modelErr, ops=(1, 20))
    yPre = creatForecast(tree, testMat[:, 0], Eval=modelTreeEval)
    print "modelTree :", corrcoef(yPre, testMat[:, 1], rowvar=0)[0, 1]
    # outPic(testMat[:, 0], testMat[:, 1], yPre,"modelTree")
    # 回归树
    tree = creatTree(trainMat, leafType=regLeaf, errType=regErr, ops=(1, 20))
    yPre = creatForecast(tree, testMat[:, 0], Eval=regTreeEval)
    print "regTree :", corrcoef(yPre, testMat[:, 1], rowvar=0)[0, 1]
    # outPic(testMat[:, 0], testMat[:, 1], yPre,"regTree")
    # 线性回归
    ws, X, Y = linerSolve(trainMat)
    yPre = ws[1, 0].T * testMat[:, 0] + ws[0, 0]  # Y=aX+b
    print "lineReg :", corrcoef(yPre, testMat[:, 1], rowvar=0)[0, 1]
    # outPic(testMat[:, 0], testMat[:, 1], yPre,'lineReg')

5. 图像 以及 结果分析

# modelTree : 0.976041219138
# regTree : 0.964085231822
# lineReg : 0.943468423567

modelTree
regTree
lineReg

观察一下 R2 很显然 模型树很强 线性回归最弱
我觉得是数据的问题
……

6. 附 数据集

额 等建网盘吧….

版权声明:有错误麻烦赐教,感激不尽~~~(转载留言哦~)

CART(三)

四、剪枝 剪枝有预剪枝和后剪枝,预剪枝就是在树生成的过程中,加上一些限制条件使得树不会过度分裂,在上一节代码中,已经加上了预剪枝。 下面重点讲后剪枝。 后剪枝算法:  输入:已经生成的树  ...

树模型和线性回归 在回归问题中的比较

最近使用GBRT和LR解决回归问题,总体来说发现GBRT能很快收敛,且误差mse通常比lr小。但使用过程中发现利用GBRT进行回归大部分情况的回归值都接近真实值,但也会存在一些错的很离谱的回归值,反而...

matlab论文 线性回归问题

  • 2011年12月27日 12:11
  • 620KB
  • 下载

非线性函数回归问题

  • 2013年11月15日 17:32
  • 3KB
  • 下载

Tensorflow 回归问题

转载自传送门,看的还是可以的,代码结构优美。下面增加一些自己的理解和注释。这个代码和tensorflow分类问题代码相似,就是一个是用最后状态的输出。一个是用所有时刻的输出。# View more p...

机器学习 回归问题

  • 2012年03月26日 14:35
  • 704KB
  • 下载

用keras创建拟合网络解决回归问题Regression

实现了正弦曲线的拟合,即regression问题。 创建的模型单输入单输出,两个隐层分别为100、50个神经元。 在keras的官方文档中,给的例子多是关于分类的。因此在测试regression时...
  • csmqq
  • csmqq
  • 2016年05月16日 14:32
  • 7323

机器学习精简教程之五——用scikit-learn求解多项式回归问题

本文转自:http://www.shareditor.com/blogshow/?blogId=56 多元真实情况未必是线性的,有时需要增加指数项,也就是多项式回归,现实世界的曲线关系都是通...

回归问题和评估分类器准确率

线性回归问题可以利用最小二乘法来确定误差,通过使误差最小化来确定线性方程的系数,而最小化可以通过求导来确定。 非线性方程可以通过变量替换等方式转化为线性方程 广义线性模型可以用于对离散取值变量进行...

机器学习(回归问题中的相关度和决定系数)

1.皮尔狲相关系数: 1.1衡量两个值线性相关强度的量 1.2取值范围:[-1,1]: 正向相关:>0,负向相关:...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:树回归问题
举报原因:
原因补充:

(最多只允许输入30个字)