线性回归找到最佳拟合直线:寻找使得拟合误差最小的线性回归系数w
采用平方误差。
标准回归函数和数据载入函数:
from numpy import * def loadDataSet(filename): numFeatures=len(open(filename).readline().split(' '))-1 #tag键分隔文本的文件 dataMat=[] labelMat=[] f=open(filename) for line in f.readlines(): curLine=line.strip().split(' ') lineArr=[] for i in range(numFeatures): 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: #linalg.det()计算行列式,行列式等于0时矩阵不可逆 print('此矩阵不可逆') return ws=xTx.I*(xMat.T*yMat) return ws
返回值ws即为回归系数,因为训练数据集上只有两个特征,所以ws只有两个元素。
预测值计算:
yHat=ws[0]*x[0]+ws[1]*x[1]
向量形式:yHat=WS*X
绘制数据集散点图和拟合直线:
def plotLine(xArr,yArr,ws): xMat=mat(xArr) yMat=mat(yArr) # yHat=xMat*ws import matplotlib.pyplot as plt fig=plt.figure() ax=fig.add_subplot(111) # print(yMat) # print(yMat.T) # print(yMat.T.flatten()) # print(yMat.T[:,0].flatten().A[0]) ax.scatter(xMat[:,1].flatten().A[0],yMat.T[:,0].flatten().A[0],s=5,c='y') xCopy=xMat.copy() xCopy.sort(0) yHat=xCopy*ws ax.plot(xCopy[:,1],yHat,c='r') #plot划线,scatter画点 plt.show()
测试输出:
if __name__=='__main__': xArr,yArr=loadDataSet('ex0.txt') ws = standRegres(xArr, yArr) print('回归系数为:', ws) plotLine(xArr, yArr, ws)
输出结果为:
回归系数为: [[3.00774324] [1.69532264]]
衡量预测值yHat序列和真实值y序列的匹配程度:相关系数
xMat = mat(xArr) yMat = mat(yArr) yHat=xMat*ws corr=corrcoef(yHat.T,yMat) print('相关系数为:',corr)
输出:
相关系数为: [[1. 0.98647356] [0.98647356 1. ]]
局部加权线性回归:上述方法明显欠拟合,因为它求的是具有最小均方误差的无偏估计,拟合函数不能获得很好的预测结果,局部加权线性回归允许在估计中引入一些偏差,从而降低预测的均方误差。
原理:给待测点附近的数据点赋予一定的权重,然后再基于上述的最小均方误差进行普通的回归预测。距离预测点越近的数据点权重越大,对预测结果影响越大。类似于数值分析中的分段线性插值。
回归系数时m阶单位矩阵,核函数选择高斯核。
局部加权线性回归函数和预测函数:
def lwlr(testPoint,xArr,yArr,k=1.0): xMat=mat(xArr) yMat=mat(yArr).T m=shape(xMat)[0] weights=mat(eye(m)) 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('此矩阵不可逆!') return ws=xTx.I*(xMat.T*(weights*yMat)) return testPoint*ws 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 plotLineAndPoint(xArr,yArr,yHat): import matplotlib.pyplot as plt xMat=mat(xArr) sortedIndex=xMat[:,1].argsort(0) xSorted=xMat[sortedIndex][:,0,:] #三维矩阵的切片操作 figure=plt.figure() ax=figure.add_subplot(111) ax.plot(xSorted[:,1],yHat[sortedIndex],c='r') ax.scatter(xMat[:,1].flatten().A[0],mat(yArr).T.flatten().A[0],s=5,c='y') plt.show() def lwlr(testPoint,xArr,yArr,k=1.0): xMat=mat(xArr) yMat=mat(yArr).T m=shape(xMat)[0] weights=mat(eye(m)) 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('此矩阵不可逆!') return ws=xTx.I*(xMat.T*(weights*yMat)) return testPoint*ws 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
测试:
if __name__=='__main__': xArr,yArr=loadDataSet('ex0.txt') yHat = lwlrTest(xArr, xArr, yArr, 0.003) plotLineAndPoint(xArr, yArr, yHat)
输出:
上述回归曲线是在高斯核函数里取k=0.003时的结果,考虑了太多噪声,导致了过拟合,不同的k值对回归效果影响很大:
k=0.5:
k=0.1:
k=0.01
k=0.001
此时部分样本计算过程发生了零溢出,矩阵不可逆,拟合曲线断续,而且过拟合。
k=0.01时拟合效果很好。
局部加权线性回归预测效果好,但是计算量大,因为对每个点的预测都需要使用整个数据集来更新权重矩阵进而求出回归系数。优化方法:距离预测点很远的数据点权重很小,接近零,可以忽略此种数据点,只计算距离预测点近的数据点。
使用以上回归算法预测鲍鱼年龄时发现:(误差衡量标准为方差)
k值越小的核函数得到的回归模型训练误差越小,这是因为使用较小的核造成了过拟合,其对新数据的预测效果较差。核大的回归模型训练误差大但是测试误差小。
简单线性回归测试误差与局部加权回归类似。这表明必须在未知数据上比较效果才能选取到最佳回归模型。一般取多个数据集测试来比较预测效果。
缩减系数来理解数据:岭回归
如果数据的特征个数大于样本点,以上计算过程会出错,因为输入数据的矩阵xMat不满秩,不能求逆。
岭回归:
def ridgeRegres(xMat,yMat,lam=0.2): xTx=xMat.T*xMat denom=xTx+eye(shape(xMat)[1])*lam if linalg.det(denom)==0.0: print('此矩阵不可逆!') return ws=denom.I*(xMat.T*yMat) #n*1阶 # print(ws) return ws def ridgeTest(xArr,yArr): xMat = mat(xArr) yMat = mat(yArr).T yMean=mean(yMat,0) yMat=yMat-yMean xMean=mean(xMat,0) # print(xMean) xVar=var(xMat,0) xMat=(xMat-xMean)/xVar numTest=30 wMat=zeros((numTest,shape(xMat)[1])) #30*n for i in range(numTest): ws=ridgeRegres(xMat,yMat,exp(i-10)) wMat[i,:]=ws.T return wMat
绘制回归系数随着lambda的变化曲线:
def plotRidgeWeights(ridgeWeights): # print(ridgeWeights) import matplotlib.pyplot as plt fig=plt.figure() ax=fig.add_subplot(111) axisX = [(i - 10) for i in range(30)] # log(λ)作为横坐标 ax.plot(axisX,ridgeWeights) plt.show()
测试:
if __name__=='__main__': xArr,yArr=loadDataSet('abalone.txt') wMat=ridgeTest(xArr,yArr) plotRidgeWeights(wMat)
输出:
横坐标是log(lambda),八条曲线分别表示八个回归系数的变化。为了定量地找到最佳回归系数值,需要交叉验证。
前向逐步回归:属于一种贪心算法,即每一步都尽可能减少误差。一开始,所有的权重都设置为0,然后每一步所做的决策是对某个权重增加或者减少一个很小的值。
该算法简单,直接迭代产生系数矩阵,类似于牛顿下山法,由初始值开始迭代,产生符合精度要求的结果即可。
def regularize(xMat): xMat2Regularize=xMat.copy() xMean=mean(xMat2Regularize,0) xVar=var(xMat2Regularize,0) xMat2Regularize=(xMat2Regularize-xMean)/xVar return xMat2Regularize def stageWise(xArr,yArr,step=0.01,numIter=100): xMat=mat(xArr) yMat=mat(yArr).T yMean=mean(yMat) yMat=yMat-yMean xMat=regularize(xMat) m,n=shape(xMat) returnMat=zeros((numIter,n)) ws=zeros((n,1)) wsTest=ws.copy() wsMax=ws.copy() for i in range(numIter): # print('权重矩阵为:',ws.T) lowestError=inf for j in range(n): for sign in [-1,1]: wsTest=ws.copy() wsTest[j]+=step*sign yTest=xMat*wsTest error=rssError(yMat.A,yTest.A) # .A 矩阵转化成数组 if error<lowestError: lowestError=error wsMax=wsTest ws=wsMax.copy() returnMat[i,:]=ws.T return returnMat
绘制回归系数与迭代次数之间的关系曲线:
def plotFigure(weights): # print('最后十次迭代产生的权重向量为: ',weights[-10:-1,:]) import matplotlib.pyplot as plt fig = plt.figure() ax = fig.add_subplot(111) ax.plot(weights) plt.show()
测试算法:
if __name__=='__main__': xArr,yArr=loadDataSet('abalone.txt') result=stageWise(xArr,yArr,0.001,5000) plotFigure(result)
输出为:
最后十次迭代产生的权重向量为: [[ 0.044 -0.011 0.12 0.022 2.023 -0.963 -0.105 0.187] [ 0.043 -0.011 0.12 0.022 2.023 -0.963 -0.105 0.187] [ 0.044 -0.011 0.12 0.022 2.023 -0.963 -0.105 0.187] [ 0.043 -0.011 0.12 0.022 2.023 -0.963 -0.105 0.187] [ 0.044 -0.011 0.12 0.022 2.023 -0.963 -0.105 0.187] [ 0.043 -0.011 0.12 0.022 2.023 -0.963 -0.105 0.187] [ 0.044 -0.011 0.12 0.022 2.023 -0.963 -0.105 0.187] [ 0.043 -0.011 0.12 0.022 2.023 -0.963 -0.105 0.187] [ 0.044 -0.011 0.12 0.022 2.023 -0.963 -0.105 0.187]]
使用最小二乘法拟合直线得到的回归系数如下:
[[ 0.0430442 -0.02274163 0.13214087 0.02075182 2.22403814 -0.99895312 -0.11725427 0.16622915]]