对于对给定数据集X和对应目标值y,线性回归目的是要找到回归系数w,使用线性方程来拟合这些数据点。
1.标准线性回归:
通常求取回归系数的做法是,求得使平方误差最下的w
平方误差:
解出回归系数:
直接用此公式时需注意矩阵的求逆,需要判断矩阵行列式不为零,否则不能直接求逆。
from numpy import *
import matplotlib.pyplot as plt
def loadDataSet(fileName): #general function to parse tab -delimited floats
numFeat = len(open(fileName).readline().split('\t')) - 1 #get number of fields
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):
X=mat(xArr)
y=mat(yArr).T
xTx=X.T*X
if linalg.det(xTx)==0:#检查是否可逆
print('This matrix is singular, cannot do inverse')
return
w=xTx.I*(X.T*y)
return w
标准线性回归
2.局部加权线性回归(LWLR)
线性回归的缺点是容易出现欠拟合,采用局部加权线性回归,给与每一个待预测点附近的点一定的权重,在该子集上基于最小平方误差进行回归。这种算法每次预测前需要先选出相应数据子集。对应回归系数的解为:
其中W是一个矩阵,用来给每一个数据点赋予权重。类似于SVM中的核,常用的核是高斯核,介绍见下图(见《机器学习实战》P142)。
#局部加权回归
def lwlr(testpoint,xArr,yArr,k=1):
X=mat(xArr)
y=mat(yArr).T
m=shape(X)[0]
weights=mat(eye(m))
for i in range(m):
diffMat=testpoint-X[i,:]
weights[i,i]=exp(diffMat*diffMat.T/(-2.0*k**2))
xTx=X.T*(weights*X)
if linalg.det(xTx)==0:
print('This matrix is singular, cannot do inverse')
return
w=xTx.I*(X.T*(weights*y))
return testpoint*w
def lwlrTest(testArr, xArr, yArr, k=1.0): # loops over all the data points and applies lwlr to each one
m = shape(testArr)[0]
yHat = zeros(m)
for i in range(m):
yHat[i] = lwlr(testArr[i], xArr, yArr, k)
return yHat
k=0.05
k=0.01
k=0.005
当K=1时,所有数据权重相同,拟合结果与标准回归相同。当k=0.01时,拟合效果较好。当k=0.005时,纳入了太多的噪声点,拟合曲线与数据点过于贴近,训练误差很小,但是会造成过拟合,对新数据的预测能力降低。
3.缩减法:
当样本特征个数n大于样本点数m时,X为非满秩矩阵,将不可逆。此时可以使用缩减法将某些不重要的特征去掉,降低模型的复杂度,
岭回归是一种常见的缩减法。简单来说就是在上加上,使得矩阵不奇异,从而可以求逆。这种情况下,回归系数计算公式变为:
#岭回归
def ridgeRegress(xMat,yMat,lam=0.2):
xTx=xMat.T*xMat
denom=xTx+eye(shape(xMat)[1])*lam
if linalg.det(denom)==0:
print('This matrix is singular, cannot do inverse')
return
w=denom.I*(xMat.T*yMat)
return w
def ridgeTest(xArr,yArr):
xMat=mat(xArr)
yMat=mat(yArr).T
yMean=mean(yMat,0)
yMat=yMat-yMean
xMean=mean(xMat,0)
varX=var(xMat,0)
xMat=(xMat-xMean)/varX
numTestPts=30
wMat=mat(zeros((numTestPts,shape(xMat)[1])))
for i in range(numTestPts):
ws=ridgeRegress(xMat,yMat,lam=exp(i-10))
wMat[i, :]=ws.T
return wMat
计算不同i=0:29,对应=,以指数级变化,这样可以更容易看出在很大和很小时对回归系数的影响。如下图所示,在最左边很小时,得到原始的回归系数(与标准回归相同),在最右边,系数缩减为零。可以通过交叉验证得到合适的值。
x,y=loadDataSet('D:\python\code\machinelearninginaction\Ch08\\abalone.txt')
W=ridgeTest(x,y)
print(W)
fig=plt.figure()
ax=fig.add_subplot(111)
for i in range(8):
ax.plot(W[:,i],label='w%s'% i)
ax.legend(loc='upper right')
plt.show()
前项逐步线性回归:
开始对每一个权重取值为0,然后进行迭代,每次对w减小或者增大一个很小的数值,然后计算平方误差,若误差减小,则更新w值。
#归一化特征矩阵;使得每一特征相同重要
def regularize(xMat):#regularize by columns
inMat = xMat.copy()
inMeans = mean(inMat,0) #calc mean then subtract it off
inVar = var(inMat,0) #calc variance of Xi then divide by it
inMat = (inMat - inMeans)/inVar
return inMat
#计算平方误差
def rssError(yArr,yHatArr): #yArr and yHatArr both need to be arrays
return ((yArr-yHatArr)**2).sum()
#前项逐步线性规划
def stageWise(xArr,yArr,eps=0.01,numIt=500):
xMat = mat(xArr)
yMat = mat(yArr).T
yMean = mean(yMat, 0)
yMat = yMat - yMean # can also regularize ys but will get smaller coef
xMat = regularize(xMat)
m, n = shape(xMat)
ws=zeros((n,1))
wsMax=ws.copy()
W=mat(zeros((numIt,n)))
for i in range(numIt):
lowestError = 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
W[i,:]=wsMax.T
ws = wsMax.copy()
return W
下图所示为步长eps=0.005,迭代1000次,w的变化曲线。
逐步线性回归可以帮助我们更好地认识数据特征,对模型进行改进。建立一个模型后,可以运行该算法,选择出比较重要的特征,可以对不重要的特征及时停止分析。每迭代100次便可以构建一个模型,运用交叉验证的方法选出最优参数。