zoukankan      html  css  js  c++  java
  • 机器学习:浅析使用二元决策树进行回归分析

    1.引言        

           学过数据结构的同学对二叉树应该不陌生:二叉树是一个连通的无环图,每个节点最多有两个子树的树结构。如下图(一)就是一个深度k=3的二叉树。

           

                (图一)                                                          (图二)

           二元决策树与此类似。不过二元决策树是基于属性做一系列二元(是/否)决策。每次决策从下面的两种决策中选择一种,然后又会引出另外两种决策,依次类推直到叶子节点:即最终的结果。也可以理解为是对二叉树的遍历,或者很多层的if-else嵌套。

            这里需要特别说明的是:二元决策树中的深度算法与二叉树中的深度算法是不一样的。二叉树的深度是指有多少层,而二元决策树的深度是指经过多少层计算。以上图(一)为例,二叉树的深度k=3,而在二元决策树中深度k=2。

            图二就是一个二元决策树的例子,其中最关键的是如何选择切割点:即X[0]<=-0.075中的-0.0751是如何选择出来的?

    2.二元决策树切割点的选择

           切割点的选择是二元决策树最核心的部分,其基本思路是:遍历所有数据,尝试每个数据作为分割点,并计算此时左右两侧的数据的离差平方和,并从中找到最小值,然后找到离差平方和最小时对应的数据,它就是最佳分割点。下面通过具体的代码讲解这一过程:

    import numpy
    import matplotlib.pyplot as plot
    
    #建立一个100数据的测试集
    nPoints = 100
    
    #x的取值范围:-0.5~+0.5的nPoints等分
    xPlot = [-0.5+1/nPoints*i for i in range(nPoints + 1)]
    
    #y值:在x的取值上加一定的随机值或者叫噪音数据
    #设置随机数算法生成数据时的开始值,保证随机生成的数值一致
    numpy.random.seed(1)
    ##随机生成宽度为0.1的标准正态分布的数值
    ##上面的设置是为了保证numpy.random这步生成的数据一致
    y = [s + numpy.random.normal(scale=0.1) for s in xPlot]
    
    
    #离差平方和列表
    sumSSE = []
    for i in range(1, len(xPlot)):
        #以xPlot[i]为界,分成左侧数据和右侧数据
        lhList = list(xPlot[0:i])
        rhList = list(xPlot[i:len(xPlot)])
    
        #计算每侧的平均值
        lhAvg = sum(lhList) / len(lhList)
        rhAvg = sum(rhList) / len(rhList)
    
        #计算每侧的离差平方和
        lhSse = sum([(s - lhAvg) * (s - lhAvg) for s in lhList])
        rhSse = sum([(s - rhAvg) * (s - rhAvg) for s in rhList])
    
        #统计总的离差平方和,即误差和
    
        sumSSE.append(lhSse + rhSse)
    
    ##找到最小的误差和
    minSse = min(sumSSE)
    ##产生最小误差和时对应的数据索引
    idxMin = sumSSE.index(minSse)
    ##打印切割点数据及切割点位置
    print("切割点位置:"+str(idxMin)) ##49
    print("切割点数据:"+str(xPlot[idxMin]))##-0.010000000000000009
    
    ##绘制离差平方和随切割点变化而变化的曲线
    plot.plot(range(1, len(xPlot)), sumSSE)
    plot.xlabel('Split Point Index')
    plot.ylabel('Sum Squared Error')
    plot.show()

    3.使用二元决策树拟合数据

        这里使用sklearn.tree.DecisionTreeRegressor函数。下面只显示了主要代码,数据生成部分同上:

    from sklearn import tree
    from sklearn.tree import DecisionTreeRegressor
    
    ##使用二元决策树拟合数据:深度为1
    ##说明numpy.array(xPlot).reshape(1, -1):这是传入参数的需要:list->narray
    simpleTree = DecisionTreeRegressor(max_depth=1)
    simpleTree.fit(numpy.array(xPlot).reshape(-1,1), numpy.array(y).reshape(-1,1))
    ##读取训练后的预测数据
    y_pred  = simpleTree.predict(numpy.array(xPlot).reshape(-1,1))
    
    ##绘图
    plot.figure()
    plot.plot(xPlot, y, label='True y')
    plot.plot(xPlot, y_pred, label='Tree Prediction ', linestyle='--')
    plot.legend(bbox_to_anchor=(1,0.2))
    plot.axis('tight')
    plot.xlabel('x')
    plot.ylabel('y')
    plot.show()

        结果如下图:

                                             (图三)

       当深度依次为2(图四)、深度为6(图5)时的结果:

          

                                     (图四)                                                                                (图五)

    4.二元决策树的过度拟合

            二元决策树同普通最小二乘法一样,都存在拟合过度的情况,如图五所示,几乎看不到预测值的曲线,这就是拟合过度了。判断是否拟合过度有两种方法:

            1)观察结果图。这个很好理解,就是直接看绘制的对比图。

            2)比较决策树终止节点的数目与数据的规模。生产图五的曲线的深度是6(最深会有7层),即会有26=64个节点,而数据集中一共才有100个数据,也就是说有很多节点是只包括一个数据的。

    5.二元决策树深度的选择

           一般是通过不同深度二元决策树的交叉验证(前面已讲过原理)来确定最佳深度的,基本思路:

           1)确定深度列表:

           2)设置采用几折交叉验证

           3)计算每折交叉验证时的样本外数据的均方误差

           4)绘图,观察结果

           下面就通过深度分别为1~7的10折交叉验证来检验下最佳深度。

      

    import numpy 
    import matplotlib.pyplot as plot
    from sklearn import tree
    from sklearn.tree import DecisionTreeRegressor
    
    #建立一个100数据的测试集
    nPoints = 100
    
    #x的取值范围:-0.5~+0.5的nPoints等分
    xPlot = [-0.5+1/nPoints*i for i in range(nPoints + 1)]
    
    #y值:在x的取值上加一定的随机值或者叫噪音数据
    #设置随机数算法生成数据时的开始值,保证随机生成的数值一致
    numpy.random.seed(1)
    ##随机生成宽度为0.1的标准正态分布的数值
    ##上面的设置是为了保证numpy.random这步生成的数据一致
    y = [s + numpy.random.normal(scale=0.1) for s in xPlot]
    
    ##测试数据的长度
    nrow = len(xPlot)
    ##设置二元决策树的深度列表
    depthList = [1, 2, 3, 4, 5, 6, 7]
    ##每个深度下的离差平方和
    xvalMSE = []
    ##设置n折交叉验证
    nxval = 10
    ##外层循环:深度循环
    for iDepth in depthList:
        ##每个深度下的样本外均方误差
        oosErrors =0
        ##内层循环:交叉验证循环
        for ixval in range(nxval+1):
            #定义训练集和测试集标签
            xTrain = []  #训练集
            xTest = []   #测试集
            yTrain = []  #训练集标签
            yTest = []   #测试集标签
    
            for a in range(nrow):
                ##如果采用a%ixval==0的方式写,会有除数为0的错误
                if a%nxval != ixval%nxval:
                    xTrain.append(xPlot[a])
                    yTrain.append(y[a])
                else :
                    xTest.append(xPlot[a])
                    yTest.append(y[a])
                    
            ##深度为max_depth=iDepth的训练
            treeModel = DecisionTreeRegressor(max_depth=iDepth)
            ##转换参数类型
            treeModel.fit(numpy.array(xTrain).reshape(-1, 1), numpy.array(yTrain).reshape(-1, 1))
            ##读取预测值:使用测试集获取样本外误差
            treePrediction = treeModel.predict(numpy.array(xTest).reshape(-1, 1))
            ##离差列表:使用测试标签获取样本外误差
            error = [yTest[r] - treePrediction[r] for r in range(len(yTest))]
            ##每个深度下的样本外均方误差和
            oosErrors += sum([e * e for e in error])
       
        #计算每个深度下的样本外平均离差平方和
        mse = oosErrors/nrow
        ##添加到离差平方和列表
        xvalMSE.append(mse)
        
    ##绘图---样本外离差和的平方平均值随深度变化的曲线
    plot.plot(depthList, xvalMSE)
    plot.axis('tight')
    plot.xlabel('Tree Depth')
    plot.ylabel('Mean Squared Error')
    plot.show()

    结果如图:

                         (图六)

           从图中可以看出,当深度为3时的效果最好,下面我们把深度调成3,观察结果(为了效果调整了上面代码的颜色值):

                                       (图七)

  • 相关阅读:
    01-初学总结之《谭浩强C程序设计》
    00-计算机经典参考书籍
    (转)android图片压缩总结
    am等adb命令小总结
    (原创)在service中定时执行网络操作的几点说明
    (转)访问者模式
    (原创)用Receiver和SystemService监听网络状态,注册Receiver的两种方式
    (原创)Activity启动模式之singleTask
    (原创)开发微信公众平台遇到的乱码等问题的解决
    (转载)XML解析之-XStream解析
  • 原文地址:https://www.cnblogs.com/lc1217/p/6739612.html
Copyright © 2011-2022 走看看