机器学习实战-预测数值型数据:回归

本章首先介绍线性回归,包括其名称的由来和实现。接下来本章将讨论回归在“欠拟合”的情况下的缩减技术。最后将融合所有技术预测鲍鱼年龄和玩具售价。

线性回归
优点:结果易于理解,计算上不复杂
缺点:对非线性的数据拟合不好
适用数据类型:数值型和标称型数据
下面开始计算标准回归函数和数据导入函数

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 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:#计算行列式
        print "This matrix is singular, connot do inverse"
        return
    ws = xTx.I * (xMat.T*yMat)#.I是逆
    ws = linalg.solve(xTx,xMat.T*yMat)#一上一句同义
    return ws

下面开始测试数据

In [30]: import regression
    ...: xArr,yArr = regression.loadDataSet('ex0.txt')
    ...: ws = regression.standRegres(xArr,yArr)
In [34]: xArr[0:2]
Out[34]: [[1.0, 0.067732], [1.0, 0.42781]]

第一个值为1,即x0。第二个值x1,就是横坐标

In [35]: ws
Out[35]: 
matrix([[ 3.00774324],
        [ 1.69532264]])

ws存放的是回归系数。下面开始使用新的ws计算yHat:

xMat = mat(xArr)
yMat = mat(yArr)
yHat = xMat*ws

然后绘制散点图

import matplotlib.pyplot as plt
fig = plt.figure()
ax = fig.add_subplot(111)
ax.scatter(xMat[:,1].flatten().A[0],yMat.T[:,0])
xCopy = xMat.copy()
xCopy.sort(0)#先排序
yHat = xCopy*ws
ax.plot(xCopy[:,1],yHat)
plt.show()
最佳拟合直线

在Python中,NumPy库提供了相关系数的计算方法:可以通过命令corrcoef(yEstimate,yActual)来计算相关性。

In [41]: yHat = xMat*ws

In [42]: corrcoef(yHat.T,yMat)
Out[42]: 
array([[ 1.        ,  0.98647356],
       [ 0.98647356,  1.        ]])

线性回归有时会出现欠拟合的现象,因为她求的是具有最小均方误差的无偏估计。所以有些时候允许在估计中引入一些偏差,从而降低预测的均分误差。

#局部加权线性回归函数
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):
        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):
    m = shape(testArr)[0]
    yHat = zeros(m)
    for i in range(m):
        yHat[i] = lwlr(testArr[i],xArr,yArr,k)
    return yHat

并输入如下命令

xArr,yArr = regression.loadDataSet('ex0.txt')
#对单点估计
In [53]: yArr[0]
Out[53]: 3.176513

In [58]: regression.lwlr(xArr[0],xArr,yArr,1.0)
Out[58]: matrix([[ 3.12204471]])

In [59]: regression.lwlr(xArr[0],xArr,yArr,0.001)
Out[59]: matrix([[ 3.20175729]])

yHat = regression.lwlrTest(xArr,xArr,yArr,0.003)#为了得到数据集所有的点

然后开始绘图,首先对xArr排序:

xMat=mat(xArr)
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],mat(yArr).T.flatten().A[0], s=2,c='red')#c:散点的颜色,s:散点的大小 ,alpha:是透明程度,make:散点样式
plt.show()

下面是三种k的取值下的结果图:

k=0.003
k=0.01
k=1.0

k=0.003的时候显然是过拟合,k=1.0的时候和最小二乘法差不多。但局部加权线性回归也存在计算量的问题,因为他对每个点做预测时都必须使用整个数据集。
接下来,我们将回归用于真实数据。

#加入regression,py
def rssError(yArr,yHatArr):
    return ((yArr-yHatArr)**2).sum()

先计算数据

abX,abY = regression.loadDataSet('abalone.txt')
yHat01 = regression.lwlrTest(abX[0:99],abX[0:99],abY[0:99],0.1)
yHat1 = regression.lwlrTest(abX[0:99],abX[0:99],abY[0:99],1)
yHat10 = regression.lwlrTest(abX[0:99],abX[0:99],abY[0:99],10)

然后计算错误指标

In [92]: regression.rssError(abY[0:99],yHat01.T)
Out[92]: 56.820227823583345

In [93]: regression.rssError(abY[0:99],yHat1.T)
Out[93]: 429.89056187016047

In [94]: regression.rssError(abY[0:99],yHat10.T)
Out[94]: 549.11817088250825

但是k=0.1显然是过拟合现象

我们使用新的数据来看预测效果

In [102]: yHat01 = regression.lwlrTest(abX[100:199],abX[0:99],abY[0:99],0.1)
     ...: regression.rssError(abY[100:199],yHat01.T)
     ...: 
Out[102]: 23989.306318956587

In [103]: yHat1 = regression.lwlrTest(abX[100:199],abX[0:99],abY[0:99],1)
     ...: regression.rssError(abY[100:199],yHat1.T)

Out[103]: 573.52614418975463

In [104]: yHat10 = regression.lwlrTest(abX[100:199],abX[0:99],abY[0:99],10)
     ...: regression.rssError(abY[100:199],yHat10.T)

上述结果可以看出,核大小等于10的时候误差反而最小。
接下来和简单线性回归作比较:

In [105]: ws = regression.standRegres(abX[0:99],abY[0:99])

In [106]: yHat = mat(abX[100:199])*ws

In [107]: regression.rssError(abY[100:199],yHat.T.A)
Out[107]: 518.636315324674

简单线性回归达到了与局部加权线性回归类似的效果。这说明:必须在未知数据上比较效果才能选取最佳模型。
局部加权线性回归的问题在于,每次必须在整个数据集上运行。
下面使用岭回归处理数据

def ridgeRegres(xMat,yMat,lam=0.2):#计算回归函数,lam即Python保留关键字lambda
    xTx = xMat.T*xMat
    denom = xTx + eye(shape(xMat)[1])*lam#岭
    if linalg.det(denom) == 0.0:
        print "This martrix 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
    xMeans = mean(xMat,0)
    xVar = var(xMat,0)
    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

然后开始绘图

import regression
abX,abY = regression.loadDataSet('abalone.txt')
ridgeWeights = regression.ridgeTest(abX,abY)
import matplotlib.pyplot as plt
fig = plt.figure()
ax = fig.add_subplot(111)
ax.plot(ridgeWeights)
plt.show()
log(lambda)

在最左边,lambda最小的时候,可以得到所有系数的原始值(与线性回归一致);在最右边,系数全部缩减为0;在中间部分的某值将可以取得最好的预测效果。为了定量地找到最佳参数值,还需要进行交叉验证。

前后逐步回归可以得到和lasso差不多的效果,但更加简单。它属于一种贪心算法,即每一步都尽可能减少误差。一开始,所有的权重都设为1,然后每一步所做的决策是对某个权重增加或者减少一个很小的值。

伪代码如下:

数据标准化,使其分布满足0均值和单位方差:
  在每轮迭代过程中:
    设置当前最小误差Lowe'sError为正无穷
    对每个特征:
      增大或缩小:
        改变一个系数得到一个新的w
        计算新W下的误差
        如果误差Error小于当前最小误差Lowe'sError:设置Wbest等于当前的W
      将W设置为新的Wbest
#前向逐步线性回归
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
                      
def regularize(xMat):
    inMat = xMat.copy()
    inMeans = mean(inMat,0)   
    inVar = var(inMat,0)
    inMat = (inMat - inMeans)/inVar
    return inMat

并且测试这段代码

In [22]: import regression
    ...: xArr,yArr = regression.loadDataSet('abalone.txt')
    ...: regression.stageWise(xArr,yArr,0.01,200)
    ...: 
[[ 0.  0.  0.  0.  0.  0.  0.  0.]]
[[ 0.    0.    0.    0.01  0.    0.    0.    0.  ]]
[[ 0.    0.    0.    0.02  0.    0.    0.    0.  ]]
[[ 0.    0.    0.    0.03  0.    0.    0.    0.  ]]
[[ 0.    0.    0.    0.04  0.    0.    0.    0.  ]]
[[ 0.    0.    0.    0.05  0.    0.    0.    0.  ]]
[[ 0.    0.    0.    0.06  0.    0.    0.    0.  ]]
[[ 0.    0.    0.01  0.06  0.    0.    0.    0.  ]]
[[ 0.    0.    0.01  0.06  0.    0.    0.    0.01]]
[[ 0.    0.    0.01  0.06  0.    0.    0.    0.02]]
#省略部分
[[ 0.05  0.    0.09  0.03  0.31 -0.64  0.    0.36]]
[[ 0.04  0.    0.09  0.03  0.31 -0.64  0.    0.36]]
[[ 0.05  0.    0.09  0.03  0.31 -0.64  0.    0.36]]
[[ 0.04  0.    0.09  0.03  0.31 -0.64  0.    0.36]]
[[ 0.05  0.    0.09  0.03  0.31 -0.64  0.    0.36]]
[[ 0.04  0.    0.09  0.03  0.31 -0.64  0.    0.36]]
[[ 0.05  0.    0.09  0.03  0.31 -0.64  0.    0.36]]
[[ 0.04  0.    0.09  0.03  0.31 -0.64  0.    0.36]]

可以发现w1和w6都是0,这表明他们不对目标值产生影响。在eps设为0.01的情况下,一段时间后系数就已经饱和并在特定值之间来回震荡,这是因为步长太大的缘故。
下面试试更小的步长和更多的步数:

regression.stageWise(xArr,yArr,0.001,5000)

array([[ 0.   ,  0.   ,  0.   , ...,  0.   ,  0.   ,  0.   ],
       [ 0.   ,  0.   ,  0.   , ...,  0.   ,  0.   ,  0.   ],
       [ 0.   ,  0.   ,  0.   , ...,  0.   ,  0.   ,  0.   ],
       ..., 
       [ 0.043, -0.011,  0.12 , ..., -0.963, -0.105,  0.187],
       [ 0.044, -0.011,  0.12 , ..., -0.963, -0.105,  0.187],
       [ 0.043, -0.011,  0.12 , ..., -0.963, -0.105,  0.187]])

接下来把这些结果和最小二乘法进行比较,后者的结果可以通过如下代码获得:

In [25]: xMat = mat(xArr)
    ...: yMat = mat(yArr).T
    ...: xMat = regression.regularize(xMat)
    ...: yM = mean(yMat,0)
    ...: yMat = yMat - yM
    ...: weights = regression.standRegres(xMat,yMat.T)
In [27]: weights.T
Out[27]: 
matrix([[ 0.0430442 , -0.02274163,  0.13214087,  0.02075182,  2.22403814,
         -0.99895312, -0.11725427,  0.16622915]])

逐步线性回国算法主要的优点在于他可以帮助人们理解现有的模型并做出改进。当应用缩减方法(如逐步回归或岭回归)时,模型也就增加了偏差,与此同时却减小了模型的方差。

下面的部分涉及谷歌api,但是原文太旧,找不到新版怎么操作,暂时搁浅...
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,319评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,801评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,567评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,156评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,019评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,090评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,500评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,192评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,474评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,566评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,338评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,212评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,572评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,890评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,169评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,478评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,661评论 2 335

推荐阅读更多精彩内容