• 机器学习实战笔记-预测数值型数据:回归


    8. 1 用线性回归找到最佳拟合直线

    线性回归 
    优点:结果易于理解,计算上不复杂。 
    缺点:对非线性的数据拟合不好。 
    适用数据类型:数值型和标称型数据。

    回归的目的是预测数值型的目标值。最直接的办法是依据输人写出一个目标值的计算公式。 
    假如你想要预测姐姐男友汽车的功率大小,可能会这么计算: 
    HorsePower = 0.0015* annualSalary - 0.99* hoursListeningToPublicRadio 
    这就是所谓的回归方程(regression equation),其中的0.0015和-0.99称作回归系数(regression weights) ,求这些回归系数的过程就是回归。一旦有了这些回归系数,再给定输人,做预测就非常容易了。具体的做法是用回归系数乘以输人值,再将结果全部加在一起,就得到了预测值

    回归的一般方法 
    (1)收集数据:采用任意方法收集数据。 
    (2)准备数据:回归需要数值型数据,标称型数据将被转成二值型数据。 
    (3)分析数据:绘出数据的可视化二维图将有助于对数据做出理解和分析,在采用缩减法求得新回归系数之后, 可以将新拟合线绘在图上作为对比。 
    (4)训练算法:找到回归系数。 
    (5)测试算法:使用R2或者预测值和数据的拟合度,来分析模型的效果。 
    (6)使用算法:使用回归,可以在给定输入的时候预测出一个数值,这是对分类方法的提升,因为这样可以预测连续型数据而不仅仅是离散的类别标签。

    应当怎样从一大堆数据里求出回归方程呢?假定输人数据存放在矩阵X中,而回归系数存放在向量w中。那么对于给定的数据x1 , 预测结果将会通过Y1=XT1w给出。现在的问题是,手里有一些x和对应的y,怎样才能找到w呢?一个常用的方法就是找出使误差最小的w,这里的误差是指预测值和真实值之间的差值,使用该误差的简单累加将使得正差值和负差值相互抵消,所以我们采用平方误差

    平方误差可以写作: 

    $sum_{i=1}^{n}$


    用矩阵表示还可以写做(yxw)T(yxw)。如果对w求导,得到XT(YXw),令其等于零,解出w如下: 

    w=(XTX)1XTy


    w上方的小标记表示,这是当前可以估计出的w最优解。从现有数据上估计出的w可能并不是数据中的真实城值,所以这里使用了一个“帽”符号来表示它仅是w的一个最佳估计。(不知道怎么把冒号添上去,汗) 
    值得注意的是,上述公式中包含(XTX1), 也就是需要对矩阵求逆,因此这个方程只在逆矩阵存在的时候适用。然而,矩阵的逆可能并不存在,因此必须要在代码中对此作出判断。 
    上述的最佳w求解是统计学中的常见问题,除了矩阵方法外还有很多其他方法可以解决。通过调用沖Numpy库里的矩阵方法,我们可以仅使用几行代码就完成所需功熊。该方法也称作OLS,意 思是”普通最小二乘法”(ordinary least squares)。

    下面看看实际效果,对于图8-1中的散点图,下面来介绍如何给出该数据的最佳拟合直线。 
    这里写图片描述

    标准回归函数和数据导人函数,代码如下所示:

    #general function to parse tab -delimited floats
    def loadDataSet(fileName):
        #get number of fields,默认最后一列为y      
        numFeat = len(open(fileName).readline().split('	')) - 1 
        dataMat = []; labelMat = []
        fr = open(fileName)
        for line in fr.readlines():
            lineArr =[]
            curLine = line.strip().split('	')
            for i in range(numFeat):
                lineArr.append(float(curLine[i]))
            dataMat.append(lineArr)
            labelMat.append(float(curLine[-1]))
        return dataMat,labelMat
    
    def standRegres(xArr,yArr):
        xMat = mat(xArr); yMat = mat(yArr).T
        xTx = xMat.T*xMat
        #防止xTx无法求逆矩阵
        if linalg.det(xTx) == 0.0:
            print "This matrix is singular, cannot do inverse"
            return
        #利用之前推导出的公式求出w
        ws = xTx.I * (xMat.T*yMat)
        return ws

    测试截图如下: 
    这里写图片描述 
    变量ws存放的就是回归系数。在用内积来预测y的时候,第一维将乘以前面的常数X0,第二维将乘以输人变量X1。因为前面假定了X0=1,所以最终会得到y =ws[0]+ws [1]*X1。这里的y实际是预测出的,为了和真实的y值区分开来,我们将它记为yHat 。下面使用新的ws值计算yHat: 
    这里写图片描述 
    注意,如果直线上的数据点次序混乱,绘图时将会出现问题,所以首先要将点按照升序排列,使用sort方法。 
    这里写图片描述

    几乎任一数据集都可以用上述方法建立模型,那么,如何判断这些模型的好坏呢?比较一下图8-3的两个子图,如果在两个数据集上分别作线性回归,将得到完全一样的模型(拟合直线)。显然两个数据是不一样的,那么模型分别在二者上的效果如何?我们当如何比较这些效果的好坏呢?有种方法可以计算预测值沖社序列和真实值乂序列的匹配程度,男防尤是计算这两个序列的相关系数。 
    这里写图片描述

    在python中 ,Numpy库提供了相关系数的计算方法:可以通过命令corrcoef(yEstimate,yActual)来计算预测值和真实值的相关性。下面我们就在前面的数据集上做个实验。 
    这里写图片描述 
    该矩阵包含所有两两组合的相关系数。可以看到,对角线上的数据是1.0,因为yMat和自己的匹配是最完美的,而YHat和YMat的相关系数为0.98。

    8.2 局部加权线性回归 
    线性回归的一个问题是有可能出现欠拟合现象,因为它求的是具有最小均方误差的无偏估计 。显而易见,如果模型欠拟合将不能取得最好的预测效果。所以有些方法允许在估计中引人一些偏差,从而降低预测的均方误差

    其中的一个方法是局部加权线性回归(Locally Weighted Linear Regression, LWLR )。在该算法中 ,我们给待预测点附近的每个点赋予一定的权重;然后与8.1节类似,在这个子集上基于最小均方差来进行普通的回归。与kNN一样, 这种算法每次预测均需要事先选取出对应的数据子集。该算法解出回归系数w的形式如下: 

    w=(XTWX)1XTWy


    其中w是一个矩阵,用来给每个数据点赋予权重。 
    LWLR使用”核”(与支持向量机中的核类似)来对附近的点赋予更高的权重。核的类型可以自由选择,最常用的核就是高斯核,高斯核对应的权重如下: 

    w(i,i)=exp(|xix|2k2)


    这样就构建了一个只含对角元素的权重矩阵w并且点xx(i)越近,w(i,i)将会越 大 。上述公式包含一个需要用户指定的参数k,它决定了对附近的点赋予多大的权重,这也是使用LWLR时唯一需要考虑的参数,在图8-4中可以看到参数k与权重的关系。 
    这里写图片描述

    局部加权线性回归函数,代码如下:

    def lwlr(testPoint,xArr,yArr,k=1.0):
        #将二维数组xMat, yMat转化为矩阵
        xMat = mat(xArr); yMat = mat(yArr).T
        m = shape(xMat)[0]
        #加权矩阵,初始化为m维单位矩阵
        weights = mat(eye((m)))
        for j in range(m):                      #next 2 lines create weights matrix
            #循环求出testPoint与矩阵中每行向量的差
            diffMat = testPoint - xMat[j,:]
            #利用权重公式求出testPoint对应的此矩阵的权重
            weights[j,j] = exp(diffMat*diffMat.T/(-2.0*k**2))
        #依然利用上节的公式求出xTx,不同的是对xMat加上了权重,即每行为x1乘以对应的权重
        xTx = xMat.T * (weights * xMat)
        #测试xTx是否可逆
        if linalg.det(xTx) == 0.0:
            print("This matrix is singular, cannot do inverse")
            return
        #依然利用上节的公式求ws,不同在于每行的x和y都分别乘以对应的权重
        ws = xTx.I * (xMat.T * (weights * yMat))
        #返回局部加权预测值
        return testPoint * ws
    
    def lwlrTest(testArr,xArr,yArr,k=1.0):  #loops over all the data points and applies lwlr to each one
        m = shape(testArr)[0]
        yHat = zeros(m)
        for i in range(m):
            #循环利用lwlr函数计算出testArr每行向量对应的ws,算出加权预测值
            yHat[i] = lwlr(testArr[i],xArr,yArr,k)
        return yHat

    测试代码如下: 
    这里写图片描述 
    测试截图如下,当k=0.003时
    这里写图片描述 
    当k=0.01时
    这里写图片描述 
    当k=1时
    这里写图片描述

    当k=1.0时权重很大,如同将所有的数据视为等权重,得出的最佳拟合直线与标准的回归一致。使用k=0.01得到了非常好的效果,抓住了数据的潜在模式使用k=0.003纳人了太多的噪声点,拟合的直线与数据点过于贴近,过拟合。 
    局部加权线性回归也存在一个问题,即增加了计算量,因为它对每个点做预测时都必须使用整个数据集。k=0.01时可以得到很好的估计,但是同时看一下k=0.01的情况,就会发现大多数据点的权重都接近零如果避免这些计算将可以减少程序运行时间,从而缓解因计算量增加带来的问题

    8.3 示例:预测鲍鱼的年龄

    接下来,我们将回归用于真实数据。在data目录下有一份来自UCI数据集合的数据,记录了鲍鱼(一种介壳类水生动物)的年龄。鲍鱼年龄可以从鲍鱼壳的层数推算得到。 
    计算预测值和实际值方差的代码如下:

    def rssError(yArr,yHatArr): #yArr and yHatArr both need to be arrays
        return ((yArr-yHatArr)**2).sum()

    测试截图如下: 
    这里写图片描述 
    可以看到,使用较小的核将得到较低的误差。那么,为什么不在所有数据集上都使用最小的核呢?这是因为使用最小的核将造成过拟合,对新数据不一定能达到最好的预测效果。下面看看它们在新数据上面的表现: 
    测试截图如下: 
    这里写图片描述 
    从上述结果可以看到,在上面的三个参数中,核大小等于10时的测试误差最小,但它在训练集上的误差却是最大的。接下来再来和简单的线性回归做个比较: 
    这里写图片描述 
    简单线性回归达到了与局部加权线性回归类似的效果。这也表明一点,必须在未知数据上比较效果才能选取到最佳模型。那么最佳的核大小是10吗?或许是,但如果想得到更好的效果,应该用10个不同的样本集做10次测试来比较结果。

    本例展示了如何使用局部加权线性回归来构建模型,可以得到比普通线性回归更好的效果。局部加权线性回归的问题在于,每次必须在整个数据集上运行。也就是说为了做出预测,必须保存所有的训练数据。下面将介绍另一种提高预测精度的方法,并分析它的优势所在。

    8.4 缩减系数来”理解”数据

    如果数据的特征比样本点还多应该怎么办?是否还可以使用线性回归和之前的方法来做预测?答案是否定的,即不能再使用前面介绍的方法。这是因为在计算(XTX)1的时候会出错。 
    为了解决这个问题,统计学家引入了岭回归(ridge regression)的概念,这就是本节将介绍的第一种缩减方法。接着lasso法,该方法效果很好但计算复杂。本节最后介绍了第二种缩减方法,称为前向逐步回归,可以得到与lasso差不多的效果,且更容易实现。

    8.4.1 岭回归

    简单说来,岭回归就是在矩阵XTX上加一个λI从而使得矩阵非奇异,进而能对XTX+λI求逆。其中矩阵I是一个mm的单位矩阵,对角线上元素全为1 ,其他元素全为0。而λ是一个用户定义的数值,后面会做介绍。在这种情况下,回归系数的计算公式将变成: 

    w=(XTX+λI)1XTy


    岭回归最先用来处理特征数多于样本数的情况,现在也用于在估计中加人偏差,从而得到更好的估计。这里通过引入λ限制了所有w之和通过引人该惩罚项,能够减少不重要的参数,这个技术在统计学中也叫做缩减(shrinkage )。

    岭回归中的岭是什么? 
    岭回归使用了单位矩阵乘以常量λ,我们观察其中的单位矩阵, 可以看到值1贯穿整个对角线,其余元素全是0。形象地,在0构成的平面上有一条1组成的“岭”,这就是岭回归中的“岭”的由来 
    缩减方法可以去掉不重要的参数,因此能更好地理解数据。此外,与简单的线性回归相比,缩减法能取得更好的预测效果

    岭回归,代码如下所示:

    #根据岭回归公式计算出ws
    def ridgeRegres(xMat,yMat,lam=0.2):
        xTx = xMat.T*xMat
        denom = xTx + eye(shape(xMat)[1])*lam
        #如果lam设定为0的时候一样可能会产生错误,所以这里仍需要做一个检查
        if linalg.det(denom) == 0.0:
            print("This matrix is singular, cannot do inverse")
            return
        ws = denom.I * (xMat.T*yMat)
        return ws
    
    def ridgeTest(xArr,yArr):
        xMat = mat(xArr); yMat=mat(yArr).T
        #mean表示求出y的均值
        yMean = mean(yMat,0)
        yMat = yMat - yMean     #to eliminate X0 take mean off of Y
        #regularize X's
        #求出xArr各列均值
        xMeans = mean(xMat,0)   #calc mean then subtract it off
        #求出xArr各列方差
        xVar = var(xMat,0)      #calc variance of Xi then divide by it
        xMat = (xMat - xMeans)/xVar
        numTestPts = 30
        #wMat表示岭回归系数矩阵,初始化为(30,n)维零数组
        wMat = zeros((numTestPts,shape(xMat)[1]))
        #循环30次,根据不同的lambda填充ws
        for i in range(numTestPts):
            ws = ridgeRegres(xMat,yMat,exp(i-10))
            wMat[i,:]=ws.T
        return wMat
    处理完成后就可以在30个不同的λ下调用ridgeRegres()函数。注意,这里的λ应以指数级变化,这样可以看出λ在取非常小的值时和取非常大的值时分别对结果造成的影响。最后将所有的回归系数输出到一个矩阵并返回。

    测试截图: 
    这里写图片描述
    该图绘出了回归系数与log(λ)的关系。在最左边,即λ最小时,可以得到所有系数的原始值(与线性回归一致);而在右边,系数全部缩减成0 ;在中间部分的某值将可以取得最好的预测效果为了定量地找到最佳参数值,还需要进行交叉验证另外,要判断哪些变量对结果预测最具有影响力,观察它们对应的系数大小就可以。

    8.4.2 lasso

    不难证明,在增加如下约束时,普通的最小二乘法回归会得到与岭回归的一样的公式: 

    k=1nw2k<=λ

    上式限定了所有回归系数的平方和不能大于λ使用普通的最小二乘法回归在当两个或更多的特征相关时,可能会得出一个很大的正系数和一个很大的负系数。正是因为上述限制条件的存在,使用岭回归可以避免这个问题。

    与岭回归类似,另一个缩减方法lassso也对回归系数做了限定,对应的约束条件如下: 

    k=1n|wk|<=λ


    唯一的不同点在于,这个约束条件使用绝对值取代了平方和。虽然约束形式只是稍作变化,结果却大相径庭:λ足够小的时候,一些系数会因此被迫缩减到0 ,这个特性可以帮助我们更好地理解数据。这两个约束条件在公式上看起来相差无几,但细微的变化却极大地增加了计算复杂度(为了在这个新的约束条件下解出回归系数,需要使用二次规划算法)。下面将介绍一个更为简单的方法来得到结果,该方法叫做前向逐步回归。

    8.4.3 前向逐步回归

    前向逐步回归算法可以得到与lasso差不多的效果,但更加简单。它属于一种贪心算法即每一步都尽可能减少误差一开始,所有的权重都设为1,然后每一步所做的决策是对某个权重增加或减少一个很小的值

    该算法的伪代码如下所示: 
    数据标准化,使其分布满足0均值和单位方差 
    在每轮迭代过程中: 
     设置当前最小误差lowestError为正无穷 
     对每个特征: 
      增大或缩小: 
        改变一个系数得到一个新的W 
        计算新W下的误差 
        如果误差Error小于当前最小误差lowestError:设置Wbest等于当前的W 
     将W设置为新的Wbest

    前向逐步线性回归,代码如下:

    def regularize(xMat):#regularize by columns
        inMat = xMat.copy()
        inMeans = mean(inMat,0)   #calc mean then subtract it off
        inVar = var(inMat,0)      #calc variance of Xi then divide by it
        inMat = (inMat - inMeans)/inVar
        return inMat
    
    def stageWise(xArr,yArr,eps=0.01,numIt=100):
        xMat = mat(xArr); yMat=mat(yArr).T
        #mean()函数表示对矩阵求均值,axis=0指定按列求均值
        yMean = mean(yMat,0)
        yMat = yMat - yMean     #can also regularize ys but will get smaller coef
        #对xMat进行标准化处理,标准化处理函数为regularize()
        xMat = regularize(xMat)
        m,n=shape(xMat)
        #返回所有迭代中ws的变化情况,初始化为(numIt,n)维矩阵
        returnMat = zeros((numIt,n)) #testing code remove
        #回归系数ws初始化为(n,1)维零数组
        ws = zeros((n,1)); wsTest = ws.copy(); wsMax = ws.copy()
        #迭代numIt次,每次迭代,循环n*2次(每个特征有增大和减小两个方向),找出令rssError最小的方向(哪个特征,对应增大还是减小),保存ws,下次迭代在ws基础上做更新
        for i in range(numIt):
            print(ws.T)
            lowestError = inf; 
            for j in range(n):
                for sign in [-1,1]:
                    wsTest = ws.copy()
                    wsTest[j] += eps*sign
                    yTest = xMat*wsTest
                    rssE = rssError(yMat.A,yTest.A)
                    if rssE < lowestError:
                        lowestError = rssE
                        wsMax = wsTest
            ws = wsMax.copy()
            returnMat[i,:]=ws.T
        return returnMat

    测试截图如下: 
    这里写图片描述 
    这里写图片描述 
    上述结果中值得注意的是w1w6都是0 ,这表明它们不对目标值造成任何影响,也就是说这些特征很可能是不需要的。另外,在参数eps(步长)设置为0.01的情况下,一段时间后系数就已经饱和并在特定值之间来回震荡,这是因为步长太大的缘故。这里会看到,第一个权重在0.04和0.05之间来回震荡

    下面试着用更小的步长和更多的步数,测试结果截图如下: 
    这里写图片描述 
    绘制returnMat,截图如下: 
    这里写图片描述
    接下来把这些结果与最小二乘法进行比较,后者的结果可以通过如下代码获得: 
    这里写图片描述 
    可以看到在5000次迭代以后,逐步线性回归算法与常规的最小二乘法效果类似。 
    逐步线性回归算法的实际好处并不在于能绘出这样漂亮的图,主要的优点在于它可以帮助人们理解现有的模型并做出改进当构建了一个模型后,可以运行该算法找出重要的特征,这样就有可能及时停止对那些不重要特征的收集。最后,如果用于测试,该算法每100次迭代后就可以构建出一个模型,可以使用类似于10折交叉验证的方法比较这些模型,最终选择使误差最小的模型

    当应用缩减方法(如逐步线性回归或岭回归)时,模型也就增加了偏差(bias),与此同时却减小了模型的方差

    8.5 权衡偏差与方差

    任何时候,一旦发现模型和测量值之间存在差异,就说出现了误差。当考虑模型中的“噪声”或者说误差时,必须考虑其来源。你可能会对复杂的过程进行简化,这将导致在模型和测量值之间出现“噪声”或误差,若无法理解数据的真实生成过程,也会导致差异的发生。另外,测量过程本身也可能产生“噪声”或者问题。下面举一个例子,8.1节和8.2节处理过一个从文件导人的二维数据。实话来讲,这个数据是我自己造出来的,其具体的生成公式如下: 

    y=3.0+1.7x+0.1sin(30x)+0.06N(0,1)


    其中N(0,1)是一个均值为0、方差为1的正态分布。在8.1节中,我们尝试过仅用一条直线来拟合上述数据。不难想到,直线所能得到的最佳拟合应该是3.0+1.7x这一部分。这样的话,误差部分就是0.1sin(30x)+0.06N(0,1)。在8.2节和8.3节,我们使用了局部加权线性回归来试图捕捉数据背后的结构。该结构拟合起来有一定的难度,因此我们测试了多组不同的局部权重来找到具有最小测试误差的解。 
    图8-8给出了训练误差和测试误差的曲线图,上面的曲线就是测试误差,下面的曲线是训练误差。根据8.3节的实验我们知道:如果降低核的大小,那么训练误差将变小。从图8-8来看,从左到右就表示了核逐渐减小的过程。 
    这里写图片描述 
    一般认为,上述两种误差由三个部分组成:偏差、测量误差和随机噪声。在8.2节和8.3节,我们通过引人了三个越来越小的核来不断增大模型的方差。

    8.4节介绍了缩减法,可以将一些系数缩减成很小的值或直接缩减为0,这是一个增大模型偏差的例子。通过把一些特征的回归系数缩减到0,同时也就减少了模型的复杂度。例子中有8个特征,消除其中两个后不仅使模型更易理解,同时还降低了预测误差。图8-8的左侧是参数缩减过于严厉的结果,而右侧是无缩减的效果。

    方差是可以度量的。如果从鲍鱼数据中取一个随机样本集(例如取其中100个数据)并用线性模型拟合,将会得到一组回归系数。同理,再取出另一组随机样本集并拟合,将会得到另一组回归系数。这些系数间的差异大小也就是模型方差大小的反映。上述偏差与方差折中的概念在机器学习十分流行并且反复出现。 
    下一节将介绍上述理论的应用:首先从拍卖站点抽取一些数据,再使用一些回归法进行实验来为数据找到最佳的岭回归模型。这样就可以通过实际效果来看看偏差和方差间的折中效果

    8.6示例:预测乐高玩具套装的价格

     用回归法预测乐高套装的价格 
      (1)收Google Shopping的API收集数据。 
      (2)准备数据:从返回的JSON数据中抽取价格。 
      (3)分析数据:可视化并观察数据。 
      (4)训练算法:构建不同的模型,釆用逐步线性回归和直接的线性回归模型。 
      (5)测试算法:使用交叉验证来测试不同的模型,分析哪个效果最好。 
      (6)使用算法:这次练习的目标就是生成数据模型。

    在这个例子中,我们将从不同的数据集上获取价格,然后对这些数据建立回归模型。需要做的第一件事就是如何获取数据。

    8.6.1 收集数据:使用Google购物的API

    Google已经为我们提供了一套购物的API来抓取价格。在使用处1之前,需要注册一个Goolge的账号,然后访问Google API的控制台来确保购物API可用。完成之后就可以发送HTTP请求,API将以JSON格式返回所需的产品信息。Python提供了JSON解析模块,我们可以从返回的JSON格式里整理出所需数据。详细的API介绍可以参见:http://code.google.com/apis/shopping/search/v1/getting_started.html

    购物信息的获取函数,代码如下:

    def searchForSet(retX, retY, setNum, yr, numPce, origPrc):
        sleep(10)
        myAPIstr = 'AIzaSyD2cR2KFyx12hXu6PFU-wrWot3NXvko8vY'
        searchURL = 'https://www.googleapis.com/shopping/search/v1/public/products?key=%s&country=US&q=lego+%d&alt=json' % (myAPIstr, setNum)
        #通过google api获取对应商品编号的数据
        pg = urllib2.urlopen(searchURL)
        #将数据转化为json格式
        retDict = json.loads(pg.read())
        #遍历json数据,填充retX,retY
        for i in range(len(retDict['items'])):
            try:
                currItem = retDict['items'][i]
                if currItem['product']['condition'] == 'new':
                    newFlag = 1
                else: newFlag = 0
                listOfInv = currItem['product']['inventories']
                for item in listOfInv:
                    sellingPrice = item['price']
                    if  sellingPrice > origPrc * 0.5:
                        print("%d	%d	%d	%f	%f" % (yr,numPce,newFlag,origPrc, sellingPrice))
                        retX.append([yr, numPce, newFlag, origPrc])
                        retY.append(sellingPrice)
            except: print('problem with item %d' % i)
    
    def setDataCollect(retX, retY):
        searchForSet(retX, retY, 8288, 2006, 800, 49.99)
        searchForSet(retX, retY, 10030, 2002, 3096, 269.99)
        searchForSet(retX, retY, 10179, 2007, 5195, 499.99)
        searchForSet(retX, retY, 10181, 2007, 3428, 199.99)
        searchForSet(retX, retY, 10189, 2008, 5922, 299.99)
        searchForSet(retX, retY, 10196, 2009, 3263, 249.99)

    测试截图如下:(写的时候没有翻墙。。。) 
    这里写图片描述

    8.6.2 训练算法:建立模型

    使用线性回归得到的模型如下: 
    $55319.9727.59Year0.00268NumPieces11.22NewOrUsed+2.57originalprice 
    这个模型的预测效果非常好,但模型本身并不能令人满意。它对于数据拟合得很好,但看上去没有什么道理。从公式看,套装里零部件越多售价反而会越低。另外,该公式对新套装也有一定的惩罚。 
    下面使用缩减法中一种,即岭回归再进行一次实验。前面讨论过如何对系数进行缩减,但这 
    次将会看到如何用缩减法确定最佳回归系数

    交叉验证测试岭回归,代码如下:

    def crossValidation(xArr,yArr,numVal=10):
        m = len(yArr)                           
        indexList = range(m)
        #errorMat保存不同训练样本(默认为10次不同的训练样本和测试样本)对应30个lambda(ridgeTest()将lambda按指数级递减得到30个不同lambda对应的相关系数)的rssError,初始化为(10,30)维零矩阵
        errorMat = zeros((numVal,30))#create error mat 30columns numVal rows
        for i in range(numVal):
            trainX=[]; trainY=[]
            testX = []; testY = []
            #shuffle()表示对range(m)重新洗牌,即让每次迭代对应的训练集和测试集都不一样
            random.shuffle(indexList)
            for j in range(m):#create training set based on first 90% of values in indexList
                if j < m*0.9: 
                    trainX.append(xArr[indexList[j]])
                    trainY.append(yArr[indexList[j]])
                else:
                    testX.append(xArr[indexList[j]])
                    testY.append(yArr[indexList[j]])
            wMat = ridgeTest(trainX,trainY)    #get 30 weight vectors from ridge
            for k in range(30):#loop over all of the ridge estimates
                matTestX = mat(testX); matTrainX=mat(trainX)
                meanTrain = mean(matTrainX,0)
                varTrain = var(matTrainX,0)
                matTestX = (matTestX-meanTrain)/varTrain #regularize test with training params
                yEst = matTestX * mat(wMat[k,:]).T + mean(trainY)#test ridge results and store
                errorMat[i,k]=rssError(yEst.T.A,array(testY))
                #print errorMat[i,k]
        meanErrors = mean(errorMat,0)#calc avg performance of the different ridge weight vectors
        #求出最小的meanErrors
        minMean = float(min(meanErrors))
        #meanErros==minMean的那一列对应的即是使误差最小的lambda,而这一列对应的即为找出最佳相关系数的wMat的行数
        bestWeights = wMat[nonzero(meanErrors==minMean)]
        #can unregularize to get model
        #when we regularized we wrote Xreg = (x-meanX)/var(x)
        #we can now write in terms of x not Xreg:  x*w/var(x) - meanX/var(x) +meanY
        xMat = mat(xArr); yMat=mat(yArr).T
        meanX = mean(xMat,0); varX = var(xMat,0)
        #注意,岭回归将xMat和yMat标准化了,而线性回归不用标准化,所以我们要将数据"还原"
        unReg = bestWeights/varX
        print("the best model from Ridge Regression is:
    ",unReg)
        print("with constant term: ",-1*sum(multiply(meanX,unReg)) + mean(yMat))

    来看一下整体的运行效果,在%獅如叫丫中输人程序清单8-6中的代码并保存,然后执行如下命令: 
    这里写图片描述 
    可以看到,该结果与最小二乘法没有太大差异。我们本期望找到一个更易于理解的模型,显然没有达到预期效果。为了达到这一点,我们来看一下在缩减过程中回归系数是如何变化的,输人下面的命令 
    这里写图片描述

    这些系数是经过不同程度的缩减得到的。首先看第1行,第4项比第2项的系数大5倍,比第1项大57倍。这样看来,如果只能选择一个特征来做预测的话,我们应该选择第4个特征,也就是原始价格。如果可以选择2个特征的话,应该选择第4个和第2个特征。 
    这种分析方法使得我们可以挖掘大量数据的内在规律。在仅有4个特征时,该方法的效果也许并不明显;但如果有100个以上的特征,该方法就会变得十分有效:它可以指出哪些特征是关键的,而哪些特征是不重的

    本章小结

    与分类一样,回归也是预测目标值的过程。回归与分类的不同点在于,前者预测连续型变量,而后者预测离散型变量。回归是统计学中最有力的工具之一。在回归方程里,求得特征对应的最佳回归系数的方法是最小化误差的平方和。给定输人矩阵X,如果X的逆存在并可以求得的话,回归法都可以直接使用。数据集上计算出的回归方程并不一定意味着它是最佳的,可以便用预测值yHat和原始值y的相关性来度量回归方程的好坏。 
    当数据的样本数比特征数还少时候,矩阵XTX的逆不能直接计算。即便当样本数比特征数多时,XTX的逆仍有可能无法直接计算,这是因为特征有可能高度相关。这时可以考虑使用岭回归,因为当XTX的逆不能计算时,它仍保证能求得回归参数。 
    岭回归是缩减法的一种,相当于对回归系数的大小施加了限制。另一种很好的缩减法是lasso。Lasso难以求解,但可以使用计算简便的逐步线性回归方法来求得近似结果。 
    缩减法还可以看做是对一个模型增加偏差的同时减少方差偏差方差折中是一个重要的概念,可以帮助我们理解现有模型并做出改进,从而得到更好的模型。 
    本章介绍的方法很有用。但有些时候数据间的关系可能会更加复杂,如预测值与特征之间是非线性关关系,这种情况下使用线性的模型就难以拟合

  • 相关阅读:
    ubuntu中文版man
    每日英语:Auto Makers Accelerate Efforts to Develop Self-Driving Cars
    每日英语:How Often Do Gamblers Really Win?
    每日英语:Nanjing's New Sifang Art Museum Illustrates China's Cultural Boom
    每日英语:Political Gridlock, Beijing Style
    每日英语:How Your Knees Can Predict the Weather
    每日英语:Chinese Show Global Real-Estate Appetite
    每日英语:The Delicate Protocol Of Hugging
    每日英语:Making the Most of Your Lunch Hour
    每日英语:The Most Destructive, Unpredictable Force in Tech
  • 原文地址:https://www.cnblogs.com/kevincong/p/7852583.html
Copyright © 2020-2023  润新知