回归分析及实际案例:预测鲍鱼年龄

上一篇文章:线性回归(Linear regression)算法

引入:

 1、线性回归:

算法的优点:

 结果易于理解,计算不复杂

缺点:对非线性数据拟合不好

目标:平方误差和最小

 求解(对参数w求导等于0)的回归系数:

模型预测:

f(x)=W^{T}X

"""
函数说明:标准回归

Parameters:
    xArr - 特征矩阵
    yArr -响应值


Returns:
     ws- 回归系数

Author:
    heda3
Blog:
    https://blog.csdn.net/heda3
Modify:
    2020-01-10
"""   
def standRegres(xArr,yArr):
    xMat = mat(xArr); yMat = mat(yArr).T
    xTx = xMat.T*xMat#计算xTx
    if linalg.det(xTx) == 0.0:#判断行列式是否为0
        print("This matrix is singular, cannot do inverse")
        return
    ws = xTx.I * (xMat.T*yMat)#计算回归系数
    return ws

注意行列式不为零才可以计算逆矩阵

#加载测试数据 

from numpy import *

def loadDataSet(fileName):      #general function to parse tab -delimited floats
    numFeat = len(open(fileName).readline().split('\t')) - 1 #get number of fields 
    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

数据描述:

特征:2,响应:数值型

使用线性回归预测数据并绘制散点图+计算模型的效果(相关系数计算)

 ##测试线性回归
 xArr,yArr=loadDataSet('ex0.txt')
 xArr[0:2]
 ws=standRegres(xArr,yArr)#计算回归系数
 xMat=mat(xArr)
 yMat=mat(yArr)
 #绘散点图
 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])#.A转变为数组
 xCopy=xMat.copy()
 xCopy.sort(0)#维度,行排序
 yHat=xCopy*ws
 ax.plot(xCopy[:,1],yHat)
 plt.show
 #求预测值和真实值的相关系数
 yHat1=xMat*ws
 corrcoef(yHat1.T,yMat)

2、局部加权线性回归

Locally Weighted Linear Regression, LWLR
线性回归存在的问题是:出现欠拟合

解决方法:在估计中引入偏差,从而降低预测的均方误差

   也即是通过在每一小段进行拟合,以逼近真实的数据

局部加权线性回归相比普通线性回归的问题是:每次必须在整个数据集上运行,也即是必须要保存所有的训练数据

思路:在待预测点附近的每个点赋予一定的权重

求解的回归系数:

对比线性回归:

其中的W矩阵用于给每个数据点赋予权重

权重W的选择,通过使用不同的核:

例如高斯核

需要调节的参数:一个 k

#对单点估计

"""
函数说明:局部加权线性回归

Parameters:
    testPoint x空间的任意一点
    xArr - 特征矩阵
    yArr -响应值
    k 和权重有关,当k越小则使用的越少的局部数据集进行训练,k=1相当于标准线性回归

Returns:
    某个点的预测结果  testPoint * ws - 

Author:
    heda3
Blog:
    https://blog.csdn.net/heda3
Modify:
    2020-01-10
"""   
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,:]     #x-x[i]
        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

#对多点(数据集)估计

"""
函数说明:为数据集中的每个点调用lwlr
  
Parameters:
    testArr 测试的数据集
    xArr - 特征矩阵
    yArr -响应值
    k

Returns:
     testPoint * ws - 

Author:
    heda3
Blog:
    https://blog.csdn.net/heda3
Modify:
    2020-01-10
"""  
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

对数据进行预测+绘制散点图

 ##测试局部加权线性回归
 xArr,yArr=loadDataSet('ex0.txt')
 #对单点估计
 yArr[0]
 lwlr(xArr[0],xArr,yArr,1.0)
 lwlr(xArr[0],xArr,yArr,0.001)
 #数据集中所有点的估计
 yHat=lwlrTest(xArr,xArr,yArr,0.01)
 #绘制散点图
 xMat=mat(xArr)
 srtInd=xMat[:,1].argsort(0)
 xSort=xMat[srtInd][:,0,:] #等价于xMat[srtInd.flatten().A[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')#.A转变为数组
 plt.show()

设置参数k值为0.01

问题:

数据的特征比样本点多时,计算矩阵的(XTX)的逆出错,也即是输入数据(特征)矩阵不是满秩的矩阵

或者是数据特征之间是高度相关时也不能计算

如何减少特征数,如何减少不重要的特征?

解决方法:缩减系数

1)岭回归

2)lasso

3)LAR

4)PCA回归

5)子集选择

1)岭回归

X^{T}X的基础上加\lambda I使得矩阵非奇异,从而能对X^{T}X+\lambda I求逆

求解的回归系数:

对比标准线性回归:

选择的参数:\lambda    可通过交叉验证确定

注意:数据需要先标准化处理(X-mean)/var

"""
函数说明:岭回归
  
Parameters:
    xMat- 数据的特征 假设有n 样本个数有m
    yMat- 响应值
    lam-- 调节的参数
Returns:
     ws-- 计算出的回归系数
Author:
    heda3
Blog:
    https://blog.csdn.net/heda3
Modify:
    2020-01-10
"""  
def ridgeRegres(xMat,yMat,lam=0.2):
    xTx = xMat.T*xMat#2*2   n*n
    denom = xTx + eye(shape(xMat)[1])*lam#n*n
    if linalg.det(denom) == 0.0:
        print("This matrix is singular, cannot do inverse")
        return
    ws = denom.I * (xMat.T*yMat)
    return ws
"""
函数说明:岭回归参数lambda调节
  
Parameters:
    xArr- 特征矩阵
    yArr- 响应值

Returns:
    wMat - 返回一组w(维数和特征数对应)系数
Author:
    heda3
Blog:
    https://blog.csdn.net/heda3
Modify:
    2020-01-10
"""  
def ridgeTest(xArr,yArr):
    xMat = mat(xArr); yMat=mat(yArr).T
    #数据标准化处理
    yMean = mean(yMat,0)
    yMat = yMat - yMean     #to eliminate X0 take mean off of Y
    #regularize X's
    xMeans = mean(xMat,0)   #calc mean then subtract it off
    xVar = var(xMat,0)      #calc variance of Xi then divide by it
    xMat = (xMat - xMeans)/xVar
    numTestPts = 30#设置lambda参数迭代次数
    wMat = zeros((numTestPts,shape(xMat)[1]))#30*2的矩阵
    for i in range(numTestPts):
        ws = ridgeRegres(xMat,yMat,exp(i-10))
        wMat[i,:]=ws.T
    return wMat

2)lasso

对回归系数的约束:

3)前向逐步回归

每一步都尽可能的减少误差,通过设置初始权重为1,每一步所做的决策是对某个权重增加或减少一个很小的值

算法步骤:

通过多次迭代后得到趋于稳定的回归参数! 

可调节的参数:步长和迭代次数 

"""
函数说明:前向逐步线性回归
  
Parameters:
    xArr- 特征矩阵
    yArr- 响应值
    eps=0.01  每次迭代需要调整的步长
    numIt=100 迭代次数
Returns:
    returnMat - 返回一组w(维数和特征数对应)系数
Author:
    heda3
Blog:
    https://blog.csdn.net/heda3
Modify:
    2020-01-11
"""  
def stageWise(xArr,yArr,eps=0.01,numIt=100):
    xMat = mat(xArr); yMat=mat(yArr).T
    #数据的标准化
    yMean = mean(yMat,0)
    yMat = yMat - yMean     #can also regularize ys but will get smaller coef
    xMat = regularize(xMat)
    m,n=shape(xMat)
    returnMat = zeros((numIt,n)) #testing code remove
    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):#n个特征也即是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()#迭代numIt次找到最小误差的回归系数
        returnMat[i,:]=ws.T
    return returnMat

 案例1:预测鲍鱼年龄

问题出发点是:鲍鱼的年龄是通过贝壳的年轮计数确定,此方法耗时费力,如何依据其它的一些参数来推测鲍鱼的年龄?

数据集来源:UCI Machine Learning Repository: Abalone Data Set

数据集的描述:

上述的数据集给出的8个特征属性

Name / Data Type / Measurement Unit / Description 
----------------------------- 
Sex / nominal / -- / M, F, and I (infant) 
Length / continuous / mm / Longest shell measurement 
Diameter / continuous / mm / perpendicular to length 
Height / continuous / mm / with meat in shell 
Whole weight / continuous / grams / whole abalone 
Shucked weight / continuous / grams / weight of meat 
Viscera weight / continuous / grams / gut weight (after bleeding) 
Shell weight / continuous / grams / after being dried 

响应值:

构建模型预测

1)使用标准线性回归模型预测鲍鱼年龄

 ##使用标准线性回归模型
 ws=standRegres(abX[0:99],abY[0:99])
 yHat=mat(abX[100:199])*ws
 rssError(abY[100:199],yHat.T.A)

    新数据上表现平方误差=518.6 

2)使用局部线性加权线性回归模型预测鲍鱼年龄

 ##使用局部加权线性回归
 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)
 #测试集表现
 yHat01=lwlrTest(abX[100:199],abX[100:199],abY[100:199],0.1)
 yHat1=lwlrTest(abX[100:199],abX[100:199],abY[100:199],1)
 yHat10=lwlrTest(abX[100:199],abX[100:199],abY[100:199],10)
 x1=rssError(abY[100:199],yHat01.T)
 print(x1)
 x2=rssError(abY[100:199],yHat1.T)
 print(x2)
 x3=rssError(abY[100:199],yHat10.T)
 print(x3)

在不同的参数k下的效果,以及在训练集和测试集上的表现:

平方误差结果:

训练集下:

测试集下:

在未知数据上比较效果才可以很好的选择模型,10折交叉验证

3)岭回归模型预测鲍鱼年龄

 ##使用岭回归
 abX,abY=loadDataSet('abalone.txt')
 ridgeWeights=ridgeTest(abX,abY)#得到不同lambda下计算出的回归系数
 #绘制回归系数
 import matplotlib.pyplot as plt
 fig=plt.figure()
 ax=fig.add_subplot(111)
 ax.plot(ridgeWeights)
 plt.show()

横坐标为lambda值,y轴为各回归系数 

通过上图看看出哪些变量对结果的预测具有影响力

为定量找到最佳参数值lambda,需要进行交叉验证获得误差最小的lambda

使用10折交叉验证计算得出最佳的岭回归系数,参与预测新的数据

在岭回归中要求数据要标准化再参与计算,那么在训练完成后新的数据如何进行预测?这个新的数据怎么利用训练的数据进行标准化?

解决方法是:利用在训练数据中得出的回归参数,通过变换实现变相的在新数据预测时的标准化

 新的数据一般预测过程:

数据标准化:XT=(XTest-mean(XTrain))/Var(XTrain)

 预测:Ytest=XT*Ws+mean(YTrain)

将上述的公式变换:

Ytest=((XTest-mean(XTrain))/Var(XTrain))*Ws+mean(YTrain)

UnReg=Ws/Var(XTrain)

constantTerm=-mean(XTrain)*Ws/Var(XTrain)+mean(YTrain)

Ytest=XTest*UnReg+constantTerm(现在的新变换后的预测过程)

###交叉验证--岭回归
ridgeWs,ridgeunReg,ridgeConstantTerm=crossValidation(abX[0:99],abY[0:99],10)#目的是找出最佳的岭回归系数
##测试均方误差
####和标准线性回归的比较
xMat=mat(abX[100:199])
yMat=mat(abY[100:199]).T
ridgeyHat=xMat*ridgeunReg.T+ridgeConstantTerm#岭回归预测
rssError(abY[100:199],ridgeyHat.T.A)#误差计算

换种写法:

###交叉验证--岭回归
ridgeWs,ridgeunReg,ridgeConstantTerm=crossValidation(abX[0:99],abY[0:99],10)#目的是找出最佳的岭回归系数
##测试均方误差
####和标准线性回归的比较
xMat=mat(abX[100:199])
yMat=mat(abY[100:199])
ridgeyHat=xMat*ridgeunReg.T+ridgeConstantTerm#岭回归预测
rssError(yMat.A,ridgeyHat.T.A)#误差计算
xxx=yMat.A
yyy=ridgeyHat.T.A

"""
函数说明:交叉验证测试岭回归
  
Parameters:
    xArr - 特征
    yArr - 标签
    numVal=10 - 交叉验证的次数

Returns: 
    bestWeights 最佳的岭回归参数
    为了和标准线性回归比较
    unReg,constantTerm 数据标准化还原后的特征参数和常量参数
Author:
    heda3
Blog:
    https://blog.csdn.net/heda3
Modify:
    2020-01-28
""" 
def crossValidation(xArr,yArr,numVal=10):
    m = len(yArr)#样本点个数                           
    indexList = list(range(m))
    errorMat = zeros((numVal,30))#create error mat 30columns numVal rows
    for i in range(numVal):#交叉验证
        trainX=[]; trainY=[]
        testX = []; testY = []
        random.shuffle(indexList)#随机打乱样本索引
        #训练集和测试集的划分 90%训练  10%测试
        for j in range(m):#create training set based on first 90% of values in indexList
            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)    #30*特征数 get 30 weight vectors from ridge
        #30组回归系数
        for k in range(30):#loop over all of the ridge estimates
            matTestX = mat(testX); matTrainX=mat(trainX)
            meanTrain = mean(matTrainX,0)
            varTrain = var(matTrainX,0)
            
            matTestX = (matTestX-meanTrain)/varTrain #regularize test with training params
            yEst = matTestX * mat(wMat[k,:]).T + mean(trainY)#test ridge results and store
            errorMat[i,k]=rssError(yEst.T.A,array(testY))
            #print errorMat[i,k]
    #计算所有这些误差值的均值
    meanErrors = mean(errorMat,0)#errorMat为 10*30   30个岭回归参数  10次交叉验证  按照把轴向数据求平均  得到每列数据的平均值,也即是10折交叉验证的平均   calc avg performance of the different ridge weight vectors
    
    minMean = float(min(meanErrors))#哪个岭回归参数下的误差最小
    bestWeights = wMat[nonzero(meanErrors==minMean)]#找出误差最小的回归参数
    #can unregularize to get model
    #when we regularized we wrote Xreg = (x-meanX)/var(x)
    #we can now write in terms of x not Xreg:  x*w/var(x) - meanX/var(x) +meanY
    xMat = mat(xArr); yMat=mat(yArr).T
    meanX = mean(xMat,0); varX = var(xMat,0)
    unReg = bestWeights/varX
    print("the best model from Ridge Regression is:\n",unReg)
    #标准化后数据还原
    constantTerm=-1*sum(multiply(meanX,unReg)) + mean(yMat)
    print("with constant term: ",constantTerm)
    return bestWeights,unReg,constantTerm

平方误差结果:

4)前向逐步回归模型预测鲍鱼年龄

 ##使用逐步回归
 xArr,yArr=loadDataSet('abalone.txt')
 returnMat1=stageWise(xArr,yArr,0.01,200)#出现来回震荡情况,原因是步长太大?原因是系数已经饱和需要调小系数
 #对比更小的步长
 returnMat2=stageWise(xArr,yArr,0.001,200)
 #绘制回归系数
 import matplotlib.pyplot as plt
 fig=plt.figure()
 ax=fig.add_subplot(111)
 ax.plot(returnMat2)
 plt.show()

参数:步长和迭代次数 

基于上图可以较好的发现重要特征

定量的选择模型参数:使用类似交叉验证方法

实验结果:

##数据训练
returnMat2=stageWise(xArr,yArr,0.001,5000)
###预测
####数据标准化
xMat = mat(xArr); yMat=mat(yArr).T
xMean=mean(xMat,0)
VarX=var(xMat,0)
yMean = mean(yMat,0)

Xtest=(mat(xArr[100:199])-xMean)/VarX#标准化后的测试数据
yMat=mat(yArr[100:199])#实际标签数据

Ytest=Xtest*mat(returnMat2[4999,:]).T+yMean
rssError(yMat.A,Ytest.T.A)#.A转变为数组

均方误差: 

参考:

《机器学习实战》

您可以使用局部加权线性回归(Locally Weighted Linear Regression,简称LWLR)来预测鲍鱼年龄。下面是一个使用MATLAB实现的案例示例: ```matlab % 加载数据 data = load('abalone.csv'); X = data(:, 1:end-1); y = data(:, end); % 定义局部加权线性回归函数 function theta = lwlr(X_train, y_train, x, tau) m = size(X_train, 1); weights = exp(-sum((X_train - x).^2, 2) / (2 * tau^2)); % 计算权重 W = diag(weights); % 构建权重矩阵 X_train = [ones(m, 1), X_train]; % 添加偏置项 theta = pinv(X_train' * W * X_train) * X_train' * W * y_train; % 计算参数 end % 设置参数 tau = 0.1; % tau值越小,考虑的样本越少,模型越复杂;tau值越大,考虑的样本越多,模型越简单 % 预测鲍鱼年龄 x = [0.455, 0.365, 0.095, 0.514, 0.224, 0.101, 0.15]; % 样本特征值 y_pred = lwlr(X, y, x, tau); % 预测年龄 disp(['预测年龄:', num2str(y_pred)]); ``` 上述代码中,首先从`abalone.csv`文件中加载数据,其中最后一列是鲍鱼年龄,其余列是鲍鱼的特征。然后定义了一个`lwlr`函数来实现局部加权线性回归,该函数接受训练集特征值`X_train`、训练集标签值`y_train`、待预测样本特征值`x`以及`tau`值作为输入,返回预测年龄值。在`lwlr`函数内部,首先计算出每个训练样本与待预测样本之间的距离,然后根据距离计算出权重,构建权重矩阵,并利用加权最小二乘法计算出模型参数。最后,使用预测函数对指定样本特征值进行预测,并输出预测结果。 请注意,上述代码仅为示例,实际应用中可能需要对数据进行预处理、设置适当的`tau`值以及进行模型评估等。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

heda3

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值