机器学习day14 机器学习实战树回归之CART与模型树

这几天完成了树回归的相关学习,这一部分内容挺多,收获也挺多,刚刚终于完成了全部内容,非常开心。

树回归这一章涉及了CART,CART树称作(classify and regression tree) 分类与回归树,既可以用于分类,也可以用于回归。这正是前面决策树没有说到的内容,在这里补充一下。正好也总结一下我们学的3种决策树。

ID3:用信息增益来选择特性进行分类,只能处理分类问题。缺点是往往偏向于特性种类多的特性进行分解,比如特性A有2种选择,特性B有3种选择,混乱度差不多的情况下,ID3会偏向特性B进行选择,这样会使得树变得复杂,增加了建树难度,节点的增加也使得过拟合情况的出现。因为这个缺点,我们发明了ID3的优化版C4.5。

C4.5:用信息增益率来选择特性进行分类,信息增益和特性种类的个数多少正好抵消,信息增益率可以客观的表示选择某一特性标签的混乱程度。

CART:用基尼指数(类似于熵的概念)来选择特性进行分类。用总方差进行回归预测。上述两种算法分割特性是一旦选择了该特性,该特性便不再起作用,由于该分割过于‘迅速’,不能更好的利用特性的价值,CART算法横空出世。

在以上的ID3和C4.5,只能处理分类问题,在遇到标签为连续型数值便无法处理,所以CART的优势便体现出来,CART可以进行回归分析,预测回归值,称作树回归,CART可以进行剪枝操作避免过拟合,分为预剪枝和后剪枝,后剪枝后面会有,预剪枝为修改函数的参数ops。也可以把叶节点的回归数值改成线性回归,称作模型树。回归效果略优于简单的线性回归(最后的三种回归结果我们可以看到)。

step1:

读取数据:

#读取数据
def loadDataSet(filename):
    feanum = len(open(filename).readline().strip().split('\t'))
    datamat = []
    f = open(filename)
    for i in f.readlines():
        line = i.strip().split('\t')
        l = []
        for j in range(feanum):
            l.append(float(line[j]))
        datamat.append(l)
    return datamat

step2:

递归创建回归树:

#创建树(ops两个参数第一个表示最小允许误差,第二个为样本最小容量)
def createTree(dataset, leaftype = regLeaf, errortype = regError, ops = [1, 4]):
    tree = {}
    fea, val = chooseBestFeatrue(dataset, leaftype, errortype, ops)
    if fea == None:
        return val
    tree['fea'] = fea
    tree['val'] = val
    lefttree, righttree = splitDataSet(dataset, fea, val)
    tree['left'] = createTree(lefttree, leaftype, errortype, ops)
    tree['right'] = createTree(righttree, leaftype, errortype, ops)
    return tree

step3:

回归树中的分割子集函数:

#分类函数
def splitDataSet(Data, featrue, val):
    mat0 = []
    mat1 = []
    data = mat(Data)
    m = shape(data)[0]
    for i in range(m):
        if data[i, featrue] > val:
            mat0.append(data[i].tolist()[0])
        else:
            mat1.append(data[i].tolist()[0])
    return mat0, mat1

step4:

回归树中的查找最佳特性函数:

停止条件:

1:分类的子集数量小于4个

2:误差值降低量小于最小允许值

3:标签值都为重复值,无需再次分解,返回为叶子节点。

#寻找最佳分类特性
def chooseBestFeatrue(dataset, leaftype = regLeaf, errortype = regError, ops = [1, 4]):
    d = mat(dataset)
    leastnum = ops[1]
    leasterr = ops[0]
    m, n = shape(d)
    if len(set(d[ : , -1].T.tolist()[0])) == 1:
        return None, leaftype(d)
    errorsum = errortype(d)
    bestfea = -1
    bestval = -1
    besterror = inf
    for i in range(n - 1):
        for temp in set(d[ : , i].T.tolist()[0]):
            mat0, mat1 = splitDataSet(d, i, temp)
            if shape(mat0)[0] < leastnum or shape(mat1)[0] < leastnum:
                continue
            tempsumerror = errortype(mat0) + errortype(mat1)
            if tempsumerror < besterror:
                bestfea = i
                bestval = temp
                besterror = tempsumerror
    if (errorsum - besterror) < leasterr:
        print errorsum, besterror
        return None, leaftype(d)
    mat0, mat1 = splitDataSet(d, bestfea, bestval)
    if shape(mat0)[0] < leastnum or shape(mat1)[0] < leastnum:
        return None, leaftype(d)
    return bestfea, bestval   

step5:

相关函数:

#计算平方误差
def regError(dataset):
    t = mat(dataset)
    return var(t[ : , -1]) * shape(t)[0]

#计算叶子节点的值(叶子节点的平均值)
def regLeaf(dataset):
    t = mat(dataset)
    return mean(t[ : , -1])
#判断节点是否为树
def isTree(tree):
    return isinstance(tree, dict)

#节点值的平均值
def getMean(tree):
    if isTree(tree['left']):
        tree['left'] = getMean(tree['left'])
    if isTree(tree['right']):
        tree['right'] = getMean(tree['right'])
    return (tree['right'] + tree['left']) / 2.0     

我们看一下效果:

图1:

结果为:

图2:

结果为:

我们看到符合我们的预期。

step6:

回归树的剪枝:这里的剪枝和算法中的剪枝不同,算法中的剪枝为剪去不必要的节点,这里的剪枝为把繁琐的节点合并。目的位避免CART过拟合。分为预剪枝和后剪枝,

此为后剪枝,预剪枝为修改函数的参数ops。

这里有两个细节:

1:当没有数据进入树节点的时候,我们把这个树合并为一个节点。

2:当该节点的左节点和右节点都为值时,我们判断是分开好还是合并好。

为取得更好的剪枝效果,应该预剪枝和后剪枝一起用。

#回归树剪枝
def pruneTree(tree, testdata):
    if shape(testdata)[0] == 0:
        return getMean(tree)
    if isTree(tree['left']) or isTree(tree['right']):
        lset, rset = splitDataSet(testdata, tree['fea'], tree['val'])
    if isTree(tree['left']):
        tree['left'] = pruneTree(tree['left'], lset)
    if isTree(tree['right']):
        tree['right'] = pruneTree(tree['right'], rset)
    if (not isTree(tree['left'])) and (not isTree(tree['right'])):
        l, r = splitDataSet(testdata, tree['fea'], tree['val'])
        lset = mat(l)
        rset = mat(r)
        e1 = 0.0
        e2 = 0.0
        #书中忽略了这一点可能会有一个集合没有元素
        if shape(lset)[1] != 0:
            e1 = sum(power(lset[ : , -1] - tree['left'], 2))
        if shape(rset)[1] != 0:
            e2 = sum(power(rset[ : , -1] - tree['right'], 2))
        errornomerge = e1 + e2
        average = (tree['left'] + tree['right']) / 2.0
        test = mat(testdata)
        errormerge = sum(power(test[ : , -1] - average, 2))
        if errormerge < errornomerge:
            print 'merging'
            return average
        else:
            return tree
    else:
        return tree

step7:

模型树:

修改了回归树中的参数函数和加入了叶节点的线性回归函数。

#模型树线性回归函数(简单的线性回归函数,矩阵逆不存在时出错)
def lineSolve(data):
    linedata = mat(data)
    m, n = shape(linedata)
    x = mat(ones((m, n)))
    x[ : , 1 : n] = linedata[ : , 0 : n - 1]
    y = linedata[ : , -1]
    xtemp = x.T * x
    if linalg.det(xtemp) == 0.0:
        print 'error,没有逆矩阵'
        return
    w = xtemp.I * x.T * y
    return x, y, w

#模型树的误差
def modError(dataset):
    x, y, w = lineSolve(dataset)
    yhat = x * w
    return sum(power(yhat - y, 2))

#模型树的叶子节点    
def modLeaf(dataset):
    x, y, w = lineSolve(dataset)
    return w

图为:

结果为:

叶子节点的值为w,x * w得到y的预测值。

step8:

树回归的预测函数:

#回归值(树回归)
def regValue(value, dataset):
    return value

#回归值(模型树回归)
def modValue(value, dataset):
    data = mat(dataset)
    n = shape(data)[1]
    x = mat(ones((1, n + 1)))
    x[ : , 1 : n + 1] = data
    yhat = x * value
    return yhat

#树回归预测
def predictTree(tree, t, valuetype = regValue):
    testdata = mat(t)
    m, n = shape(testdata)
    yhat = mat(zeros((m, 1)))
    for i in range(m):
        yhat[i, 0] = predictValue(tree, testdata[i], valuetype)
    return yhat

#回归预测值
def predictValue(tree, test, valuetype = regValue):
    if not isTree(tree):
        return valuetype(tree, test)
    if test[tree['fea']] > tree['val']:
        return predictValue(tree['left'], test, valuetype)
    else:
        return predictValue(tree['right'], test, valuetype)         

接下来我们进行3种回归的比较:

图为:

相关系数越接近1,相关性越大。

1:CART树回归:

相关系数为:

2:模型树:

相关系数为:

3:线性回归

相关系数为:

可见大小顺序为 模型树 > 回归树 > 线性回归

终于完成了监督学习的相关学习部分,SVM还不懂,有机会学习一下SVM的应用,明天开始学习无监督学习,先学习聚类算法。加油!

时间: 2024-12-08 16:17:39

机器学习day14 机器学习实战树回归之CART与模型树的相关文章

机器学习经典算法详解及Python实现--CART分类决策树、回归树和模型树

摘要: Classification And Regression Tree(CART)是一种很重要的机器学习算法,既可以用于创建分类树(Classification Tree),也可以用于创建回归树(Regression Tree),本文介绍了CART用于离散标签分类决策和连续特征回归时的原理.决策树创建过程分析了信息混乱度度量Gini指数.连续和离散特征的特殊处理.连续和离散特征共存时函数的特殊处理和后剪枝:用于回归时则介绍了回归树和模型树的原理.适用场景和创建过程.个人认为,回归树和模型树

机器学习实战之树回归

一,引言 尽管线性回归包含了一些强大的方法,但这些方法创建的模型需要拟合所有的样本数据.当数据拥有众多特征并且特征之间的关系比较复杂时,构建全局线性模型就会非常困难.并且,在实际生活中很多问题都是非线性的,很难通过全局线性模型来拟合所有数据. 解决上述非线性数据的拟合问题的一个可行的方法是,将数据集切分成很多份容易建模的数据,然后再利用线性回归方法来对切分后的数据子集分别建模,如果切分后仍难以拟合线性模型就继续切分.这样,就可以比较好的拟合全局数据. 二,CART算法 CART算法,即分类回归树

机器学习实战-逻辑回归

什么是回归? 假设现在有些数据点,我用直线对这些点进行拟合(该线叫做最佳拟合直线),这个拟合的过程就叫做回归. Logistic回归? 这里,Logistic回归进行分类的主要思想:根据现有数据对分类的边界线建立回归公式,以此边界线进行分类.这里的回归指的是最佳拟合,就是要找到边界线的回归公式的最佳拟合的参数集.训练时使用最优化算法寻找最佳拟合参数. 基于Logistic回归和Sigmoid函数的分类 对于边界线建立的回归函数,能够接受所有的输入然后预测出类别.例如,对于二分类的情况下,上述函数

机器学习中的数学(1)-回归(regression)、梯度下降(gradient descent)

机器学习中的数学(1)-回归(regression).梯度下降(gradient descent) 版权声明: 本文由LeftNotEasy所有,发布于http://leftnoteasy.cnblogs.com.如果转载,请注明出处,在未经作者同意下将本文用于商业用途,将追究其法律责任. 前言: 上次写过一篇关于贝叶斯概率论的数学,最近时间比较紧,coding的任务比较重,不过还是抽空看了一些机器学习的书和视频,其中很推荐两个:一个是stanford的machine learning公开课,在

TensorFlow教程03:针对机器学习初学者的MNIST实验——回归的实现、训练和模型评估

实现回归模型 为了用python实现高效的数值计算,我们通常会使用函数库,比如NumPy,会把类似矩阵乘法这样的复杂运算使用其他外部语言实现.不幸的是,从外部计算切换回Python的每一个操作,仍然是一个很大的开销.如果你用GPU来进行外部计算,这样的开销会更大.用分布式的计算方式,也会花费更多的资源用来传输数据. TensorFlow也把复杂的计算放在python之外完成,但是为了避免前面说的那些开销,它做了进一步完善.Tensorflow不单独地运行单一的复杂计算,而是让我们可以先用图描述一

Coursera机器学习-第三周-逻辑回归Logistic Regression

Classification and Representation 1. Classification Linear Regression (线性回归)考虑的是连续值([0,1]之间的数)的问题,而Logistic Regression(逻辑回归)考虑的是离散值(例如只能取0或1而不能取0到1之间的数)的问题.举个例子,你需要根据以往季度的电力数据,预测下一季度的电力数据,这个时候需要使用的是线性回归,因为这个值是连续的,而不是离散的.而当你需要判断这个人抽烟还是不抽烟的问题时,就需要使用逻辑回

机器学习——模型树

和回归树(在每个叶节点上使用各自的均值做预测)不同,模型树算法需要在每个叶节点上都构建出一个线性模型,这就是把叶节点设定为分段线性函数,这个所谓的分段线性(piecewise linear)是指模型由多个线性片段组成. #####################模型树##################### def linearSolve(dataSet): #模型树的叶节点生成函数 m,n = shape(dataSet) X = mat(ones((m,n))); Y = mat(ones

学习日志---树回归(回归树,模型树)

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)  

Machine Learning in Action -- 树回归

前面介绍线性回归,但实际中,用线性回归去拟合整个数据集是不太现实的,现实中的数据往往不是全局线性的 当然前面也介绍了局部加权线性回归,这种方法有些局限 这里介绍另外一种思路,树回归 基本思路,用决策树将数据集划分成若干个子集,然后再子集上再用线性回归进行拟合 决策树是种贪心算法,最简单典型的决策树算法是ID3 ID3,每次都选取最佳特征来进行划分,并且按照特征的取值来决定划分的个数,比如性别,就划分为男,女 在决定最佳特征时,用香农熵作为指标,表示当前的划分是否会让数据更加有序 ID3的局限是,