机器学习实战(三)岭回归,lasso回归

在线性回归模型中,其参数估计公式为 β = ( X T X ) − 1 X T y β=(X^{T}X)^-1X^Ty β=(XTX)1XTy不可逆时无法求出 β β β,另外如果 ∣ X T X ∣ |X^{T}X| XTX越趋近于0,会使得回归系数趋向于无穷大,此时得到的回归系数是无意义的。解决这类问题可以使用岭回归和LASSO回归,主要针对自变量之间存在多重共线性或者自变量个数多于样本量的情况。

注:线性回归模型的详解,博客

https://blog.csdn.net/qs17809259715/article/details/90613983

一、正则化

1.什么是正则化

  • 对损失函数(目标函数)加入一个惩罚项,使得模型由多解变为更倾向其中一个解。
  • 在最小二乘法中,可以这样理解。XTX可能是不可逆的,通过加上正则项,迫使弱的特征的系数缩减为0.

2.正则化项
前面使用多项式回归,如果多项式最高次项比较大,模型就容易出现过拟合。正则化是一种常见的防止过拟合的方法,一般原理是在代价函数后面加上一个对参数的约束项,这个约束项被叫做正则化项(regularizer)。在线性回归模型中,通常有两种不同的正则化项:

  • 加上所有参数(不包括 θ 0 θ_0 θ0)的绝对值之和,即 l 1 l_1 l1范数,此时叫做Lasso回归;
  • 加上所有参数(不包括 θ 0 θ_0 θ0)的平方和,即 l 2 l_2 l2范数,此时叫做岭回归.

:正则化是用来防止过拟合的方法。在最开始学习机器学习的课程时,只是觉得这个方法就像某种魔法一样非常神奇的改变了模型的参数。但是一直也无法对其基本原理有一个透彻、直观的理解。直到最近再次接触到这个概念,经过一番苦思冥想后终于有了我自己的理解。

一、岭回归

1.定义

岭回归:是一种专用于共线性数据分析的有偏估计回归方法,实质上是一种改良的最小二乘估计法,通过放弃最小二乘法的无偏性,以损失部分信息、降低精度为代价,获得回归系数更为符合实际、更可靠的回归方法,对病态数据的耐受性远远强于最小二乘法。

2.为什么会有岭回归?

总结起来看,主要有两个原因:

  • ①当样本少于特征(数据点少于变量个数),输入数据的矩阵x非满秩矩阵(即|xTx|≈0),求逆的时候会出问题;
  • ②当样本之间存在共线性(也就是强相关性),普通的最小二乘法得到到的回归系数估计得方差很大,会导致估计值很不稳定;

3.公式推导
在这里插入图片描述
在这里插入图片描述
L2范数惩罚项的加入使得 ( X T X + λ E ) (X^{T}X+λE) (XTX+λE)满秩,保证了可逆,但是也由于惩罚项的加入,使得回归系数β的估计不再是无偏估计。所以岭回归是以放弃无偏性、降低精度为代价解决病态矩阵问题的回归方法。
单位矩阵的 I I I对角线上全是1,像一条山岭一样,这也是岭回归名称的由来。

4.λ 的选择

  • 模型的方差:回归系数的方差
  • 模型的偏差:预测值和真实值的差异

随着模型复杂度的提升,在训练集上的效果就越好,即模型的偏差就越小;但是同时模型的方差就越大。对于岭回归的λ 而言,随着λ 的增大, ∣ X T X + λ I ∣ |X^{T}X+λI| XTX+λI就越大, ( X T X + λ I ) − 1 (X^{T}X+λI)^-1 (XTX+λI)1就越小,模型的方差就越小;而λ越大使得β的估计值更加偏离真实值,模型的偏差就越大。所以岭回归的关键是找到一个合理的λ 值来平衡模型的方差和偏差。
根据凸优化,可以将岭回归模型的目标函数J(β) 最小化问题等价于
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

  • (1)岭迹法确定λ 值

在这里插入图片描述

  • 各回归系数的岭估计基本稳定;
  • 用最小二乘估计时符号不合理的回归系数,其岭估计的符号变得合理;
  • 回归系数没有不合乎经济意义的绝对值;
  • 残差平方和增大不太多。

但实际操作,就是取”平稳拐点
在这里插入图片描述

  • (2)交叉验证法确定λ值

交叉验证法的思想是,将数据集拆分为k个数据组(每组样本量大体相当),从k组中挑选k-1组用于模型的训练,剩下的1组用于模型的测试,则会有k-1个训练集和测试集配对,每一种训练集和测试集下都会有对应的一个模型及模型评分(如均方误差),进而可以得到一个平均评分。对于λ值则选择平均评分最优的λ值。

5.岭回归的计算过程:

  • 将输入矩阵做数据标准化(所有特征值都减去各自的均值并除以方差)
  • 取一组λ,带入公式得到一族ω估计值
  • 绘制岭迹图,取得岭迹图的平稳拐点对应的λ

6.代码实现

#!/usr/bin/env python 
# -*- coding:utf-8 -*-
from numpy import *
import matplotlib.pyplot as plt
from linearRegression import regression
# TODO 岭回归

def ridgeRegres(xMat, yMat, lam=0.2):
    xTx = xMat.T * xMat
    # eye()是numpy用于生成单位矩阵的函数,lam岭回归系数
    denom = xTx + eye(shape(xMat)[1]) * lam
    if linalg.det(denom) == 0.0:
        # 这个矩阵是奇异的,不能做逆矩阵
        print("This matrix 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
    # print(yMat)
    # np.mean(a,axis):沿着axis方向求算术平均值
    # axis=0,按行方向计算,即每列
    # axis=1,按列方向计算,即每行
    yMean = mean(yMat, 0)
    # print(yMat)
    # 减去均值  TODO 这个怎么计算的?
    yMat = yMat - yMean
    # print(yMat)
    xMeans = mean(xMat, 0)
    # print(xMeans)
    # np.var(a,axis):沿着axis方向求方差
    xVar = var(xMat, 0)
    # 数据标准化操作:所有特征值都减去各自的均值并除以方差
    xMat = (xMat - xMeans) / xVar
    # 生成30个不同lam以测试ridgeRegres
    numTestPts = 30
    # 生成30行,shape(xMat)[1]列的0矩阵
    wMat = zeros((numTestPts, shape(xMat)[1]))
    # print(wMat)
    for i in range(numTestPts):
        ws = ridgeRegres(xMat, yMat, exp(i - 10))
        wMat[i, :] = ws.T           # 也就是第0维度上的i个元素,(i行)
    return wMat
if __name__ == '__main__':
    # 岭回归的测试
    abX, abY = regression.loadDataSet('abalone.txt')
    ridgeWeights = ridgeTest(abX, abY)
    fig = plt.figure()
    ax = fig.add_subplot(111)
    ax.set(ylabel='w', xlabel='log(lambda)')
    ax.plot(ridgeWeights)
    plt.show()


二、lasso回归

1.lasso回归的理解
在这里插入图片描述
2.参数推导

Lasso回归于岭回归非常相似,它们的差别在于使用了不同的正则化项,惩罚项由L2范数变为L1范数。最终都实现了约束参数从而防止过拟合的效果。但是Lasso之所以重要,还有另一个原因是:Lasso能够将一些作用比较小的特征的参数训练为0,从而获得稀疏解。也就是说用这种方法,在训练模型的过程中实现了降维(特征筛选)的目的。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
3.λ 的选择

  • 直接使用交叉验证法

4.代码实现

#!/usr/bin/env python
# -*- coding:utf-8 -*-
# https://blog.csdn.net/xiaozhu_1024/article/details/80585151 lasso回归
from numpy import *
from linearRegression import regression,PredictingTheAgeOfAbalone
import matplotlib.pyplot as plt
# TODO 正则化
def regularize(xMat):
    inMat = xMat.copy()
    inMeans = mean(inMat,0)
    inVar = var(inMat,0)
    inMat = (inMat-inMeans)/inVar
    return inMat


# TODO lasso回归
# eps步长,numInt迭代次数
def stageWise(xArr, yArr, eps = 0.01, numInt = 100):
    xMat = mat(xArr); yMat = mat(yArr).T
    yMean = mean(yMat, 0)
    yMat = yMat - yMean
    xMat = regularize(xMat)
    m,n = shape(xMat)
    returnMat = zeros((numInt, n))
    ws = zeros((n, 1))
    # wsTest = ws.copy()
    wsMax = ws.copy()
    for i in range(numInt):  # 控制迭代次数
        # print (ws.T)          # 打印上次迭代的结果
        lowestError = inf;   # 初始化误差
        # print(lowestError)
        for j in range(n):    # 迭代n个特征
            # 对每个特征系数执行增加和减少eps*sign操作
            for sign in [-1, 1]:  #对每个特征加上或减去
                # print(sign)
                wsTest = ws.copy()
                wsTest[j] += eps * sign
                # 变化后计算相应预测值
                yTest = xMat * wsTest
                # 保存最小误差以及对应的回归系数
                rssE = PredictingTheAgeOfAbalone.rssError(yMat.A, yTest.A)
                # print(rssE)
                # print("===============================")
                if rssE < lowestError:
                    lowestError = rssE
                    wsMax = wsTest
                # print(wsMax)
            # print("++++++++++++")
        ws = wsMax.copy()
        returnMat[i , :] = ws.T
        # print(returnMat)
    return returnMat

# 测试
# if __name__ == '__main__':
#     # lasso回归的测试
#     xArr, yArr = regression.loadDataSet('abalone.txt')
#     print("===============lasso回归的测试====================")
#     print(stageWise(xArr, yArr, 0.01, 200))
#     # 换用更小的步长和更多的步数:
#     print("===============换用更小的步长和更多的步数:====================")
#     print(stageWise(xArr, yArr, 0.001, 5000))
#
#     # 结果与最小二乘法进行比较
#     xMat = mat(xArr)
#     yMat = mat(yArr).T
#     xMat = regularize(xMat)
#     yM = mean(yMat, 0)
#     yMat = yMat - yM
#     weights = regression.standRegres(xMat, yMat.T)
#     print("============== 结果与最小二乘法进行比较=================")
#     print(weights.T)




def plot(weights):
     plt.rcParams['font.sans-serif'] = ['SimHei']
     plt.rcParams['axes.unicode_minus'] = False
     fig = plt.figure()
     ax = fig.add_subplot(111)
     ax.plot(weights)
     ax_title_text = ax.set_title(u'回归系数与迭代次数的关系')
     ax_xlabel_text = ax.set_xlabel(u'迭代次数')
     ax_ylabel_text = ax.set_ylabel(u'回归系数')
     plt.setp(ax_title_text, size=20, weight='bold', color='red')
     plt.setp(ax_xlabel_text, size=10, weight='bold', color='black')
     plt.setp(ax_ylabel_text, size=10, weight='bold', color='black')
     plt.show()



# 鲍鱼数据集上执行逐步线性回归法得到的系数与迭代次数间的关系
if __name__ == '__main__':
    abX, abY = regression.loadDataSet('abalone.txt')
    weights = stageWise(abX, abY, 0.001, 5000)
    plot(weights)

注:
相对于岭回归而言,可以看到LASSO回归剔除了两个变量,降低了模型的复杂度,同时减少了均方误差,提高了模型的拟合效果。

参考文档:
[1]《统计学习方法》李航著

[2]《机器学习实战》Peter Harrington著

[3]https://blog.csdn.net/weixin_43374551/article/details/83688913

[4]https://www.cnblogs.com/Belter/p/8536939.html

[5]https://blog.csdn.net/qq_35826213/article/details/90069398

  • 5
    点赞
  • 41
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值