本主题讲述线性回归的欠拟合问题:
- 怎么评估拟合效果
一. 回归中的问题
回归一词来自一种任务背景,测试孩子与父母身高的关系,如果父母身高比较高,孩子身高也会比较高,但没有父母的平均身高高,更加趋向于人类平均身高,这就是身高回归平均身高的一种现象,称为回归。
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.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()
1.3. 相关系数
在数学上提供了一个专门的概念来描述这种数据集的拟合性:相关系数。
|-相关系数:用来度量两个向量相关程度的量。
相关系数的公式定义:
|-
其中表示的样本协方差(无偏):
其中表示向量的样本方差(无偏方差):
|-其中表示样本均值:
1.4. 练习作业
- 实现协方差,方差与均值;
- 调用numpy中协方差,方差与均值函数;
- 对比自己实现的计算结果与numpy的计算结果;
- 理解总体方差(有偏)与样本方差(无偏)的区别
1.5. 数据集相关性评估
下面对上面两个数据集,计算他们的相关系数。
|- 计算预测输出值与真实值之间的相关系数,从而可以判定数据集的拟合性好坏。
- 数据集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是与自己的相关系数;
|-对角线第二个1是与自己的相关系数;
|-反对角线上的两个数是与、与的相关性系数。
自己与自己的相关系数是最好的,就是1。完全不相关的的就是0,等于协方差完全无关。
- 数据集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明显具有较好的拟合效果。
更好的拟合性表现在数据集样本点尽可能在回归(拟合)直线上。
- 问题
能否有好的方式提高数据的拟合性?这就可以解决样本欠拟合的问题。
二、局部加权回归算法
局部加权回归(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. 局部加权实现
因为是总体拟合性考虑,所以每个测试样本对总体训练样本都有一个加权矩阵,该测试样本根据加权矩阵计算出对应的预测值。这样预测值总体的拟合性就会更好点。
- 结构设计
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)
- 实现
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)
- 拟合可视化
通过可视化来观察拟合效果;为了保证代码的完整性,我们把计算于可视化代码放在一起。
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()
- 拟合度量计算
通过相关系数,来判定拟合的效果;
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这样的函数运算。