计算预测值序列和真实值的匹配程度,可以计算两个序列的相关系数,corrcoef(yHat.T, yMat)。
from numpy import * 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 ii in range (numFeat): lineArr.append(float(curLine[ii])) dataMat.append(lineArr) labelMat.append(float(curLine[-1])) return dataMat, labelMat ### Standard Regression ### def stdRegres(xArr, yArr): xMat = mat(xArr) yMat = mat(yArr).T xTx = xMat.T * xMat if linalg.det(xTx) == 0.0: print "This matrix is singular, cannot do inverse" return ws = xTx.I * (xMat.T * yMat) return ws def testing(xArr, yArr, ws): xMat = mat(xArr) yMat = mat(yArr) yHat = xMat * ws corr = corrcoef(yHat.T, yMat) 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() return corr
基本线性回归有可能出现欠拟合现象,所以有些方法允许在估计中引入一些偏差(模型预测值与数据之间的差异),从而降低预测的均方误差。
LWLR即是利用了此原理,给待预测点附近的每个点赋予一定的权重(LR则是所有点权重相同),然后在这个子集上(实际这里仍为整个训练数据集)执行LR,最终解出的回归系数为。权重矩阵用来对每个数据点赋予权重。LWLR使用与SVM类似的核来对附近的点赋予更高的权重,最常用的核就是高斯核,高斯核对应的权重矩阵为一对角矩阵,其中为待预测点,参数决定了对附近的点赋予多大的权重,越小,相应更关注附近的点而忽视较远的点。当为1时,基本相当于LR,当很小时,易导致过拟合。
由于LWLR对每个点做预测时都必须使用这个数据集,无疑增加了计算量。但仔细观察,当很小时,可以看到较远的数据点的权重都接近零,从而可以去除这些点参与计算,缓解因计算量增加带来的问题,即方差越小(也说核越大),相当于对应模型的复杂度越低。
### Locally Weighted Linear Regression ### def lwlr(testPoint, xArr, yArr, k=1.0): xMat = mat(xArr) yMat = mat(yArr).T N = shape(xMat)[0] weights = mat(eys(N)) for ii in range(N): diffMat = testPoint - xMat[ii,:] weights[ii,ii] = 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): N = shape(testArr)[0] yHat = zeros(N) for ii in range(N): yHat[ii] = lwlr(testArr[ii], xArr, yArr, k) return yHat def rssError(yArr, yHatArr): return ((yArr-yHatArr)**2).sum()
当数据的特征数高于样本数,或者特征之间高度相关时,会导致奇异,从而限制了LR和LWLR的应用。这时需要考虑使用缩减法。
缩减法,可以理解为对回归系数的大小施加约束后的LR,也可以看作是对一个模型增加偏差(模型预测值与数据之间的差异)的同时减少方差(模型之间的差异)。
一种缩减法是岭回归(L2),另一种是lasso法(L1),但由于计算复杂,一般用效果差不多但更容易实现的前向逐步回归法。下面针对这两种方法详细介绍。
岭回归实际上相当于有约束条件情况的最小二乘法回归,即回归系数为。惩罚的引入能够减少不重要的参数,从而更好滴理解数据。参数的选择,需要将原训练数据分成训练数据和测试数据,在训练数据上训练回归系数,然后在测试数据上测试性能,通过选取不同的来重复上述过程,最终选取使得预测误差最小的。
### Ridge Regression ### def ridgeRegres(xMat, yMat, lam=0.2): xTx = xMat.T * xMat denom = xTx + lam*eye(shape(xMat)[1]) 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) xMeans = mean(xMat, 0) xVar = var(xMat, 0) xMat = (xMat-xMeans) / xVar yMat = mat(yArr).T yMean = mean(yMat, 0) yMat = yMat - yMean numTestPts = 30 wMat = zeros((numTestPts, shape(xMat)[1])) for ii in range(numTestPts): ws = ridgeRegres(xMat, yMat, exp(ii-10)) wMat[ii,:] = ws.T return wMat
和岭回归相似的lasso法是利用约束条件,虽然很相似,但在这种约束下解除回归系数,需要二次规划算法,从而大大增加了计算复杂度。前向逐步算法可以得到和lasso法差不多的效果但更加简单,其伪代码如下:
观察每次循环得到的回归系数,有时一段时间后系数就已经饱和并在特定值之间来回震荡,这是由于步长设置的过大。
当构建一个模型后,可以运行该算法找到重要的特征,这样就有可能及时停止对那些不重要特征(非常小,趋于零的回归系数)的收集。
### Front stage-wise Regression ### def stageWise(xArr, yArr, step=0.01, numIt=100) : xMat = mat(xArr) xMat = regularize(xMat) yMat = mat(yArr).T yMean = mean(yMat) yMat = yMat - yMean N, n = shape(xMat) returnMat = zeros((numIt, n)) ws = zeros((n,1)) wsTest = ws.copy() weMax = ws.copy() for ii in range(numIt) : print ws.T lowestErr = inf for jj in range(n) : for sign in [-1,1] : wsTest = ws.copy() wsTest[jj] += step*sign yTest = xMat*wsTest rssE = rssError(yMat.A, yTest.A) if rssE < lowestErr : lowestErr = rssE wsMax = wsTest ws = wsMax.copy() returnMat[ii,:] = ws.T return returnMat