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

《机器学习实战》8.2 局部加权线性回归

关灯直达底部

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

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

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

LWLR使用“核”(与支持向量机中的核类似)来对附近的点赋予更高的权重1 。核的类型可以自由选择,最常用的核就是高斯核,高斯核对应的权重如下:

1. 读者要注意区分这里的权重W和回归系数w;与kNN一样,该加权模型认为样本点距离越近,越可能符合同一个线性模型。——译者注

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

图8-4 每个点的权重图(假定我们正预测的点是x = 0.5),最上面的图是原始数据集,第二个图显示了当k = 0.5时,大部分的数据都用于训练回归模型;而最下面的图显示当k = 0.01时,仅有很少的局部点被用于训练回归模型

下面看看模型的效果,打开文本编辑器,将程序清单8-2的代码添加到文件regression.py中。

程序清单8-2 局部加权线性回归函数

def lwlr(testPoint,xArr,yArr,k=1.0):    xMat = mat(xArr); yMat = mat(yArr).T    m = shape(xMat)[0]    weights = mat(eye((m)))    #❶  创建对角矩阵     for j in range(m):        #❷ 权重值大小以指数级衰减         diffMat = testPoint - xMat[j,:]        weights[j,j] = exp(diffMat*diffMat.T/(-2.0*k**2))    xTx = xMat.T * (weights * xMat)    if linalg.det(xTx) == 0.0:        print /"This matrix is singular, cannot do inverse/"        return    ws = xTx.I * (xMat.T * (weights * yMat))        return testPoint * wsdef lwlrTest(testArr,xArr,yArr,k=1.0):    m = shape(testArr)[0]    yHat = zeros(m)    for i in range(m):        yHat[i] = lwlr(testArr[i],xArr,yArr,k)    return yHat    

程序清单8-2中代码的作用是,给定x空间中的任意一点,计算出对应的预测值yHat。函数lwlr的开头与程序清单8-1类似,读入数据并创建所需矩阵,之后创建对角权重矩阵weights❶。权重矩阵是一个方阵,阶数等于样本点个数。也就是说,该矩阵为每个样本点初始化了一个权重。接着,算法将遍历数据集,计算每个样本点对应的权重值:随着样本点与待预测点距离的递增,权重将以指数级衰减❷。输入参数k控制衰减的速度。与之前的函数standRegress一样,在权重矩阵计算完毕后,就可以得到对回归系数ws的一个估计。

程序清单8-2中的另一个函数是lwlrTest,用于为数据集中每个点调用lwlr,这有助于求解k的大小。

下面看看实际效果,将程序清单8-2的代码加入到regression.py中并保存,然后在Python提示符下输入如下命令:

>>> reload(regression)<module /'regression/' from /'regression.py/'>  

如果需要重新载入数据集,则输入:

>>> xArr,yArr=regression.loadDataSet(/'ex0.txt/')    

可以对单点进行估计:

>>> yArr[0]3.1765129999999999>>> regression.lwlr(xArr[0],xArr,yArr,1.0)matrix([[ 3.12204471]])>>> regression.lwlr(xArr[0],xArr,yArr,0.001)matrix([[ 3.20175729]])   

为了得到数据集里所有点的估计,可以调用lwlrTest函数:

>>> yHat = regression.lwlrTest(xArr, xArr, yArr,0.003)  

下面绘出这些估计值和原始值,看看yHat的拟合效果。所用的绘图函数需要将数据点按序排列,首先对 xArr排序:

xMat=mat(xArr)>>> srtInd = xMat[:,1].argsort(0)>>> xSort=xMat[srtInd][:,0,:]    

然后用Matplotlib绘图:

>>> fig = plt.figure>>> ax = fig.add_subplot(111)>>> ax.plot(xSort[:,1],yHat[srtInd])[<matplotlib.lines.Line2D object at 0x03639550>]>>> ax.scatter(xMat[:,1].flatten.A[0], mat(yArr).T.flatten.A[0] , s=2,c=/'red/')<matplotlib.collections.PathCollection object at 0x03859110>>>> plt.show  

可以观察到如图8-5所示的效果。图8-5给出了k在三种不同取值下的结果图。当k = 1.0时权重很大,如同将所有的数据视为等权重,得出的最佳拟合直线与标准的回归一致。使用k = 0.01得到了非常好的效果,抓住了数据的潜在模式。下图使用k = 0.003纳入了太多的噪声点,拟合的直线与数据点过于贴近。所以,图8-5中的最下图是过拟合的一个例子,而最上图则是欠拟合的一个例子。下一节将对过拟合和欠拟合进行量化分析。

图8-5 使用3种不同的平滑值绘出的局部加权线性回归结果。上图中的平滑参数k = 1.0,中图k = 0.01,下图k = 0.003。可以看到,k = 1.0时的模型效果与最小二乘法差不多,k = 0.01时该模型可以挖出数据的潜在规律,而k = 0.003时则考虑了太多的噪音,进而导致了过拟合现象

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

到此为止,我们已经介绍了找出最佳拟合直线的两种方法,下面用这些技术来预测鲍鱼的年龄。