首页 » 机器学习实战 » 机器学习实战全文在线阅读

《机器学习实战》8.1 用线性回归找到最佳拟合直线

关灯直达底部

线性回归

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

回归的目的是预测数值型的目标值。最直接的办法是依据输入写出一个目标值的计算公式。假如你想要预测姐姐男友汽车的功率大小,可能会这么计算:

 HorsePower = 0.0015*annualSalary - 0.99*hoursListeningToPublicRadio  

这就是所谓的回归方程(regression equation),其中的0.0015和-0.99称作回归系数(regression weights),求这些回归系数的过程就是回归。一旦有了这些回归系数,再给定输入,做预测就非常容易了。具体的做法是用回归系数乘以输入值,再将结果全部加在一起,就得到了预测值1。

1. 此处的回归系数是一个向量,输入也是向量,这些运算也就是求出二者的内积。——译者注

说到回归,一般都是指线性回归(linear regression),所以本章里的回归和线性回归代表同一个意思。线性回归意味着可以将输入项分别乘以一些常量,再将结果加起来得到输出。需要说明的是,存在另一种称为非线性回归的回归模型,该模型不认同上面的做法,比如认为输出可能是输入的乘积。这样,上面的功率计算公式也可以写做:

HorsePower = 0.0015*annualSalary/hoursListeningToPublicRadio     

这就是一个非线性回归的例子,但本章对此不做深入讨论。

回归的一般方法

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

“回归”一词的来历

今天我们所知道的回归是由达尔文(Charles Darwin)的表兄弟Francis Galton发明的。Galton于1877年完成了第一次回归预测,目的是根据上一代豌豆种子(双亲)的尺寸来预测下一代豌豆种子(孩子)的尺寸。Galton在大量对象上应用了回归分析,甚至包括人的身高。他注意到,如果双亲的高度比平均高度高,他们的子女也倾向于比平均高度高,但尚不及双亲。孩子的高度向着平均高度回退(回归)。Galton在多项研究上都注意到这个现象,所以尽管这个英文单词跟数值预测没有任何关系,但这种研究方法仍被称作回归2。

2. Ian Ayres, Super Crunchers (Bantam Books, 2008), 24.

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

平方误差可以写做:

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

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

值得注意的是,上述公式中包含(XTX)-1,也就是需要对矩阵求逆,因此这个方程只在逆矩阵存在的时候适用。然而,矩阵的逆可能并不存在,因此必须要在代码中对此作出判断。

上述的最佳w求解是统计学中的常见问题,除了矩阵方法外还有很多其他方法可以解决。通过调用NumPy库里的矩阵方法,我们可以仅使用几行代码就完成所需功能。该方法也称作OLS,意思是“普通最小二乘法”(ordinary least squares)。

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

图8-1 从ex0.txt得到的样例数据

程序清单8-1可以完成上述功能。打开文本编辑器并创建一个新的文件regression.py,添加其中的代码。

程序清单8-1 标准回归函数和数据导入函数

from numpy import *def loadDataSet(fileName):    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,labelMatdef standRegres(xArr,yArr):    xMat = mat(xArr); yMat = mat(yArr).T    xTx = xMat.T*xMat    if linalg.det(xTx) == 0.0:        print /"This matrix is singular, cannot do inverse/"        return    ws = xTx.I * (xMat.T*yMat)    return ws  

第一个函数loadDataSet与第7章的同名函数是一样的。该函数打开一个用tab键分隔的文本文件,这里仍然默认文件每行的最后一个值是目标值。第二个函数standRegres用来计算最佳拟合直线。该函数首先读入xy并将它们保存到矩阵中;然后计算XTX,然后判断它的行列式是否为零,如果行列式为零,那么计算逆矩阵的时候将出现错误。NumPy提供一个线性代数的库linalg,其中包含很多有用的函数。可以直接调用linalg.det来计算行列式。最后,如果行列式非零,计算并返回w。如果没有检查行列式是否为零就试图计算矩阵的逆,将会出现错误。NumPy的线性代数库还提供一个函数来解未知矩阵,如果使用该函数,那么代码ws=xTx.I * (xMat.T*yMat)应写成ws=linalg.solve(xTx, xMat.T*yMatT)

下面看看实际效果,使用loadDataSet将从数据中得到两个数组,分别存放在XY中。与分类算法中的类别标签类似,这里的Y是目标值。

>>> import regression>>> from numpy import *>>> xArr,yArr=regression.loadDataSet(/'ex0.txt/')     

首先看前两条数据:

>>> xArr[0:2][[1.0, 0.067732000000000001], [1.0, 0.42781000000000002]]    

第一个值总是等于1.0,即X0。我们假定偏移量就是一个常数。第二个值X1,也就是我们图中的横坐标值。

现在看一下standRegres函数的执行效果:

>>> ws = regression.standRegres(xArr,yArr)>>> wsmatrix([[ 3.00774324],        [ 1.69532264]])      

变量ws存放的就是回归系数。在用内积来预测y的时候,第一维将乘以前面的常数X0,第二维将乘以输入变量X1。因为前面假定了X0=1,所以最终会得到y=ws[0]+ws[1]*X1。这里的y实际是预测出的,为了和真实的y值区分开来,我们将它记为yHat。下面使用新的ws值计算yHat

>>> xMat=mat(xArr)>>> yMat=mat(yArr)>>> yHat = xMat*ws   

现在就可以绘出数据集散点图和最佳拟合直线图:

>>> import matplotlib.pyplot as plt>>> fig = plt.figure>>> ax = fig.add_subplot(111)>>> ax.scatter(xMat[:,1].flatten.A[0], yMat.T[:,0].flatten.A[0])<matplotlib.collections.CircleCollection object at 0x04ED9D30>  

上述命令创建了图像并绘出了原始的数据。为了绘制计算出的最佳拟合直线,需要绘出yHat的值。如果直线上的数据点次序混乱,绘图时将会出现问题,所以首先要将点按照升序排列:

>>> xCopy=xMat.copy>>> xCopy.sort(0)>>> yHat=xCopy*ws>>> ax.plot(xCopy[:,1],yHat)[<matplotlib.lines.Line2D object at 0x0343F570>]>>> plt.show    

我们将会看到类似于图8-2的效果图。

图8-2 ex0.txt的数据集与它的最佳拟合直线

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

图8-3 具有相同回归系数(0和2.0)的两组数据。上图的相关系数是0.58,而下图的相关系数是0.99

在Python中,NumPy库提供了相关系数的计算方法:可以通过命令corrcoef(yEstimate, yActual)来计算预测值和真实值的相关性。下面我们就在前面的数据集上做个实验。

与之前一样,首先计算出y的预测值yMat

>>> yHat = xMat*ws    

再来计算相关系数(这时需要将yMat转置,以保证两个向量都是行向量):

>>> corrcoef(yHat.T, yMat)array([[ 1. , 0.98647356],       [ 0.98647356, 1. ]])     

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

最佳拟合直线方法将数据视为直线进行建模,具有十分不错的表现。但是图8-2的数据当中似乎还存在其他的潜在模式。那么如何才能利用这些模式呢?我们可以根据数据来局部调整预测,下面就会介绍这种方法。