局部加权线性回归(内含代码)

在之前的博客中我们已经简单讨论过一些回归的算法,如使用假设和梯度下降法的单变量线性回归和多变量线性回归以及采用正规方程的线性回归,这次我们简单讨论一下局部加权线性回归(Local Weighted Liner Regression)。
局部加权回归可以看做正规方程的一种改进,通过上次博客中的代码,我们针对那个数据集会产生一个下图所示的拟合曲线:
局部加权线性回归(内含代码)_第1张图片
从上面可以看出,该曲线拟合的效果不是很好,存在着欠拟合的现象,但是对于正规方程来说这是最好的拟合曲线。所以我们想寻求一种方式对上述方法进行改进,从而降低估计时的均方误差。
其中一种方法就是局部加权回归。该算法不像正规方程一样,对于所有的预测点,训练样本集上对它的作用是相同的,这种算法采用核的方法对于不同测试样本赋予训练样本不同权值,该方法求解出回归系数θ的形式如下:

θ=(XTWX)1XTWy

其中,W是一个矩阵,代表对于每个数据点赋予的不同权重。
W确定的方式一般采用核的方法实现,其中最常用的核是高斯核,高斯核的确定方式为:

W(i,i)=exp(|x(i)x|2k2)

上式中的k值决定了对于附近的点应该赋予多大的权值。高斯核的物理含义为:对于靠近测试点的样本点,赋予更大的权值距离测试点越远的样本点,权值越小。下图显示了k值与权重的关系(假设预测点为0.5):
局部加权线性回归(内含代码)_第2张图片
当k=0.5时,大部分的数据都用于训练回归模型;而对于k=0.01时,仅有很少的数据点用于训练回归模型。
从上面分析中,我们可以看出,相对于正规方程,局部加权线性回归的计算复杂度大得多,因为对于每个测试点进行预测时,都需要所有的训练数据集。但是该方法的拟合效果明显可以好于正规方程的拟合效果。

局部加权线性回归的代码如下:

# -*- coding: utf-8 -*-
"""
Created on Fri Nov 17 09:58:51 2017

@author: YL Wang
"""
import numpy as np
import matplotlib.pyplot as plt

def loadDataSet(fileName):

    num_fea=len(open(fileName).readline().split(','))-1 #获得特征的数量
    fr=open(fileName)   #打开文件
    featureArr=[]
    labelArr=[]
    # 读取每一行的数据
    for line in fr.readlines():
        line_fea=[]
        line_data=line.strip().split(',')   #将每一行的数据按照','分隔开(使用什么分隔开看数据本身的构成)
        line_data.insert(0,'1') #将特征进行扩充,将特征矩阵转化为增广矩阵的形式
        for i in range(num_fea+1):
            line_fea.append(float(line_data[i]))    #得到每一行的特征

        featureArr.append(line_fea)
        labelArr.append(float(line_data[-1]))   #得到每一行的标签

    return featureArr,labelArr

# 对于每个测试点求得最佳的theta
def lwlr(testpoint,featureArr,labelArr,k=1.0):
    feature = np.mat(featureArr)
    label = np.mat(labelArr).T
    num_sample = np.size(label)    #确定样本的个数

    weight = np.mat(np.eye(num_sample))   #初始化权重矩阵

    #确定权重
    for i in range(num_sample):
        diffMat = testpoint - feature[i,:]
        weight[i,i] = np.exp((diffMat*diffMat.T)/(-2*k**2))   
    xTwx = feature.T * weight * feature

    if np.linalg.det(xTwx) == 0.0:
        print("This matrix is singular, cannot do inverse")
        return
    theta = xTwx.I * feature.T * weight * label

    return theta

# 使用训练样本来进行测试性能    
def lwlrtest(featureArr,labelArr,k):
    feature = np.mat(featureArr)
    label = np.mat(labelArr)
    num_sample = np.size(feature[:,1])
    predict = np.zeros(num_sample)
    for i in range(num_sample):
        testpoint = feature[i,:]
        theta = lwlr(testpoint,feature,label,k)
        predict[i] = testpoint * theta

    return predict


## 主程序
#得到特征以及对应的标签
featureArr,labelArr=loadDataSet('ex1data1.txt')
# 得到训练集预测的结果
k = 0.4
predict = lwlrtest(featureArr,labelArr,k)

# 绘制图像
feature = np.mat(featureArr)
srtInd = feature[:,1].argsort(0)    # 对特征进行排序,得到排序后特征的标签
feature_sort = feature[srtInd][:,0,:]   #得到排序后的特征
label = np.mat(labelArr)
fig = plt.figure()
ax = fig.add_subplot(111)
ax.plot(feature_sort[:,1],predict[srtInd])  #绘制拟合的曲线
ax.scatter(feature[:,1].flatten().A[0],label.T.flatten().A[0],s=2,c='red')

下面显示了对于不同的k值,产生的拟合效果:
当k=1时的拟合效果
局部加权线性回归(内含代码)_第3张图片
当k=0.5时的拟合效果
局部加权线性回归(内含代码)_第4张图片
当k=0.2时的拟合效果
局部加权线性回归(内含代码)_第5张图片

明显看出k值的选择对于拟合的效果有很大的影响,当k=1时,拟合效果不是很好,仍然存在较大的均方误差;对于k=0.5时,拟合效果会好很多;很明显对于k=0.2时,拟合效果有点过拟合。

本人菜鸟一枚,有理解不对的地方欢迎指正。

你可能感兴趣的:(机器学习)