ML04-局部加权线性回归

本主题讲述线性回归的欠拟合问题:

  1. 怎么评估拟合效果

一. 回归中的问题

  回归一词来自一种任务背景,测试孩子与父母身高的关系,如果父母身高比较高,孩子身高也会比较高,但没有父母的平均身高高,更加趋向于人类平均身高,这就是身高回归平均身高的一种现象,称为回归。

1. 拟合性问题

线性回归中,怎么判定训练的模型的好坏呢?因为数据集本身的影响,一般会产生如下两种情况:
  |-(1)欠拟合
  |-(2)过拟合
  
我们使用下面两个数据集来建立直观判定,哪一种数据集会产生更好的拟合?
  |- 拟合好坏的判别标准。

1.1. 数据集1
% matplotlib inline
import numpy as np
import matplotlib.pyplot as plt

x_dat=np.loadtxt('ex2x.dat')
y_dat=np.loadtxt('ex2y.dat')

figure=plt.figure('数据集可视化',figsize=(6,4))
ax=figure.add_axes([0.1,0.1,0.8,0.8],xlabel='年龄',ylabel='身高')
ax.set_xlim(0,10)
ax.set_ylim(0,1.5)
ax.scatter(x_dat,y_dat,label='年龄身高数据集',marker='.',color=(0,0,1,1))
plt.show()
数据集1-年龄与身高

1.2. 数据集2

% matplotlib inline
import numpy as np
import matplotlib.pyplot as plt

# 第一列用来做偏置项1的,真正的数据是第2列与第3列
data=np.loadtxt('ex0.txt')

figure=plt.figure('数据集可视化',figsize=(6,4))
ax=figure.add_axes([0.1,0.1,0.8,0.8],xlabel='年龄',ylabel='身高')
ax.set_xlim(-0.2,1.2)
ax.set_ylim(1,5.5)
ax.scatter(data[:,1],data[:,2],label='不知名数据集',marker='.',color=(1,0,0,1))
plt.show()
数据集2-采用正态分布手工构造的数据集
1.3. 相关系数

在数学上提供了一个专门的概念来描述这种数据集的拟合性:相关系数。
  |-相关系数:用来度量两个向量相关程度的量。
  
相关系数的公式定义:
  |-
  其中表示的样本协方差(无偏):
  其中表示向量的样本方差(无偏方差):
    |-其中表示样本均值:

1.4. 练习作业
  1. 实现协方差,方差与均值;
  2. 调用numpy中协方差,方差与均值函数;
  3. 对比自己实现的计算结果与numpy的计算结果;
  4. 理解总体方差(有偏)与样本方差(无偏)的区别
1.5. 数据集相关性评估

下面对上面两个数据集,计算他们的相关系数。
  |- 计算预测输出值与真实值之间的相关系数,从而可以判定数据集的拟合性好坏。

  1. 数据集1的相关系数计算(sklearn实现)
% matplotlib inline
import matplotlib.pyplot as plt
from sklearn.linear_model import *
import numpy as np

X_DATA = np.loadtxt('ex2x.dat')
Y_DATA = np.loadtxt('ex2y.dat')
regression=LinearRegression()
X_DATA=X_DATA.reshape(-1, 1)
Y_DATA=Y_DATA.reshape(-1, 1)
regression.fit(X_DATA, Y_DATA)

#  预测输出
Y=regression.predict(X_DATA)
#print(Y)
print(Y.shape)
print(Y_DATA.shape)
# 判定Y_DATA与Y的相关程度

# 必须确保是行向量
cor=np.corrcoef(Y.T,Y_DATA.T)
print(cor)

# 可视化
figure=plt.figure('数据集可视化',figsize=(6,4))
ax=figure.add_axes([0.1,0.1,0.8,0.8],xlabel='年龄',ylabel='身高')
ax.set_xlim(0,10)
ax.set_ylim(0,1.5)
ax.scatter(X_DATA,Y_DATA,label='年龄身高数据集',marker='.',color=(0,0,1,1))

ax.plot(X_DATA,Y,label='线性拟合',color='r',linewidth=3)
ax.legend()

plt.show()
(50, 1)
(50, 1)
[[1.         0.92631702]
 [0.92631702 1.        ]]
数据集1-的线性回归效果,相关系数:0.92631702

  上面的相关系数的说明:
    |-在对角线上是自己与自己的关联系数;
      |-对角线上第一个1是与自己的相关系数;
      |-对角线第二个1是与自己的相关系数;
    |-反对角线上的两个数是与、与的相关性系数。
  
  自己与自己的相关系数是最好的,就是1。完全不相关的的就是0,等于协方差完全无关。

  1. 数据集2的相关系数计算(sklearn实现)
% matplotlib inline
import matplotlib.pyplot as plt
from sklearn.linear_model import *
import numpy as np

data=np.loadtxt('ex0.txt')
X_DATA=data[:,1]
Y_DATA=data[:,2]
regression=LinearRegression()
X_DATA=X_DATA.reshape(-1, 1)
Y_DATA=Y_DATA.reshape(-1, 1)
regression.fit(X_DATA, Y_DATA)

#  预测输出
Y=regression.predict(X_DATA)
#print(Y)
print(Y.shape)
print(Y_DATA.shape)
# 判定Y_DATA与Y的相关程度

# 必须确保是行向量
cor=np.corrcoef(Y.T,Y_DATA.T)
print(cor)

# 可视化
figure=plt.figure('数据集可视化',figsize=(6,4))
ax=figure.add_axes([0.1,0.1,0.8,0.8],xlabel='年龄',ylabel='身高')
ax.set_xlim(-0.2,1.2)
ax.set_ylim(1,5.5)
ax.scatter(data[:,1],data[:,2],label='不知名数据集',marker='.',color=(1,0,0,1))
ax.plot(X_DATA,Y,label='线性拟合',color='b',linewidth=3)

ax.legend()
plt.show()
(200, 1)
(200, 1)
[[1.         0.98647356]
 [0.98647356 1.        ]]
数据集2的线性回归效果,相关系数:0.986473561

  从上面两个数据集的直观视觉与相关系数都看得出来,数据集2明显具有较好的拟合效果。
  更好的拟合性表现在数据集样本点尽可能在回归(拟合)直线上。

  1. 问题
      能否有好的方式提高数据的拟合性?这就可以解决样本欠拟合的问题。

二、局部加权回归算法

  局部加权回归(Locally Weighted Linear Regression:LWLR)
  局部加权回归是解决线性回归欠拟合问题二提出的。
  解决问题的出发点是:线性回归的最终核心是使均方差函数最小(最小均方误差),局部加权是引入加权的方式,降低均方误差,使得拟合性的度量结果更好。
  因为线性拟合中的均方差是无偏的,适当引入一些偏差,可以有效降低均方差。

1. 局部加权模型

  
  其中就是局部加权系数。
  
  其中加权系数的确定是一个麻烦的事情,该系数必须确保,预测样本点离已知样本越远加权系数越小,预测样本点离已知样本越近加权系数越大。如果大家还记得正态分布(高斯分布)函数的性质,如果在0点取值1,在接近无穷远点,取值0。我们就利用正态分布这个特性,可以取,其其他位置的元素都为0,对角线上的值为:
  
  矩阵形式是:
  
  下面使用图示的方式,来理解该系数在回归训练中控制训练样本参与训练的作用与价值。

% matplotlib inline
import numpy as np
import matplotlib.pyplot as plt


# 图形数据
sigma=1
x_0=0
x=np.linspace(-1,1,101,dtype=np.float32)
y_1=np.exp((x_0-x)**2/(-2.0*sigma*sigma))
sigma=0.5
y_05=np.exp((x_0-x)**2/(-2.0*sigma*sigma))
sigma=0.1
y_001=np.exp((x_0-x)**2/(-2.0*sigma*sigma))

# 坐标轴
figure=plt.figure('机器学习-回归优化',figsize=(6,4))
ax=figure.add_axes([0.1,0.1,0.8,0.8],label='高斯分布系数权重关系')
ax.set_xlim(-1,1)
ax.set_ylim(0,1)
# 图形绘制
ax.plot(x,y_1,color=(1,0,0,1),label='$\sigma=1$')
ax.plot(x,y_05,color=(0,1,0,1),label='$\sigma=0.5$')
ax.plot(x,y_001,color=(0,0,1,1),label='$\sigma=0.01$')
ax.legend()
plt.show()

正态分布的值变化可以决定样本参与线性回归的权重大小

2. 局部加权实现

  因为是总体拟合性考虑,所以每个测试样本对总体训练样本都有一个加权矩阵,该测试样本根据加权矩阵计算出对应的预测值。这样预测值总体的拟合性就会更好点。

  1. 结构设计
import matplotlib.pyplot as plt
import numpy as np

# 求一个样本的局部权重预测值(下面函数按照多维模式,但只考虑1维情况)
class  LWLinearRegression:
    def __init__(self, sample_x, label_y, sigma=1.0):
        self.X=sample_x
        self.Y=label_y
    
    def train_one_sample(self, data):
        # data 测试样本
        # 数据加权预测值
        # 数据格式化
        # 1. 计算局部加权矩阵
        # 2. 计算线性回归系数矩阵
        # 3.计算预测值
        return [0.0]
        
    def train(self, datasets):
        Y=np.zeros(shape=(datasets.shape[0],1), dtype=np.float)
        # 循环计算预测值
        for idx in range(datasets.shape[0]):
            Y[idx]=self.train_one_sample(datasets[idx])
        return Y

# 数据初始化
data=np.loadtxt('ex0.txt')
X_DATA=data[:,1]
Y_DATA=data[:,2]
# 数据格式化
X=np.zeros(shape=(X_DATA.shape[0], 2), dtype=np.float) # 考虑偏置项,在测试特征加一维
Y=Y_DATA.reshape(Y_DATA.shape[0], 1)
X[:, 0]=X_DATA
X[:, 1]=1

lwlr=LWLinearRegression(X, Y)
predict=lwlr.train(X)
#print(predict)
  1. 实现
import matplotlib.pyplot as plt
import numpy as np

# 求一个样本的局部权重预测值(下面函数按照多维模式,但只考虑1维情况)
class  LWLinearRegression:
    def __init__(self, sample_x, label_y, sigma=1.0):
        self.X=sample_x
        self.Y=label_y
        self.sigma=sigma
    
    def train_one_sample(self, data):
        # data 测试样本
        # 数据加权预测值
        # 数据格式化
        # 1. 计算局部加权矩阵
        local_weight=np.zeros(shape=(self.X.shape[0],self.X.shape[0]),dtype=np.float32)
        for idx in range(local_weight.shape[0]):
            # 求差
            diff=data-self.X[idx,:]
            # 求平方,然后除以sigma
            sqrt=np.matmul(diff,diff.T)/(-2.0*(self.sigma**2))
            # 计算局部加权矩阵的对角线
            local_weight[idx,idx]=np.exp(sqrt)
        # 2. 计算线性回归系数矩阵
        # 这里应该先做一个奇异值判定(我们暂时不去判定,等系统处理发生的异常)
        W=np.matmul(
                np.matmul(
                    np.matmul(
                        np.linalg.inv(
                            np.matmul(
                                np.matmul(
                                    self.X.T,
                                    local_weight
                                ),
                                self.X
                            )
                        ),
                        self.X.T
                    ),
                    local_weight
                ),
                self.Y
            )
        # 3.计算预测值
        out_value=np.matmul(data,W)
        return out_value
        
    def train(self, datasets):
        Y=np.zeros(shape=(datasets.shape[0],1), dtype=np.float)
        # 循环计算预测值
        for idx in range(datasets.shape[0]):
            Y[idx]=self.train_one_sample(datasets[idx])
        return Y

# 数据初始化
data=np.loadtxt('ex0.txt')
X_DATA=data[:,1]
Y_DATA=data[:,2]
# 数据格式化
X=np.zeros(shape=(X_DATA.shape[0], 2), dtype=np.float) # 考虑偏置项,在测试特征加一维
Y=Y_DATA.reshape(Y_DATA.shape[0], 1)
X[:, 0]=X_DATA
X[:, 1]=1

lwlr=LWLinearRegression(X, Y)
# 使用训练集作为测试集
predict=lwlr.train(X)
#print(predict) 
  1. 拟合可视化
      通过可视化来观察拟合效果;为了保证代码的完整性,我们把计算于可视化代码放在一起。
import matplotlib.pyplot as plt
import numpy as np

# 求一个样本的局部权重预测值(下面函数按照多维模式,但只考虑1维情况)
class  LWLinearRegression:
    def __init__(self, sample_x, label_y, sigma=1.0):
        self.X=sample_x
        self.Y=label_y
        self.sigma=sigma
    
    def train_one_sample(self, data):
        # data 测试样本
        # 数据加权预测值
        # 数据格式化
        # 1. 计算局部加权矩阵
        local_weight=np.zeros(shape=(self.X.shape[0],self.X.shape[0]),dtype=np.float32)
        for idx in range(local_weight.shape[0]):
            # 求差
            diff=data-self.X[idx,:]
            # 求平方,然后除以sigma
            sqrt=np.matmul(diff,diff.T)/(-2.0*(self.sigma**2))
            # 计算局部加权矩阵的对角线
            local_weight[idx,idx]=np.exp(sqrt)
        # 2. 计算线性回归系数矩阵
        # 这里应该先做一个奇异值判定(我们暂时不去判定,等系统处理发生的异常)
        W=np.matmul(
                np.matmul(
                    np.matmul(
                        np.linalg.inv(
                            np.matmul(
                                np.matmul(
                                    self.X.T,
                                    local_weight
                                ),
                                self.X
                            )
                        ),
                        self.X.T
                    ),
                    local_weight
                ),
                self.Y
            )
        # 3.计算预测值
        out_value=np.matmul(data,W)
        return out_value
        
    def train(self, datasets):
        Y=np.zeros(shape=(datasets.shape[0],1), dtype=np.float)
        # 循环计算预测值
        for idx in range(datasets.shape[0]):
            Y[idx]=self.train_one_sample(datasets[idx])
        return Y

# 数据初始化
data=np.loadtxt('ex0.txt')
X_DATA=data[:,1]
Y_DATA=data[:,2]
# 数据格式化
X=np.zeros(shape=(X_DATA.shape[0], 2), dtype=np.float) # 考虑偏置项,在测试特征加一维
Y=Y_DATA.reshape(Y_DATA.shape[0], 1)
X[:, 0]=X_DATA
X[:, 1]=1

sigma=0.003
lwlr=LWLinearRegression(X, Y,sigma)
# 使用训练集作为测试集
predict=lwlr.train(X)
#print(predict) 


# 可视化
# 可视化
figure=plt.figure('数据集可视化',figsize=(6,4))
ax=figure.add_axes([0.1,0.1,0.8,0.8],xlabel='年龄',ylabel='身高')
ax.set_xlim(-0.2,1.2)
ax.set_ylim(1,5.5)
ax.scatter(X_DATA,Y_DATA,label='不知名数据集',marker='.',color=(1,0,0,1))

# 排序显示
# 得到排序索引
sort_idx=X_DATA.argsort(0)
# 得到排序的结果
X_SORT=X_DATA[sort_idx]
Y_SORT=predict[sort_idx]
ax.plot(X_SORT,Y_SORT,label='线性拟合',color='b',linewidth=3)

ax.legend()
plt.show()
局部加权线性回归的可视化效果,相关系数:0.99931945
  1. 拟合度量计算
      通过相关系数,来判定拟合的效果;
import matplotlib.pyplot as plt
import numpy as np

# 求一个样本的局部权重预测值(下面函数按照多维模式,但只考虑1维情况)
class  LWLinearRegression:
    def __init__(self, sample_x, label_y, sigma=1.0):
        self.X=sample_x
        self.Y=label_y
        self.sigma=sigma
    
    def train_one_sample(self, data):
        # data 测试样本
        # 数据加权预测值
        # 数据格式化
        # 1. 计算局部加权矩阵
        local_weight=np.zeros(shape=(self.X.shape[0],self.X.shape[0]),dtype=np.float32)
        for idx in range(local_weight.shape[0]):
            # 求差
            diff=data-self.X[idx,:]
            # 求平方,然后除以sigma
            sqrt=np.matmul(diff,diff.T)/(-2.0*(self.sigma**2))
            # 计算局部加权矩阵的对角线
            local_weight[idx,idx]=np.exp(sqrt)
        # 2. 计算线性回归系数矩阵
        # 这里应该先做一个奇异值判定(我们暂时不去判定,等系统处理发生的异常)
        W=np.matmul(
                np.matmul(
                    np.matmul(
                        np.linalg.inv(
                            np.matmul(
                                np.matmul(
                                    self.X.T,
                                    local_weight
                                ),
                                self.X
                            )
                        ),
                        self.X.T
                    ),
                    local_weight
                ),
                self.Y
            )
        # 3.计算预测值
        out_value=np.matmul(data,W)
        return out_value
        
    def train(self, datasets):
        Y=np.zeros(shape=(datasets.shape[0],1), dtype=np.float)
        # 循环计算预测值
        for idx in range(datasets.shape[0]):
            Y[idx]=self.train_one_sample(datasets[idx])
        return Y

# 数据初始化
data=np.loadtxt('ex0.txt')
X_DATA=data[:,1]
Y_DATA=data[:,2]
# 数据格式化
X=np.zeros(shape=(X_DATA.shape[0], 2), dtype=np.float) # 考虑偏置项,在测试特征加一维
Y=Y_DATA.reshape(Y_DATA.shape[0], 1)
X[:, 0]=X_DATA
X[:, 1]=1

sigma=0.003
lwlr=LWLinearRegression(X, Y,sigma)
# 使用训练集作为测试集
predict=lwlr.train(X)
#print(predict) 

cor=np.corrcoef(predict.T,Y.T)
print(cor)
[[1.         0.99931945]
 [0.99931945 1.        ]]

  从相关系数来看,拟合的效果应该是非常的好了!
  但是该代码中,还是可能存在矩阵奇异问题。当取其中sigma参数为很小的时候,由于参与训练的样本减少,矩阵出现奇异的可能性非常大。


  奇异值问题的解决留待下一个主题讲解。
  本主题中故意采用了ndarray的向量形式。如果使用标准的矩阵运算,则在格式化上面会简单很多,因为矩阵(np.matrix)支持*这样的内积运算符号。就不用使用matmul这样的函数运算。

你可能感兴趣的:(ML04-局部加权线性回归)