《机器学习实战》学习笔记———预测数值型数据:回归


引言

前面所学习的基本都是分类问题,分类问题的目标变量是标称型数据,或者离散型数据。而回归的目标变量为连续型,也即是回归对连续型变量做出预测,最直接的办法是依据输入写出一个目标值的计算公式,这样,对于给定的输入,利用该公式可以计算出相应的预测输出。这个公式称为回归方程,而求回归方程显然就是求该方程的回归系数,而一旦有了这些回归系数,再给定输入,就可以将这些回归系数乘以输入值,就得到了预测值。


用线性回归拟合

回归方程(regression equation),回归系数(regression weights),求回归系数的过程就是回归。说到回归,一般都是指线性回归(linear regression),还存在非线性回归模型。

假定输入数据存放在矩阵 X X 中,而回归系数存放在向量w中,那么对于给定的数据 X1 X 1 ,预测结果将会通过 Y1=XT1w Y 1 = X 1 T w 。问题是,自己有一些 x x 和对应的y,如何找到 w w 呢?常用的方法是找到使误差(一般采用平方误差,否则正负误差会相互抵消)最小的w。平方误差可写作:

mi=1(yixTiw)2 ∑ i = 1 m ( y i − x i T w ) 2

用矩阵表示还可以写成 (YXw)T(YXw) ( Y − X w ) T ( Y − X w ) ,如果对 w w 求导,得到XT(YXw),令其等于零,解出 w w 如下(这里的w^w^是ww的一个最优解、最佳估计):

w^=(XTX)1XTY
公式中的 (XTX)1 ( X T X ) − 1 是对矩阵求逆,需要在代码中判断逆矩阵是否存在,可以计算行列式,如果行列式为零,那么计算逆矩阵的时候会出现错误。这种求解最佳 w w 的方法称为OLS“普通最小二乘法”(ordinary least squares)。

求解完w,可以计算 Y^=Xw^ Y ^ = X w ^ ,然后可以来计算预测值和真实值的相关性。

实例分析

对于下图的散点图
这里写图片描述
可用如下代码完成回归过程

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, cannot do inverse")
        return
    ws = xTx.I * (xMat.T*yMat)
    return ws

再运行如下代码

xArr,yArr=loadDataSet('ex0.txt')
ws = standRegres(xArr,yArr)
print(ws)

可知回归系数为
这里写图片描述
将其图像化出即可得

xMat=mat(xArr)
yMat=mat(yArr)
yHat = xMat*ws
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()

这里写图片描述

最佳拟合直线方法将数据视为直线进行建模。但数据中似乎还存在其他的潜在模式。可根据数据来局部调整预测。


局部加权线性回归

线性回归会出现欠拟合现象,因为它求的是最小均方误差的无偏估计。可以在估计中引入一些偏差,从而降低预测的均方误差。其中一个方法是局部加权线性回归(Locally Weighted Linear Regression,LWLR),该算法中给待测点附近的每个点赋予一定的权重,然后在这个子集上基于最小均方差来进行普通的回归。与kNN一样,此算法每次预测均需事先选取出对应的数据子集。该算法解出的回归系数的形式如下:

w^=(XTWX)1XTWY w ^ = ( X T W X ) − 1 X T W Y
其中 w w 是一个矩阵,用来给每个数据点赋予权重。LWLR使用“核”(与支持向量机中的核类似)来对附近的点赋予更高的权重。核的类型可以自由选择,最常用的是高斯核,高斯核对应的权重如下:

w(i,i)=exp(|x(i)x|2k2)
这样就构建了一个只含对角元素的权重矩阵 W W ,并且点x w(i,i) w ( i , i ) 越近, w(i,i) w ( i , i ) 将会越大,公式中的 k k 需要用户指定,它决定了对附近的点赋予多大的权重。

要注意区分权重W和回归系数 w w ,与kNN一样,该加权模型认为样本点距离越近,越可能符合同一个线性模型。

实例分析

针对上面的数据,运用如下代码

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

运用取不同的k值,得到不同的回归效果

xArr,yArr=loadDataSet('ex0.txt')
ws = standRegres(xArr,yArr)
yHat=lwlrTest(xArr,xArr,yArr,0.003)#or1,0.01
xMat=mat(xArr)
srtInd=xMat[:,1].argsort(0)
xSort=xMat[srtInd][:,0,:]
fig=plt.figure()
ax = fig.add_subplot(111)
ax.plot(xSort[:,1],yHat[srtInd])
ax.scatter(xMat[:,1].flatten().A[0],mat(yArr).T[:,0].flatten().A[0],s=2,c='red')
plt.show()

k k =1.0时

k=0.01时
这里写图片描述
k k =0.03时
这里写图片描述
k的选取有事能挖出数据的潜在规律,有时考虑太多的噪声,进而导致了过拟合现象,如 k=0.03 k = 0.03 时。


实例:预测鲍鱼的年龄

将回归用于真实数据,有一份来自UCI数据聚合的数据,记录了鲍鱼(一种介壳类水生动物)的年龄。鲍鱼年龄可以从鲍鱼壳的层数推算得到。
这里写图片描述
加入如下代码

def rssError(yArr,yHatArr): 
    return ((yArr-yHatArr)**2).sum()

再添加:

abX,abY=loadDataSet('abalone.txt')
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)
rssError(abY[0:99],yHat01.T)
rssError(abY[0:99],yHat1.T)
rssError(abY[0:99],yHat10.T)

可得到三种不同 k k <script type="math/tex" id="MathJax-Element-70">k</script>值得误差
这里写图片描述
可以看到。使用较小的核将得到较低的误差。但有时不用最小的核的原因是会造成过拟合,对新数据不一定能达到最好的预测效果。比如将上述预测用于在新数据上

abX,abY=loadDataSet('abalone.txt')
yHat01=lwlrTest(abX[100:199],abX[0:99],abY[0:99],0.1)
print(rssError(abY[100:199],yHat01.T))
yHat1=lwlrTest(abX[100:199],abX[0:99],abY[0:99],1)
print(rssError(abY[100:199],yHat1.T))
yHat10=lwlrTest(abX[100:199],abX[0:99],abY[0:99],10)
print(rssError(abY[100:199],yHat10.T))

这里写图片描述
可看出,在上面三个参数,核大于10时的测试误差最小,但在训练集上的误差是最大的。


代码

from numpy import *
import matplotlib.pyplot as plt

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, cannot do inverse")
        return
    ws = xTx.I * (xMat.T*yMat)
    return ws

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

def lwlrTestPlot(xArr,yArr,k=1.0):  #same thing as lwlrTest except it sorts X first
    yHat = zeros(shape(yArr))       #easier for plotting
    xCopy = mat(xArr)
    xCopy.sort(0)
    for i in range(shape(xArr)[0]):
        yHat[i] = lwlr(xCopy[i],xArr,yArr,k)
    return yHat,xCopy

def rssError(yArr,yHatArr): #yArr and yHatArr both need to be arrays
    return ((yArr-yHatArr)**2).sum()

参考文献

《机器学习实战》
https://blog.csdn.net/namelessml/article/details/52554754


评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值