赞
踩
线性回归过程就是:将输入项乘上一些常量,然后再累加起来,就得到预测值。那么如何找到这些常量呢?
根据回归过程,可以得到一般公式:
现在问题是,我们有x的值和y的值,如何求出向量w的值,常用的方法就是是误差最小的 w,所以采用平方误差
对W进行求导,令其为零,解出W如下:
值得注意的是,上述公式包含对矩阵的求逆,那么如果该矩阵的行列式等于0,就不可逆,所以必须先判断矩阵是否可逆。
注:求解W的方法是统计学常用的方法,称之为最小二乘法(least square)
求解W的代码:
#加载数据 def loadDataSet(filename): fr = open(filename) numFeat = len(fr.readline().strip().split('\t')) - 1 dataMat = []; labelMat = [] for line in fr.readlines(): lineArr = [] currLine = line.strip().split('\t') for i in range(numFeat): lineArr.append(float(currLine[i])) dataMat.append(lineArr) labelMat.append(float(currLine[-1])) return dataMat,labelMat #计算W def standRegers(xArr,yArr): #首先转换为矩阵,方便计算 xMat = mat(xArr) yMat = mat(yArr).T xTx = xMat.T * xMat #判断矩阵的行列式是否为0 if linalg.det(xTx) == 0.0: print "这个矩阵没有逆矩阵" return #求出W ws = xTx.I * (xMat.T * yMat) return ws
通过matplotlib将数据可视化,并画出最佳拟合直线
#加载数据 xArr, yArr = loadDataSet('Ch08/ex0.txt') ws = standRegers(xArr,yArr) #转换为矩阵 xMat = mat(xArr) yMat = mat(yArr) #预测的y值 yHat = xMat*ws fig = plt.figure() axes = fig.add_subplot(111) axes.scatter(xMat[:,1].flatten().A[0],yMat.T[:,0].flatten().A[0]) #将xMat的值复制一份,将x的排序,避免直线上的点出现混乱 xCopy = xMat.copy() xCopy.sort(0) yHat = xCopy * ws axes.plot(xCopy[:,1],yHat) plt.show()
实验结果:
线性回归的一个问题就是容易出现欠拟合的现象,就比如上图的实验结果,数据点分明是弯曲向上的,结果预测结果是直线,虽然也不错,但是结果能不能更好呢?其中一个方法就是局部加权线性回归,我们给预测点附近的每个点赋予一定权重,
回归系数:
其中的W是一个矩阵,用来给每个数据点赋予权重,该算法使用高斯核对附近的点赋予更高的权重,高斯核的权重如下:
代码实现局部加权线性回归函数
#testpoint是一个测试点,k是高斯核的参数,决定了对附近的点赋予多大的权重 #函数作用:给定x空间的任意一点,求出对应的预测值yHat def lwlr(testPoint,xArr,yArr,k=1.0): xMat = mat(xArr) yMat = mat(yArr).T m = shape(xMat)[0] #初始化权重为1,eye返回一个单位矩阵 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)) #返回预测值yHat return testPoint * ws #用于为没个数据点调用lwlr函数 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
利用matplotlib将数据可视化:
- x, y = loadDataSet('Ch08/ex0.txt')
- #print y[0]
- #print lwlr(x[0],x,y,0.001)
- yHat = lwlrTest(x,x,y,0.003)
- x = mat(x)
- srtInd = x[:,1].argsort(0)
- xSort = x[srtInd][:,0,:]
- fig = plt.figure()
- axes = fig.add_subplot(111)
- axes.plot(xSort[:,1],yHat[srtInd])
- axes.scatter(x[:,1].flatten().A[0],mat(y).T[:,0].flatten().A[0],
- s = 2,c = 'red')
- plt.show()
实验结果
实验可得:k=1.0时和最小二乘法差不对,k=0.003时则考虑了太多的噪声点,出现了过拟合的现象,k=0.01时模型与样本点基本吻合,可以挖掘许多潜在的价值。
我们使用一种函数rssError来计算预测值与实际值之间的误差
- def rssError(yArr,yHatArr):
- return ((yArr - yHatArr)**2).sum()
abX,abY = loadDataSet('Ch08/abalone.txt') #k值分别取0.1 1.0 10 来比较较好的高斯核 yHat01 = lwlrTest(abX[0:99],abX[0:99],abY[0:99],0.1) yHat1 = lwlrTest(abX[0:99],abX[0:99],abY[0:99],1) yHat10 = lwlrTest(abX[0:99],abX[0:99],abY[0:99],10) #对已知的数据计算误差 print 'k=0.1预测已知数据\t',rssError(abY[0:99],yHat01.T) print 'k=1.0预测已知数据\t',rssError(abY[0:99],yHat1.T) print 'k=10预测已知数据\t',rssError(abY[0:99],yHat10.T) yHat01 = lwlrTest(abX[100:199],abX[0:99],abY[0:99],0.1) yHat1 = lwlrTest(abX[100:199],abX[0:99],abY[0:99],1) yHat10 = lwlrTest(abX[100:199],abX[0:99],abY[0:99],10) #对未知的数据计算误差 print 'k=0.1预测未知数据\t',rssError(abY[100:199],yHat01.T) print 'k=1.0预测未知数据\t',rssError(abY[100:199],yHat1.T) print 'k=10预测未知数据\t',rssError(abY[100:199],yHat10.T),"\n"
实验结果:
可以看到在对已知的数据进行预测时,核(k)的值越小越好,因为更加拟合,但是在对未知的数据进行预测时,表现都较差,并且k值越小反而越差。也就是说这种方法,每次必须在整个数据集上运行,要训练所有的数据才行。
现在考虑一个问题,如果特征比样本还多怎么办?n>m,输入矩阵不再是满秩矩阵,在求逆过程中会出错。
为了解决这一问题,提出岭回归,除此之外还有lasso法以及前项逐步回归
岭回归就是在矩阵上加入一个,从而使得矩阵是可以求逆,所以计算公式就变成了这样
求解的公式变为
这新增的一项就称之为惩罚项,能够减少不必要的参数,也称之为 缩减
代码部分:
#计算加入惩罚项后的w,lam是加入的单位矩阵的系数,这里定义为0.2 def ridgeRegres(xMat,yMat,lam = 0.2): xTx = xMat.T * xMat denom = xTx + eye(shape(xMat)[1]) * lam #仍然判断是否可逆,如果lam=0,那么相当于没有加入惩罚项 if linalg.det(denom) == 0.0: print "矩阵不可逆" return ws = denom.I *(xMat.T * yMat) return ws #通过设置不同的lam,对结果造成怎样的影响 def ridgeRegresTest(xArr,yArr): xMat = mat(xArr) yMat = mat(yArr).T #数据标准化,为了让数据对结果的影响是一致的, # 类似于概率论中的数据标准化, ymean = mean(yMat,0) yMat = yMat - ymean xmeans = mean(xMat,0) #取xMat的方差 xVar = var(xMat,0) #(x-均值)/方差 xMat = (xMat-xmeans)/xVar #设置迭代次数 numTestPts = 30 wMat = zeros((numTestPts,shape(xMat)[1])) for i in range(numTestPts): #记录不同的lam值带来的效果,lam的值是指数变化 ws = ridgeRegres(xMat,yMat,exp(i-10)) wMat[i,:] = ws.T return wMat
实验结果
可以看到,当lam非常小的时候,系数与普通回归一样,而lam非常大的时候,所有的回归系数都变为0,可以在中间某处找到一个使得预测结果最好的lam的值。
前向逐步回归算法属于一种贪心算法,即每一步都尽可能减少误差。一开始,所有的权重都设置为1,然后每一步所做的决策是对某个权重增加或减少一个很小的值。
代码:
- #数据标准化
- def regularize(xMat):#regularize by columns
- inMat = xMat.copy()
- inMeans = mean(inMat,0)
- inVar = var(inMat,0)
- inMat = (inMat - inMeans)/inVar
- return inMat
-
- def stageWise(xArr,yArr,eps = 0.01,numIt = 100):
- xMat = mat(xArr);yMat = mat(yArr).T
- ymean = mean(yMat,0)
- yMat = yMat - ymean
- xMat = regularize(xMat)
- m,n = shape(xMat)
- returnMat = zeros((numIt,n))
- 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()
- returnMat[i,:] = ws.T
- return returnMat
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。