机器学习(回归五)——线性回归-局部加权线性回归

前面博客有讲到,样本如果不是线性的可以通过多项式扩展,映射到多维空间来拟合。如此之外,还可以做一个局部加权线性回归(Locally Weighted Linear Regression,LWLR)。
直观图:
在这里插入图片描述

图1

在这里插入图片描述

图2

公式

在该算法中,我们给待预测点附近的每个点赋予一定的权重。与kNN一样,这种算法每次预测均需要事先选取出对应的数据子集。

该算法解出回归系数 w 的形式如下:
w ^ = ( X T W X ) 1 X T W y \hat{w}=(X^T W X)^{-1}X^T W y
其中 w 是一个矩阵,用来给每个数据点赋予权重。

LWLR使用“核”(与支持向量机中的核类似)来对附近的点赋予更高的权重 。核的类型可以自由选择,最常用的核就是高斯核,高斯核对应的权重如下:
w ( i , i ) = e x p ( x ( i ) x 2 k 2 ) w(i,i)=exp \left( \frac{|x^{(i)}-x|}{-2k^2} \right)
这样就构建了一个只含对角元素的权重矩阵 w w ,该函数称为指数衰减函数。它根据要预测的点与数据集中的点的距离来为数据集中的点赋权值,并且点 x x x ( i ) x^{(i)} 越近, w ( i , i ) w(i,i) 将会越大,也就是说,当某点离要预测的点越远,其权重越小,否则越大。上述公式包含一个需要用户指定的参数 k(也叫波长参数) ,它决定了对附近的点赋予多大的权重,可以说是控制了权重值随距离下降的速度,这也是使用LWLR时唯一需要考虑的参数,在下图中可以看到参数 k 与权重的关系:
在这里插入图片描述

图3

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

注意:使用该方式主要应用到样本之间的相似性考虑,主要内容在SVM中再考虑(核函数)。

除引之外还有另外一种常用的指数衰减函数:
w ( i , i ) = e x p ( ( x ( i ) x ) 2 2 k 2 ) w(i,i)=exp \left( \frac{ \left(x^{(i)}-x \right)^2 }{-2k^2} \right)
当然这两种是类似,只不过一个是绝对值,一个是平方的形式。

损失函数

局部加权线性回归的损失函数,与普通线性回归的损失函数平方和损失函数

  • 普通线性回归损失函数
    J ( θ ) = i = 1 m ( h θ ( x ( i ) ) y ( i ) ) 2 J(θ)=\sum_{i=1}^m \left( h_θ (x^{(i)})−y^{(i)} \right)^2
  • 局部加权线性回归损失函数:
    J ( θ ) = i = 1 m w ( i ) ( h θ ( x ( i ) ) y ( i ) ) 2 J(θ)=\sum_{i=1}^m w^{(i)} \left( h_θ (x^{(i)})−y^{(i)} \right)^2

:以上两个式子同样可以加上正则项

代码实现

from numpy import *
import matplotlib.pyplot as plt

 
# 加载数据  返回数据和目标值
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, labelMat

 
# 利用公式计算回归系数
def standRegres(xArr, yArr):
    xMat = mat(xArr); yMat = mat(yArr).T
    xTx = xMat.T * xMat  # 公式步骤
    if linalg.det(xTx) == 0.0:
        print("行列式为0,奇异矩阵,不能做逆")
        return
    ws = xTx.I * (xMat.T * yMat)  # 解线性方程组
    # ws = linalg.solve(xTx,xMat.T*yMat)  # 也可以使用函数来计算 线性方程组
    return ws

 
# 局部加权线性回归 返回该条样本预测值

def lwlr(testPoint, xArr, yArr, k=1.0):
    xMat = mat(xArr); yMat = mat(yArr).T
    m = shape(xMat)[0]
    weights = mat(eye((m)))  # 创建为单位矩阵,再mat转换数据格式     因为后面是与原数据矩阵运算,所以这里是为了后面运算且不带来其他影响
    for j in range(m):  # 利用高斯公式创建权重W     遍历所有数据,给它们一个权重
        diffMat = testPoint - xMat[j, :]  # 高斯核公式1
        weights[j, j] = exp(diffMat * diffMat.T / (-2.0 * k ** 2))  # 高斯核公式2    矩阵*矩阵.T 转行向量为一个值    权重值以指数级衰减
    xTx = xMat.T * (weights * xMat)  # 求回归系数公式1
    if linalg.det(xTx) == 0.0:  # 判断是否有逆矩阵
        print("行列式为0,奇异矩阵,不能做逆")
        return
    ws = xTx.I * (xMat.T * (weights * yMat))  # 求回归系数公式2
    return testPoint * ws

 
# 循环所有点求出所有的预测值
def lwlrTest(testArr, xArr, yArr, k=1.0):  # 传入的k值决定了样本的权重,1和原来一样一条直线,0.01拟合程度不错,0.003纳入太多噪声点过拟合了
    m = shape(testArr)[0]
    yHat = zeros(m)
    for i in range(m):
        yHat[i] = lwlr(testArr[i], xArr, yArr, k)  # 返回该条样本的预测目标值
    return yHat


if __name__ == '__main__':
    xArr, yArr = loadDataSet('ex0.txt')
    # 求所有预测值
    yHat = lwlrTest(xArr, xArr, yArr, 0.01)
    print(yHat)
    # 绘制数据点和拟合线(局部加权线性回归)
    xMat = 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], color='red')
    ax.scatter(xMat[:, 1].flatten().A[0], mat(yArr).T.flatten().A[0])
    plt.show()

代码中写的是K=0.01。下图中给出了k在三种不同取值下的结果图。当k = 1.0时权重很大,如同将所有的数据视为等权重,得出的最佳拟合直线与标准的回归一致。使用k = 0.01得到了非常好的效果,抓住了数据的潜在模式。下图使用k = 0.003纳入了太多的噪声点,拟合的直线与数据点过于贴近。所以,图中的最下图是过拟合的一个例子,而最上图则是欠拟合的一个例子。
在这里插入图片描述

图4

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

总结

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

发布了108 篇原创文章 · 获赞 333 · 访问量 39万+

猜你喜欢

转载自blog.csdn.net/zhanglianhai555/article/details/103632813