机器学习实战5(回归篇)

目录

1、回归

2、回归代码

3、预测鲍鱼的年龄


1、回归

        前面的文章介绍了很多分类算法,分类的目标变量是标称型数据,而本文将会对连续型的数据做出预测。主要讲解简单的线性回归和局部加权线性回归,并通过预测鲍鱼年龄的实例进行实战演练。

        说到回归,一般都是指线性回归(linear regression),所以本文里的回归和线性回归代表同一个意思。线性回归意味着可以将输入项分别乘以一些常量,再将结果加起来得到输出。需要说明的是,存在另一种成为非线性回归的回归模型,该模型不认同上面的做法,比如认为输出可能是输入的乘积。

        应该怎么从一大堆数据里求出回归方程呢?假定输入数据存放在矩阵X中,结果存放在向量y中:

         而回归系数存放在向量w中:

        应当怎样从一大堆数据里求出回归方程呢?假定输人数据存放在矩阵x中,而回归系数存放在向量w中。那么对于给定的数据x,预测结果将会通过y=w^{\tau } x给出。现在的问题是,手里有一些x和对应的y,怎样才能找到w呢?一个常用的方法就是找出使误差最小的w。这里的误差是指预测y值和真实y值之间的差值,使用该误差的简单累加将使得正差值和负差值相互抵消,所以我们采用平方误差。

         用矩阵表示还可以写做:

         为啥能这么变化,记住一个前提:若x为向量,则默认x为列向量,x^T为行向量。将上述提到的数据矩阵X和标签向量y带进去,就知道为何这么变化了。在继续推导之前,我们要先明确一个目的:找到w,使平方误差和最小。因为我们认为平方误差和越小,说明线性回归拟合效果越好。现在,我们用矩阵表示的平方误差和对w进行求导:

         得到:

         令上述公式等于0,得到:

         w上方的小标记表示,这是当前可以估计出的w的最优解。从现有数据上估计出的w可能并不是数据中的真实w值,所以这里使用了一个"帽"符号来表示它仅是w的一个最佳估计。

        值得注意的是,上述公式中包含逆矩阵,也就是说,这个方程只在逆矩阵存在的时候使用,也即是这个矩阵是一个方阵,并且其行列式不为0。

        述的最佳w求解是统计学中的常见问题,除了矩阵方法外还有很多其他方法可以解决。通过调用NumPy库里的矩阵方法,我们可以仅使用几行代码就完成所需功能。该方法也称作OLS, 意思是“普通小二乘法”(ordinary least squares)。

2、回归代码

        数据集:数据在这!

        提取码:3liq

        第一列都为1.0,即x0。第二列为x1,即x轴数据。第三列为x2,即y轴数据。

标准回归函数和数据导入函数:

        第一个函数loadDataset ()加载数据。该函数打开一个用tab键分隔的文本文件,这里仍然默认文件每行的最后一个值是目标值。第二个函数standRegres()用来计算最佳拟合直线。该函数首先读人x和y并将它们保存到矩阵中;然后计算x^{\tau }x,然后判断它的行列式是否为零,如果行列式为零,那么计算逆矩阵的时候将出现错误。NumPy提供一个线性代数的库linalg,其中包含很多有用的函数。可以直接调用linalg.det()来计算行列式。最后,如果行列式非零,计算并返回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 standRegress(xArr,yArr):
    xMat = np.mat(xArr)
    yMat = np.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)
    fig = plt.figure()
    ax = fig.add_subplot(111)
    ax.scatter(xMat[:,1].flatten().A[0], yMat.flatten().A[0], s = 20, c = 'blue',alpha = .5)
    xCopy = xMat.copy()
    xCopy.sort(0)
    yHat = xCopy*ws #预测值
    ax.plot(xCopy[:,1],yHat)
    plt.show()
    return ws

        绘画出来的数据如图:

         输出的ws的值:

 局部加权线性回归:

        线性回归的一个问题是有可能出现欠拟合现象,因为它求的是具有最小均方误差的无偏估计。显而易见,如果模型欠拟合将不能取得最好的预测效果。所以有些方法允许在估计中引入一些偏差,从而降低预测的均方误差。

        其中的一个方法是局部加权线性回归(Locally Weighted Linear Regression,LWLR)。在该算法中,我们给待预测点附近的每个点赋予一定的权重;在这个子集上基于最小均方差来进行普通的回归。与kNN一样,这种算法每次预测均需要事先选取出对应的数据子集。该算法解出回归系数w的形式如下:

         其中W是一个矩阵,这个公式跟我们上面推导的公式的区别就在于W,它用来给每个店赋予权重。

        LWLR使用"核"(与支持向量机中的核类似)来对附近的点赋予更高的权重。核的类型可以自由选择,最常用的核就是高斯核,高斯核对应的权重如下:

         这样我们就可以根据上述公式,编写局部加权线性回归,我们通过改变k的值,可以调节回归效果,编写代码如下:

#局部加权线性回归
def lwlr(testpoint,xArr,yArr,k):
    xMat = np.mat(xArr)
    yMat = np.mat(yArr).T
    m = shape(xMat)[0]
    weights = np.mat(eye((m))) #创建权重对角矩阵
    for j in range(m): #遍历数据集计算每个样本的权重
        diffMat = testpoint - xMat[j,:]
        weights[j,j] = exp(diffMat*diffMat.T/(-2*k**2))
    xTx = xMat.T*(weights*xMat)
    if linalg.det(xTx) ==0.0:
        print("this matrix is singular,cannot do inverser")
        return
    ws = xTx.I*(xMat.T*(weights*yMat))
    return testpoint*ws

def lwlrTest(testArr,xArr,yArr,k):
    m = shape(testArr)[0]
    yHat = zeros(m)
    for i in range(m):
        yHat[i] = lwlr(testArr[i],xArr,yArr,k)  #为每个数据集调用lwlr。这有助于求解k 的大小

    #画图
    xMat = np.mat(xArr)
    strInd = xMat[:,1].argsort(0)
    xSort = xMat[strInd][:,0,:]
    fig = plt.figure()
    ax = fig.add_subplot(111)
    ax.plot(xSort[:,1],yHat[strInd])
    ax.scatter(xMat[:,1].flatten().A[0], np.mat(yArr).flatten().A[0], s = 20, c = 'red',alpha = .5)
    plt.show()
    return yHat
xArr, yArr = loadDataSet('ex0.txt')
yHat = lwlrTest(xArr,xArr,yArr,1.0) #记得修改k值

        我们可以不断修改k的值:

        当k=1.0时:

         当k=0.01时:

 当k=0.003时:

         可以看到,当k越小,拟合效果越好。但是当k过小,会出现过拟合的情况,例如k等于0.003的时候。

3、预测鲍鱼的年龄

        接下来,我们将回归用于真实数据。在data目录下有一份来自UCI数据集合的数据,记录了鲍鱼(一种介壳类水生动物)的年龄。鲍鱼年龄可以从鲍鱼壳的层数推算得到。

        可以看到,数据集是多维的,所以我们很难画出它的分布情况。每个维度数据的代表的含义没有给出,不过没有关系,我们只要知道最后一列的数据是y值就可以了,最后一列代表的是鲍鱼的真实年龄,前面几列的数据是一些鲍鱼的特征,例如鲍鱼壳的层数等。我们不做数据清理,直接用上所有特征,测试下我们的局部加权回归。

        新建abalone.py文件,添加rssError函数,用于评价最后回归结果。

def rssError(yArr,yHatArr):
    return ((yArr-yHatArr)**2).sum()
from numpy import *
import numpy as np
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 rssError(yArr,yHatArr):
    return ((yArr-yHatArr)**2).sum()

#局部加权线性回归
def lwlr(testpoint,xArr,yArr,k):
    xMat = np.mat(xArr)
    yMat = np.mat(yArr).T
    m = shape(xMat)[0]
    weights = np.mat(eye((m))) #创建权重对角矩阵
    for j in range(m): #遍历数据集计算每个样本的权重
        diffMat = testpoint - xMat[j,:]
        weights[j,j] = exp(diffMat*diffMat.T/(-2*k**2))
    xTx = xMat.T*(weights*xMat)
    if linalg.det(xTx) ==0.0:
        print("this matrix is singular,cannot do inverser")
        return
    ws = xTx.I*(xMat.T*(weights*yMat))
    return testpoint*ws

def lwlrTest(testArr,xArr,yArr,k):
    m = shape(testArr)[0]
    yHat = zeros(m)
    for i in range(m):
        yHat[i] = lwlr(testArr[i],xArr,yArr,k)  #为每个数据集调用lwlr。这有助于求解k 的大小

    #画图
    xMat = np.mat(xArr)
    strInd = xMat[:,1].argsort(0)
    xSort = xMat[strInd][:,0,:]
    fig = plt.figure()
    ax = fig.add_subplot(111)
    ax.plot(xSort[:,1],yHat[strInd])
    ax.scatter(xMat[:,1].flatten().A[0], np.mat(yArr).flatten().A[0], s = 20, c = 'red',alpha = .5)
    plt.show()
    return yHat

def standRegress(xArr,yArr):
    xMat = np.mat(xArr)
    yMat = np.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)
    fig = plt.figure()
    ax = fig.add_subplot(111)
    ax.scatter(xMat[:,1].flatten().A[0], yMat.flatten().A[0], s = 20, c = 'blue',alpha = .5)
    xCopy = xMat.copy()
    xCopy.sort(0)
    yHat = xCopy*ws #预测值
    ax.plot(xCopy[:,1],yHat)
    plt.show()
    return ws


abX, abY = loadDataSet('abalone.txt')
print('训练集与测试集相同:局部加权线性回归,核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)
print('k=0.1时,误差大小为:',rssError(abY[0:99], yHat01.T))
print('k=1  时,误差大小为:',rssError(abY[0:99], yHat1.T))
print('k=10 时,误差大小为:',rssError(abY[0:99], yHat10.T))
print('')
print('训练集与测试集不同:局部加权线性回归,核k的大小是越小越好吗?更换数据集,测试结果如下:')
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时,误差大小为:',rssError(abY[100:199], yHat01.T))
print('k=1  时,误差大小为:',rssError(abY[100:199], yHat1.T))
print('k=10 时,误差大小为:',rssError(abY[100:199], yHat10.T))
print('')
print('训练集与测试集不同:简单的线性归回与k=1时的局部加权线性回归对比:')
print('k=1时,误差大小为:', rssError(abY[100:199], yHat1.T))
ws = standRegress(abX[0:99], abY[0:99])
yHat = np.mat(abX[100:199]) * ws
print('简单的线性回归误差大小:', rssError(abY[100:199], yHat.T.A))

        输出为:

 

     可以看到,当k=0.1时,训练集误差小,但是应用于新的数据集之后,误差反而变大了。这就是经常说道的过拟合现象。我们训练的模型,我们要保证测试集准确率高,这样训练出的模型才可以应用于新的数据,也就是要加强模型的普适性。可以看到,当k=1时,局部加权线性回归和简单的线性回归得到的效果差不多。这也表明一点,必须在未知数据上比较效果才能选取到最佳模型。那么最佳的核大小是10吗?或许是,但如果想得到更好的效果,应该用10个不同的样本集做10次测试来比较结果。

        本示例展示了如何使用局部加权线性回归来构建模型,可以得到比普通线性回归更好的效果。局部加权线性回归的问题在于,每次必须在整个数据集上运行。也就是说为了做出预测,必须保存所有的训练数据。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值