参考:

  1. Python3《机器学习实战》学习笔记(十一):线性回归基础篇之预测鲍鱼年龄 - Jack-Cui
  2. Python3《机器学习实战》学习笔记(十二):线性回归提高篇之乐高玩具套件二手价预测 - Jack-Cui

一、回归问题概述

回归的目的是预测数值型的目标值。
最直接的办法是依据输入写出一个目标值的计算公式。这些公式被称作回归方程(regression equation),而其中的参数称作回归系数(regression weights),求这些回归系数的过程就是回归。一旦有了这些回归系数,再给定输入,做预测就非常容易了。
具体的做法是用回归系数乘以输入值,再将结果全部加在一起,就得到了预测值。

说到回归,一般都是指线性回归(linear regression),线性回归意味着可以将输入项分别乘以一些常量,再将结果加起来得到输出。

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

二、线性回归及相关原理

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

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

平方误差的矩阵形式可以写做:
(y−Xw)T(y−Xw)(y-Xw)^{T}(y-Xw)(y−Xw)T(y−Xw)

如果对w求导,得到XT(Y−Xw)X^{T}(Y-Xw)XT(Y−Xw),令其等于零,解出
w如下:
ω^=(XTX)−1XTY\hat{\omega}=(X^{T}X)^{-1}X^{T}Yω^=(XTX)−1XTY

w上方的小标记表示,这是当前可以估计出的w的最优解。从现有数据上估计出的w可能并不是数据中的真实w值,所以这里使用了一个“帽”符号来表示它仅是w的一个最佳估计

上述的最佳w求解是统计学中的常见问题,除了矩阵方法外还有很多其他方法可以解决。该方法也称作OLS,意思是“普通最小二乘法”(ordinary least squares)。

2.局部加权线性回归

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

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

该算法解出回归系数ω\omegaω的形式如下:
ω^=(XTWX)−1XTWY\hat{\omega}=(X^{T}WX)^{-1}X^{T}WYω^=(XTWX)−1XTWY

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

LWLR使用“核”(与支持向量机中的核类似)来对附近的点赋予更高的权重。核的类型可以自由选择,最常用的核就是高斯核,高斯核对应的权重如下:
W(i,i)=exp(∣xi−x∣−2k2)W(i,i)=exp(\frac{|x^{i}-x|}{-2k^2})W(i,i)=exp(−2k2∣xi−x∣​)

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

3.岭回归

如果数据的特征比样本点还多应该怎么办?
是否还可以使用线性回归和之前的方法来做预测?答案是否定的,即不能再使用前面介绍的方法。这是因为在计算(XTX)−1(X^{T}X)^{-1}(XTX)−1的时候会出错。

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

为了解决这个问题,统计学家引入了岭回归(ridge regression)的概念,这就是第一种缩减方法。另外还有lasso法,该方法效果很好但计算复杂。第二种缩减方法称为前向逐步回归,可以得到与lasso差不多的效果,且更容易实现。

简单说来,岭回归就是在矩阵XTXX^{T}XXTX上加一个λI从而使得矩阵非奇异,进而能对XTX+λIX^{T}X+ λIXTX+λI求逆。
其中矩阵I是一个m×m的单位矩阵,对角线上元素全为1,其他元素全为0。而λ是一个用户定义的数值。
在这种情况下,回归系数的计算公式将变成:
ω^=(XTX+λI)−1XTY\hat{\omega}=(X^{T}X+ λI)^{-1}X^{T}Yω^=(XTX+λI)−1XTY
岭回归最先用来处理特征数多于样本数的情况,现在也用于在估计中加入偏差,从而得到更好的估计。这里通过引入λ来限制了所有w之和,通过引入该惩罚项,能够减少不重要的参数,这个技术在统计学中也叫做缩减(shrinkage)。.

缩减方法可以去掉不重要的参数,因此能更好地理解数据。此外,与简单的线性回归相比,缩减法能取得更好的预测效果。
与前几章里训练其他参数所用的方法类似,这里通过预测误差最小化得到λ:数据获取之后,首先抽一部分数据用于测试,剩余的作为训练集用于训练参数w。训练完毕后在测试集上测试预测性能。通过选取不同的λ来重复上述测试过程,最终得到一个使预测误差最小的λ

4.lasso

在增加如下约束时,普通的最小二乘法回归会得到与岭回归的一样的公式:
∑k=1nwk2≤λ\sum ^{n}_{k=1}w_{k}^{2}\leq \lambda k=1∑n​wk2​≤λ
上式限定了所有回归系数的平方和不能大于λ
使用普通的最小二乘法回归在当两个或更多的特征相关时,可能会得出一个很大的正系数和一个很大的负系数。正是因为上述限制条件的存在,使用岭回归可以避免这个问题。

与岭回归类似,另一个缩减方法lasso也对回归系数做了限定,对应的约束条件如下:
∑k=1n∣wk∣≤λ\sum ^{n}_{k=1}|w_{k}|\leq \lambda k=1∑n​∣wk​∣≤λ
这个约束条件使用绝对值取代了平方和。虽然约束形式只是稍作变化,结果却大相径庭:在λ足够小的时候,一些系数会因此被迫缩减到0,这个特性可以帮助我们更好地理解数据。

这个细微的变化却极大地增加了计算复杂度(为了在这个新的约束条件下解出回归系数,需要使用二次规划算法)。所以我们将使用更为简单的方法来得到结果,该方法叫做前向逐步回归。

5.前向逐步回归

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

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

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

6.权衡偏差与方差

任何时候,一旦发现模型和测量值之间存在差异,就说出现了误差。当考虑模型中的“噪声”或者说误差时,必须考虑其来源。你可能会对复杂的过程进行简化,这将导致在模型和测量值之
间出现“噪声”或误差,若无法理解数据的真实生成过程,也会导致差异的发生。另外,测量过程本身也可能产生“噪声”或者问题。

上图给出了训练误差和测试误差的曲线图,上面的曲线就是测试误差,下面的曲线是训练误差。我们知道:如果降低核的大小,那么训练误差将变小。从上图来看,从左到右就表示了核逐渐减小的过程。 上图的左侧是参数缩减过于严厉的结果,而右侧是无缩减的效果。

缩减法可以将一些系数缩减成很小的值或直接缩减为0,增大了模型偏差。通过把一些特征的回归系数缩减到0,同时也就减少了模型的复杂度。当数据集中有多个特征时,消除其中部分后不仅使模型更易理解,同时还降低了预测误差。

三、代码实现

1.普通最小二乘法

import numpy as np
import matplotlib.pyplot as pltdef loadDataSet(fileName):"""加载数据集- - - -fileName - 文件路径"""numFeat = len(open(fileName).readline().split('\t')) - 1xArr = []; yArr = []fr = open(fileName)for line in fr.readlines():lineArr =[]curLine = line.strip().split('\t')for i in range(numFeat):lineArr.append(float(curLine[i]))xArr.append(lineArr)yArr.append(float(curLine[-1]))return xArr, yArrdef standRegres(xArr,yArr):"""计算回归系数w- - - -xArr - x数据集yArr - y数据集"""xMat = np.mat(xArr); yMat = np.mat(yArr).TxTx = xMat.T * xMatif np.linalg.det(xTx) == 0.0:print("矩阵为奇异矩阵,不能求逆")returnws = xTx.I * (xMat.T*yMat)return wsdef plotRegression():"""绘制回归曲线和数据点"""#加载数据集并计算回归系数xArr, yArr = loadDataSet('7-Regression/data.txt')ws = standRegres(xArr, yArr) xMat = np.mat(xArr)yMat = np.mat(yArr)#深拷贝xMat矩阵并排序xCopy = xMat.copy()xCopy.sort(0)yHat = xCopy * wsfig = plt.figure()ax = fig.add_subplot(111)ax.plot(xCopy[:, 1], yHat, c = 'red')ax.scatter(xMat[:,1].flatten().A[0], yMat.flatten().A[0], s = 20, c = 'blue',alpha = .5)plt.title('DataSet')plt.xlabel('X')plt.show()if __name__ == '__main__':plotRegression()

结果:

2.局部加权线性回归

from matplotlib.font_manager import FontPropertiesdef lwlr(testPoint, xArr, yArr, k = 1.0):"""函数说明:使用局部加权线性回归计算回归系数w- - - -testPoint - 测试样本点xArr - x数据集yArr - y数据集k - 高斯核的k,自定义参数"""xMat = np.mat(xArr); yMat = np.mat(yArr).Tm = 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))xTx = xMat.T * (weights * xMat)                                        if np.linalg.det(xTx) == 0.0:print("矩阵为奇异矩阵,不能求逆")return#计算回归系数ws = xTx.I * (xMat.T * (weights * yMat))                            return testPoint * wsdef lwlrTest(testArr, xArr, yArr, k=1.0):  """函数说明:局部加权线性回归测试- - - -testArr - 测试数据集xArr - x数据集yArr - y数据集k - 高斯核的k,自定义参数"""#计算测试数据集大小m = np.shape(testArr)[0]                                            yHat = np.zeros(m)    #对每个样本点进行预测for i in range(m):                                                    yHat[i] = lwlr(testArr[i],xArr,yArr,k)return yHatdef plotlwlrRegression():"""绘制多条局部加权回归曲线"""font = FontProperties(fname=r'/Users/lql70/Downloads/simsun.ttc', size=16) xArr, yArr = loadDataSet('7-Regression/data.txt')    #根据局部加权线性回归计算yHat,分别取k=1,0.01,0.003yHat_1 = lwlrTest(xArr, xArr, yArr, 1.0) yHat_2 = lwlrTest(xArr, xArr, yArr, 0.01)yHat_3 = lwlrTest(xArr, xArr, yArr, 0.003)xMat = np.mat(xArr) yMat = np.mat(yArr) #排序,返回索引值srtInd = xMat[:, 1].argsort(0) xSort = xMat[srtInd][:,0,:]fig, axs = plt.subplots(nrows=3, ncols=1,sharex=False, sharey=False, figsize=(10,8)) #绘制回归曲线                                       axs[0].plot(xSort[:, 1], yHat_1[srtInd], c = 'red')axs[1].plot(xSort[:, 1], yHat_2[srtInd], c = 'red')axs[2].plot(xSort[:, 1], yHat_3[srtInd], c = 'red')#绘制样本点axs[0].scatter(xMat[:,1].flatten().A[0], yMat.flatten().A[0], s = 20, c = 'blue', alpha = .5)axs[1].scatter(xMat[:,1].flatten().A[0], yMat.flatten().A[0], s = 20, c = 'blue', alpha = .5)axs[2].scatter(xMat[:,1].flatten().A[0], yMat.flatten().A[0], s = 20, c = 'blue', alpha = .5)#设置标题,x轴label,y轴labelaxs0_title_text = axs[0].set_title(u'局部加权回归曲线,k=1.0',FontProperties=font)axs1_title_text = axs[1].set_title(u'局部加权回归曲线,k=0.01',FontProperties=font)axs2_title_text = axs[2].set_title(u'局部加权回归曲线,k=0.003',FontProperties=font)plt.setp(axs0_title_text, size=8, weight='bold', color='red')  plt.setp(axs1_title_text, size=8, weight='bold', color='red')  plt.setp(axs2_title_text, size=8, weight='bold', color='red')  plt.xlabel('X')plt.show()if __name__ == '__main__':#1.线性回归#plotRegression()#2.局部加权回归plotlwlrRegression()

结果:

可以看出:
当k = 1.0时权重很大,如同将所有的数据视为等权重,得出的最佳拟合直线与标准的回归一致,出现了欠拟合
使用k = 0.01时得到了非常好的效果,抓住了数据的潜在模式。
使用k = 0.003时纳入了太多的噪声点,拟合的直线与数据点过于贴近,出现了过拟合

3.岭回归

为了使用岭回归和缩减技术,首先需要对特征做标准化处理。因为,我们需要使每个维度特征具有相同的重要性。
这里使用的标准化处理比较简单,就是将所有特征都减去各自的均值并除以方差。

def ridgeRegres(xMat, yMat, lam = 0.2):"""岭回归- - - -xMat - x数据集yMat - y数据集lam - 缩减系数"""xTx = xMat.T * xMatdenom = xTx + np.eye(np.shape(xMat)[1]) * lamif np.linalg.det(denom) == 0.0:print("矩阵为奇异矩阵,不能转置")returnws = denom.I * (xMat.T * yMat)return wsdef ridgeTest(xArr, yArr):"""岭回归测试xMat - x数据集yMat - y数据集"""xMat = np.mat(xArr); yMat = np.mat(yArr).T#数据标准化#行与行操作,求均值yMean = np.mean(yMat, axis = 0)#数据减去均值yMat = yMat - yMean                                    xMeans = np.mean(xMat, axis = 0) #行与行操作,求方差xVar = np.var(xMat, axis = 0) #数据减去均值除以方差实现标准化                       xMat = (xMat - xMeans) / xVar  #30个不同的lambda测试                      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.Treturn wMatdef plotwMat():"""绘制岭回归系数矩阵"""font = FontProperties(fname=r'/Users/lql70/Downloads/simsun.ttc', size=16) abX, abY = loadDataSet('7-Regression/abalone.txt')redgeWeights = ridgeTest(abX, abY)fig = plt.figure()ax = fig.add_subplot(111)ax.plot(redgeWeights)    ax_title_text = ax.set_title(u'log(lambda)与回归系数的关系', FontProperties = font)ax_xlabel_text = ax.set_xlabel(u'log(lambda)', FontProperties = font)ax_ylabel_text = ax.set_ylabel(u'回归系数', FontProperties = font)plt.setp(ax_title_text, size = 20, weight = 'bold', color = 'red')plt.setp(ax_xlabel_text, size = 10, weight = 'bold', color = 'black')plt.setp(ax_ylabel_text, size = 10, weight = 'bold', color = 'black')plt.show()if __name__ == '__main__':#1.线性回归#plotRegression()#2.局部加权回归#plotlwlrRegression()#3.岭回归plotwMat()

结果:

可以看出,在最左边,即λ最小时,可以得到所有系数的原始值(与线性回归一致);而在右边,系数全部缩减成0;在中间部分的某值将可以取得最好的预测效果。为了定量地找到最佳参数值,还需要进行交叉验证。

4.前向逐步回归

def regularize(xMat, yMat):"""数据标准化- - - -xMat - x数据集yMat - y数据集"""    inxMat = xMat.copy()inyMat = yMat.copy()yMean = np.mean(yMat, 0)inyMat = yMat - yMeaninMeans = np.mean(inxMat, 0)inVar = np.var(inxMat, 0) inxMat = (inxMat - inMeans) / inVarreturn inxMat, inyMatdef rssError(yArr,yHatArr):"""计算平方误差- - - -yArr - 预测值yHatArr - 真实值"""return ((yArr-yHatArr)**2).sum()def stageWise(xArr, yArr, eps = 0.01, numIt = 100):"""前向逐步线性回归- - - -xArr - x输入数据yArr - y预测数据eps - 每次迭代需要调整的步长numIt - 迭代次数"""#数据初始化、标准化xMat = np.mat(xArr); yMat = np.mat(yArr).TxMat, yMat = regularize(xMat, yMat)m, n = np.shape(xMat)returnMat = np.zeros((numIt, n))                                                ws = np.zeros((n, 1))                                                            wsTest = ws.copy()wsMax = ws.copy()#迭代numIt次for i in range(numIt):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 = rssEwsMax = wsTestws = wsMax.copy()#记录numIt次迭代的回归系数矩阵returnMat[i,:] = ws.T return returnMatdef plotstageWiseMat():"""绘制岭回归系数矩阵"""font = FontProperties(fname=r'/Users/lql70/Downloads/simsun.ttc', size=16) xArr, yArr = loadDataSet('7-Regression/abalone.txt')returnMat = stageWise(xArr, yArr, 0.005, 1000)fig = plt.figure()ax = fig.add_subplot(111)ax.plot(returnMat)    ax_title_text = ax.set_title(u'前向逐步回归:迭代次数与回归系数的关系', FontProperties = font)ax_xlabel_text = ax.set_xlabel(u'迭代次数', FontProperties = font)ax_ylabel_text = ax.set_ylabel(u'回归系数', FontProperties = font)plt.setp(ax_title_text, size = 15, weight = 'bold', color = 'red')plt.setp(ax_xlabel_text, size = 10, weight = 'bold', color = 'black')plt.setp(ax_ylabel_text, size = 10, weight = 'bold', color = 'black')plt.show()if __name__ == '__main__':#1.线性回归#plotRegression()#2.局部加权回归#plotlwlrRegression()#3.岭回归#plotwMat()#4.前向逐步回归plotstageWiseMat()

结果:

上图是鲍鱼数据集上执行逐步线性回归法得到的系数与迭代次数间的关系。逐步线性回归得到了与lasso相似的结果,但计算起来更加简便。

四、实战:预测乐高玩具套装的价格

  • 步骤:用回归法预测乐高套装的价格
    (1) 收集数据:用Google Shopping的API收集数据。
    (现在这个所需API已经关闭,只能通过读取指定html文件获取相关商品数据)
    (2) 准备数据:从返回的JSON数据中抽取价格。
    (3) 分析数据:可视化并观察数据。
    (4) 训练算法:构建不同的模型,采用逐步线性回归和直接的线性回归模型。
    (5) 测试算法:使用交叉验证来测试不同的模型,分析哪个效果最好。
    (6) 使用算法:这次练习的目标就是生成数据模型。

1.载入模型

from bs4 import BeautifulSoupdef scrapePage(retX, retY, inFile, yr, numPce, origPrc):"""从页面读取数据,生成retX和retY列表retX - 数据XretY - 数据YinFile - HTML文件yr - 年份numPce - 乐高部件数目origPrc - 原价"""# 打开并读取HTML文件with open(inFile, encoding='utf-8') as f:html = f.read()soup = BeautifulSoup(html)i = 1# 根据HTML页面结构进行解析currentRow = soup.find_all('table', r = "%d" % i)while(len(currentRow) != 0):currentRow = soup.find_all('table', r = "%d" % i)title = currentRow[0].find_all('a')[1].textlwrTitle = title.lower()# 查找是否有全新标签if (lwrTitle.find('new') > -1) or (lwrTitle.find('nisb') > -1):newFlag = 1.0else:newFlag = 0.0# 查找是否已经标志出售,我们只收集已出售的数据soldUnicde = currentRow[0].find_all('td')[3].find_all('span')if len(soldUnicde) == 0:print("商品 #%d 没有出售" % i)else:# 解析页面获取当前价格soldPrice = currentRow[0].find_all('td')[4]priceStr = soldPrice.textpriceStr = priceStr.replace('$','')priceStr = priceStr.replace(',','')if len(soldPrice) > 1:priceStr = priceStr.replace('Free shipping', '')sellingPrice = float(priceStr)# 去掉不完整的套装价格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)i += 1currentRow = soup.find_all('table', r = "%d" % i)def setDataCollect(retX, retY):"""依次读取六种乐高套装的数据,并生成数据矩阵"""#2006年的乐高8288,部件数目800,原价49.99;其他同理可推scrapePage(retX, retY, '7-Regression/setHtml/lego8288.html', 2006, 800, 49.99)scrapePage(retX, retY, '7-Regression/setHtml/lego10030.html', 2002, 3096, 269.99)scrapePage(retX, retY, '7-Regression/setHtml/lego10179.html', 2007, 5195, 499.99)scrapePage(retX, retY, '7-Regression/setHtml/lego10181.html', 2007, 3428, 199.99)scrapePage(retX, retY, '7-Regression/setHtml/lego10189.html', 2008, 5922, 299.99)scrapePage(retX, retY, '7-Regression/setHtml/lego10196.html', 2009, 3263, 249.99)if __name__ == '__main__':#1.线性回归#plotRegression()#2.局部加权回归#plotlwlrRegression()#3.岭回归#plotwMat()#4.前向逐步回归#plotstageWiseMat()#5.实战:预测乐高玩具套装的价格lgX = []lgY = []setDataCollect(lgX, lgY)

结果:

2006    800     0       49.990000       85.000000
2006    800     0       49.990000       102.500000
2006    800     0       49.990000       77.000000
商品 #4 没有出售
...
2009    3263    1       249.990000      499.990000
商品 #10 没有出售
2009    3263    0       249.990000      399.950000
商品 #12 没有出售
2009    3263    1       249.990000      331.510000

2.简单的线性回归

def useStandRegres():"""使用简单的线性回归"""lgX = []lgY = []setDataCollect(lgX, lgY)#在第0列加入1,作为最后常数项data_num, features_num = np.shape(lgX)lgX1 = np.mat(np.ones((data_num, features_num + 1)))lgX1[:, 1:5] = np.mat(lgX)ws = standRegres(lgX1, lgY)print('%f%+f*年份%+f*部件数量%+f*是否为全新%+f*原价' % (ws[0],ws[1],ws[2],ws[3],ws[4]))     

结果:

55319.970081-27.592822*年份-0.026839*部件数量-11.220848*是否为全新+2.576041*原价

这个模型的预测效果非常好,但模型本身并不能令人满意。它对于数据拟合得很好,但看上去没有什么道理。从公式看,套装里零部件越多售价反而会越低。另外,该公式对新套装也有一定的惩罚。

3.岭回归

def crossValidation(xArr, yArr, numVal = 10):"""交叉验证岭回归- - - -xArr - x数据集yArr - y数据集numVal - 交叉验证次数"""#统计样本个数并生成索引值列表m = len(yArr)indexList = list(range(m))errorMat = np.zeros((numVal,30))#交叉验证numVal次for i in range(numVal):trainX = []; trainY = []testX = []; testY = []#打乱次序random.shuffle(indexList)#划分数据集:90%训练集,10%测试集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) #获得30个不同lambda下的岭回归系数                                               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).TmeanX = np.mean(xMat,0); varX = np.var(xMat,0)unReg = bestWeights / varX  print('%f%+f*年份%+f*部件数量%+f*是否为全新%+f*原价' % ((-1 * np.sum(np.multiply(meanX,unReg)) + np.mean(yMat)), unReg[0,0], unReg[0,1], unReg[0,2], unReg[0,3]))  if __name__ == '__main__':#1.线性回归#plotRegression()#2.局部加权回归#plotlwlrRegression()#3.岭回归#plotwMat()#4.前向逐步回归#plotstageWiseMat()#5.实战:预测乐高玩具套装的价格#useStandRegres()lgX = []lgY = []setDataCollect(lgX, lgY)crossValidation(lgX, lgY)

结果:

76623.641543-38.176231*年份-0.003174*部件数量+35.161731*是否为全新+2.001705*原价

可以看到,该结果与最小二乘法没有太大差异。我们本期望找到一个更易于理解的模型,显然没有达到预期效果。

4.观察缩减过程中回归系数的变化

if __name__ == '__main__':#1.线性回归#plotRegression()#2.局部加权回归#plotlwlrRegression()#3.岭回归#plotwMat()#4.前向逐步回归#plotstageWiseMat()#5.实战:预测乐高玩具套装的价格#useStandRegres()lgX = []lgY = []setDataCollect(lgX, lgY)print(ridgeTest(lgX, lgY))

结果:

[[-1.45288906e+02 -8.39360442e+03 -3.28682450e+00  4.42362406e+04][-1.46649725e+02 -1.89952152e+03 -2.80638599e+00  4.27891633e+04][-1.44450432e+02  8.55488076e+02 -1.35089285e+00  4.00885735e+04]...[-3.62836944e-05  3.70302314e-07  1.77873811e-04  6.01500768e-06][-1.33480028e-05  1.36226645e-07  6.54365445e-05  2.21279795e-06][-4.91045279e-06  5.01149871e-08  2.40728171e-05  8.14042912e-07]]

这些系数是经过不同程度的缩减得到的。
首先看第1行,第4项比第2项的系数大5倍,比第1项大57倍。
这样看来,如果只能选择一个特征来做预测的话,我们应该选择第4个特征,也就是原始价格。
如果可以选择2个特征的话,应该选择第4个和第2个特征。

这种分析方法使得我们可以挖掘大量数据的内在规律。在仅有4个特征时,该方法的效果也许并不明显;但如果有100个以上的特征,该方法就会变得十分有效:它可以指出哪些特征是关键的,而哪些特征是不重要的

五、小结

  1. 与分类一样,回归也是预测目标值的过程。回归与分类的不同点在于,前者预测连续型变量,而后者预测离散型变量。

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

  3. 当数据的样本数比特征数还少时候,矩阵XTXX^{T}XXTX的逆不能直接计算。即便当样本数比特征数多时,XTXX^{T}XXTX的逆仍有可能无法直接计算,这是因为特征有可能高度相关。这时可以考虑使用岭回归,因为当XTXX^{T}XXTX的逆不能计算时,它仍保证能求得回归参数。
    岭回归是缩减法的一种,相当于对回归系数的大小施加了限制。

  4. 另一种很好的缩减法是lasso。
    Lasso难以求解,但可以使用计算简便的逐步线性回归方法来求得近似结果。缩减法还可以看做是对一个模型增加偏差的同时减少方差。

  5. 偏差方差折中是一个重要的概念,可以帮助我们理解现有模型并做出改进,从而得到更好的模型。

“机器学习实战”刻意练习——回归问题:线性回归(最小二乘、岭回归、逐步回归)相关推荐

  1. python 最小二乘回归 高斯核_「机器学习」一文读懂线性回归、岭回归和Lasso回归...

    点击上方蓝色字体,关注AI小白入门哟 作者 | 文杰 编辑 | yuquanle 本文介绍线性回归模型,从梯度下降和最小二乘的角度来求解线性回归问题,以概率的方式解释了线性回归为什么采用平方损失,然后 ...

  2. 【机器学习】一文读懂线性回归、岭回归和Lasso回归

    来源 | AI小白入门 作者 | 文杰 编辑 | yuquanle 完整代码见:原文链接 1. 线性回归 1.1 线性回归 ​ 假设有数据有T={(x(1),y(1)),...,(x(i),y(i)) ...

  3. [机器学习-原理篇]学习之线性回归、岭回归、Lasso回归

    线性回归.岭回归.Lasso回归 前言 一,线性回归--最小二乘 二,Lasso回归 三,岭回归 四, Lasso回归和岭回归的同和异 五, 为什么 lasso 更容易使部分权重变为 0 而 ridg ...

  4. 机器学习实战刻意练习 —— Task 02. 朴素贝叶斯

    机器学习实战刻意练习 第 1 周任务   分类问题:K-邻近算法   分类问题:决策树 第 2 周任务   分类问题:朴素贝叶斯   分类问题:逻辑回归 第 3 周任务   分类问题:支持向量机 第 ...

  5. [机器学习-实践篇]学习之线性回归、岭回归、Lasso回归,tensorflow实现的线性回归

    线性回归.岭回归.Lasso回归 前言 1.线性回归 2. 岭回归 3. Lasso回归 4. tensorflow利用梯度下降实现的线性回归 前言 本章主要介绍线性回归.岭回归.Lasso回归,te ...

  6. 机器学习总结(一):线性回归、岭回归、Lasso回归

    机器学习总结(一):线性回归.岭回归.Lasso回归 参考网址:https://blog.csdn.net/hzw19920329/article/details/77200475 主要是岭回归(脊回 ...

  7. 基于回归分析的广告投入销售额预测——K邻近,决策树,随机森林,线性回归,岭回归

    基于回归分析的广告投入销售额预测--K邻近,决策树,随机森林,线性回归,岭回归 文章目录 基于回归分析的广告投入销售额预测--K邻近,决策树,随机森林,线性回归,岭回归 1. 项目背景 2. 项目简介 ...

  8. Spark MLlib回归算法------线性回归、逻辑回归、SVM和ALS

    Spark MLlib回归算法------线性回归.逻辑回归.SVM和ALS 1.线性回归: (1)模型的建立: 回归正则化方法(Lasso,Ridge和ElasticNet)在高维和数据集变量之间多 ...

  9. 使用线性回归,岭回归,Lasso回归预测鲍鱼年龄

    实验目的 掌握数据预处理方法 掌握线性回归预测基本原理与实现. 实验问题背景 鲍鱼的年龄可以通过鲍鱼壳的"环数"来判断,但是获取这个"环数"是十分耗时的,需要锯 ...

最新文章

  1. 微软职位内部推荐-Software Development Engineer II
  2. C语言static 具体分析
  3. OSS控制台集成将数据库实时备份到OSS的功能
  4. SpringCloud(第 057 篇)CentOS7 安装 maven 编译工具
  5. ExtJS中layout的12种布局风格
  6. php json与接口的使用,api接口与json
  7. SCI论文写作训练营笔记汇总02_英文科技论文阅读与解析
  8. HugeGraph 图数据库索引介绍 - 范围索引,全文索引
  9. 【报告分享】疫情期间抖音、快手带货趋势分析报告.pdf(附下载链接)
  10. HTML5开发从入门到精通学习路线图
  11. think-cell学习
  12. 操作系统 | 实验五 页面置换算法
  13. 18年拼多多学霸批算法笔试
  14. TV端影视APP开发搭建需要注意哪些问题?
  15. APP界面设计教程---手机ui高级实战案例(完整版)
  16. 基于远程服务器的共享文件实现
  17. num_workers
  18. Python实现办公自动化
  19. DMIPS, TOPS, FLOPS, FLOPs, GMACs, FMA
  20. 西门子S7-1500PLC大型程序,各种FB块PTO控制20多个轴

热门文章

  1. airmon-ng破解wifi
  2. python 数组 动态赋值_动态数组在Python中的实现
  3. 重启好多次路由器,还是上不了网怎么办?
  4. 最新xshell免费版 地址持续更新
  5. 如何删除Ubuntu的GRUB启动项
  6. 海量监控视频应该如何存储?
  7. 如何判断点P是否在三角形ABC内?
  8. 【15】将一个正整数分解质因数 例如:输入90,打印出90=2*3*3*5
  9. CodeCraft-21 and Codeforces Round #711 (Div. 2)ABC题解
  10. 什么是实用拜占庭容错机制?