8.2 局部加权线性回归
线性回归的一个问题是有可能出现欠拟合现象,因为它求的是具有最小均方误差的无偏估计。显而易见,如果模型欠拟合将不能取得最好的预测效果。所以有些方法允许在估计中引入一些偏差,从而降低预测的均方误差。
其中的一个方法是局部加权线性回归(Locally Weighted Linear Regression,为LWLR)。在该算法中,我们给待预测点附近的每个点赋予一定的权重;然后与8.1节类似,在这个子集上基于最小均方差来进行普通的回归。与kNN一样,这种算法每次预测均需要事先选取出对应的数据子集。该算法解出回归系数w的形式如下:
其中w是一个矩阵,用来给每个数据点赋予权重。
LWLR使用“核”(与支持向量机中的核类似)来对附近的点赋予更高的权重1 。核的类型可以自由选择,最常用的核就是高斯核,高斯核对应的权重如下:
1. 读者要注意区分这里的权重W和回归系数w;与kNN一样,该加权模型认为样本点距离越近,越可能符合同一个线性模型。——译者注.
这样就构建了一个只含对角元素的权重矩阵w,并且点x与x(i)越近,w(i,i)将会越大。上述公式包含一个需要用户指定的参数k,它决定了对附近的点赋予多大的权重,这也是使用LWLR时唯一需要考虑的参数,在图8-4中可以看到参数k与权重的关系。
下面看看模型的效果,打开文本编辑器,将程序清单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 * ws
def 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)
如果需要重新载入数据集,则输入:
>>> 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])
[]
>>> ax.scatter(xMat[:,1].flatten().A[0], mat(yArr).T.flatten().A[0] , s=2,c='red')
>>> plt.show()
可以观察到如图8-5所示的效果。图8-5给出了k在三种不同取值下的结果图。当k = 1.0时权重很大,如同将所有的数据视为等权重,得出的最佳拟合直线与标准的回归一致。使用k = 0.01得到了非常好的效果,抓住了数据的潜在模式。下图使用k = 0.003纳入了太多的噪声点,拟合的直线与数据点过于贴近。
所以,图8-5中的最下图是过拟合的一个例子,而最上图则是欠拟合的一个例子。下一节将对过拟合和欠拟合进行量化分析。
以挖出数据的潜在规律,而k = 0.003时则考虑了太多的噪音,进而导致了过拟合现象局部加权线性回归也存在一个问题,即增加了计算量,因为它对每个点做预测时都必须使用整个数据集。
从图8-5可以看出,*k *= 0.01时可以得到很好的估计,但是同时看一下图8-4中k = -0.01的情况,就会发现大多数据点的权重都接近零。如果避免这些计算将可以减少程序运行时间,从而缓解因计算量增加带来的问题。
到此为止,我们已经介绍了找出最佳拟合直线的两种方法,下面用这些技术来预测鲍鱼的年龄。
本书评论