回归的目的是预测数值型的目标值。
回归方程(regression equation)主要是求回归系数,一旦有了回归系数,在给定输入,做预测就是用回归系数乘以输入值,在将结果全部加在一起,就得到了预测值。(因为回归系数是一个向量,输入也是向量,这些运算就是求出二者的内积)
回归的一般方法:
1)收集数据:采用任意方法收集数据
2)准备数据:回归需要数值型数据,标称型数据将被转换为二值型数据
3)分析数据,二维图
4)训练算法:找到回归系数
5)测试算法:使用或者预测值和数据的拟合度,来分析模型的效果
6)使用算法:使用回归,在给定的时候预测出一个数值
求回归方程:
假定输入数据存放在矩阵X中,而回归系数存放在向量w中。那么对于给定的数据,预测结果将会通过给出。通过x,y数据找到回归系数向量w,一个常用的方法是找出误差最小的w。这里的误差是指预测值和真实值之间的差值。避免正差值和负差值之间相互抵消,因此采用平方误差。
平方误差可以写作
用矩阵表示还可以写作,如果对w求导,得到,令其等于零,解出w
w上方的小标记表示,这事当前可以估计出的w的最优解,从现有数据上估计出的w可能并不是数据中的真实值,所以这里使用了一个"帽"符号来表示是w的一个最佳估计。注意:公式中包含,也就是需要对矩阵求逆,代码中需要判断矩阵的逆到底存不存在
另外还可以使用NumPy库中的矩阵方法,该方法叫OLS,意思是“普通最小二乘法”(ordinary least squares)
from numpy import *
#加载数据
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):
xMat = mat(xArr); yMat = mat(yArr).T
xTx = xMat.T*xMat
if linalg.det(xTx) == 0.0:# det判断行列式
print ("This matrix is singular, cannot do inverse")
return
ws = xTx.I * (xMat.T*yMat)
return ws
import matplotlib.pyplot as plt
xmat=mat(xarr)
ymat=mat(yarr)
yhat=xmat*ws
fig=plt.figure()
ax=fig.add_subplot(111)
#a是个矩阵或者数组,a.flatten()就是把a降到一维,默认是按横的方向降
# 此时的a是个矩阵,降维后还是个矩阵,矩阵.A(等效于矩阵.getA())变成了数组,A[0]就是数组里的第一个元素
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.savefig("2.png")
plt.show()
几乎任意数据集都可以用上述方法建立模型,但是判断模型的好坏,可以通过计算着两个序列的相关系数来,计算预测值yHat序列和真实值y序列的匹配程度。
yhatT=yhat.T
coef=corrcoef(yhatT,ymat)
print(coef)
[[1. 0.13653777]
[0.13653777 1. ]]
线性回归的一个问题是有可能出现欠拟合现象,因为它求的是具有最小均方误差的无偏估计。
局部加权线性回归(Locally weighted linear regression,LWLR)。在该算法中,给每个待测点附近的每个点赋予一定的权重;然后基于最小均方差来进行普通的回归。
其中w是一个矩阵,用来给每个数据点赋予权重
LWLR使用“核”(与支持向量机中的核类似)来对附近的点赋予更高的权重。核的类型可以自由选择,最常用的核是高斯核,高斯核对应的权重如下
这样就构建了一个只含有对角元素的权重矩阵,并且点x与x(i)越近,w(i,i)将会越大()。参数k,决定了对附近的点赋予多大的权重,这也是LWLR时唯一需要考虑的参数,
import matplotlib.pyplot as plt
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): #next 2 lines create weights matrix
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
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
xarr,yarr=loadDataSet('ex0.txt')
xmat=mat(xarr)
ymat=mat(yarr)
yhat=lwlrTest(xarr,xarr,yarr,0.01)
srcInd=xmat[:,1].argsort(0)
xsort=xmat[srcInd][:,0,:]
print(xsort)
fig=plt.figure()
ax=fig.add_subplot(111)
ax.plot(xsort[:,1],yhat[srcInd])
ax.scatter(xmat[:,1].flatten().A[0],mat(yarr).T.flatten().A[0],s=2,c='red')
plt.show()
局部加权线性回归也存在一个问题,即增加了计算量,因为他对每个点做预测是都必须使用整个数据集
使用较小的核将得到较低的误差,但是不能所有数据集都使用最小的核,因为使用最小的核将造成过拟合,对新数据不一定能达到最好的预测效果。
如果数据的特征比样本点还多,就不能使用之前的方法来做预测。因为计算的时候回报错。特征比样本点还多(n>m),也就是说输入数据的矩阵X不是满秩矩阵。非满秩矩阵在求逆向时会出现问题。一般采用岭回归和lasso回归
岭回归就是在矩阵上加一个I 求逆,其中矩阵I是mxm的单位矩阵,是用户定义的一个数值,回归系数变为
岭回归最先用来处理特征数多于样本数的情况,现在也用于在估计中加入偏差,从而得到更好的估计。通过限制来限制所有w之和,通过引入惩罚性,能够减少不重要的参数,能更好的理解数据,这个技术叫做缩减(shrinkage)
通过预测误差最小化得到。通过选取不同的来重复上述测试过程,最终得到一个是测试误差最小的
import matplotlib.pyplot as plt
#岭回归
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("This matrix is singular, cannot do inverse")
return
ws = denom.I * (xMat.T*yMat)
return ws
def ridgeTest(xArr,yArr):
xMat = mat(xArr); yMat=mat(yArr).T
yMean = mean(yMat,0)
yMat = yMat - yMean #to eliminate X0 take mean off of Y
#regularize X's
xMeans = mean(xMat,0) #calc mean then subtract it off
xVar = var(xMat,0) #calc variance of Xi then divide by it
xMat = (xMat - xMeans)/xVar #特征数据标准化处理
numTestPts = 30
wMat = zeros((numTestPts,shape(xMat)[1]))
for i in range(numTestPts):
ws = ridgeRegres(xMat,yMat,exp(i-10))
wMat[i,:]=ws.T
return wMat
abx,aby=loadDataSet('abalone.txt')
ridgeweights=ridgeTest(abx,aby)
fig=plt.figure()
ax=fig.add_subplot(111)
ax.plot(ridgeweights)
plt.show()
增加如下约束时,普通的最小二乘法回归会得到与岭回归的一样的公式:
使用普通的最小二乘法回归在当两个或更多的特征相关时,可能会得出一个很大的正系数和一个很大的负系数。正是因为此约束条件存在,使用岭回归可以避免此问题。
与岭回归类似,另一个缩减方法lasso也对回归系数做了限定,对应的约束条件
此处约束条件使用绝对值取代了平方和。当足够小的时候,一些系数会因此被迫缩减到0,这个特性可以帮我们更好的理解数据。
一种贪心算法,即每一步都尽可能减少误差。一开始所有权重都等于1,然后每一步所做的决策是对某个权重增加或减少一个很小的值。
'''
数据标准化,使其分布满足0均值和单位方差
在每轮跌倒过程中:
设置当前最小误差lowestError为正无穷
对每个特征:
增大或缩小:
改变一个系数得到一个新的w
计算新w下的误差
如果误差Error小于当前最小误差lowestError:设置wbest等于当前的w
将w设置为新的wbest
'''
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
#逐步线性回归算法的实现,与lasso做法相似单计算简单
def stageWise(xArr,yArr,eps=0.01,numIt=100):
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)
#returnMat = zeros((numIt,n)) #testing code remove
ws = zeros((n,1)); wsTest = ws.copy(); wsMax = ws.copy()
for i in range(numIt):
print(ws.T)
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
ws = wsMax.copy()
stagedata2=stageWise(abx,aby,0.001,5000)
xmat=mat(abx)
ymat=mat(aby).T
xmat=regularize(xmat)
ym=mean(ymat,0)
ymat=ymat-ym
weights=standRegres(xmat,ymat.T)
print(weights.T)
...
[[ 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]]
可以看到进过5000次迭代后,逐步线性回归算法与常规的最小二乘法效果类似。该算法主要优点可以找出重要的特征,这样就可能及时停止对那些不重要特征的收集。
模型和测量之间存在差异,就出现了误差。对复杂的过程进行简化,这将导致差异的发生。
一般采用降低核大小和采用缩减法。缩减法通过吧一些特征的回归系数缩减到0,同时也就减少了模型的复杂度。
用回归法预测乐高套装的价格:
1)收集数据:任何方式
2)准备数据:从返回的JSON中抽取价格
3)分析数据:可视化并观察数据
4)训练算法:构建不同的模型,采用逐步线性回归和直接的线性回归模型
5)测试算法:使用交叉验证来测试不同的模型,分析哪个效果最好
6)使用算法:这次练习的目标就是生产数据模型
6.1收集数据,
from time import sleep
import json
import urllib
def searchForSet(retX, retY, setNum, yr, numPce, origPrc):
sleep(10) #防止短时间内有过多的api调用
myAPIstr = 'AIzaSyD2cR2KFyx12hXu6PFU-wrWot3NXvko8vY'
searchURL = 'https://www.googleapis.com/shopping/search/v1/public/products?key=%s&country=US&q=lego+%d&alt=json' % (myAPIstr, setNum)
pg = urllib.request.urlopen(searchURL)
retDict = json.loads(pg.read()) #打开和解析操作
for i in range(len(retDict['items'])):
try:
currItem = retDict['items'][i]
if currItem['product']['condition'] == 'new':
newFlag = 1
else: newFlag = 0
listOfInv = currItem['product']['inventories']
for item in listOfInv:
sellingPrice = item['price']
#信息过滤可以采用贝叶斯来判断,此处使用剪短的启发式
if sellingPrice > origPrc * 0.5:
print("%d\t%d\t%d\t%f\t%f" % (yr,numPce,newFlag,origPrc, sellingPrice))
retX.append([yr, numPce, newFlag, origPrc])
retY.append(sellingPrice)
except: print('problem with item %d' % i)
def setDataCollect(retX, retY):
searchForSet(retX, retY, 8288, 2006, 800, 49.99)
searchForSet(retX, retY, 10030, 2002, 3096, 269.99)
searchForSet(retX, retY, 10179, 2007, 5195, 499.99)
searchForSet(retX, retY, 10181, 2007, 3428, 199.99)
searchForSet(retX, retY, 10189, 2008, 5922, 299.99)
searchForSet(retX, retY, 10196, 2009, 3263, 249.99)
使用 standRegres ,在新数据集上进行回归处理拿到ws,得到公式
#交叉验证测试回归
def crossValidation(xArr,yArr,numVal=10):
m = len(yArr)
indexList = range(m)
errorMat = zeros((numVal,30))#create error mat 30columns numVal rows
for i in range(numVal):
trainX=[]; trainY=[]
testX = []; testY = []
random.shuffle(indexList) #对数据进行混洗
for j in range(m):#create training set based on first 90% of values in indexList
if j < m*0.9:
trainX.append(xArr[indexList[j]])
trainY.append(yArr[indexList[j]])
else:
testX.append(xArr[indexList[j]])
testY.append(yArr[indexList[j]])
wMat = ridgeTest(trainX,trainY) #get 30 weight vectors from ridge
for k in range(30):#loop over all of the ridge estimates
matTestX = mat(testX); matTrainX=mat(trainX)
meanTrain = mean(matTrainX,0)
varTrain = var(matTrainX,0)
matTestX = (matTestX-meanTrain)/varTrain #regularize test with training params
yEst = matTestX * mat(wMat[k,:]).T + mean(trainY)#test ridge results and store
errorMat[i,k]=rssError(yEst.T.A,array(testY))#保存每个λ对应的多个误差值
#print errorMat[i,k]
meanErrors = mean(errorMat,0)#calc avg performance of the different ridge weight vectors
minMean = float(min(meanErrors))
bestWeights = wMat[nonzero(meanErrors==minMean)]
#can unregularize to get model
#when we regularized we wrote Xreg = (x-meanX)/var(x)
#we can now write in terms of x not Xreg: x*w/var(x) - meanX/var(x) +meanY
xMat = mat(xArr); yMat=mat(yArr).T
meanX = mean(xMat,0); varX = var(xMat,0)
unReg = bestWeights/varX
print("the best model from Ridge Regression is:\n",unReg)
print ("with constant term: ",-1*sum(multiply(meanX,unReg)) + mean(yMat))
通过交叉验证落后得到的的回归系数,存在大小不一,如果在特征很多的情况下,可以方便的选择出那些特征是关键的,那些特征是不重要的。
回归与分类不同,分类预测离散型变量,回归预测连续性变量。求特征对应的最佳回归系数的方法是最小化误差的平方和。前提是的逆存在。数据集上计算的回归方程不一定是最佳的,可以使用预测yhat和原始值y,的相关性来度量回归方程的好坏。
当样本数比特征数还少的时候,的逆不能直接计算,可以考虑是用岭回归,
岭回归是缩减法的一种,相当于对回归系数的大小做了限制。另一种锁甲你发是lasso,lasso难以求解,可以考虑使用简单的逐步线性回归来求得近似结果
缩减法可以看成是对一个模型增加编差的同时减少方差。偏差方差折中是一个重要的概念