【机器学习实战-8章】回归

回归是前述监督学习方法的延续。
监督学习指的是有目标变量或预测目标的机器学习方法。回归与分类的不同,就在于其目标变量是连续数值型

8.1 用线性回归找到最佳拟合直线

  1. 线性回归优缺点:
    优点:结果易于理解,计算上不复杂
    缺点:对非线性数据拟合不好
  2. 回归的目的是预测数值型的目标值。
    最直接的方法是依据输入写出一个目标值的计算公式。这就是所谓的回归方程(regression equation).方程中的常数为回归系数,求这些系数的过程就是回归。(注意区分logistic回归与本章的回归的区别)
  3. 回归的一般方法
    1)收集数据
    2)准备数据:回归需要数值型数据,标称型数据将被转成二值型数据
    3)分析数据:给出数据的可视化二维图将有助于对数据做出理解和分析,在采用缩减法求得新回归洗漱之后,可以将新拟合线绘在图上作为对比
    4)训练算法:找到回归系数
    5)测试算法:使用R^2或者预测值和数据的拟合度,来分析模型的结果
    6)使用算法:使用回归,可以在给定输入的时候预测出一个数值,这是对分类方法的提升,因为这样可以预测连续型数据而不仅仅的离散的类别标签。
    假定数据存放在矩阵X中,而回归系数存放在向量W中。对于戈丁的数据X1,预测结果将会通过Y1 = X1^2W给出。怎么找到这个W呢?一个常用的方法就是找出使平方误差最小的W。
    在logistic回归中,我们使用平方误差,然后寻找平方误差梯度的最大方向来更新alpha回归系数
    在线性回归中,对平方误差关于回归系数求导置0,取得系数值。得W = (XTX)-1X^Ty.对W的求解,除了矩阵方法,还有牛顿最小二乘法,后者迭代次数更少。
    两者的求解回归系数过程并没什么不同,最终收敛点都是导数为0或接近于0的点。Logistic回归接近的可能是一个局部最小,而线性回归是全局最小。最终两个的假设函数不同,logistic回归使用的是sigmoid函数,而线性回归使用的是线性函数Y1 = X1^2W。最重要的一点是一个针对的是离散型数据,一个是连续型数据
  4. 如何比较模型的拟合效果-相关系数
    计算预测值yHat序列和真实值y序列的匹配程度,可通过计算两个序列的相关系数实现
    np.corrcoef(yHat.T,yMat)
"""标注回归函数和数据导入函数"""
import numpy as np
import matplotlib.pyplot as plt
from numpy.ma import exp

#标准线性回归算法
def loadDataSet(filename):
    numFeat = len(open(filename).readline().split('\t')) - 1
    dataArr = []
    labelArr = []
    fr = open(filename)
    for line in fr.readlines():
        lineArr = []
        curline = line.strip().split('\t')
        for i in range(numFeat):
            lineArr.append(float(curline[i]))
        dataArr.append(lineArr)
        labelArr.append(float(curline[-1]))
    return dataArr,labelArr
def standRegress(xArr,yArr):
    """标准回归函数:W = (X^TX)^-1X^Ty"""
    xMat = np.mat(xArr)
    yMat = np.mat(yArr)
    xTx = xMat.T*xMat
    #判断矩阵是否满秩,矩阵求逆
    if np.linalg.det(xTx) == 0: #np.linalg.det(xTx)行列式
        print("this matrix is singular,cannot do inverse")
        return 
    weights = xTx.I * (xMat.T*yMat.T) #xTx.I矩阵求逆
    return weights
def plotLine(xArr,yArr,weights):
    """绘制拟合直线"""
    weights = np.mat(weights)
    xMat = np.mat(xArr)
    yMat = np.mat(yArr)
    fig = plt.figure()
    ax = fig.add_subplot(111)
    ax.scatter(xMat[:,1].flatten().A[0],yMat.T[:,0].flatten().A[0])
    #上述命令创建了图像并绘出了原始的数据。为了绘制计算出的最佳拟合直线,需要绘出yHat的值。如果直线上的数据点次序混乱,绘图时将会出现问题,所以首先要将点按升序排列
    xCopy = xMat.copy()
    xCopy.sort(0)
    yHat = xCopy*weights
    ax.plot(xCopy[:,1],yHat)
    plt.show()
    return yHat,yMat
def coef(yHat,yMat):
    coefResult = np.corrcoef(yHat.T,yMat)
    return coefResult

8.2 局部加权线性回归

线性回归的一个问题是有可能出现欠拟合现象,因为它求的是具有最小均方误差的无偏估计。
  1. 无偏估计
    设总体ξ的概率分布函数为F(x;θ),其中x为变元,θ∈Θ为未知参数,Θ称为参数空间。(ξ1,ξ2,…,ξn)为取自总体ξ的随机样本,若 (ξ1,…,ξn)是参数θ的一个估计量,且对一列θ∈Θ关系式Eθ(θ’(ξ1,…,ξn))=θ成立,其中,E[θ’(ξ1,ξ2,…,ξn)]表示数学期望,则称 (ξ1…ξn)为θ的无偏估计,并称 具有无偏性。
  2. 局部加权线性回归(locally weighted linear regression,LWLR)
    在该算法中,我们给待预测点附近的每个点赋予一定的权重,然后在这个子集上基于最小均方差来进行普通的回归。与KNN一样,这种算法每次预测均需要事先选取出对应的数据子集。该算法解出回归系数w的形式如下:
    W = (X.TWX).I*X.TWy 其中W是一个矩阵,用来给每个数据点赋予权重
    LWLR使用"核"(与支持向量机中的核类似)来对附近的点赋予更高的权重。核的类型可以自由选择,最常用的核就是高斯核。
    在这里插入图片描述
#局部加权线性回归算法
#局部加权线性回归建立模型,可以得到比普通线性回归更好的效果
#问题在于:LWLR每次必须在整个数据集上运行,也就是说为了做出预测,必须保存所有的训练数据
def lwlr(testPoint,xArr,yArr,k = 1.0):
    """局部加权线性回归函数:W = (X.TWX).I*X.TWy
    给定X空间中的任意一点,计算出对应的预测值yHat
    随着样本点与待预测点距离的递增,权重将以指数级衰减。参数k控制衰减的速度。"""
    xMat = np.mat(xArr)
    yMat = np.mat(yArr).T
    m = np.shape(xMat)[0]
    weights = np.mat(np.eye((m)))   #生成对角单位矩阵
    for j in range(m):
        diffMat = testPoint - xMat[j,:]
        weights[j,j] = exp(diffMat*diffMat.T/(-2.0*k**2))   #高斯函数,随着样本点与待预测点距离的递增,权重将以指数级衰减。参数k控制衰减的速度。
    xTx = xMat.T*(weights*xMat)
    if np.linalg.det(xTx) == 0.0:
        print("this matrix is singular,cannot do inverse")
        return 
    ws = xTx.T * (xMat.T*(weights*yMat))
    return ws
def lwlrTest(testArr,xArr,yArr,k=1.0):
    m = np.shape(testArr)[0]
    yHat = np.zeros(m)
    for i in range(m):
        ws = lwlr(testArr[i],xArr,yArr,k)
        yHat[i] = testArr[i]*ws
    return yHat
def rssError(yArr,yHatArr):
    sumError = ((yArr - yHatArr)**2).sum()
    errorRate = sumError/len(yArr)
    return errorRate

8.3 缩减系数来“理解”数据

如果数据的特征比样本点还多怎么办?

8.4.1 岭回归

  1. 简单来说,岭回归就是在矩阵X.TX上加上一个CI从而使矩阵非奇异,进而能对X.TX+CI求逆。矩阵I是一个mxm的单位矩阵。C是一个用户定义的数值。C=lambda
    最终:w = (X.TX+CI).IX.Ty
    通过引入C来限制了所有W之和,通过引入该惩罚项,能够减少不重要的参数,这就是“缩减(shrinkage)”
  2. 在增加如下约束时,普通的最小二乘法回归会得到与岭回归一样的公式。
    Σ(Wi)^2 <= lambda i={1-n},n为特征数
  3. 如何得到lambda?
    预测误差最小化得到:数据获取之后,首先抽一部分数据用于测试,剩余的作为训练集用于训练参数W。训练完毕后在测试集上测试预测性能。通过选取不同的C来重复上述测试过程,最终得到一个使预测误差最小的C。
#岭回归
#w = (X.TX+CI).IX.Ty
def ridgeRegres(xMat,yMat,lam=0.2):
    """给定lambda下的岭回归求解"""
    xTx = xMat.T*xMat
    denom = xTx + np.eye(np.shape(xMat)[1])*lam #X.TX+CI
    if np.linalg.det(denom) == 0.0: #如果lambda设置为0,仍可能出现错误,故仍需判断行列式
        print('this matrix is singular,cannot do inverse')
        return 
    ws = denom.I * (xMat.T*yMat)    #w = (X.TX+CI).IX.Ty
    return ws
def ridgeTest(xArr,yArr):
    """对特征进行标准化处理,使每维特征具有相同的重要性"""
    xMat = np.mat(xArr)
    yMat = np.mat(yArr).T
    #数据标准化:所有特征都减去各自均值并除以方差
    yMean = np.mean(yMat,0)
    yMat = yMat - yMean
    xMeans = np.mean(xMat,0) 
    print(xMeans)
    xVar = np.var(xMat,0)
    xMat = (xMat - xMeans)/xVar
    numTestPts = 30 #30个不同的lambda
    wMat = np.zeros((numTestPts,np.shape(xMat)[1]))
    for i in range(numTestPts):
        ws = ridgeRegres(xMat,yMat,exp(i-10))   #lambda以指数变化
        wMat[i,:] = ws.T
    return wMat
def regularize(xMat):
    """标准化处理"""
    xMean = np.mean(xMat,0)
    xMat = xMat - xMean
    xVar = np.var(xMat,0)
    returnMat = xMat/xVar
    return returnMat

8.4.2 lasso

    与岭回归类似,lasso也对回归系数做了限定:
        Σ |Wi| <= lambda
    在lambda足够小的时候,一些系数会因此被迫缩减到0

8.4.3 前向逐步回归

  1. 前向逐步回归是一种贪心算法,即每一步都尽可能减少误差,一开始,所有的权重都设为1,然后每一步所做的决策是对某个权重增加或减少一个很小的值。
  2. 该算法的伪代码:
    数据标准化,使其分布满足0均值和单位方差
    在每轮迭代过程中:
    设置当前最小误差lowestError为正无穷
    对每个特征:
    增大或缩小:
    改变一个系数得到一个新的W
    计算新W下的误差
    如果五擦汗Error小于当前最小误差lowestError:
    设置Wbest等于当前的W
    将W设置为新的Wbest
  3. 当构建了一个模型后,可以运行该算法找出重要的特征(不重要的特征,算法会给出权重为0)
    当应用了缩减方法(如逐步线性回归或岭回归)时,模型也就增加了偏差(bias),与此同时却减小了模型的方差。
#前向逐步回归
def stageWise(xArr,yArr,eps=0.01,numIt=100):
    """前向逐步回归:输入数据xArr,预测变量yArr,每次迭代需要调整的步长eps,迭代次数numIt\n
    贪心算法在所有特征上运行两次for循环,分别计算增加或减少该特征对误差的影响。
    在参数设置为0.01的情况下,一段时间后系数就已经饱和并在特定值之间来回震荡,这是由于步长eps太大的缘故。
    """
    xMat = np.mat(xArr)
    yMat = np.mat(yArr).T
    yMean = np.mean(yMat,0)
    yMat = yMat - yMean
    xMat = regularize(xMat)  #按照均值为0方差为1进行标准化处理
    m,n = np.shape(xMat)
    returnMat = np.zeros((numIt,n))
    ws = np.zeros((n,1))
    wsTest = ws.copy()
    wsMax = ws.copy()
    for i in range(numIt):
        print(ws.T)
        lowestError = np.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
 if __name__ == '__main__':
    #测试普通线性回归
    # xArr,yArr = loadDataSet('./M_08_回归/ex0.txt')
    # weights = standRegress(xArr,yArr)
    # print('==============')
    # print(weights)
    # yHat,yMat = plotLine(xArr,yArr,weights)
    # print(coef(yHat,yMat))
    #预测鲍鱼的年龄
    abX,abY = loadDataSet('./M_08_回归/abalone.txt')
    #不同的K值
    # 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)
    # errorRate01 = rssError(abY[0:99],yHat01.T)
    # errorRate1 = rssError(abY[0:99],yHat1.T)
    # errorRate10 = rssError(abY[0:99],yHat10.T)
    #前向逐步回归
    returnMat = stageWise(abX,abY,0.01,200)
    print(returnMat)

8.5 权衡偏差与方差

两组随机样本集拟合得到两组不同的回归系数,这些系数间的差异大小也就是模型方差大小的反映。

8.6 示例:预测乐高玩具套装的价格

用回归法预测乐高套装的价格
1.收集数据:用google shopping的API收集数据
2.准备数据:从返回的json数据中抽取价格
3.分析数据:可视化并观察数据
4.训练算法:采用不同的模型,采用逐步线性回归和直接的线性回归模型
5.测试算法:使用交叉验证来测试不同的模型,分析哪个效果最好
6.使用算法:生成数据模型

import numpy as np
import random
from time import sleep
import json
from urllib.request import urlopen
from numpy.ma import exp
def searchForSet(retX, retY, setNum, yr, numPce, origPrc):
    sleep(10)
    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 = 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)
def rssError(yArr,yHatArr):
    sumError = ((yArr - yHatArr)**2).sum()
    errorRate = sumError/len(yArr)
    return errorRate
#岭回归
def ridgeRegres(xMat,yMat,lam=0.2):
    """给定lambda下的岭回归求解"""
    xTx = xMat.T*xMat
    denom = xTx + np.eye(np.shape(xMat)[1])*lam #X.TX+CI
    if np.linalg.det(denom) == 0.0: #如果lambda设置为0,仍可能出现错误,故仍需判断行列式
        print('this matrix is singular,cannot do inverse')
        return 
    ws = denom.I * (xMat.T*yMat)    #w = (X.TX+CI).IX.Ty
    return ws
def ridgeTest(xArr,yArr):
    """对特征进行标准化处理,使每维特征具有相同的重要性"""
    xMat = np.mat(xArr)
    yMat = np.mat(yArr).T
    #数据标准化:所有特征都减去各自均值并除以方差
    yMean = np.mean(yMat,0)
    yMat = yMat - yMean
    xMeans = np.mean(xMat,0) 
    print(xMeans)
    xVar = np.var(xMat,0)
    xMat = (xMat - xMeans)/xVar
    numTestPts = 30 #30个不同的lambda
    wMat = np.zeros((numTestPts,np.shape(xMat)[1]))
    for i in range(numTestPts):
        ws = ridgeRegres(xMat,yMat,exp(i-10))   #lambda以指数变化
        wMat[i,:] = ws.T
    return wMat
def crossValidation(xArr,yArr,numVal = 10):
    """numVal:交叉验证的次数"""
    m = len(yArr)
    indexList = range(m)
    errorMat = np.zeros((numVal,30))
    for i in range(numVal):
        trainX = []
        trainY = []
        testX = []
        testY = []
        random.shuffle(indexList)   #实现训练和测试数据的随机抽取
        #分为训练集和测试集
        for j in range(m):
            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)
        for k in range(30):
            #用训练时的参数将测试数据标准化
            matTestX = np.mat(testX)
            matTrainX = np.mat(trainX)
            meanTrain = np.mean(matTrainX,0)
            varTrain = np.var(matTrainX,0)
            matTestX = (matTestX - meanTrain)/varTrain
            yEst = matTestX*np.mat(wMat[k,:]).T+np.mean(trainY)
            errorMat[i,k] = rssError(yEst.T.A,np.array(testY))
    meanErrors = np.mean(errorMat,0)
    minMean = float(min(meanErrors))
    bestWeights = wMat[np.nonzero(meanErrors==minMean)]
    print("the best regression params :",bestWeights)
    xMat = np.mat(xArr)
    yMat = np.mat(yArr).T
    meanX = np.mean(xMat,0)
    varX = np.var(xMat,0)
    #数据还原-便于与其他的算法模型拟合结果进行比较
    unReg = bestWeights/varX
    print('the best model from Ridge Regression is:\n',unReg)
    print('with constant term',-1*sum(np.multiply(meanX,unReg))+np.mean(yMat))
if __name__ == '__main__':
    lgX = []; lgY = []
    setDataCollect(lgX,lgY)
    #需要添加对应常数项的特征X0(X0=1)
    m,n = np.shape(lgX)
    print(m)
    lgX1 = np.mat(np.ones(m,n+1))
    lgX1[:,1:5] = np.mat(lgX)

    

8.7 小结

在回归方程中,求得特征对应的最佳回归系数的方法是最小化误差的平方和
给定输入矩阵X,如果X.TX的逆存在,回归法都可以直接使用
数据集上计算出的回归方程并不一定意味着它是最佳的,可以使用预测值yHat和原始值y的相关性来度量回归方程的好坏
岭回归是缩减法的一种,相当于对回归系数的大小施加了限制。另一种很好的缩减法是lasso.lasso难以求解,但可以使用计算简便的逐步线性回归方法来求得近似结果
缩减法可以看作是对一个模型增加偏差的同时减小方差的一种方法。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值