机器学士实战(笔记):第 8 章 预测数值型数据:回归

第二部分 利用回归预测数值型数据

第 8 章 预测数值型数据:回归

[TOC]

本章内容

  • 线性回归
  • 局部加权线性回归
  • 岭回归和逐步线性回归
  • 预测鲍鱼年龄和玩具售价

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

线性回归:

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

回归的目的是预测数值型的目标值。最直接的办法是依据输入写成一个目标值的计算公式。

y=a_1*x_1+a_2*x_2

这就是回归方程,其中的 a1 和 a2 称作回归系数(regression weights),求这些回归系数的过程就是回归。一旦有了这些回归系数,再给定输入,做预测就非常容易了,具体的做法是用回归系数乘以输入值,再将结果全部加在一起,就得到了预测值。(这里的回归都是指线性回归(linear regression)

回归的一般方法:

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

应当怎样从一大堆数据里求出回归方程呢?假定输入数据存放在矩阵 X 中,而回归系数存放在向量 W 中,那么对于给定的数据 x1,预测结果将会通过

Y_1=X^T_1 W

给出。现在的问题是,有一些 x 个对应的 Y,怎样才能找到 w 呢?一个常用的方法就是找出误差最小的 w。这里的误差是指预测 y 值和真实 y 值之间的差值,使用该差值的简单累加将使得正差值和发差值相互抵消,所以使用平方误差公式:

\sum^m_{i=1}(y_i-x^T_i w)^2

用矩阵表示为:

(y-Xw)^T(y-Xw)

如果对 w 求导,得到

X^T(y-Xw)

令其等于零,解出 w 如下:

\hat{w}=(X^T X)^{-1}X^T y

w 上方的小标记表示,这是当前可以估计出的 w 的最优解。

值得注意的是,上述公式中包含

X^T X^{-1}

,也就是需要对矩阵求逆,因此这个方程只在逆矩阵存在的时候适用(代码中可以用伪逆矩阵)。上述的最佳 w 求解的统计学中的常见问题,除了矩阵方法外还有其他方法可以解决。该方法也称为 OLS,即普通最小二乘法(ordinary least squares)

对于图8-1的散点图,下面来介绍如何给出该数据的最佳拟合直线。

图8-1

添加 loadDataSet() 函数:

def loadDataSet(fileName):
    """
    读取文本文件
    :param fileName:
    :return:
    """
    numFeat = len(open(fileName).readline().split('\t')) - 1
    dataMat = []
    labelMat = []
    fr = open(fileName)
    for line in fr.readlines():
        lineArr = []
        curLine = line.strip().split('\t')
        for i in range(numFeat):
            lineArr.append(float(curLine[i]))
        dataMat.append(lineArr)
        labelMat.append(float(curLine[-1]))
    return dataMat, labelMat

添加 standRegres 函数:

def standRegres(xArr, yArr):
    """计算最佳拟合直线"""
    xMat = np.mat(xArr)
    yMat = np.transpose(np.mat(yArr))
    xTx = np.transpose(xMat) * xMat
    if np.linalg.det(xTx) == 0.0: # 计算 xTx 的行列式,为零则不能计算逆矩阵
        print("this matrix is singular,cannot do inverse")
        return
    ws = xTx.I * (xMat.T * yMat)
    # ws = np.linalg.solve(xTx, xMat.T * yMat) # 伪逆矩阵,可以不判断行列式是否为零
    return ws

测试:

xArr,yArr = loadDataSet('ex0.txt')
ws = standRegres(xArr,yArr) # 回归系数
# print(ws) # [[ 3.00774324] [ 1.69532264]]
xMat = np.mat(xArr)
yMat = np.mat(yArr)
yHat = xMat * ws # 预测的 y 值: y=ws[0]+ws[1]*X1
print(np.cov(yHat.T, yMat)) # 协方差 [[ 0.24664409  0.24664409] [ 0.24664409  0.25345439]]
print(np.corrcoef(yHat.T, yMat)) # 相关系数 [[ 1.  0.98647356] [ 0.98647356  1.  ]]

# 绘制数据集散点图和最佳拟合直线图
fig = plt.figure()
ax = fig.add_subplot(111)
ax.scatter(xMat[:,-1].flatten().A[0], yMat.T[:,0].flatten().A[0])
xCopy = xMat.copy()
xCopy.sort(0)
yHat = xCopy*ws
ax.plot(xCopy[:,-1],yHat)
plt.show()
图8-2

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

图8-3

在 numpy 中提供了 corrcoef 来计算预测值与真实值的相关性.

协方差:

Cov(X,Y)=\frac{1}{m}\sum^m_{i=1}(x_i-\bar{x})(y_i-\bar{y})

相关系数:

r=\frac{Cov(X,Y)}{\sqrt{\delta_x}\sqrt{\delta_y}}

其中,

\sqrt{\delta_x}$$、$$\sqrt{\delta_y}

表示 X、Y 的方差,并且 |r|<=1。

该矩阵包含所有两两组合的相关系数。可以看到,对角线上的数据是 1.0,因为 yMat 和自己的匹配是最完美的,而 yHat 和 yMat 的相关系数为 0.98.

2. 局部加权线性回归

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

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

\hat{w}=(X^T WX)^{-1}X^TWy

其中 w 是一个矩阵,用来给每个数据点赋予权重。

LWLR 使用“核”来对附近的点赋予更高的权重。核的类型可以自由选择,最常用的核就是高斯核,其对应的权重如下:

w(i,i)=exp(\frac{|x^{(i)}-x|}{-2k^2})

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

图8-4

添加 lwlr() 函数:

def lwlr(testPoint, xArr, yArr, k=1.0):
    """
    局部加权线性回函数
    :param testPoint:
    :param xArr:
    :param yArr:
    :param k:用于控制衰减速度
    :return:
    """
    xMat = np.mat(xArr)
    yMat = np.mat(yArr).T
    m = np.shape(xMat)[0]
    weights = np.mat(np.eye((m))) # 创建对角矩阵,阶数等于样本点个数
    for j in range(m):
        # 随着样本点与待预测点距离的递增,权重值大小以指数级衰减
        diffMat = testPoint - xMat[j,:]
        weights[j,j] = np.exp(diffMat*diffMat.T/(-2.0*k**2)) # 参数 k,控制衰减的速度
    xTx = xMat.T * (weights * xMat)
    if np.linalg.det(xTx) == 0.0:
        print('this matrix is singular, cannot do inverse')
    ws = xTx.I * (xMat.T * (weights * yMat))
    return testPoint * ws

添加 lwlrTest() 函数:

def lwlrTest(testArr, xArr, yArr, k=1.0):
    """
    用于为数据集中每个点调用 lwlr(),这有助于求解 k 的大小
    :param testArr:
    :param xArr:
    :param yArr:
    :param k:
    :return:
    """
    m = np.shape(testArr)[0]
    yHat = np.zeros(m)
    for i in range(m):
        yHat[i] = lwlr(testArr[i], xArr, yArr, k)
    return yHat

测试:

xArr, yArr = loadDataSet('ex0.txt')
yHat = lwlrTest(xArr, xArr, yArr, 0.01) # k=1, 0.01, 0.003
xMat = np.mat(xArr)
srtInd = xMat[:,1].argsort(0)
xSort = xMat[srtInd][:,0,:]
fig = plt.figure()
ax = fig.add_subplot(111)
ax.plot(xSort[:,1], yHat[srtInd])
ax.scatter(xMat[:,1].flatten().A[0], np.mat(yArr).T.flatten().A[0], s=2, c='red')
plt.show()

可以观察到如图8-5所示的效果。图8-5 给出了 k 在不同取值下的结果图。

图8-5

局部加权线性回归也存在一个问题,即增加了计算量,因为它对每个点做预测时都必须使用整个数据集。从图8-5可以看出,k=0.01 时可以得到很好的估计,但是同时看到涂8-4中 k=0.01 的情况,就会发现大多数据点的权重都接近零。如果避免这些计算将可以减少程序运行时间,从而缓解因计算量增加带来的问题。

3. 示例:预测鲍鱼的年龄

在data目录下有一份来自 UCI 数据集合的数据,记录了鲍鱼的年龄。鲍鱼年龄可以从鲍鱼壳的层数推算得到。

预测代码:

abX, abY = loadDataSet('abalone.txt')
yHat01 = lwlrTest(abX[0:99], abX[0:99], abY[0:99], 0.1)
yHat1 = lwlrTest(abX[0:99], abX[0:99], abY[0:99], 1)
yHat10 = lwlrTest(abX[0:99], abX[0:99], abY[0:99], 10)
print(rssError(abY[0:99], yHat01.T)) # 56.7886874305
print(rssError(abY[0:99], yHat1.T)) # 429.89056187
print(rssError(abY[0:99], yHat10.T)) # 549.118170883

可以看到,使用较小的核将得到较低的误差,那么,为什么不在所有数据集上都使用最小的核呢?这是因为 使用最小的核将造成过拟合,对新数据不一定能达到最好的预测效果。下面就来看看它们在新数据上的表现:

yHat01 = lwlrTest(abX[100:199], abX[0:99], abY[0:99], 0.1)
yHat1 = lwlrTest(abX[100:199], abX[0:99], abY[0:99], 1)
yHat10 = lwlrTest(abX[100:199], abX[0:99], abY[0:99], 10)
print(rssError(abY[100:199], yHat01.T)) # 57913.5155016
print(rssError(abY[100:199], yHat1.T)) # 573.52614419
print(rssError(abY[100:199], yHat10.T)) # 517.571190538

从上述结果可以看到,核大小等于 10 时的测试误差最小,但它在训练集上的误差却最大的。接下来和简单的线性回归做个比较:

ws = standRegres(abX[0:99], abY[0:99])
yHat = np.mat(abX[100:199]) * ws
print(rssError(abY[100:199], yHat.T.A)) # 518.636315325

简单线性回归达到了与局部加权线性回归类似的结果,这也表明一点,必须在未知数据上比较效果才能选取到最佳模型。

4. 缩减系数来“理解”数据

如果数据的特征比样本点还多应该怎么办?是否还可以使用线性回归和之前的额方法来做预测?答案是否定的,即不能再使用前面介绍的方法。这是因为在计算

(X^TX)^{-1}

的时候会出错。

如果特征比样本点还多 (n>m) 也就是说输入数据的矩阵 X 不是满秩矩阵。非满秩矩阵在求逆时会出问题。

这节介绍的三种方法就是来解决这个问题。

4.1 岭回归

岭回归(ridge regression)就是在矩阵 $$X^TX$$ 上加一个 $$\lambda I$$ 从而使得矩阵非奇异,进而能对 $$X^T X+\lambda I$$ 求逆。其中矩阵 I 是一个 m*m 的单位矩阵。而 lambda 是一个用户定义数值,后面会做介绍。在这种情况下, 回归系数的计算公式将变成:

\hat{w}=(X^T X+\lambda I)^{-1}X^T y

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

添加 ridgeRegres() 函数:

def ridgeRegres(xMat, yMat, lam=0.2):
    """
    计算回归系数
    :param xMat: 
    :param yMat: 
    :param lam: 
    :return: 
    """
    xTx = xMat.T * xMat
    denom = xTx + np.eye(np.shape(xMat)[1]) * lam # 
    if np.linalg.det(denom) == 0.0:
        print("this matrix is singular,cannot do inverse")
        return
    ws = denom.I * (xMat.T * yMat)
    # ws = np.linalg.solve(xTx + np.eye(np.shape(xMat)[1]) * lam, xMat.T * yMat) # 用伪逆矩阵求解
    return ws

添加 ridgeTest() 函数:

def ridgeTest(xArr, yArr):
    """
    在一组lam上测试结果
    :param xArr:
    :param yArr:
    :return:
    """
    xMat = np.mat(xArr)
    yMat = np.mat(yArr).T
    yMean = np.mean(yMat, 0) # 求均值
    yMat = yMat - yMean
    xMeans = np.mean(xMat, 0) # 求均值
    xVar = np.var() # 求方差
    xMat = (xMat - xMeans)/xVar # 数据标准化:所有特征都减去各自的均值并除以方差
    numTestPts = 30
    wMat = np.zeros((numTestPts, np.shape(xMat)[1]))
    for i in range(numTestPts):
        ws = ridgeRegres(xMat, yMat, np.exp(i-10))
        wMat[i,:] = ws.T
    return wMat

下面看一下鲍鱼数据集上的运行结果:

abX, abY = loadDataSet('abalone.txt')
ridgeWeights = ridgeTest(abX, abY)
fig = plt.figure()
ax = fig.add_subplot(111)
ax.plot(ridgeWeights)
plt.show()

这样就得到了 30 个不同 lambda 所对应回归系数。图8-6是所见的额效果图:

图8-6

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

4.2 lasso

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

\sum^n_{k=1}w^2_k \leq \lambda

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

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

\sum^n_{k=1}|w_k| \leq \lambda

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

4.3 前向逐步回归

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

该算法的伪代码如下:

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

添加 regularize() 函数:

def regularize(xMat):
    """
    把特征按照均值为0,方差为1进行标准化
    :param xMat:
    :return:
    """
    inMat = xMat.copy()
    inMeans = np.mean(inMat, 0)
    inVar = np.var(inMat, 0)
    inMat = (inMat - inMeans)/inVar
    return inMat

添加 stageWise() 函数:

def stageWise(xArr, yArr, eps=0.01, numIt=100):
    """
    逐步线性回归算法
    :param xArr:
    :param yArr:
    :param eps: 步长
    :param numIt: 迭代次数
    :return:
    """
    xMat =np.mat(xArr)
    yMat = np.mat(yArr).T
    yMean = np.mean(yMat, 0)
    yMat = yMat - yMean
    xMat = regularize(xMat)
    m,n = np.shape(xMat)
    returnMat = np.zeros((numIt,n))
    ws = np.zeros((n,1))
    wsTest = ws.copy() # 为了实现贪心算法建立了ws的两个副本
    wsMax = ws.copy()
    for i in range(numIt): # 迭代
        print('ws.T:',ws.T)
        lowestError = float('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

测试一下实际效果:

xArr, yArr = loadDataSet('abalone.txt')
# print(stageWise(xArr,yArr,0.01,200))
print(stageWise(xArr,yArr,0.001,5000))

与最小二乘法进行比较,发现在5000次迭代后,逐步线性回归算法与常规的最小二乘法效果类似。使用 0.005 的 epsilon 值并经过 1000 次迭代后的结果参见图8-7:

图8-7

逐步线性回归算法的实际好处并不在于能绘出图8-7这样漂亮的图,主要的优点在于它可以帮助人们理解现有的模型并作出改进。当构建一个模型后,可以运行该算法找出重要的特征,这样就有可能及时停止对那些不重要特征的收集。最后,如果用于测试,该算法每 100 次迭代后就可以构建出一个模型,可以使用类似于 10 折交叉验证比较这些模型,最终选择使误差最小的模型。

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

5. 权衡偏差与方差

图8-8给出了训练误差和测试误差的曲线图,上面的曲线图就是测试误差,下面的曲线是训练误差。根据8.3节的实验我们知道:如果降低核的大小,那么训练误差将变小。从图8-8来看,从左到右就表示了核逐渐减小的过程。

图8-8

一般认为,上述两种误差由三个部分组成:偏差、测量误差和随机噪声。在8.2节和8.3节,我们通过引入了三个越来越小的核来不断增大模型方差。

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

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

一种乐高套装基本上在几年后就会停产,但乐高的收藏着之间仍会在停产后彼此交易,为了给乐高套装估价,下面将用本章的回归技术来建立一个预测模型。

示例:用回归法预测乐高套装的价格:

  1. 收集数据:用 google shopping 的 api 收集数据。
  2. 准备数据:从返回的 json 数据中抽取价格。
  3. 分析数据:可视化并观察数据。
  4. 训练算法:构建不同的模型,采用逐步线性回归和直接的线性回归模型。
  5. 测试算法:使用交叉验证来测试不同的模型,分析哪个效果更好。
  6. 使用算法:这次练习的目的就是生成数据模型。

6.1 收集数据:使用 google 购物的 api

详细 api 介绍可参见:http://code.google.com/apis/shopping/search/v1/gettting_started.html

添加 searchForSet() 函数:

from time import sleep
import json
import urllib

def searchForSet(retX, retY, setNum, yr, numPce, origPrc):
    """
    调用google 购物 api 并保证数据抽取的正确性
    :param retX:
    :param retY:
    :param setNum:
    :param yr:
    :param numPce:
    :param origPrc:
    :return:
    """
    sleep(10) # 防止短时间内有过多的 api 调用
    myAPIstr = 'get from code.google.com'
    searchURL = 'http://www.googleapis.com/shopping/search/v1/public/products?key=%s&country=US&q=lego+%d&alt=json' % (myAPIstr,setNum)
    pg = urllib.request.urlopen(searchURL)
    retDict = json.loads(pg.read())
    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\t%d\t%d\t%f\t%f" % (yr, numPce, newFlag, origPrc, sellingPrice))
                    retX.append([yr, numPce, newFlag, origPrc])
                    retY.append(sellingPrice)
        except:
            print('problem with item %d' % i)

测试(无法访问google api):

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)

lgX = []
lgY = []
setDataCollect(lgX, lgY)

6.2 训练算法:建立模型

上一节从网上收集到了一些真实的数据,下面将为这些数据构建一个模型。构建的模型可以对售价作出预测,并帮助我们理解现有数据。

使用常规的线性回归:

# print(np.shape(lgX)) # (58, 4)
lgX1 = np.mat(np.ones((58,5))) # 创建一个全 1 的矩阵
lgX1[:,1:5] = np.mat(lgX) # 将原数据矩阵 lgX 复制到新数据矩阵 lgX1 的第 1 到第 5 列
ws = standRegres(lgX1, lgY) # 回归算法
print(lgX1[0]*ws)
print(lgX1[-1]*ws)
print(lgX1[43]*ws)

使用岭回归确定最佳回归系数:

def crossValidation(xArr, yArr, numVal=10):
    """
    交叉验证测试岭回归
    :param xArr:
    :param yArr:
    :param numVal: 交叉验证的次数
    :return:
    """
    m = len(yArr)
    indexList = range(m)
    errorMat = np.zeros((numVal, 30))
    for i in range(numVal): # 数据分为训练集和测试集
        trainX = []
        trainY = []
        testX = []
        testY = []
        np.random.shuffle(indexList) # 对数据混洗
        for j in range(m):
            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) # wMat 保存岭回归中的所有回归系数
    for k in range(30):
        # 用训练时的参数将测试数据标准化
        matTestX = np.mat(testX)
        matTrainX = np.mat(trainX)
        meanTrain = np.mean(matTrainX, 0)
        varTrain = np.var(matTrainX, 0)
        matTestX = (matTestX - meanTrain)/varTrain
        yEst = matTestX * np.mat(wMat[k,:]).T + np.mean(trainY)
        errorMat[i,k] = rssError(yEst.T.A, np.array(testY))
    meanErrors = np.mean(errorMat, 0)
    minMean = float(min(meanErrors))
    bestWeights = wMat[np.nonzero(meanErrors==minMean)]
    xMat = np.mat(xArr);
    yMat = np.mat(yArr).T
    meanX = np.mean(xMat, 0)
    varX = np.var(xMat, 0)
    unReg = bestWeights/varX
    print("the best model from Ridge regression is :\n", unReg) # 标准化后的数据还原
    print("with constant term:",-1 * sum(np.multiply(meanX, unReg)) + np.mean(yMat))

运行:

crossValidation(lgX, lgY, 10)

我们本期望找到一个更易于理解的模型,显然没有达到预期结果。为了达到这一点,我们来看一下在缩减过程中回归系数是如何变化的,输入一下的命令:

print(ridgeTest(lgX, lgY)) 

这些系数是经过不同程度的缩减得到的。

这种分析方法似的我们可以挖掘大量数据的内在规律。在特征比较多的时候,可以指出哪些特征时关键的,而哪些特征时不重要的。

7. 本章小结

在回归方程中,求得特征对应的最佳回归系数的方法是最小化误差的平方差。给定输入矩阵 X,如果 $X^TX$ 的逆存在并可以求得的话,回归法对可以直接使用。数据集上计算出的回归方程并不一定意味着它是最佳,可以使用预测值 yHat 和原始值 y 的相关性来度量回归方程的好坏。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,519评论 5 468
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,842评论 2 376
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,544评论 0 330
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,742评论 1 271
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,646评论 5 359
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,027评论 1 275
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,513评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,169评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,324评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,268评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,299评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,996评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,591评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,667评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,911评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,288评论 2 345
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,871评论 2 341