1.简单的线性回归
假定输入数据存放在矩阵X中,而回归系数存放在向量W中,则对于给定的数据X1,预测结果将会是
这里的向量都默认为列向量
现在的问题是手里有一些x和对应的y数据,怎样才能找到W呢?一个常用的方法是找到使误差最小的W,这里的误差是指预测y值与真实y值之间的差值,使用该误差的简单累加将使得正差值和负差值相互抵消,所以我们采用平方误差。
平方误差可以写做:
用矩阵表示可以写成
使用上式对w进行求导:
具体可参考https://blog.csdn.net/nomadlx53/article/details/50849941
令上述导数为0,即可得
2. 局部加权线性回归
线性回归的一个问题是有可能出现欠拟合现象,因为它求的是具有小均方误差的无偏估计。显而易见,如果模型欠拟合将不能取得好的预测效果。所以有些方法允许在估计中引入一 些偏差,从而降低预测的均方误差。
其中的一个方法是局部加权线性回归(Locally Weighted Linear Regression,LWLR)。在该方法中,我们给待预测点附近的每个点赋予一定的权重。与kNN一样,这种算法每次预测均需要事先选取出对应的数据子集。该算法解除回归系数W的形式如下:
LWLR使用"核"(与支持向量机中的核类似)来对附近的点赋予更高的权重。核的类型可以自由选择,最常用的核就是高斯核,高斯核对应的权重如下:
其中k需要用户设定,它决定了对附近的点赋予多大的权重,控制衰减速度
下面是代码
from numpy import mat,linalg,corrcoef,shape,eye,exp,zeros
import matplotlib.pyplot as plt
import numpy as np
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
"""
函数说明:线性回归
ParameterS:
xArr - x数据集
yArr - y数据集
Returns:
ws - 回归系数
"""
def standRegres(xArr, yArr):
xMat = mat(xArr); yMat = mat(yArr).T
xTx = xMat.T*xMat
# linalg.det()计算行列式,若为0,则不可逆
if linalg.det(xTx) == 0.0:
print("This matrix is singular, cannot do inverse")
return
#回归系数ws = (X^TX)^-1X^Ty
ws = xTx.I * (xMat.T * yMat)
return ws
def plotStandRegres(xMat, yMat, ws):
fig = plt.figure()
ax = fig.add_subplot(111)
xCopy = xMat.copy()
yHat = xCopy * ws
# flatten()函数Return a copy of the array collapsed into one dimension.
# .A将矩阵转化为数组
ax.scatter(xMat[:, 1].flatten().A[0], yMat.T[:, 0].flatten().A[0])
ax.plot(xCopy[:, 1], yHat,'r')
plt.show()
"""
函数说明:局部加权线性回归
Parameters:
testPoint - 测试点,xArr - x数据集,yArr - y数据集,
k - 高斯核的参数,自定义,控制衰减速度
Returns:
测试点y值
"""
def lwlr(testPoint, xArr, yArr, k=1.0):
xMat = mat(xArr) # m * n
yMat = mat(yArr).T # m * 1
m = shape(xMat)[0]
weights = mat(eye(m)) #生成对角矩阵,对角线上元素全为1,其他为0
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
"""
函数说明:局部加权线性回归测试函数
Parameters:
testPoint - 测试x数据集,xArr - x数据集,yArr - y数据集,
k - 高斯核的参数,自定义,控制衰减速度
Returns:
yHat - 预测y数据集
"""
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
def plotLwlr(xMat, yMat):
plt.rcParams['font.sans-serif'] = ['SimHei']
plt.rcParams['axes.unicode_minus'] = False
strInd = xMat[:,1].argsort(0) #对x进行排序,并返回排序下标
xSort = xMat[strInd][:,0,:]
yHat1 = lwlrTest(xArr, xArr, yArr, 1.0)
yHat2 = lwlrTest(xArr, xArr, yArr, 0.01)
yHat3 = lwlrTest(xArr, xArr, yArr, 0.003)
fig, axs = plt.subplots(nrows=3, ncols=1, sharex=False, sharey=False, figsize=(10, 8))
axs[0].plot(xSort[:, 1], yHat1[strInd], c='red')
axs[1].plot(xSort[:, 1], yHat2[strInd], c='red')
axs[2].plot(xSort[:, 1], yHat3[strInd], c='red')
axs[0].scatter(xMat[:, 1].flatten().A[0], yMat.T[:, 0].flatten().A[0])
axs[1].scatter(xMat[:, 1].flatten().A[0], yMat.T[:, 0].flatten().A[0])
axs[2].scatter(xMat[:, 1].flatten().A[0], yMat.T[:, 0].flatten().A[0])
#设置标题,x轴y轴label
axs0_title_text = axs[0].set_title(u'局部加权回归曲线,k=1.0')
axs1_title_text = axs[1].set_title(u'局部加权回归曲线,k=0.01')
axs2_title_text = axs[2].set_title(u'局部加权回归曲线,k=0.003')
plt.setp(axs0_title_text, size=8, weight='bold', color='red')
plt.setp(axs1_title_text, size=8, weight='bold', color='red')
plt.setp(axs2_title_text, size=8, weight='bold', color='red')
plt.xlabel('X')
plt.ylabel('Y')
plt.show()
if __name__ == '__main__':
xArr, yArr = loadDataSet('ex0.txt')
ws = standRegres(xArr, yArr)
xMat = mat(xArr)
yMat = mat(yArr)
yHat = xMat * ws # 预测y值
plotStandRegres(xMat, yMat, ws)
#计算相关系数
print(corrcoef(yHat.T, yMat))
plotLwlr(xMat, yMat)
简单线性回归的运行结果:
相关系数:
局部加权回归:
从图中可以看出在三种k的取值中,k=0.01的结果相对更好一些,k=0.003时会出现过拟合的情况。
局部加权回归存在一个问题:增加了计算量,每次必须在整个数据集上运行
3.示例:预测鲍鱼的年龄
""" 函数说明:计算误差 """ def rssError(yArr,yHatArr): #yArr and yHatArr both need to be arrays return ((yArr-yHatArr)**2).sum() if __name__ == '__main__': xArr, yArr = regression.loadDataSet('abalone.txt') print('训练集与测试集相同:局部加权线性回归,核k的大小对预测的影响:') yHat01 = regression.lwlrTest(xArr[0:99], xArr[0:99], yArr[0:99], 0.1) yHat1 = regression.lwlrTest(xArr[0:99], xArr[0:99], yArr[0:99], 1) yHat10 = regression.lwlrTest(xArr[0:99], xArr[0:99], yArr[0:99], 10) print('k=0.1时,误差大小为:',rssError(yArr[0:99], yHat01.T)) print('k=1时,误差大小为:',rssError(yArr[0:99], yHat1.T)) print('k=10.1时,误差大小为:',rssError(yArr[0:99], yHat10.T)) print('') print('训练集与测试集不同:局部加权线性回归,核k的大小对预测的影响:') yHat01 = regression.lwlrTest(xArr[100:199], xArr[0:99], yArr[0:99], 0.1) yHat1 = regression.lwlrTest(xArr[100:199], xArr[0:99], yArr[0:99], 1) yHat10 = regression.lwlrTest(xArr[100:199], xArr[0:99], yArr[0:99], 10) print('k=0.1时,误差大小为:',rssError(yArr[100:199], yHat01.T)) print('k=1时,误差大小为:',rssError(yArr[100:199], yHat1.T)) print('k=10时,误差大小为:',rssError(yArr[100:199], yHat10.T)) print('') print('训练集与测试集不同:简单的线性归:') ws = regression.standRegres(xArr[0:99], yArr[0:99]) # 8 * 1 yHat = xArr[100:199] * ws print(rssError(yArr[100:199],yHat.T.A))
从中可以看出使用训练数据集做测试集时,k=0.1时的误差最小,但使用不同训练集的数据做测试数据集时,k=0.1的误差最大,说明了可能出现了过拟合的情况,因此在选取k值时应选择使测试误差最小的值。同时我们可以看到,当k=1时,局部加权线性回归和简单的线性回归得到的效果差不多。这也表明一点,必须在未知数据上比较效果才能选取到最佳模型。
参考:
《机器学习实战第8章》
http://cuijiahua.com/blog/2017/11/ml_11_regression_1.html