1、线性回归
通过一个线性方程去拟合一些数据点。可设直线方程为 y=wx. 其中w称为回归系数。问题是,如何从一堆x和对应的y中确定w?一个常用的方法就是找出使误差最小的w。这里的误差是指预测y值和真实y值之间的差值,我们采用平方误差,写作:
用矩阵还可以写作: ,如果对w求导,得到,令其等于零,解出w为:
注意此处公式包含对矩阵求逆,所以求解时需要先对矩阵是否可逆做出判断。以上求解w的过程也称为“普通最小二乘法”。
Python实现代码如下:
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
def standRegres(xArr,yArr):
'''求回归系数'''
xMat = np.mat(xArr); yMat = np.mat(yArr).T
xTx = xMat.T*xMat
if np.linalg.det(xTx) == 0.0:#判断行列式是否为0
print("This matrix is singular, cannot do inverse")
return
ws = xTx.I * (xMat.T*yMat)#也可以用NumPy库的函数求解:ws=linalg.solve(xTx,xMat.T*yMatT)
return ws
if __name__ == "__main__":
'''线性回归'''
xArr,yArr=loadDataSet('ex0.txt')
ws=standRegres(xArr,yArr)
xMat=np.mat(xArr)
yMat=np.mat(yArr)
#预测值
yHat=xMat*ws
print(yHat)
print('-----------------------------')
#计算预测值和真实值得相关性
corrf=np.corrcoef(yHat.T,yMat)# 求相关系数0.986---相关系数越大,模型越好
print(corrf)
#绘制数据集散点图和最佳拟合直线图
#创建图像并绘出原始的数据
import matplotlib.pyplot as plt
fig=plt.figure()
ax=fig.add_subplot(111)
ax.scatter(xMat[:,1].flatten().A[0],yMat.T[:,0].flatten().A[0])
#绘最佳拟合直线,需先要将点按照升序排列
xCopy=xMat.copy()
xCopy.sort(0)
yHat = xCopy*ws
ax.plot(xCopy[:,1],yHat)
plt.show()
2、局部加权线性回归
局部加权线性回归给待预测点附近的每个点赋予一定的权重,用于解决线性回归可能出现的欠拟合现象。与kNN法类似,这种算法每次预测均需要事先选取出对应的数据子集,然后在这个子集上基于最小均分差来进行普通的回归。该算法解出回归系数的形式如下:
其中w是一个权重矩阵,通常采用核函数来对附近的点赋予权重,最常用的核函数是高斯核,如下:
这样就构建了一个只含对角元素的权重矩阵W并且点x与x(i)越近,w(i,i)将会越大,k值控制衰减速度,且k值越小被选用于训练回归模型的数据集越小。
Python实现代码:
import numpy as np
## 2、局部加权线性回归---用于解决线性回归可能出现的欠拟合现象
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
def lwlr(testPoint,xArr,yArr,k=1.0): #为任意一个测试点得到其预测值
'''局部加权线性回归函数'''
xMat = np.mat(xArr); yMat = np.mat(yArr).T
m =np. shape(xMat)[0]
weights =np. mat(np.eye((m)))#创建对角矩阵
for j in range(m):
diffMat = testPoint - xMat[j,:]
#高斯核计算权重
weights[j,j] = np.exp(diffMat*diffMat.T/(-2.0*k**2))
xTx = xMat.T * (weights * xMat)
if np.linalg.det(xTx) == 0.0:
print("This matrix is singular, cannot do inverse")
return
ws = xTx.I * (xMat.T * (weights * yMat))
return testPoint * ws
def lwlrTest(testArr,xArr,yArr,k=1.0):
'''为数据集中每个点调用lwlr()'''
m = np.shape(testArr)[0]
yHat = np.zeros(m)
for i in range(m):
yHat[i] = lwlr(testArr[i],xArr,yArr,k)
return yHat
def rssError(yArr,yHatArr): #返回预测误差
return((yArr-yHatArr)**2).sum()
if __name__ == "__main__":
'''局部加权线性回归'''
xArr,yArr=loadDataSet('ex0.txt')
#拟合
yHat=lwlrTest(xArr,xArr,yArr,0.01) #改变k的值,当k=1.0时回到线性回归(欠拟合),当k=0.03时过拟合
#绘图
xMat=np.mat(xArr)
yMat=np.mat(yArr)
srtInd = xMat[:,1].argsort(0) #数据点排序
xSort=xMat[srtInd][:,0,:]
import matplotlib.pyplot as plt
fig=plt.figure()
ax=fig.add_subplot(111)
ax.plot(xSort[:,1],yHat[srtInd])
ax.scatter(xMat[:,1].flatten().A[0],yMat.T[:,0].flatten().A[0],s=2,c='red')
plt.show()
##预测鲍鱼年龄的误差
abX,abY=loadDataSet('abalone.txt')
yHat01=lwlrTest(abX[0:99],abX[0:99],abY[0:99],0.1)
error1=rssError(abY[0:99],yHat01)
error2=rssError(abY[100:199],yHat01) #对于新的训练集效果差
print(error1)
print('----------------------------------')
print(error2)
3、岭回归
如果数据的特征比样本点多(n>m),也就是说输入数据的矩阵x不是满秩矩阵。而非满秩矩阵在求逆时会出错,所以此时不能使用之前的线性回归方法。为解决这个问题,统计学家引入了岭回归的概念。
简单来说,岭回归就是在矩阵xTx上加一个λI从而使得矩阵非奇异,进而能对 xTx+λI 求逆,其中I是一个mxm的单位矩阵。在这种情况下,回归系数的计算公式将变成:
这里通过引入λ来限制了所有w之和,通过引入该惩罚项,能减少不重要的参数,这个技术在统计学中也叫缩减。
Python实现代码:
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
def ridgeRegres(xMat,yMat,lam=0.2): ##lam默认为0.2
'''计算岭回归系数'''
xTx = xMat.T*xMat
denom = xTx +np. eye(np.shape(xMat)[1])*lam
if np.linalg.det(denom) == 0.0:
print("This matrix is singular, cannot do inverse")
return
ws = denom.I * (xMat.T*yMat)
return ws
def ridgeTest(xArr,yArr):
'''用于在一组lambda上测试结果'''
xMat = np.mat(xArr); yMat=np.mat(yArr).T
yMean = np.mean(yMat,0)
yMat = yMat - yMean #数据标准化
xMeans =np. mean(xMat,0)
xVar = np.var(xMat,0)
xMat = (xMat - xMeans)/xVar #所有特征减去各自的均值并除以方差
numTestPts = 30 #取30个不同的lambda调用函数
wMat = np.zeros((numTestPts,np.shape(xMat)[1]))
for i in range(numTestPts):
ws = ridgeRegres(xMat,yMat,np.exp(i-10)) #lam以指数级变化
wMat[i,:]=ws.T
return wMat
if __name__ == "__main__":
'''岭回归'''
abX,abY=loadDataSet('abalone.txt')
ridgeWeights = ridgeTest(abX,abY)#得到30组回归系数
#缩减效果图
import matplotlib.pyplot as plt
fig=plt.figure()
ax=fig.add_subplot(111)
ax.plot(ridgeWeights)
plt.show()
4、前向逐步回归
前向逐步回归算法属于一种贪心算法,即每一步尽可能减少误差。一开始,所有的权重都设为1,然后每一步所做的决策是对某个权重增加或减少一个很小的值。
Python实现代码:
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 def regularize(xMat): '''数据标准化函数''' inMat = xMat.copy() inMeans = np.mean(inMat,0) inVar = np.var(inMat,0) inMat = (inMat - inMeans)/inVar return inMat def rssError(yArr,yHatArr): '''计算均方误差大小''' return ((yArr-yHatArr)**2).sum() def stageWise(xArr,yArr,eps=0.01,numIt=100): ''' 逐步线性回归算法 eps:表示每次迭代需要调整的步长 ''' xMat = np.mat(xArr); yMat=np.mat(yArr).T yMean = np.mean(yMat,0) yMat = yMat - yMean xMat = regularize(xMat) m,n=np.shape(xMat) returnMat = np.zeros((numIt,n)) #testing code remove #为了实现贪心算法建立ws的两份副本 ws =np. zeros((n,1)); wsTest = ws.copy(); wsMax = ws.copy() for i in range(numIt): print(ws.T) lowestError = np.inf; for j in range(n):#对每个特征 for sign in [-1,1]:#分别计算增加或减少该特征对误差的影响 wsTest = ws.copy() wsTest[j] += eps*sign yTest = xMat*wsTest rssE = rssError(yMat.A,yTest.A) #取最小误差 if rssE < lowestError: lowestError = rssE wsMax = wsTest ws = wsMax.copy() returnMat[i,:]=ws.T return returnMat if __name__ == "__main__": '''前向逐步线性回归''' abX,abY=loadDataSet('abalone.txt') stageWise(abX,abY,0.01,200)