[学习笔记] [机器学习] 4. [下] 线性回归(线性回归、损失函数、优化算法:正规方程,梯度下降、Boston房价预测、欠拟合和过拟合、正则化、岭回归、模型保存与模型加载)

6. 梯度下降和正规方程的对比

问题 梯度下降 正规方程
学习率 需要选择合适的学习率 不需要
求解特点 需要多次迭代求解 一次运算得出
线性问题 可以解决 可以解决
非线性问题 可以解决 不可以解决
时间复杂度 难以直接给出的(受到初始值、学习率、迭代次数等多种因素的影响) O ( n 3 ) O(n^3) O(n3)
适用场景 特征数量多(特征数是特征的个数(列的个数),不是样本的数量) 特征数量少

说明

  • 对于梯度下降算法,学习率需要经过一定的训练才能选择一个合适的值,而且如果学习率选取不好可能导致算法不收敛或者收敛速度过慢。
  • 正规方程的计算时间复杂度确实为 O ( n 3 ) O(n^3) O(n3),但是对于特征数量不是非常大的数据集,计算时间也不会太长。而且部分线性回归模型库(如 scikit-learn)已经针对计算效率做了优化,例如使用 SVD 分解来避免直接求逆矩阵的计算。

Q1:为什么说正规方程不能解决拟合问题?
A1:正规方程是一种求解线性回归的方法,它可以通过最小化误差的平方和寻找数据的最佳函数匹配。正规方程法是基于最小二乘法的,所以也被称为最小二乘法。但是,正规方程法不能解决拟合问题,因为当特征数量大于样本数量时,矩阵 X \rm X X 的转置矩阵 X \rm X X 不可逆。在这种情况下,我们可以使用岭回归或Lasso回归等方法来解决问题。


Q2:大规模数据集和小规模数据集说的样本数量大还是特征数大?
A2:大规模数据集和小规模数据集通常指的是样本数量的差异,而不是特征数的差异。

大规模数据集通常包含数百万或数十亿个样本,而小规模数据集通常只有数百到数千个样本。这种区分基于对机器学习算法性能和训练效果的影响,因为更多的数据可以帮助模型更好地泛化并提高准确性。

相比之下,特征数量通常在所有数据集中都是相似的,因为特征数量主要取决于数据类型和领域知识。但是,在某些情况下,例如自然语言处理中,特征数量可能随着文本长度和结构的变化而变化。

6.1 算法选择依据

  • 对于小规模数据,可以使用正规方程(但正规方程不能解决拟合问题)或岭回归。
  • 对于大规模数据,可以使用梯度下降法。

说明

  • 对于小规模数据,正规方程确实是一个有效的解决方案。但是如果训练集较小(例如少于 1000 条记录),使用岭回归可能会更好,因为岭回归可以控制模型的复杂度以避免过拟合。
  • 对于大规模数据,虽然梯度下降法是一个常用的算法,但是还有其他优化方法可以使用,例如随机梯度下降、批量梯度下降和迷你批量梯度下降等。此外,通常需要对数据进行特征缩放或者使用正则化技术(如 L1 或 L2 正则化)来帮助提高算法效率和泛化性能。

小结

  • 损失函数【知道】
    • 最小二乘法
  • 线性回归优化方法【知道】
    • 正规方程
    • 梯度下降法
  • 正规方程:一蹴而就【知道】
    • 利用矩阵的逆和转置进行一步求解
    • 只适合样本和特征比较少的情况
  • 梯度下降法:循序渐进【知道】
    • 梯度的概念
      • 单变量 —— 切线(斜率)
      • 多变量 —— 向量
    • 梯度下降法中关注的两个参数
      • α \alpha α —— 学习率,也就是参数在更新时的步长
        • 步长太小 —— 下山太慢
        • 步长太大 —— 容易跳过极小值点(不是最大值点,而是极小值点!)
      • 为什么梯度要加一个负号:梯度方向是上升最快的方向,负号就是下降最快的方向
  • 梯度下降法和正规方程选择依据【知道】
    • 小规模数据:
      • 正规方程:LinearRegression(不能解决拟合问题)
      • 岭回归:RidgeRegression
    • 大规模数据:
      • 梯度下降法:SGDRegressor

7. 梯度下降方法介绍

学习目标:

  • 掌握梯度下降法的推导过程
  • 知道全梯度下降算法的原理
  • 知道随机梯度下降算法的原理
  • 知道随机平均梯度下降算法的原理
  • 知道小批量梯度下降算法的原理

上一节中给大家介绍了最基本的梯度下降法实现流程,本节我们将进一步介绍梯度下降法的详细过算法推导过程和常见的梯度下降算法。

7.1 详解梯度下降算法

7.1.1 梯度下降的相关概念复习

在详细了解梯度下降的算法之前,我们先复习相关的一些概念。

  1. 步长(Learning rate):步长决定了在梯度下降迭代的过程中,每一步沿梯度负方向前进的长度。用前面下山的例子,步长就是在当前这一步所在位置沿着最陡峭最易下山的位置走的那一步的长度。
  2. 特征(Feature):指的是样本中输入部分,比如 2 个单特征的样本 ( x ( 0 ) x^{(0)} x(0), y ( 0 ) y^{(0)} y(0)), ( x ( 1 ) x^{(1)} x(1), y ( 1 ) y^{(1)} y(1)),则第一个样本特征为 x ( 0 ) x^{(0)} x(0),第一个样本输出为 y ( 0 ) y^{(0)} y(0)(这里是有两个特征,注意区别)。
  3. 假设函数(Hypothesis function):在监督学习中,为了拟合输入样本,而使用的假设函数,记为 h θ ( x ) h_\theta(x) hθ(x)。比如对于单个特征的 m m m 个样本 ( x ( i ) x^{(i)} x(i), y ( i ) y^{(i)} y(i)) ( i = 1 , 2 , . . . , m i = 1, 2, ..., m i=1,2,...,m),可以采用拟合函数如下: h θ ( x ) = θ 0 + θ 1 × x h_\theta(x) = \theta_0 + \theta_1 \times x hθ(x)=θ0+θ1×x
  4. 损失函数(Loss function):为了评估模型拟合的好坏,通常用损失函数来度量拟合的程度。损失函数极小化,意味着拟合程度最好,对应的模型参数即为最优参数。在线性回归中,损失函数通常为样本输出和假设函数的差取平方。比如对于 m m m 个样本 ( x i , y i ) ( i = 1 , 2 , . . . , m ) (x_i, y_i) (i=1, 2, ..., m) (xi,yi)(i=1,2,...,m),采用线性回归,则损失函数为: L ( θ 0 , θ 1 ) = 1 m ∑ i = 1 m ( h θ ( x i ) − y i ) 2 \mathcal{L}(\theta_0, \theta_1) = \frac{1}{m}\sum^m_{i = 1}(h_\theta(x_i) - y_i)^2 L(θ0,θ1)=m1i=1m(hθ(xi)yi)2

其中, x i x_i xi 表示第 i i i 个样本特征, y i y_i yi 表示第 i i i 个样本对应的输出, h θ ( x i ) h_\theta(x_i) hθ(xi) 为假设函数。

7.1.2 梯度下降法的推导流程

一、先决条件:确认优化模型的假设函数和损失函数

比如对于线性回归,假设函数表示为 h θ ( x 1 , x 2 , . . . , x n ) h_\theta(x_1, x_2, ..., x_n) hθ(x1,x2,...,xn) = θ 0 + θ 1 × x 1 + θ 2 × x 2 + . . . + θ n × x n \theta_0 + \theta_1 \times x_1 + \theta_2 \times x_2 + ... + \theta_n \times x_n θ0+θ1×x1+θ2×x2+...+θn×xn。其中 θ i ( i = 0 , 1 , 2 , . . . , n ) \theta_i(i=0, 1, 2, ..., n) θi(i=0,1,2,...,n) 为模型参数, x i ( i = 0 , 1 , 2 , . . . , n ) x_i(i = 0, 1, 2, ..., n) xi(i=0,1,2,...,n) 为每个样本的 n n n 个特征值。这个表示可以简化,我们增加一个特征 x 0 = 1 x_0 = 1 x0=1,这样

h θ ( x 0 , x 1 , . . . , x n ) = ∑ i = 0 n θ i x i h_\theta(x_0, x_1, ..., x_n) = \sum^n_{i = 0} \theta_i x_i hθ(x0,x1,...,xn)=i=0nθixi

其中, n n n 是每个样本的特征数量; θ i \theta_i θi 是模型参数。

同样是线性回归,对应于上面的假设函数,损失函数为:

L ( θ 0 , θ 1 , . . . , θ n ) = 1 2 m ∑ j = 0 m ( h θ ( x 0 j , x 1 j , . . . , x n j ) − y j ) 2 \mathcal{L}(\theta_0, \theta_1, ..., \theta_n) = \frac{1}{2m}\sum^m_{j=0}(h_\theta(x_0^{j}, x_1^{j}, ..., x_n^{j}) - y_j)^2 L(θ0,θ1,...,θn)=2m1j=0m(hθ(x0j,x1j,...,xnj)yj)2

其中:

  • n n n 是每个样本的特征数量
  • m m m 是样本数量
  • θ i \theta_i θi 是模型参数

Q:为什么是 1 2 m \frac{1}{2m} 2m1,而不是 1 m \frac{1}{m} m1
A:损失函数为 1 2 m \frac{1}{2m} 2m1 是为了方便求导。在线性回归中,我们使用的是最小二乘法,期望和实际值的方差作为损失函数。这个损失函数的导数是 1 m ∑ i = 1 m ( h θ ( x ( i ) ) − y ( i ) ) x j ( i ) \frac{1}{m}\sum_{i=1}^{m}(h_{\theta}(x^{(i)})-y^{(i)})x_j^{(i)} m1i=1m(hθ(x(i))y(i))xj(i),其中 h θ ( x ) h_{\theta}(x) hθ(x) 是假设函数, x ( i ) x^{(i)} x(i) 是第 i i i 个样本的特征向量, y ( i ) y^{(i)} y(i) 是第 i i i 个样本的标签, j j j 是特征向量的维度。如果我们使用 1 m \frac{1}{m} m1 作为损失函数,那么导数就是 ∑ i = 1 m ( h θ ( x ( i ) ) − y ( i ) ) x j ( i ) \sum_{i=1}^{m}(h_{\theta}(x^{(i)})-y^{(i)})x_j^{(i)} i=1m(hθ(x(i))y(i))xj(i),这样在梯度下降时就需要多除以一个 m m m,而使用 1 2 m \frac{1}{2m} 2m1 就可以避免这个问题。

二、算法相关参数初始化

主要是初始化模型参数 θ 0 , θ 1 , . . . , θ n \theta_0, \theta_1, ..., \theta_n θ0,θ1,...,θn,算法终止距离 ϵ \epsilon ϵ 以及步长 α \alpha α。在没有任何先验知识的时候,可以将所有的 ϵ \epsilon ϵ 初始化为0,将步长 α \alpha α 初始化为1。在之后的调优中再进行优化,以得到的更好的模型参数。

三、算法过程

步骤1:确定当前位置的损失函数的梯度,对于 θ i \theta_i θi,其梯度表达式如下:

∂ ∂ θ i L ( θ 0 , θ 1 , . . . , θ n ) \frac{\partial}{\partial{\theta_i}}\mathcal{L}(\theta_0, \theta_1, ..., \theta_n) θiL(θ0,θ1,...,θn)

步骤2:用步长乘以损失函数的梯度,得到当前位置下降的距离,即:

α ∂ ∂ θ i L ( θ 0 , θ 1 , . . . , θ n ) \alpha\frac{\partial}{\partial{\theta_i}}\mathcal{L}(\theta_0, \theta_1, ..., \theta_n) αθiL(θ0,θ1,...,θn)

对应于前面登山例子中的某一步。

步骤3:确定是否所有的 θ i \theta_i θi 梯度下降的距离都小于 ϵ \epsilon ϵ。如果小于 ϵ \epsilon ϵ 则算法终止,当前所有的 θ i ( i = 0 , 1 , . . . , n ) \theta_i(i = 0, 1, ..., n) θi(i=0,1,...,n) 即为最终结果。否则进入步骤4。

步骤4:更新所有的 θ \theta θ,对于 θ i \theta_i θi,其更新表达式如下。更新完毕后继续转入步骤1。

θ i = θ i − α 1 m ∑ j = 0 m ( h θ ( x 0 ( j ) , h θ ( x 1 ( j ) , . . . , h θ ( x n ( j ) ) − y j ) x i j ) ) \theta_i = \theta_i - \alpha \frac{1}{m} \sum^m_{j=0}(h_\theta(x^{(j)}_0, h_\theta(x^{(j)}_1, ..., h_\theta(x^{(j)}_n) - y_j)x^{j}_i)) θi=θiαm1j=0m(hθ(x0(j),hθ(x1(j),...,hθ(xn(j))yj)xij))


下面用线性回归的例子来具体描述梯度下降。假设我们的样本是:

( x 1 ( 0 ) , x 2 ( 0 ) , . . . , x n ( 0 ) 特征值 , y 0 目标值 ) , ( x 1 ( 1 ) , x 2 ( 1 ) , . . . , x n ( 1 ) 特征值 , y 1 目标值 ) , . . . , ( x 1 ( m ) , x 2 ( m ) , . . . , x n ( m ) 特征值 , y m 目标值 ) (\underset{特征值}{x^{(0)}_1, x^{(0)}_2, ..., x^{(0)}_n}, \underset{目标值}{y_0}), (\underset{特征值}{x^{(1)}_1, x^{(1)}_2, ..., x^{(1)}_n}, \underset{目标值}{y_1}), ..., (\underset{特征值}{x^{(m)}_1, x^{(m)}_2, ..., x^{(m)}_n}, \underset{目标值}{y_m}) (特征值x1(0),x2(0),...,xn(0),目标值y0),(特征值x1(1),x2(1),...,xn(1),目标值y1),...,(特征值x1(m),x2(m),...,xn(m),目标值ym)

其中, n n n 是每个样本的特征数量; m m m 是样本数量; θ i \theta_i θi 是模型参数。

损失函数如前面先决条件所述:

L ( θ 0 , θ 1 , . . . , θ n ) = 1 2 m ∑ j = 0 m ( h θ ( x 0 j , x 1 j , . . . , x n j ) − y j ) 2 \mathcal{L}(\theta_0, \theta_1, ..., \theta_n) = \frac{1}{2m}\sum^m_{j=0}(h_\theta(x_0^{j}, x_1^{j}, ..., x_n^{j}) - y_j)^2 L(θ0,θ1,...,θn)=2m1j=0m(hθ(x0j,x1j,...,xnj)yj)2

则在算法过程步骤1中对于 θ i \theta_i θi 的偏导数计算如下:

∂ ∂ θ i L ( θ 0 , θ 1 , . . . , θ n ) = 1 m ∑ j = 0 m ( h θ ( x 0 j , x 1 j , . . . , x n j ) − y j ) x i ( j ) \frac{\partial}{\partial{\theta_i}}\mathcal{L}(\theta_0, \theta_1, ..., \theta_n) = \frac{1}{m}\sum^m_{j=0}(h_\theta(x_0^{j}, x_1^{j}, ..., x_n^{j}) - y_j)x_i^{(j)} θiL(θ0,θ1,...,θn)=m1j=0m(hθ(x0j,x1j,...,xnj)yj)xi(j)

由于样本中没有 x 0 x_0 x0,上式中令所有的 x 0 x_0 x0 为1。

步骤4中模型参数 θ i \theta_i θi 的更新表达式如下:

θ i = θ i − α 1 m ∑ j = 0 m ( h θ ( x 0 ( j ) , x 1 ( j ) , . . . , x n ( j ) ) − y j ) x i ( j ) \theta_i = \theta_i - \alpha \frac{1}{m} \sum^m_{j=0}(h_\theta(x^{(j)}_0, x^{(j)}_1, ..., x^{(j)}_n) - y_j)x^{(j)}_i θi=θiαm1j=0m(hθ(x0(j),x1(j),...,xn(j))yj)xi(j)

从这个例子可以看出当前点的梯度方向是由所有的样本决定的,加 1 m \frac{1}{m} m1 是为了好理解。由于步长也为常数,它们的乘积也为常数,所以这里 α 1 m \alpha \frac{1}{m} αm1 可以用一个常数表示。


在下面一节中,咱们会详细讲到梯度下降法的变种,它们主要的区别就是对样本的采用方法不同。这里我们采用的是用所有样本。

7.2 梯度下降法大家族

常见的梯度下降算法有:

  1. 全梯度下降算法(Full gradient descent,FGD):在每次迭代中,使用所有样本计算梯度
  2. 随机梯度下降算法(Stochastic gradient descent,SGD):在每次迭代中,随机选择一个样本计算梯度
  3. 小批量梯度下降算法(Mini-batch gradient descent,MBGD):在每次迭代中,随机选择一小批样本计算梯度
  4. 随机平均梯度下降算法(Stochastic average gradient descent,SAGD):在每次迭代中,随机选择一批样本计算梯度,并对这些样本的梯度进行平均

它们都是为了正确地调节权重(模型参数)向量。通过为每个权重(模型参数)计算一个梯度,从而更新权值,使目标函数尽可能最小化。其差别在于样本的使用方式不同。

7.2.1 全梯度下降算法(Full gradient descent,FGD)

全梯度下降算法是梯度下降法最常用的形式,具体做法也就是在更新参数时使用所有的样本来进行更新。具体做法为:计算训练集所有样本误差,对其求和再取平均值作为目标函数。因为权重向量沿其梯度相反的方向移动,所以使当前目标函数减少得最多。

在整个训练数据集上计算损失函数关于参数 θ \theta θ 的梯度:

θ i = θ i − α ∑ j = 1 m ( h θ ( x 0 ( j ) , x 1 ( j ) , . . . , x n ( j ) ) − y j ) x i ( j ) \theta_i = \theta_i - \alpha \sum^m_{j = 1}(h_\theta(x^{(j)}_0, x^{(j)}_1, ..., x^{(j)}_n) - y_j)x^{(j)}_i θi=θiαj=1m(hθ(x0(j),x1(j),...,xn(j))yj)xi(j)

其中:

  • θ i \theta_i θi 表示第 i i i 个参数
  • α \alpha α 表示学习率
  • h θ ( x 0 ( j ) , x 1 ( j ) , . . . , x n ( j ) ) h_\theta(x^{(j)}_0, x^{(j)}_1, ..., x^{(j)}_n) hθ(x0(j),x1(j),...,xn(j)) 表示模型预测值
  • y j y_j yj 表示第 j j j 个样本的真实值
  • x i ( j ) x^{(j)}_i xi(j) 表示第 j j j 个样本的第 i i i 个特征

由于我们有 m m m 个样本,这里求梯度的时候就用了所有 m m m 个样本的梯度数据。

注意:

  • 因为在执行每次更新时,我们需要在整个数据集上计算所有的梯度,所以全梯度下降算法的速度会很慢,同时,全梯度下降算法无法处理超出内存容量限制的数据集
  • 全梯度下降算法同样也不能在线更新模型,即在运行的过程中,不能增加新的样本

7.2.2 随机梯度下降算法(Stochastic gradient descent,SGD)

由于全梯度下降算法FGD每迭代更新一次权重都需要计算所有样本误差,而实际问题中经常有海量的训练样本,故效率偏低,且容易陷入局部最优解,因此提出了随机梯度下降算法。

随机梯度下降算法SDG每轮计算的目标函数不再是全体样本误差,而仅是单个样本误差,即每次只代入计算一个样本目标函数的梯度来更新权重,再取下一个样本重复此过程,直到损失函数值停止下降或损失函数值小于某个阈值。

随机梯度下降算法SGD的过程简单、高效,通常可以较好地避免更新迭代收敛到局部最优解。

SGD的迭代形式为:

θ i = θ i − α ( h θ ( x 0 ( j ) , x 1 ( j ) , . . . , x n ( j ) ) − y j ) x i ( j ) \theta_i = \theta_i - \alpha(h_\theta(x^{(j)}_0, x^{(j)}_1, ..., x^{(j)}_n) - y_j)x^{(j)}_i θi=θiα(hθ(x0(j),x1(j),...,xn(j))yj)xi(j)

其中:

  • θ i \theta_i θi 表示第 i i i 个参数
  • α \alpha α 表示学习率
  • h θ ( x 0 ( j ) , x 1 ( j ) , . . . , x n ( j ) ) h_\theta(x^{(j)}_0, x^{(j)}_1, ..., x^{(j)}_n) hθ(x0(j),x1(j),...,xn(j)) 表示模型预测值
  • y j y_j yj 表示第 j j j 个样本的真实值
  • x i ( j ) x^{(j)}_i xi(j) 表示第 j j j 个样本的第 i i i 个特征

和FGD相比,没有 ∑ \sum 求和了,因为计算的是某一个样本。

优点:由于不是在全部训练数据上的损失函数,而是在每轮迭代中,随机优化某一条训练数据上的损失函数,这样每一轮参数的更新速度大大加快

缺点

  • 准确度下降。由于即使在目标函数为强凸函数的情况下,SGD仍旧无法做到线性收敛。
  • 可能会收敛到局部最优,由于单个样本并不能代表全体样本的趋势。不易于并行实现。
  • 由于SGD每次只使用一个样本迭代,若遇上噪声则容易陷入局部最优解

7.2.3 小批量梯度下降算法(Mini-batch gradient descent,MBGD)

小批量梯度下降算法MBGD是全梯度下降算法FGD和随机梯度下降算法SGD的折中方案,在一定程度上兼顾了以上两种方法的优点。

小批量梯度下降算法MBGD每次从训练样本集上随机抽取一个小样本集,在抽出来的小样本集上采用FGD迭代更新权重。被抽出的小样本集所含样本点的个数称为batch_size通常设置为2的幂次方,更有利于CPU/GPU加速处理

注意:

  • batch_size = 1,则变成了SGD
  • batch_size = n,则变成了FGD

MBGD 的迭代形式为:

θ i = θ i − α ∑ j = t t + b s − 1 ( h θ ( x 0 ( j ) , x 1 ( j ) , . . . , x n ( j ) ) − y j ) x i ( j ) \theta_i = \theta_i - \alpha \sum^{t+bs-1}_{j = t}(h_\theta(x^{(j)}_0, x^{(j)}_1, ..., x^{(j)}_n) - y_j)x^{(j)}_i θi=θiαj=tt+bs1(hθ(x0(j),x1(j),...,xn(j))yj)xi(j)

MBGD 的迭代形式为:

θ i = θ i − α ∑ j = t t + x − 1 ( h θ ( x 0 ( j ) , x 1 ( j ) , . . . , x n ( j ) ) − y j ) x i ( j ) \theta_i = \theta_i - \alpha \sum^{t+x-1}_{j = t}(h_\theta(x^{(j)}_0, x^{(j)}_1, ..., x^{(j)}_n) - y_j)x^{(j)}_i θi=θiαj=tt+x1(hθ(x0(j),x1(j),...,xn(j))yj)xi(j)

其中:

  • θ i \theta_i θi 表示第 i i i 个参数
  • α \alpha α 表示学习率
  • h θ ( x 0 ( j ) , x 1 ( j ) , . . . , x n ( j ) ) h_\theta(x^{(j)}_0, x^{(j)}_1, ..., x^{(j)}_n) hθ(x0(j),x1(j),...,xn(j)) 表示模型预测值
  • y j y_j yj 表示第 j j j 个样本的真实值
  • x i ( j ) x^{(j)}_i xi(j) 表示第 j j j 个样本的第 i i i 个特征
  • t t t 表示当前迭代的起始样本下标
  • b s bs bs 表示每次迭代使用的样本数。

上式中,也就是我们从 m m m 个样本中,选择 b s bs bs 个样本进行迭代( 1 < b s < m 1<bs<m 1<bs<m),

7.2.4 随机平均梯度下降算法(Stochastic average gradient descent,SAGD)

随机平均梯度下降算法(Stochastic average gradient descent,SAGD)是一种求解的最优化算法,是随机梯度下降算法的一种变体。它是一种基于梯度的优化方法,用于求解大规模数据集上的凸优化问题。SAGD算法在每次迭代时,使用所有样本的梯度的平均值来更新权重。相比于传统的随机梯度下降算法,SAGD算法在每次迭代时需要计算所有样本的梯度,因此计算量较大,但是收敛速度更快,且能够避免随机梯度下降算法中出现的震荡现象。

在 SGD 方法中,虽然避开了运算成本大的问题,但对于大数据训练而言,SGD 效果常不尽如人意,因为每一轮梯度更新都完全与上一轮的数据和梯度无关

随机平均梯度算法 SAGD 克服了这个问题,因为SAGD在内存中为每一个样本都维护一个旧的梯度,随机选择第 i i i 个样本来更新此样本的梯度,其他样本的梯度保持不变,然后求得所有梯度的平均值,进而更新了参数。

如此,每一轮更新仅需计算一个样本的梯度,计算成本等同于 SGD,但收敛速度快得多。其迭代形式为:

θ i = θ i − α 1 n ( h θ ( x 0 ( j ) , x 1 ( j ) , . . . , x n ( j ) ) − y j ) x i ( j ) \theta_i = \theta_i - \alpha \frac{1}{n}(h_\theta(x^{(j)}_0, x^{(j)}_1, ..., x^{(j)}_n) - y_j)x^{(j)}_i θi=θiαn1(hθ(x0(j),x1(j),...,xn(j))yj)xi(j)

其中:

  • θ i \theta_i θi 表示第 i i i 个参数
  • α \alpha α 表示学习率
  • h θ ( x 0 ( j ) , x 1 ( j ) , . . . , x n ( j ) ) h_\theta(x^{(j)}_0, x^{(j)}_1, ..., x^{(j)}_n) hθ(x0(j),x1(j),...,xn(j)) 表示模型预测值
  • y j y_j yj 表示第 j j j 个样本的真实值
  • x i ( j ) x^{(j)}_i xi(j) 表示第 j j j 个样本的第 i i i 个特征
  • n n n 表示样本个数

我们知道 SGD 是当前权重减去步长乘以梯度,得到新的权重。SAGD 中的A,就是平均的意思。具体说,就是在第 k k k 步迭代的时候,我考虑的这一步和前面 n − 1 n-1 n1 个梯度的平均值,即当前权重减去步长乘以最近 n n n 个梯度的平均值。

注意: n n n 是自己设置的,当 n = 1 n=1 n=1 的时候,就是普通的 SGD。

这个想法非常的简单,在随机中又增加了确定性,类似于 MBGD 的作用。但不同的是,SAGD 又没有去计算更多的样本,只是利用了之前计算出来的梯度,所以每次迭代的计算成本远小于 MBGD,和 SGD 相当。


以下这些算法主要用于深度学习优化

  • 动量法
    • 其实动量法(SGD with momentum)就是SAG的姐妹版
    • SAG 是对过去 K K K 次的梯度求平均值
    • SGD with momentum 是对过去所有的梯度求加权平均
  • Nesterov 加速梯度下降法
    • 类似于一个智能球,在重新遇到斜率上升时候,能够知道减速
  • Adagrad
    • 让学习率使用参数
    • 对于出现次数较少的特征,我们对其采用更大的学习率,对于出现次数较多的特征,我们对其采用较小的学习率
  • Adadelta
    • Adadelta 是 Adagrad 的一种扩展算法,以处理 Adagrad 学习速率单调递减的问题
  • RMSProp
    • 其结合了梯度平方的指数移动平均数来调节学习率的变化
    • 能够在不稳定(Non-Stationary)的目标函数情况下进行很好地收敛
  • Adam
    • 结合 AdaGrad 和 RMSProp 两种优化算法的优点
    • 是一种自适应的学习率算法

8. 线性回归api再介绍

学习目标

  • 了解正规方程的 API 及常用参数
  • 了解梯度下降法 API 及常用参数

语法1

sklearn.linear_model.LinearRegression(fit_intercept=True)
  • 作用:通过正规方程优化
  • 参数:
    • fit_intercept:是否计算偏置
  • 属性
    • LinearRegression.coef_:回归系数
    • LinearRegression.intercept_:偏置

语法2

sklearn.linear_model.SGDRegressor(loss="squared_loss", 
                                  fit_intercept=True, 
                                  learning_rate="invscaling", 
                                  eta0=0.01)
  • 作用:SGDRegressor 类实现了随机梯度下降学习,它支持不同的 Loss 函数和正则化惩罚项来拟合线性回归模型。
  • 参数:
    • loss:损失类型
      • loss="squared_loss":普通最小二乘法
    • fit_intercept:是否计算偏置
    • learning_rate: string, optional 学习率填充
      • "constant"eta = eta0
      • "optimal"eta = 1.0 / (alpha * (t + t0)) [default]
      • “invscaling”eta = eta0 / pow(t, power_t)`
        • power_t=0.25:存在父类当中
      • 对于一个常数值的学习率来说,可以使用learning_rate='constant',并使用eta0来指定学习率
  • 属性:
    • SGDRegressor.coef_:回归系数
    • SGDRegressor.intercept_:偏置

sklearn 提供给我们两种实现的 API,可以根据需要选择使用

9. 案例:波士顿房价预测

学习目标

  • 通过案例掌握正规方程和梯度下降法 API 的使用

9.1 案例背景介绍

实例数量 506
属性数量 13 数值型或类别型,帮助预测的属性
中位数(第 14 个属性)经常是学习目标
属性信息(按顺序) CRIM:城镇人均犯罪率
ZN:占地面积超过 2.5 万平方英尺的住宅用地比例
INDUS:城镇非零售业务地区的比例
CHAS:查尔斯河虚拟变量(如果土地在河边 = 1;否则是 0)
NOX:一氧化氮浓度(每 1000 万份)
RM:平均每居民房数
AGE:在 1940 年之前建成的所有者占用单位的比例
DIS:与五个波士顿就业中心的加权距离
RAD:辐射状公路的可达性指数
TAX:每10,000美元的全额物业税率
PTRATIO:城镇师生比例
B 1000(Bk - 0.63)^2:其中 Bk 是城镇的黑人比例
LSTAT:人口中地位较低人群的百分数
MEDV:以 1000 美元计算的自有住房的中位数
缺失属性值
创建者 Harrison, D. and Rubinfeld, D.L…

这是UCI ML(欧文加利福尼亚大学机器学习库)房价数据集的副本。

下载地址:https://archive.ics.uci.edu/ml/machine-learning-databases/housing/

该数据集是从位于卡内基梅隆大学维护的 StatLib 图书馆取得的。

属性名 解释 类型
CRIM 该镇的人均犯罪率 连续值
ZN 占地面积超过 25,000 平方尺的住宅用地比例 连续值
INDUS 非零售商业用地比例 连续值
CHAS 是否邻近Charles River 离散值:1=邻近;0=不邻近
Nox 一氧化氮浓度 连续值
RM 每栋房屋的年均客房数 连续值
AGE 1940年之前建成的自用单位比例 连续值
DIS 到波士顿5个就业中心的加权距离 连续值
RAD 到径向公路的可达性指数 连续值
TAX 全值财产税率 连续值
PTRATIO 学生与教师的比例 连续值
B 1000(BK - 0.63)^2,其中 BK 为黑人占比 连续值
LSTAT 低收入人群占比 连续值
MEDV 同类房屋价格的中位数 连续值

给定的这些特征,是专家们得出的影响房价的结果属性。我们此阶段不需要自己去探究特征是否有用,只需要使用这些特征。到后面量化很多特征需要我们自己去寻找

9.2 案例分析

回归当中的数据大小不一致,是否会导致结果影响较大。所以需要做标准化处理。

  • 数据分割与标准化处理
  • 回归预测
  • 线性回归的算法效果评估

9.3 回归性能评估

这里使用均方误差(Mean Squared Error,MSE)评价机制。

M S E = 1 m ∑ i = 1 m ( y i − y ‾ ) 2 \mathrm{MSE} = \frac{1}{m} \sum_{i=1}^m(y^i - \overline{y})^2 MSE=m1i=1m(yiy)2

其中, y i y^i yi 为预测值, y ‾ \overline{y} y 为真实值的平均值。

API

sklearn.metrics.mean_squared_error(y_true, y_pred)
  • 作用
    • 均方误差回归损失
  • 参数
    • y_true:真实值
    • y_pred:预测值
    • return:浮点数结果

Q:MSE和最小二乘法的区别是什么?
A:MSE和最小二乘法都是用于回归分析的方法,但是它们的计算方式不同。MSE是预测值与真实值之间差的平方和的平均值。而最小二乘法是一种优化方法,它通过最小化误差平方和来估计模型参数。

最小二乘法是求误差的平方和,而MSE是求完平方和后再求其均值

9.4 代码实现

注意:在加载 Boston 房价数据集时会报错,因为自从 scikit-learn 1.2 版本以后,load_boston 已经被移除了。Boston 房价数据集存在一个伦理问题:该数据集的作者构造了一个不可逆的变量“B”,假设种族自我隔离对房价有积极影响。此外,导致创建此数据集的研究目标是研究空气质量的影响,但它没有充分证明这个假设的有效性。

因此,scikit-learn 维护者强烈不建议使用此数据集,除非代码的目的是研究和教育数据科学和机器学习中的伦理问题。


导入必要的库:

# from sklearn.datasets import load_boston
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
from sklearn.linear_model import LinearRegression, SGDRegressor
from sklearn.metrics import mean_squared_error
import pandas as pd
import numpy as np

因为 Boston 房价数据集已被移除,因此我们需要单独获取:

# 1. 获取数据
data_url = "http://lib.stat.cmu.edu/datasets/boston"
raw_df = pd.read_csv(data_url, sep="\s+", skiprows=22, header=None)
data = np.hstack([raw_df.values[::2, :], raw_df.values[1::2, :2]])
target = raw_df.values[1::2, 2]

9.4.1 正规方程

def linear_model_1_Normal_Equation():
     """
          线性回归:正规方程
     """
     
     # 2. 数据集划分
     x_train, x_test, y_train, y_test = train_test_split(data, target, random_state=2, test_size=0.2)

     # 3. 特征工程:标准化
     transformer = StandardScaler()
     
     # fit()函数用于计算训练数据的均值和标准差,以便对训练数据进行标准化
     x_train = transformer.fit_transform(x_train)
     
     # transform()函数用于使用相同的均值和标准差来对测试数据进行标准化。这确保了我们使用相同的标准化方法来处理训练和测试数据。
     x_test = transformer.transform(x_test)
     
     # 4. 机器学习:线性回归(正规方程)
     estimator = LinearRegression()
     estimator.fit(x_train, y_train)
     
     # 5. 模型评估
     ## 5.1 获取系数
     y_predict = estimator.predict(x_test)
     print(f"预测值为:{
      
      y_predict}")
     print(f"模型中的系数为:{
      
      estimator.coef_}")
     print(f"模型中的偏置为:{
      
      estimator.intercept_}")
     
     ## 5.2 评价:均方误差
     error_mse = mean_squared_error(y_test, y_predict)
     print(f"均方误差为:{
      
      error_mse}")
     
     return None

9.4.2 梯度下降法

def linear_model_2_Gradient_Descent():
     """
          线性回归:梯度下降法
     """
     
     # 2. 数据集划分
     x_train, x_test, y_train, y_test = train_test_split(data, target, random_state=2, test_size=0.2)
     
     # 3. 特征工程:标准化
     transformer = StandardScaler()
     # fit()函数用于计算训练数据的均值和标准差,以便对训练数据进行标准化
     x_train = transformer.fit_transform(x_train)
     
     # transform()函数用于使用相同的均值和标准差来对测试数据进行标准化。这确保了我们使用相同的标准化方法来处理训练和测试数据。
     x_test = transformer.transform(x_test)
     
     # 4. 机器学习:线性回归(梯度下降法)
     estimator = SGDRegressor(max_iter=1000)  # 这个函数中的max_iter不是epoch,而是每个epoch中的迭代次数。
     estimator.fit(X=x_train, y=y_train)
     
     # 5. 模型评估
     ## 5.1 获取系数
     y_predict = estimator.predict(x_test)
     print(f"模型的预测值为:{
      
      y_predict}")
     print(f"模型中的系数为:{
      
      estimator.coef_}")
     print(f"模型中的偏置为:{
      
      estimator.intercept_}")
     
     ## 5.2 评价:MSE
     error = mean_squared_error(y_true=y_test, y_pred=y_predict)
     print(f"MSE误差为:{
      
      error}")
     
     return None

Epoch是指模型在数据集上进行训练的轮数,每一轮训练数据集称为一次迭代。而迭代次数(iteration)是指模型在数据集上进行训练的次数,每次训练使用的数据量是 batch size。

9.4.3 正规方程运行结果

if __name__ == "__main__":
     linear_model_1_Normal_Equation()

运行结果如下

预测值为:
[23.01506153 21.2115869  33.71590384 31.56542369  3.1826268   3.15381954
 27.40305304 22.2126176  14.86506114 21.34105453 30.95942941 26.70065029
 21.12624382 18.37282564 17.64315354 25.38194186 24.42970445 13.36957057
  8.66686786 18.57490534 21.73966467 20.34270529 36.5461105  20.59627495
 19.87979627 15.75766967 37.11632999 34.85897895 30.83458635 23.23441285
 18.68278505 20.749546   31.84560076 30.20214207 13.3861702  15.87078398
 13.70766096 23.74163998 25.95135088 23.18325878 28.99906539 12.50341936
 31.08347911  6.39401895 23.71801218 20.61523929 33.15362417 19.21862493
 35.89603081  0.82365329 31.90288611 31.69640543  6.58849712 34.62762996
 20.41162545 19.69277608 19.53445865 18.58689088 15.81420496 22.98764309
 19.65947045 16.36377019 18.48783369 32.76568172 35.49022568 24.58349631
 41.5854766  32.94818456 14.60990256 27.43178268  8.04470074  5.61185652
 22.21428332 18.72817007 31.02824788 26.04494485 24.60357003 24.84231113
 25.38796252 24.87762205 33.71343923 19.72606026 20.60046055 27.82692882
 38.0055624  37.24265207 22.16841364 29.6160177  31.07303315 17.93399181
 20.87524555 19.48170453 18.61409692 37.13055111 39.81659125  9.1811861
 35.30202671 30.28664671 21.0820992  13.65467682 31.38696603 24.99174874]
模型中的系数为:
[-0.93451207  0.85487686 -0.10446819  0.81541757 -1.90731862  2.54650028
  0.25941464 -2.92654009  2.80505451 -1.95699832 -2.15881929  1.09153332
 -3.91941941]
模型中的偏置为:22.44133663366339
均方误差为:18.4954

9.4.4 梯度下降法运行结果

if __name__ == "__main__":
     linear_model_2_Gradient_Descent()

运行结果如下

模型的预测值为:
[22.97008601 21.54233749 33.40398056 31.37169431  3.02490022  3.28076204
 27.33142199 22.87326808 14.89699494 21.56820457 31.06330403 26.50800701
 21.36331286 18.30966912 17.6258269  25.36147902 24.44335033 13.29265393
  8.67786007 18.55642685 21.69966353 20.61348938 36.48886977 20.52798849
 19.83874268 15.88847558 37.1515701  35.0545775  30.57079889 23.4462129
 18.4629217  20.67485546 31.9187924  30.18674511 13.33091437 16.04626997
 13.5563538  23.95300489 26.00718192 23.32099056 28.85934425 12.58600836
 31.34438891  6.28300469 23.4899127  20.51246142 33.54006522 19.15099843
 35.78666183  0.55787795 31.97353393 31.66015891  6.67574259 34.36353447
 20.19279146 19.87162432 19.401037   18.77365657 15.94213222 23.05213487
 19.40511698 16.62341646 18.50166401 32.34809036 35.1807121  24.91741065
 41.18710196 33.03099357 14.43244788 27.63546339  8.21972512  5.55359461
 22.29067012 18.56491174 31.11417781 26.1689206  24.51138147 24.64021638
 25.31640483 24.92124789 33.59732337 20.00217476 20.52346408 27.85202637
 37.94979065 37.1844462  22.02677899 29.62224074 31.18202907 17.80338565
 21.0977779  19.62423171 18.58089503 36.8570065  39.77825674  9.11104817
 35.32418125 30.74161137 21.08474487 13.81576484 31.42289792 25.00526589]
模型中的系数为:
[-0.86461037  0.76275779 -0.33309812  0.87192037 -1.76987389  2.62119033
  0.21609998 -2.8546837   2.19042893 -1.32462947 -2.11576385  1.09324102
 -3.89656514]
模型中的偏置为:[22.46238316]
MSE误差为:18.6316

9.4.5 梯度下降法不同学习率的效果

sklearn.linear_model.SGDRegressor(loss="squared_loss", 
                                  fit_intercept=True, 
                                  learning_rate="invscaling", 
                                  eta0=0.01)

我们也可以尝试去修改学习率等参数,找到效果更好的超参数。

这里我们对学习率进行修改,看看不同学习率对误差的影响(误差越小越好)。

Q:在线性回归任务中,可以使用准确率作为评估指标吗?
A:在线性回归任务中,准确率不是一个好的评估指标。因为准确率是分类问题的评估指标,而线性回归是一个回归问题。在回归问题中,我们通常使用均方误差(MSE)、均方根误差(RMSE)和平均绝对误差(MAE)等指标来评估模型的性能。

from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
from sklearn.linear_model import SGDRegressor
from sklearn.metrics import mean_squared_error, accuracy_score
import pandas as pd
import numpy as np


# 1. 获取数据
data_url = "http://lib.stat.cmu.edu/datasets/boston"
raw_df = pd.read_csv(data_url, sep="\s+", skiprows=22, header=None)
data = np.hstack([raw_df.values[::2, :], raw_df.values[1::2, :2]])
target = raw_df.values[1::2, 2]

# 2. 数据集划分
x_train, x_test, y_train, y_test = train_test_split(data, target, random_state=2, test_size=0.2)

# 3. 特征工程:标准化
transformer = StandardScaler()
# fit()函数用于计算训练数据的均值和标准差,以便对训练数据进行标准化
x_train = transformer.fit_transform(x_train)

# transform()函数用于使用相同的均值和标准差来对测试数据进行标准化。这确保了我们使用相同的标准化方法来处理训练和测试数据。
x_test = transformer.transform(x_test)

# 定义学习率列表
lr_lst = [0.00001, 0.0001, 0.001, 0.01, 0.02, 0.05, 0.10, 0.25, 0.5, 0.9]

for lr in lr_lst:
    # 4. 机器学习:线性回归(梯度下降法)
    estimator = SGDRegressor(max_iter=1000, eta0=lr)  # 这个函数中的max_iter不是epoch,而是每个epoch中的迭代次数。
    estimator.fit(X=x_train, y=y_train)

    # 5. 模型评估
    y_predict = estimator.predict(X=x_test)
    error = mean_squared_error(y_true=y_test, y_pred=y_predict)
    print(f"[学习率={
      
      lr}] MSE误差为:{
      
      error:.4f}")

运行结果:

[学习率=0.0001] MSE误差为:25.7471
[学习率=0.001] MSE误差为:18.7909
[学习率=0.01] MSE误差为:18.6316
[学习率=0.02] MSE误差为:18.4748
[学习率=0.05] MSE误差为:18.2795
[学习率=0.1] MSE误差为:18.7501
[学习率=0.25] MSE误差为:29.4968
[学习率=0.5] MSE误差为:44.6285
[学习率=0.9] MSE误差为:592136344112942678016.0000

Q1:对于 Boston 房价数据集而言,MSE误差为:18.2795,效果如何?
A1:波士顿房价数据集的 MSE 误差为 18.2795,这个数值越小,说明预测结果越准确。因此,这个结果是比较好的。

Q2:在深度学习中,误差一般都是小数,而在机器学习中,18.2795 的MSE误差可以称为“好”吗?
A2:在机器学习中,MSE 误差的大小并不是绝对的,而是相对于数据集的范围而言。因此,18.2795 的 MSE 误差可以称为“好”,但需要结合数据集的范围来看待这个数值。

9.4.6 梯度下降法不同迭代次数的效果

from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
from sklearn.linear_model import SGDRegressor
from sklearn.metrics import mean_squared_error, accuracy_score
import pandas as pd
import numpy as np


# 1. 获取数据
data_url = "http://lib.stat.cmu.edu/datasets/boston"
raw_df = pd.read_csv(data_url, sep="\s+", skiprows=22, header=None)
data = np.hstack([raw_df.values[::2, :], raw_df.values[1::2, :2]])
target = raw_df.values[1::2, 2]

# 2. 数据集划分
x_train, x_test, y_train, y_test = train_test_split(data, target, random_state=2, test_size=0.2)

# 3. 特征工程:标准化
transformer = StandardScaler()
# fit()函数用于计算训练数据的均值和标准差,以便对训练数据进行标准化
x_train = transformer.fit_transform(x_train)

# transform()函数用于使用相同的均值和标准差来对测试数据进行标准化。这确保了我们使用相同的标准化方法来处理训练和测试数据。
x_test = transformer.transform(x_test)

# 定义迭代次数列表
iter_lst = [1, 10, 100, 1000, 2000, 5000, 10000, 50000, 100000, 1000000]

for iter in iter_lst:
    # 4. 机器学习:线性回归(梯度下降法)
    estimator = SGDRegressor(max_iter=iter)  # 这个函数中的max_iter不是epoch,而是每个epoch中的迭代次数。
    estimator.fit(X=x_train, y=y_train)

    # 5. 模型评估
    y_predict = estimator.predict(X=x_test)
    error = mean_squared_error(y_true=y_test, y_pred=y_predict)
    print(f"[迭代次数={
      
      iter}] MSE误差为:{
      
      error:.4f}")

运行结果:

[迭代次数=10] MSE误差为:18.8887
[迭代次数=100] MSE误差为:18.5569
[迭代次数=1000] MSE误差为:18.6446
[迭代次数=2000] MSE误差为:18.8068
[迭代次数=5000] MSE误差为:18.5531
[迭代次数=10000] MSE误差为:18.6446
[迭代次数=50000] MSE误差为:18.5797
[迭代次数=100000] MSE误差为:18.5219
[迭代次数=1000000] MSE误差为:18.5547

9.4.7 梯度下降法不同学习率和迭代次数的效果

from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
from sklearn.linear_model import SGDRegressor
from sklearn.metrics import mean_squared_error, accuracy_score
import pandas as pd
import numpy as np


# 1. 获取数据
data_url = "http://lib.stat.cmu.edu/datasets/boston"
raw_df = pd.read_csv(data_url, sep="\s+", skiprows=22, header=None)
data = np.hstack([raw_df.values[::2, :], raw_df.values[1::2, :2]])
target = raw_df.values[1::2, 2]

# 2. 数据集划分
x_train, x_test, y_train, y_test = train_test_split(data, target, random_state=2, test_size=0.2)

# 3. 特征工程:标准化
transformer = StandardScaler()
# fit()函数用于计算训练数据的均值和标准差,以便对训练数据进行标准化
x_train = transformer.fit_transform(x_train)

# transform()函数用于使用相同的均值和标准差来对测试数据进行标准化。这确保了我们使用相同的标准化方法来处理训练和测试数据。
x_test = transformer.transform(x_test)

# 定义学习率列表
lr_lst = [0.00001, 0.0001, 0.001, 0.01, 0.02, 0.05]

# 定义迭代次数列表
iter_lst = [1000, 2000, 5000, 10000, 50000, 100000, 1000000]

log = {
    
    }
print("-" * 50)
for lr in lr_lst:
    for iter in iter_lst:
        # 4. 机器学习:线性回归(梯度下降法)
        estimator = SGDRegressor(max_iter=iter, eta0=lr)  # 这个函数中的max_iter不是epoch,而是每个epoch中的迭代次数。
        estimator.fit(X=x_train, y=y_train)

        # 5. 模型评估
        y_predict = estimator.predict(X=x_test)
        error = mean_squared_error(y_true=y_test, y_pred=y_predict)
        print(f"[学习率={
      
      lr}][迭代次数={
      
      iter}] MSE误差为:{
      
      error:.4f}")
    print("-" * 50)
[学习率=1e-05][迭代次数=1000] MSE误差为:369.9415
[学习率=1e-05][迭代次数=2000] MSE误差为:276.4349
[学习率=1e-05][迭代次数=5000] MSE误差为:143.1826
[学习率=1e-05][迭代次数=10000] MSE误差为:65.0462
[学习率=1e-05][迭代次数=50000] MSE误差为:30.8400
[学习率=1e-05][迭代次数=100000] MSE误差为:30.8408
[学习率=1e-05][迭代次数=1000000] MSE误差为:30.8405
--------------------------------------------------
[学习率=0.0001][迭代次数=1000] MSE误差为:25.7335
[学习率=0.0001][迭代次数=2000] MSE误差为:19.4445
[学习率=0.0001][迭代次数=5000] MSE误差为:19.4434
[学习率=0.0001][迭代次数=10000] MSE误差为:19.4428
[学习率=0.0001][迭代次数=50000] MSE误差为:19.4439
[学习率=0.0001][迭代次数=100000] MSE误差为:19.4458
[学习率=0.0001][迭代次数=1000000] MSE误差为:19.4455
--------------------------------------------------
[学习率=0.001][迭代次数=1000] MSE误差为:18.8040
[学习率=0.001][迭代次数=2000] MSE误差为:18.8028
[学习率=0.001][迭代次数=5000] MSE误差为:18.8255
[学习率=0.001][迭代次数=10000] MSE误差为:18.8165
[学习率=0.001][迭代次数=50000] MSE误差为:18.8200
[学习率=0.001][迭代次数=100000] MSE误差为:18.7980
[学习率=0.001][迭代次数=1000000] MSE误差为:18.7967
--------------------------------------------------
[学习率=0.01][迭代次数=1000] MSE误差为:18.5404
[学习率=0.01][迭代次数=2000] MSE误差为:18.6090
[学习率=0.01][迭代次数=5000] MSE误差为:18.5098
[学习率=0.01][迭代次数=10000] MSE误差为:18.5280
[学习率=0.01][迭代次数=50000] MSE误差为:18.6144
[学习率=0.01][迭代次数=100000] MSE误差为:18.6155
[学习率=0.01][迭代次数=1000000] MSE误差为:18.5032
--------------------------------------------------
[学习率=0.02][迭代次数=1000] MSE误差为:18.7820
[学习率=0.02][迭代次数=2000] MSE误差为:19.0602
[学习率=0.02][迭代次数=5000] MSE误差为:19.0929
[学习率=0.02][迭代次数=10000] MSE误差为:18.4048
[学习率=0.02][迭代次数=50000] MSE误差为:18.5853
[学习率=0.02][迭代次数=100000] MSE误差为:18.6895
[学习率=0.02][迭代次数=1000000] MSE误差为:18.6095
--------------------------------------------------
[学习率=0.05][迭代次数=1000] MSE误差为:19.1055
[学习率=0.05][迭代次数=2000] MSE误差为:19.1228
[学习率=0.05][迭代次数=5000] MSE误差为:20.9130
[学习率=0.05][迭代次数=10000] MSE误差为:17.4903
[学习率=0.05][迭代次数=50000] MSE误差为:17.6103
[学习率=0.05][迭代次数=100000] MSE误差为:19.9095
[学习率=0.05][迭代次数=1000000] MSE误差为:19.7085
--------------------------------------------------

小结

  • 正规方程和梯度下降法 API 在真实案例中的使用【知道】
  • 线性回归性能评估【知道】
    • 均方误差

10. 欠拟合和过拟合

学习目标

  • 掌握过拟合、欠拟合的概念
  • 掌握过拟合、欠拟合产生的原因
  • 知道什么是正则化,以及正则化的分类

10.1 定义

过拟合:一个假设在训练数据上能够获得比其他假设更好的拟合,但是在测试数据集上却不能很好地拟合数据,此时认为这个假设出现了过拟合的现象。(模型过于复杂

欠拟合:一个假设在训练数据上不能获得更好的拟合,并且在测试数据集上也不能很好地拟合数据,此时认为这个假设出现了欠拟合的现象。(模型过于简单

在这里插入图片描述

Q:那么是什么原因导致模型复杂?
A:线性回归模型在处理非线性关系的数据时会变得复杂。这是因为线性回归模型只能处理线性关系的数据,而现实中的很多事物特征与目标值之间的关系并不是简单的线性关系,因此需要引入更多的特征来描述这种复杂的关系,从而导致模型变得更加复杂。此外,如果训练数据中存在很多无用的特征,也会导致模型变得复杂。因此,在进行线性回归模型训练时,需要对特征进行筛选和优化,以提高模型的性能和泛化能力。

10.2 原因以及解决办法

一、欠拟合原因以及解决办法

  • 原因:学习到数据的特征过少。
  • 解决办法
    1. 添加其他特征项。有时候我们模型出现欠拟合的时候是因为特征项不够导致的,可以添加其他特征项来很好地解决。例如,“组合”、“泛化"、“相关性”“三类特征是特征添加的重要手段,无论在什么场景,都可以照葫芦画瓢,总会得到意想不到的效果。除上面的特征之外,“上下文特征””、“平台特征"等等,都可以作为特征添加的首选项。
    2. 添加多项式特征。这个在机器学习算法里面用的很普遍,例如将线性模型通过添加二次项或者三次项使模型泛化能力更强。

二、过拟合原因以及解决办法

  • 原因:原始特征过多,存在一些嘈杂特征,模型过于复杂是因为模型尝试去兼顾各个测试数据点。
  • 解决办法
    1. 重新清洗数据。导致过拟合的一个原因也有可能是数据不纯导致的,如果出现了过拟合就需要我们重新清洗数据。
    2. 增大数据的训练量。还有一个原因就是我们用于训练的数据量太小导致的,训练数据占总数据的比例过小。
    3. 正则化:将模型简单化。
    4. 减少特征维度:防止维度灾难。

在机器学习中,维度灾难是指在高维空间中,数据变得稀疏且难以处理的现象。当特征数量很大时,模型的复杂度会增加,这可能会导致过拟合。减少特征维度是一种防止维度灾难的方法。这可以通过特征选择、特征提取和降维来实现。在特征选择中,我们选择最相关的特征,而在特征提取中,我们将原始特征转换为新的特征。降维是一种更广泛的方法,它可以通过 PCA、LDA 等技术来实现。减少特征维度可以提高模型的泛化能力,并减少过拟合的风险。

11. 正则化

11.1 什么是正则化

在机器学习中,正则化是一种防止过拟合的技术。它通过向模型添加额外的约束来限制模型的复杂度,从而提高模型的泛化能力正则化可以通过在损失函数中添加一个正则化项来实现,这个正则化项通常是模型参数的范数。L1 和 L2 正则化是两种常见的正则化方法。L1 正则化通过将模型参数的绝对值加到损失函数中来实现,而 L2 正则化通过将模型参数的平方和加到损失函数中来实现。这两种方法都可以有效地减少过拟合。


Q:给模型添加正则项,一般是在损失函数中添加吗?
A:是的,通常情况下,正则项会被添加到损失函数中。正则化是一种用来防止过拟合的技术,它通过在损失函数中添加一个正则项来实现。这个正则项通常是模型参数的函数,它会惩罚模型复杂度过高的情况。在训练过程中,优化算法会试图最小化损失函数,这就意味着它不仅要最小化预测误差,还要最小化正则项


在解决回归过拟合中,我们可以选择正则化。但是对于其他机器学习算法如分类算法来说也会出现这样的问题,除了一些算法本身作用之外(决策树、神经网络),我们更多的也是去自己做特征选择,包括之前说的删除、合并一些特征。

在这里插入图片描述

我们从上图可以发现,当高次项太少的时候(左图),模型会欠拟合;此时我们可以增加模型的复杂度,即为其添加适当的高次项(中图),此时模型拟合的效果比较好;但如果高次项太大(太多)时,模型过于复杂,此时会出现过拟合的现象。

当出现过拟合时,一般是由于高次项太多导致的,那么我们应该如何解决这个问题?

θ 0 + θ 1 x + θ 2 x 2 + θ 3 x 3 + θ 4 x 4 我们应该尽量减少高次项特征的影响 \theta_0 + \theta_1x + \theta_2x^2 + \underset{我们应该尽量减少高次项特征的影响}{\theta_3x^3 + \theta_4x^4} θ0+θ1x+θ2x2+我们应该尽量减少高次项特征的影响θ3x3+θ4x4

其中, θ i \theta_i θi 是模型的参数。

在学习的时候,数据提供的特征有些影响模型复杂度或者这个特征的数据点异常较多,所以算法在学习的时候尽量减少这个特征的影响(甚至删除某个特征的影响),这就是正则化。

注意:调整时候,算法并不知道某个特征影响,而是去调整模型参数得出优化的结果,因此正则项是对模型进行调整

11.2 正则化类别

  • L2 正则化
    • 作用:可以使得模型中一些参数 θ i \theta_i θi 变得很小,接近于 0,从而削弱某个特征的影响。
    • 优点:越小的参数说明模型越简单,越简单的模型则越不容易产生过拟合现象
    • 代表算法:Ridge 回归(岭回归)
  • L1 正则化
    • 作用:可以使得其中一些 θ \theta θ 的值直接为0(简单粗暴),从而删除这个特征的影响。
    • 代表算法:LASSO 回归

小结

  • 欠拟合【掌握】
    • 表现:在训练集上表现不好,在测试集上表现不好。
    • 解决方法:继续学习
      1. 添加其他特征项
      2. 添加多项式特征
  • 过拟合【掌握】
    • 表现:在训练集上表现好,在测试集上表现不好。
    • 解决方法:
      1. 重新清洗数据集
      2. 增大数据的训练量
      3. 正则化
      4. 减少特征
  • 维度正则化【掌握】
    • 作用:通过限制高次项的系数进行防止过拟合。
    • L1 正则化
      • 理解:直接把高次项前面的系数变为 0
      • 代表:Lasso 回归
    • L2 正则化
      • 理解:把高次项前面的系数变成特别小的值
      • 代表:岭回归

11.3 【拓展】维度灾难(Curse of Dimensionality,CoD)

11.3.1 什么是维度灾难

维灾难就是维度灾难。在机器学习中,维度灾难通常指的是随着特征数量的增多,计算量会变得很大。这个现象涉及到数字分析、抽样、组合、机器学习、数据挖掘和数据库等领域。在机器学习中,维度灾难通常指的是在涉及到向量的计算的问题中,随着维数的增加,计算量呈指数倍增长的一种现象

简单来说,维度灾难就是指随着维度(特征数量)的增加,分类器性能逐步上升,到达某点之后,其性能便逐渐下降

在这里插入图片描述

从上图可以看到,随着维度(特征数量)的增加,分类器的性能不再上升,反而下降。


有一系列的图片,每张图片的内容可能是猫也可能是狗。我们需要构造一个分类器能够对猫、狗自动的分类。首先,要寻找到一些能够描述猫和狗的特征,这样我们的分类算法就可以利用这些特征去识别物体。猫和狗的皮毛颜色可能是一个很好的特征,考虑到红绿蓝构成图像的三基色,因此用图片三基色各自的平均值称得上方便直观。这样就有了一个简单的Fisher分类器:

if 0.5 * red + 0.3 * green + 0.2 * blue > 0.6:
  return cat
else:
  return dog

拓展】Fisher 分类器也叫 Fisher 线性判别(Fisher Linear Discriminant,FLD),或称为线性判别分析(Linear Discriminant Analysis,LDA)。它是一种线性分类器,用于将数据投影到一条直线上,以便将数据分成两个类别。

Fisher分类器的公式是:

if w1 * x1 + w2 * x2 + ... + wn * xn + b > 0:
 return cls_1
 else:
 return cls_2

其中,w1, w2, …, wn 是权重,x1, x2, …, xn 是特征,b 是偏置,cls_1 是类别 1,cls_2 是类别 2。这个公式中的权重和偏置是通过训练数据集得到的。

使用颜色特征可能无法得到一个足够准确的分类器,如果是这样的话,我们不妨加入一些诸如图像纹理(图像灰度值在其 X X X Y Y Y 方向的导数 d x dx dx d y dy dy)。这样就有 5 个特征(RedBlueGreendxdy)来设计我们的分类器。

也许 5 个特征的分类器的准确率依然无法满足要求,根据思维惯性,我们可能会再加入更多的特征,比如颜色、纹理的统计信息等等。如此下去,我们可能会得到上百个特征。那是不是我们的分类器性能会随着特征数量的增加而逐步提高呢?答案也许有些让人沮丧。事实上,当特征数量达到一定规模后,分类器的性能是在下降的

总结维度灾难就是随着维度(特征数量)的增加,分类器的性能却下降了

11.3.2 维数灾难与过拟合

我们假设猫和狗图片的数量是有限的(样本数量总是有限的)。假设有 10 张图片,接下来我们就用这仅有的 10 张图片来训练我们的分类器。

在这里插入图片描述

特征是 1 维示意图

增加一个特征,比如绿色,这样特征维数扩展到了 2 维,如下图所示。

在这里插入图片描述

特征扩展到 2 维示意图

增加一个特征后,我们依然无法找到一条简单的直线将它们有效分类。

再增加一个特征,比如蓝色,扩展到 3 维特征空间,如下图所示。

在这里插入图片描述

特征扩展到 3 维示意图

在 3 维特征空间中,我们很容易找到一个分类平面,能够在训练集上有效的将猫和狗进行分类。如下图所示。

在这里插入图片描述

特征扩展到 3 维后,分割平面示意图

乍一看,在高维空间中,我们似乎能得到更优的分类器性能。

从 1 维到 3 维,给我们的感觉是:维数越高,分类性能越优。然而,维数过高将导致一定的问题:

  • 在一维特征空间下,我们假设一个维度的宽度为 5 个单位,这样样本密度为 10 / 5 = 2 10/5=2 10/5=2
  • 在 2 维特征空间下,10 个样本所分布的空间大小 25,这样样本密度为 10 / 25 = 0.4 10/25=0.4 10/25=0.4
  • 在 3 维特征空间下,10 个样本分布的空间大小为 125,样本密度就为 10 / 125 = 0.08 10/125=0.08 10/125=0.08
  • 如果继续增加特征数量,随着维度的增加,样本将变得越来越稀疏。在这种情况下,也更容易找到一个超平面将目标分开。但是,如果我们将高维空间向低维空间投影,高维空间隐藏的问题将会显现出来:
    • 过多的特征导致的过拟合现象:模型在训练集上表现良好,但是对新数据(测试集)缺乏泛化能力。

在这里插入图片描述

高维空间训练形成的线性分类器,相当于在低维空间的一个复杂的非线性分类器。这种分类器过多的强调了训练集的准确率甚至于对一些错误/异常的数据也进行了学习,而正确的数据却无法覆盖整个特征空间。为此,这样得到的分类器在对新数据进行预测时将会出现错误。这种现象称之为过拟合,同时也是维度灾难的直接体现。

在这里插入图片描述

简单的线性分类器在训练数据上的表现不如非线性分类器,但由于线性分类器的学习过程中对噪声没有对非线性分类器敏感,因此对新数据具备更优的泛化能力。换句话说,通过使用更少的特征,避免了维数灾难的发生(也即避免了高维情况下的过拟合)。


Q:为什么高维会带来数据稀疏性问题?
A:假设有一个特征,它的取值范围 D D D 在 0 到 1 之间均匀分布,并且对狗和猫来说其值都是唯一的,我们现在利用这个特征来设计分类器。如果我们的训练数据覆盖了取值范围的 20%(例如 0 到 0.2),那么所使用的训练数据就占总样本量的 20%。

而上升到二维情况下,覆盖二维特征空间 20% 的面积,则需要在每个维度上取得 45% 的取值范围;在三维情况下,要覆盖特征空间 20% 的体积,则需要在每个维度上取得 58% 的取值范围,以此类推…

综上所述,在维度接近一定程度时,要取得同样的训练样本数量,则几乎要在每个维度上取得接近 100% 的取值范围,或者增加总样本数量,但样本数量也总是有限的。


如果一直增加特征维数,由于样本分布越来越稀疏,如果要避免过拟合的出现,就不得不持续增加样本数量。

在这里插入图片描述

数据在高维空间的中心比在边缘区域具备更大的稀疏性,数据更倾向于分布在空间的边缘区域。如下图所示。

在这里插入图片描述

不属于单位圆的训练样本比搜索空间的中心更接近搜索空间的角点。这些样本很难分类,因为它们的特征值差别很大(例如,单位正方形的对角的样本)。

一个有趣的问题是,当我们增加特征空间的维度时,圆〈超球面)的体积相对于正方形(超立方体)的体积发生怎样变化?

尺寸 d d d 的单位超立方体的体积总是 1 d = 1 1^d=1 1d=1。尺寸 d d d 和半径 0.5 0.5 0.5 的内切超球体的体积可以计算为:

V ( d ) = π d 2 Γ ( d 2 + 1 ) 0. 5 d V(d) = \frac{\pi^{\frac{d}{2}}}{\Gamma(\frac{d}{2} + 1)} 0.5^d V(d)=Γ(2d+1)π2d0.5d

在高维空间中,大多数训练数据驻留在定义特征空间的超立方体的角落中。如前所述,特征空间角落中的实例比围绕超球体质心的实例更难分类。

在这里插入图片描述

在这里插入图片描述

一个 8 维超立方体有 2 8 = 256 2^8 = 256 28=256 个角

事实证明,许多事物在高维空间中表现得非常不同。例如,如果你选择一个单位平方( 1 × 1 1\times 1 1×1平方)的随机点,它将只有大约 0.4 % 0.4\% 0.4% 的机会位于小于 0.001 0.001 0.001 的边界(换句话说,随机点将沿任何维度“极端”是非常不可能的)。但是在一个 10000 10000 10000 维单位超立方体( 1 × 1 × 1 1\times 1 \times 1 1×1×1 立方体,有一万个 1 1 1)中,这个概率大于 99.999999 % 99.999999\% 99.999999%。高维超立方体中的大部分点都非常靠近边界。更难区分的是:如果你在一个单位正方形中随机抽取两个点,这两个点之间的距离平均约为 0.52 0.52 0.52。如果在单位三维立方体中选取两个随机点,则平均距离将大致为 0.66 0.66 0.66,但是在一个100万维的超立方体中随机抽取两点呢?那么平均距离将是大约 408.25 408.25 408.25 (大约是 1 , 000 , 000 6 \frac{1,000,000}{6} 61,000,000)!

非常违反直觉:当两个点位于相同的单位超立方体内时,两点如何分离?这个事实意味着高维数据集有可能非常稀疏:大多数训练实例可能彼此远离。当然,这也意味着一个新实例可能离任何训练实例都很远,这使得预测的可信度表现得比在低维度数据中要来的差。训练集的维度越多,过度拟合的风险就越大。

理论上讲,维度灾难的一个解决方案可能是增加训练集的大小以达到足够密度的训练实例。 不幸的是,在实践中,达到给定密度所需的训练实例的数量随着维度的数量呈指数增长。 如果只有100个特征(比MNIST问题少得多),那么为了使训练实例的平均值在 0.1 以内,需要比可观察宇宙中的原子更多的训练实例(假设它们在所有维度上均匀分布)。

对于8维超立方体,大约 98 % 98\% 98% 的数据集中在其 256 个角上。当特征空间的维度达到无穷大时,从采样点到质心的 最小和最大的 欧几里得距离之差 与 最小距离本身之比 趋于零:

lim ⁡ d → ∞ d i s t m a x − d i s t m i n d i s t m i n → 0 \lim_{d \to \infty} \frac{\mathrm{dist_{max}} - \mathrm{dist_{min}}}{\mathrm{dist}_{min}} \to 0 dlimdistmindistmaxdistmin0

距离测量开始失去其在高维空间中测量的有效性。由于分类器取决于这些距离测量,因此在较低维空间中分类通常更容易,其中较少特征用于描述感兴趣对象。

如果理论无限数量的训练样本可用,则维度的诅咒不适用,可以简单地使用无数个特征来获得完美的分类。训练数据越小,应使用的功能就越少。如果 N N N 个训练样本足以覆盖单位区间大小的 1 D 1D 1D 特征空间,则需要 N 2 N^2 N2 个样本来覆盖具有相同密度的 2 D 2D 2D 特征空间,并且在 3 D 3D 3D 特征空间中需要 N 3 N^3 N3 个样本。换句话说,所需的训练实例数量随着使用的维度数量呈指数增长

12. 正则化线性模型

学习目标

  • 知道正则化中岭回归的线性模型
  • 知道正则化中 Lasso 回归的线性模型
  • 知道正则化中弹性网络的线性模型
  • 了解正则化中 Early Stopping 的线性模型

主要学习内容

  1. Ridge Regression 岭回归 → 引入 L 2 L_2 L2 正则项
  2. Lasso 回归 → 引入 L 1 L_1 L1 正则项
  3. Elastic Net 弹性网络
  4. Early stopping 早停

12.1 Ridge Regression(岭回归,又名 Tikhonov regularization)

岭回归(Ridge Regression)是一种用于处理多重共线性问题的线性回归模型。在普通最小二乘法(OLS)中,如果存在多重共线性,那么估计的系数会非常大,导致模型过拟合。岭回归通过在目标函数中加入一个 L 2 L_2 L2 正则化项,使得估计的系数更加稳定。

简单来说,岭回归是线性回归的正则化版本,即在原来的线性回归的损失函数中添加正则项(Regularization Term) α ∑ i = 1 n θ i 2 \alpha \sum_{i=1}^n \theta^2_i αi=1nθi2 以达到在拟合数据的同时,使模型权重尽可能小的目的。

岭回归的代价函数(损失函数)

J ( θ ) = M S E ( θ ) 线性回归常用的损失函数 + α ∑ i = 1 n θ i 2 L 2 正则项 J(\theta) = \underset{线性回归常用的损失函数}{ \mathrm{MSE}(\theta)} + \underset{L_2 正则项}{\alpha \sum_{i=1}^n \theta^2_i} J(θ)=线性回归常用的损失函数MSE(θ)+L2正则项αi=1nθi2

即:

J ( θ ) = 1 m ∑ i = 1 m ( θ T ⋅ x ( i ) − y ( i ) ) 2 ‾ 线性回归常用的损失函数 + α ∑ i = 1 n θ i 2 ‾ L 2 正则项 J(\theta) = \underset{线性回归常用的损失函数}{\underline{\frac{1}{m}\sum_{i = 1}^m(\theta^T \cdot x^{(i)} - y^{(i)})^2}} + \underset{L_2 正则项}{\underline{\alpha \sum_{i=1}^n \theta^2_i}} J(θ)=线性回归常用的损失函数m1i=1m(θTx(i)y(i))2+L2正则项αi=1nθi2

其中:

  • θ \theta θ 是线性回归模型的参数,用于拟合数据。在岭回归中, θ \theta θ 是一个 n n n 维向量,其中 θ i \theta_i θi 表示第 i i i 个特征的系数
  • x ( i ) x^{(i)} x(i) 是第 i i i 个样本的特征向量
  • y ( i ) y^{(i)} y(i) 是第 i i i 个样本的标签
  • α \alpha α 是正则化系数,用于控制正则化项的强度(可以理解为是一个惩罚项,用来限制 θ i \theta_i θi
  • M S E ( θ ) \mathrm{MSE}(\theta) MSE(θ) 是均方误差(Mean Squared Error)损失函数,定义为:

M S E ( θ ) = 1 m ∑ i = 1 m ( θ T ⋅ x ( i ) − y ( i ) ) 2 \mathrm{MSE}(\theta) = \frac{1}{m}\sum_{i = 1}^m(\theta^T \cdot x^{(i)} - y^{(i)})^2 MSE(θ)=m1i=1m(θTx(i)y(i))2

其中 m m m 是样本数量。

注意:当 α = 0 \alpha = 0 α=0 时,岭回归退化为线性回归。

12.2 Lasso Regression(Lasso回归)

Lasso Regression(Lasso回归)是一种线性回归模型,它在原来的线性回归的损失函数中添加正则项(Regularization Term) α ∑ i = 1 n ∣ θ i ∣ \alpha \sum_{i=1}^n |\theta_i| αi=1nθi 以达到在拟合数据的同时,使模型权重尽可能小的目的。与岭回归不同,Lasso回归使用的是 L 1 L_1 L1 正则化项。

Lasso回归的代价函数(损失函数)

J ( θ ) = M S E ( θ ) + α ∑ i = 1 n ∣ θ i ∣ J(\theta) = \mathrm{MSE}(\theta) + \alpha \sum_{i=1}^n |\theta_i| J(θ)=MSE(θ)+αi=1nθi

即:

J ( θ ) = 1 m ∑ i = 1 m ( θ T ⋅ x ( i ) − y ( i ) ) 2 + α ∑ i = 1 n ∣ θ i ∣ J(\theta) = \frac{1}{m}\sum_{i = 1}^m(\theta^T \cdot x^{(i)} - y^{(i)})^2 + \alpha \sum_{i=1}^n |\theta_i| J(θ)=m1i=1m(θTx(i)y(i))2+αi=1nθi

其中:

  • θ \theta θ 是线性回归模型的参数,用于拟合数据。在岭回归中, θ \theta θ 是一个 n n n 维向量,其中 θ i \theta_i θi 表示第 i i i 个特征的系数
  • x ( i ) x^{(i)} x(i) 是第 i i i 个样本的特征向量
  • y ( i ) y^{(i)} y(i) 是第 i i i 个样本的标签
  • α \alpha α 是正则化系数,用于控制正则化项的强度(可以理解为是一个惩罚项,用来限制 θ i \theta_i θi
  • M S E ( θ ) \mathrm{MSE}(\theta) MSE(θ) 是均方误差(Mean Squared Error)损失函数,定义为:

M S E ( θ ) = 1 m ∑ i = 1 m ( θ T ⋅ x ( i ) − y ( i ) ) 2 \mathrm{MSE}(\theta) = \frac{1}{m}\sum_{i = 1}^m(\theta^T \cdot x^{(i)} - y^{(i)})^2 MSE(θ)=m1i=1m(θTx(i)y(i))2

其中 m m m 是样本数量。

注意

  • α = 0 \alpha = 0 α=0 时,Lasso回归退化为线性回归。
  • Lasso Regression 的代价函数在 θ i = 0 \theta_i = 0 θi=0 处是不可导的
  • 解决方法:在 θ i = 0 \theta_i = 0 θi=0 处用一个次梯度向量(Subgradient vector)代替梯度。Lasso Regression 的次梯度向量如下式

g ( θ , J ) = Δ θ M S E ( θ ) + α ( s i g n ( θ 1 ) s i g n ( θ 2 ) . . . s i g n ( θ n ) ) g(\theta, J) = \Delta_\theta \mathrm{MSE}(\theta) + \alpha \begin{pmatrix} \mathrm{sign}(\theta_1) \\ \mathrm{sign}(\theta_2) \\ ... \\ \mathrm{sign}(\theta_n) \\ \end{pmatrix} g(θ,J)=ΔθMSE(θ)+α sign(θ1)sign(θ2)...sign(θn)

其中 Δ θ M S E ( θ ) \Delta_\theta \mathrm{MSE}(\theta) ΔθMSE(θ) 是均方误差损失函数 M S E ( θ ) \mathrm{MSE}(\theta) MSE(θ) 对模型参数 θ \theta θ 的梯度, s i g n ( θ i ) \mathrm{sign}(\theta_i) sign(θi) θ i \theta_i θi 的符号函数,具体为:

s i g n ( θ i ) = { − 1 θ i < 0 0 θ i x = 0 1 θ i > 0 \mathrm{sign}(\theta_i) = \begin{cases} -1 & \theta_i < 0 \\ 0 & \theta_ix = 0 \\ 1 & \theta_i > 0 \end{cases} sign(θi)= 101θi<0θix=0θi>0

Lasso Regression 有一个很重要的性质是:倾向于完全消除不重要的权重。例如,当 α \alpha α 取值相对较大时,高阶多项式退化为二次甚至是线性(因为高阶多项式特征的权重被置为 0)。

也就是说,Lasso Regression 能够自动进行特征选择,并输出一个稀疏模型(只有少数特征的权重是非零的,大部分被压缩为 0)


总结:岭回归(Ridge Regression)和 Lasso 回归(Lasso Regression)都是线性回归的变体,它们通过在损失函数中添加正则化项来防止过拟合。岭回归通过在损失函数中添加 L2 正则化项来实现,而 Lasso 回归则通过在损失函数中添加 L1 正则化项来实现。这两种正则化方法都可以缩小模型参数的值,但它们之间也有一些区别:

  • L1 正则化会产生稀疏解,即许多参数会被压缩到 0
  • 而 L2 正则化不会

12.3 Elastic Net(弹性网络)

弹性网络(Elastic Net)是一种使用 L 1 L_1 L1 L 2 L_2 L2 范数作为先验正则项训练的线性回归模型。这种组合允许拟合到一个只有少量参数是非零稀疏的模型,就像 Lasso 一样,但是它仍然保持了一些类似于 Ridge 的正则性质。我们可以利用 混合比(mix ratio) r r r 参数控制 L 1 L_1 L1 L 2 L_2 L2 的凸组合。

Elastic Net 与 Lasso 和 Ridge 回归的区别在于,它的代价函数是 L 1 L_1 L1 L 2 L_2 L2 的线性组合,而不是单独使用 L 1 L_1 L1 L 2 L_2 L2。这使得 Elastic Net 可以同时获得 Lasso 和 Ridge 的优点,即可以产生稀疏模型,同时可以保持模型的正则化属性

弹性网络的代价函数:

J ( θ ) = M S E ( θ ) + r ⋅ α ∑ i = 1 n ∣ θ i ∣ + 1 − r 2 α ∑ i = 1 n θ i 2 J(\theta) = \mathrm{MSE}(\theta) + r \cdot \alpha \sum_{i=1}^n |\theta_i| + \frac{1 - r}{2} \alpha \sum_{i = 1}^n\theta^2_i J(θ)=MSE(θ)+rαi=1nθi+21rαi=1nθi2

  • r = 0 r=0 r=0:弹性网络变为Ridge岭回归
  • r = 1 r=1 r=1:弹性网络便为Lasso回归

Q:一般来说,我们应避免使用朴素线性回归,而应对模型进行一定的正则化处理,那如何选择正则化方法呢?
A:正则化方法有 L 1 L_1 L1 正则化和 L 2 L_2 L2 正则化。 L 1 L_1 L1 正则化可以产生稀疏模型,而 L 2 L_2 L2 正则化可以防止过拟合。在选择正则化方法的时候,我们可以从数据集大小、特征数量、特征相关性和需要保留的特征等方面入手,选择合适的正则化方法。

补充】稀疏矩阵是指在数值分析中,其元素大部分为零的矩阵。反之,如果大部分元素都非零,则这个矩阵是稠密的。


小结

  • 常用:岭回归
  • 假设只有少部分特征是有用的:
    • 弹性网络
    • Lasso 回归
    • 一般来说,弹性网络的使用更为广泛。因为在特征维度高于训练样本数,或者特征是强相关的情况下,Lasso 回归的表现不太稳定
  • API:
    • from sklearn.linear_model import Ridge, ElasticNet, Lasso

12.4 Early Stopping

Early Stopping 也是正则化迭代学习的方法之一。Early Stopping 是一种防止过拟合的方法,它通过在训练过程中监控验证集的误差,当验证集误差连续若干次迭代没有下降时,就停止训练。这样可以避免模型在训练集上过拟合,提高模型的泛化能力。

在验证错误率达到最小值的时候停止训练,防止出现过拟合!


小结

  • Ridge Regression 岭回归
    • 就是把系数添加平方项,然后限制系数值的大小
      • α \alpha α 值越小,系数值越大
      • α \alpha α 越大,系数值越小
  • Lasso 回归
    • 对系数值进行绝对值处理
      • 由于绝对值在顶点处不可导,所以进行计算的过程中产生很多0,最后得到结果为稀疏矩阵
  • Elastic Net弹性网络
    • 是前两个内容的综合
    • 设置了一个混合比 r r r
      • 如果 r = 0 r=0 r=0 则退化为岭回归
      • 如果 r = 1 r=1 r=1 则退化为 Lasso 回归
  • Early stopping
    • 通过限制错误率的阈值,进行停止

13. 线性回归的改进-岭回归

学习目标:

  • 知道岭回归 API 的具体使用

13.1 岭回归 API

sklearn.linear_model.Ridge(alpha=1.0, *, fit_intercept=True, copy_X=True,
                           max_iter=None, tol=0.0001, solver='auto',
                           positive=False, random_state=None)
  • 定义:sklearn.linear_model.Ridge是一个线性回归模型,它使用线性最小二乘函数作为损失函数,正则化由 L 2 L_2 L2 范数给出。也称为岭回归或Tikhonov正则化。这个估计器内置了对多元回归的支持(即,当 y y y 是形状为(n_samples,n_targets)的 2d 数组时)。
  • 参数:
    • alpha:正则化力度,必须是正数。默认值为 1.0。
      • 该参数必须是一个大于 0 的浮点数。它没有固定的范围,可以根据实际情况来选择合适的值。
      • 通常情况下,可以通过实验来确定最佳的 alpha 值。例如,可以尝试不同的 alpha 值,并使用交叉验证来评估每个 alpha 值的性能。最终选择性能最好的 alpha 值。

需要注意的是,最佳的 alpha 值可能会因数据集而异。因此,您应该针对每个数据集都进行实验来确定最佳的 alpha 值。

  • fit_intercept:是否计算截距。默认值为True
  • solver:求解器。可以是{‘auto’,‘svd’,‘cholesky’,‘lsqr’,‘sparse_cg’,‘sag’,‘saga’}中的一个。默认值为“auto”
  • normalize:是否对数据进行归一化。默认值为False
  • normalize:数据是否进行标准化
    • normalize=False:可以在fit之前调用preprocessing.StandardScaler标准化数据
  • 属性:
    • Ridge.coef_:回归权重
    • Ridge.intercept_:回归偏置
  • 返回值:
    • 一个估计器对象。

Ridge方法相当于SGDRegressor(penalty='l2', loss="squared_loss"),只不过SGDRegressor实现了一个普通的随机梯度下降学习,因此推荐使用Ridge(实现了 SAG)。


sklearn.linear_model.RidgeCV(alphas=(0.1, 1.0, 10.0), *, 
                             fit_intercept=True, scoring=None, 
                             cv=None, gcv_mode=None, 
                             store_cv_values=False, 
                             alpha_per_target=False)
  • 作用:sklearn.linear_model.RidgeCV 是一个带有内置交叉验证的岭回归类。默认情况下,它执行高效的留一交叉验证。
  • 参数:
    • alphas:要尝试的 alpha 值数组。正则化强度;必须为正浮点数。正则化改善了问题的条件并减少了估计值的方差。较大的值指定更强的正则化。Alpha 对应于其他线性模型(如 LogisticRegression 或 LinearSVC)中的 1 /(2C)。
    • fit_intercept:是否为此模型计算截距。如果设置为 false,则不会在计算中使用截距(即数据预计居中)。
    • scoring:字符串(请参阅模型评估文档)或带有签名 scorer(estimator,X,y)的评分器可调用对象/函数。如果为 None,则当 cv 为 ‘auto’ 或 None 时为负均方误差(即使用留一交叉验证),否则为 r2 分数。
    • cv:确定交叉验证拆分策略的整数、交叉验证生成器或可迭代对象。cv 的可能输入是:None,使用高效的留一交叉验证;整数,指定折叠次数;CV 分割器;一个可迭代对象,产生(train,test)拆分作为索引数组。
    • gcv_mode:执行留一交叉验证时使用哪种策略的标志。选项是:‘auto’:如果 n_samples > n_features,则使用 ‘svd’,否则使用 ‘eigen’;‘svd’:当 X 为密集时强制使用 X 的奇异值分解,当 X 为稀疏时使用 X^T.X 的特征值分解;‘eigen’:强制通过 X.X^T 的特征分解计算。
    • store_cv_values:是否存储与每个 alpha 对应的交叉验证值(仅当 store_cv_values=True 和 cv=None 时可用)。
    • alpha_per_target:是否针对每个目标单独优化 alpha 值(从 alphas 参数列表中选择)(用于多输出设置:多个预测目标)。当设置为 True 时,在拟合后,alpha_ 属性将包含每个目标的一个值。当设置为 False 时,所有目标都使用一个 alpha。
  • 返回的属性:
    • cv_values_:每个 alpha 的交叉验证值(仅当 store_cv_values=True 和 cv=None 时可用)。在调用 fit() 后,如果 scoring 为 None,则此属性将包含均方误差,否则将包含标准化的每点预测值。
    • coef_:模型回归系数

在sklearn中,CV代表的是交叉验证(Cross-validation)

class _BaseRidgeCV(LinearModel):
  def __init__(self, alphas=(0.1, 1.0, 10.0),
               fit_intercept=True, normalize=False, scoring=None,
               cv=None, gcv_mode=None,
               store_cv_values=False):

13.2 不同正则化程度对权重系数的影响

在这里插入图片描述

  • 正则化力度越大,权重系数会越小
  • 正则化力度越小,权重系数会越大

正则化是一种用来防止过拟合的技术,它通过在损失函数中添加一个正则项来实现。

  • 正则化力度越大,正则项对损失函数的影响就越大,模型会更倾向于选择较小的权重系数来最小化损失函数。
  • 相反,正则化力度越小,正则项对损失函数的影响就越小,模型会更倾向于选择较大的权重系数来最小化损失函数。

Q1:这里的权重系数是什么?
A1:权重系数是指在回归模型中,自变量对因变量的影响程度。在线性回归模型中,每个自变量都有一个对应的权重系数,它表示该自变量每变化一个单位时,因变量的变化量。权重系数的绝对值越大,说明该自变量对因变量的影响越大;权重系数的绝对值越小,说明该自变量对因变量的影响越小。


Q2:意思是说,权重系数对模型参数起作用,对吗?
A2:权重系数本身就是模型的参数之一。在回归模型中,权重系数决定了自变量对因变量的影响程度。模型通过拟合训练数据来确定最优的权重系数,以便在新数据上做出准确的预测。


Q3:如果我们的模型是 y = a x + b y = ax + b y=ax+b,那么权重系数就是 a a a,这样理解是否正确?
A3:是的,理解是正确的。在这个简单的一元线性回归模型中, y = a x + b y = ax + b y=ax+b,其中 a a a 是权重系数,它表示自变量 x x x 对因变量 y y y 的影响程度。 b b b 是截距项,表示当 x = 0 x=0 x=0 时, y y y 的值。


Q3:正则项的目的是让模型变得简单,从而防止过拟合。那么正则项是如何影响模型的?
A3:正则项通过对模型的复杂度进行惩罚来防止过拟合。在回归模型中,正则项通常是模型权重系数的函数,它对损失函数施加了一个额外的惩罚项。这个惩罚项会鼓励模型选择较小的权重系数,从而使模型变得更简单

例如,在岭回归中,正则项是权重系数的平方和,即 ∣ ∣ w ∣ ∣ 2 2 ||w||_2^2 ∣∣w22在损失函数中添加这个正则项后,模型在拟合数据时不仅要最小化预测误差,还要最小化权重系数的平方和。这会鼓励模型选择较小的权重系数,从而防止过拟合


总之,正则项通过对模型复杂度进行惩罚来影响模型,使其在拟合数据时更倾向于选择简单的模型。

简而言之,正则化力度越大,模型的权重系数就会越小;正则化力度越小,模型的权重系数就会越大。二者呈负相关

13.3 案例:使用岭回归进行波士顿房价预测

import pandas as pd
import numpy as np
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
from sklearn.linear_model import Ridge, RidgeCV
from sklearn.metrics import mean_squared_error


# 1. 获取数据
data_url = "http://lib.stat.cmu.edu/datasets/boston"
raw_df = pd.read_csv(data_url, sep="\s+", skiprows=22, header=None)
data = np.hstack([raw_df.values[::2, :], raw_df.values[1::2, :2]])
target = raw_df.values[1::2, 2]

# 2. 数据集划分
x_train, x_test, y_train, y_test = train_test_split(data, target, test_size=0.2, random_state=2)

# 3. 特征工程:标准化
transformer = StandardScaler()
x_train = transformer.fit_transform(x_train)
x_test = transformer.transform(x_test)

# 4. 机器学习:线性回归(岭回归)
## 4.1 训练模型一:Ridge
estimator_1 = Ridge(alpha=1)  # alpha为正则化力度系数

## 4.2 训练模型二:RidgeCV
estimator_2 = RidgeCV(alphas=(0.001, 0.01, 0.1, 1, 10, 100))

## 4.3 训练
estimator_1.fit(X=x_train, y=y_train)
estimator_2.fit(X=x_train, y=y_train)

# 5. 模型评估
## 5.1 模型一
### 5.1.1 获取系数
y_predict_1 = estimator_1.predict(X=x_test)
print(f"【模型一】预测值为:\r\n{
      
      y_predict_1}")
print(f"【模型一】模型系数为:\r\n{
      
      estimator_1.coef_}")
print(f"【模型一】模型偏置为:{
      
      estimator_1.intercept_:.4f}")

### 5.1.2 误差
error = mean_squared_error(y_true=y_test, y_pred=y_predict_1)
print(f"【模型一】MSE误差为:{
      
      error:.4f}")
print("-" * 60)

## 5.2 模型二
### 5.2.1 获取系数
y_predict_2 = estimator_2.predict(X=x_test)
print(f"【模型二】预测值为:\r\n{
      
      y_predict_2}")
print(f"【模型二】模型系数为:\r\n{
      
      estimator_2.coef_}")
print(f"【模型二】模型偏置为:{
      
      estimator_2.intercept_:.4f}")
print(f"【模型二】最佳的正则化系数alpha为:{
      
      estimator_2.alpha_:.4f}")

### 5.2.2 误差
error = mean_squared_error(y_true=y_test, y_pred=y_predict_2)
print(f"【模型二】MSE误差为:{
      
      error:.4f}")

结果如下:

【模型一】预测值为:
[23.00321192 21.25113553 33.65297089 31.50768508  3.18019202  3.20859452
 27.39328707 22.31852908 14.84031324 21.38031657 30.94419376 26.68037451
 21.16191092 18.3583368  17.64010682 25.37674358 24.41841787 13.36964416
  8.69151624 18.57429154 21.76007313 20.3675708  36.49898893 20.58768589
 19.86869924 15.78758962 37.0808577  34.85810511 30.80842492 23.26457302
 18.63699725 20.76315643 31.8362047  30.17662894 13.37516806 15.91128616
 13.69148601 23.76213812 25.94122928 23.19082212 28.96707402 12.53531219
 31.0915657   6.38966225 23.67132167 20.59182691 33.17852031 19.20948493
 35.8391772   0.80868731 31.879121   31.6679153   6.613803   34.57229934
 20.35801631 19.7289094  19.53829966 18.60477001 15.84940053 22.99202734
 19.64706215 16.43650149 18.50058175 32.68151938 35.41814173 24.61082106
 41.49411556 32.92820471 14.58658382 27.44908603  8.10931001  5.6237763
 22.21994089 18.6974834  31.01693504 26.05175952 24.57434357 24.80354473
 25.36387139 24.88056688 33.67473368 19.75362793 20.58773597 27.83549398
 37.96359583 37.19356092 22.12900329 29.58354466 31.07063353 17.92066509
 20.90886029 19.50941817 18.60493829 37.06839255 39.76870941  9.18775068
 35.28224415 30.32913865 21.09441681 13.6894351  31.3705665  24.99205169]
【模型一】模型系数为:
[-0.9258041   0.83936747 -0.12711514  0.82009798 -1.87594228  2.55674364
  0.25022498 -2.89549837  2.72413708 -1.88559783 -2.1463606   1.08954177
 -3.90282358]
【模型一】模型偏置为:22.4413
【模型一】MSE误差为:18.4768
------------------------------------------------------------
【模型二】预测值为:
[22.91044323 21.53397244 33.19377343 31.06746567  3.19833094  3.64662565
 27.33264419 23.08195399 14.63749317 21.66957917 30.79673326 26.55735187
 21.41724229 18.25384215 17.61886847 25.34374455 24.33876063 13.3872763
  8.8999307  18.57336765 21.94975226 20.52205235 36.12025376 20.5267392
 19.78329343 16.01724622 36.77248739 34.81735983 30.65497556 23.48115344
 18.29495225 20.90247154 31.74835767 29.97070314 13.30015301 16.22199615
 13.58672632 23.90186731 25.84866501 23.22507831 28.73110723 12.7943185
 31.10888785  6.38366882 23.32005389 20.41410955 33.31082934 19.14499154
 35.3896122   0.74105634 31.66709313 31.44317444  6.82219299 34.16770243
 19.94988928 20.00244902 19.60787192 18.71879978 16.12643697 23.03318714
 19.60186563 17.0073295  18.61116798 32.06529326 34.88048293 24.77187058
 40.81451276 32.75890454 14.42647949 27.56015648  8.62520673  5.74344586
 22.25542399 18.47166596 30.92118035 26.08663192 24.34857104 24.52347389
 25.18295893 24.9020731  33.37919129 19.92988059 20.49440597 27.90786229
 37.62322949 36.79842319 21.82514811 29.31195281 31.03475093 17.8315383
 21.15022899 19.71920991 18.53589778 36.60922738 39.37790169  9.25932086
 35.11202025 30.59794396 21.20492907 13.94921244 31.23156595 24.99632208]
【模型二】模型系数为:
[-0.86329633  0.7285083  -0.27135102  0.85108307 -1.63780795  2.6270911
  0.18222203 -2.64613645  2.17038535 -1.42056563 -2.05032997  1.07266175
 -3.76668388]
【模型二】模型偏置为:22.4413
【模型二】最佳的正则化系数alpha为:10.0000
【模型二】MSE误差为:18.3816

小结

  • sklearn.linear_model.Ridge(alpha=1.0, fit_intercept=True, solver="auto", normalize=False)【知道】。
    • 具有 L 2 L_2 L2 正则化的线性回归
    • alpha:正则化力度,没有固定值,需要验证得到最佳的alpha
      • 正则化力度越大,权重系数会越小
      • 正则化力度越小,权重系数会越大
    • normalize=False:默认为False,对数据进行标准化处理(如果前面我们手动标准化处理了,那么就设置为False

Q:如果我们在前面使用 Standard 对数据进行了标准化,那么在定义模型时,仍然将 normalize 设置为 True,对结果有影响吗?
A:如果在前面使用 StandardScaler 对数据进行了标准化,那么在定义 Ridge 模型时,将 normalize 参数设置为 True 会对结果产生影响。

normalize 参数用于控制是否对数据进行归一化处理。如果将其设置为 True,则在拟合模型之前,会对数据进行归一化处理,使每个特征的均值为 0,方差为 1。

如果在前面已经使用 StandardScaler 对数据进行了标准化处理,那么再次将 normalize 参数设置为 True 会导致数据被归一化两次。这可能会影响模型的性能

因此,如果在前面已经对数据进行了标准化处理,那么在定义 Ridge 模型时,应该将 normalize 参数设置为 False

不要对数据进行多次标准化!

14. 几种线性回归在波士顿房价上的效果

import pandas as pd
import numpy as np
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
from sklearn.linear_model import LinearRegression, SGDRegressor, Ridge, RidgeCV
from sklearn.metrics import mean_squared_error


def model_estimation(estimator, x_test, y_test, model_name):
    """模型评估函数

    Args:
        estimator (sklearn.linear_model): 模型
        x_test (_type_): 测试集
        y_test (_type_): 测试集Ground Truth
        model_name (str): 模型名称

    Returns:
        float: 模型误差
    """
    # 获取系数
    y_predict = estimator.predict(X=x_test)
    print(f"[{
      
      model_name}] 模型系数为:\r\n{
      
      estimator.coef_}")
    print(f"[{
      
      model_name}] 模型偏置为:{
      
      int(estimator.intercept_):.4f}")

    # 误差
    error = mean_squared_error(y_true=y_test, y_pred=y_predict)
    print(f"[{
      
      model_name}] MSE 误差为:{
      
      error:.4f}")
    print("-" * 60)
    return error


if __name__ == "__main__":
    # 1. 获取数据
    data_url = "http://lib.stat.cmu.edu/datasets/boston"
    raw_df = pd.read_csv(data_url, sep="\s+", skiprows=22, header=None)
    data = np.hstack([raw_df.values[::2, :], raw_df.values[1::2, :2]])
    target = raw_df.values[1::2, 2]

    # 2. 数据集划分
    x_train, x_test, y_train, y_test = train_test_split(data, target, test_size=0.2, random_state=2, shuffle=True)

    # 3. 特征工程:标准化
    transformer = StandardScaler()
    x_train = transformer.fit_transform(x_train)
    x_test = transformer.transform(x_test)

    # 4. 定义模型
    ## 4.1 模型一:线性回归(正规方程)
    estimator_1_linear_regression = LinearRegression()
    model_name_1 = "线性回归(正规方程)"
    
    ## 4.2 模型二:线性回归:随机梯度下降
    estimator_2_sgd_regression = SGDRegressor(max_iter=100000)
    model_name_2 = "线性回归:随机梯度下降"
    
    ## 4.3 模型二:线性回归:岭回归
    estimator_3_ridge_regression = Ridge(alpha=10.0)  # alpha为正则化力度系数
    model_name_3 = "线性回归:岭回归"

    # 5. 模型训练
    estimator_1_linear_regression.fit(X=x_train, y=y_train)
    estimator_2_sgd_regression.fit(X=x_train, y=y_train)
    estimator_3_ridge_regression.fit(X=x_train, y=y_train)
    
    # 6. 模型评估
    error_1_linear_regression = model_estimation(estimator_1_linear_regression,
                                                 x_test=x_test, y_test=y_test, 
                                                 model_name=model_name_1)
    error_2_sgd_regression = model_estimation(estimator_2_sgd_regression,
                                              x_test=x_test, y_test=y_test,
                                              model_name=model_name_2)
    error_3_ridge_regression = model_estimation(estimator_3_ridge_regression,
                                                x_test=x_test, y_test=y_test,
                                                model_name=model_name_3)

    # 7. 不同模型误差对比
    print("=" * 60)
    print(f"{
      
      [model_name_1]} MSE 误差为:{
      
      error_1_linear_regression:.4f}")
    print(f"{
      
      [model_name_2]} MSE 误差为:{
      
      error_2_sgd_regression:.4f}")
    print(f"{
      
      [model_name_3]} MSE 误差为:{
      
      error_3_ridge_regression:.4f}")

运行结果:

[线性回归(正规方程)] 模型系数为:
[-0.93451207  0.85487686 -0.10446819  0.81541757 -1.90731862  2.54650028
  0.25941464 -2.92654009  2.80505451 -1.95699832 -2.15881929  1.09153332
 -3.91941941]
[线性回归(正规方程)] 模型偏置为:22.0000
[线性回归(正规方程)] MSE 误差为:18.4954
------------------------------------------------------------
[线性回归:随机梯度下降] 模型系数为:
[-0.85500705  0.72828303 -0.37024578  0.82421742 -1.71129399  2.63393284
  0.18303546 -2.77152414  2.0379826  -1.18318199 -2.09533483  1.09331864
 -3.8657579 ]
[线性回归:随机梯度下降] 模型偏置为:22.0000
[线性回归:随机梯度下降] MSE 误差为:18.5166
------------------------------------------------------------
[线性回归:岭回归] 模型系数为:
[-0.86329633  0.7285083  -0.27135102  0.85108307 -1.63780795  2.6270911
  0.18222203 -2.64613645  2.17038535 -1.42056563 -2.05032997  1.07266175
 -3.76668388]
[线性回归:岭回归] 模型偏置为:22.0000
[线性回归:岭回归] MSE 误差为:18.3816

不同模型误差对比:

['线性回归(正规方程)'] MSE 误差为:18.4954
['线性回归:随机梯度下降'] MSE 误差为:18.5166
['线性回归:岭回归'] MSE 误差为:18.3816

根据结果,三种不同的线性回归算法在波士顿房价预测问题上的表现相差不大。它们的均方误差(MSE)都在18左右。

  • 使用正规方程的线性回归模型的MSE为 18.4954。
  • 使用随机梯度下降的线性回归模型的MSE为 18.5166。
  • 使用岭回归的线性回归模型的MSE为 18.3816。

从这些结果来看,岭回归模型在这个问题上表现最好,其 MSE 最低。但是,这三种模型的 MSE 都很接近,因此它们在这个问题上的表现都差不多。

需要注意的是,这些结果可能会因数据集划分、模型参数和随机种子等因素而有所不同。要准确比较这些模型的性能,可以使用交叉验证等方法来评估它们在不同数据集划分下的平均性能。


Q1:LinearRegression 是正规方程吗?
A1:是的,LinearRegression类是使用正规方程来求解线性回归问题的。正规方程是一种解析方法,可以直接计算出线性回归模型的最优参数。它不需要迭代,但在特征数量很大时计算量会很大。


Q2:意思就是说,线性回归默认使用的是正规方程,对吗?
A2:是的,在 scikit-learn 库中,LinearRegression类默认使用正规方程来拟合线性回归模型。当然,您也可以使用其他方法来拟合线性回归模型,例如随机梯度下降(SGDRegressor 类)或岭回归(Ridge 类)。


Q3:LinearRegression, SGDRegressor, Ridge 都是线性回归,是吗?
A3:是的。LinearRegressionSGDRegressorRidge 都是用来拟合线性回归模型的。它们的区别在于拟合模型时使用的算法不同。

  • LinearRegression 使用正规方程来拟合线性回归模型。
  • SGDRegressor 使用随机梯度下降算法来拟合线性回归模型。
  • Ridge 使用岭回归算法来拟合线性回归模型。岭回归是一种带有 L2 正则化的线性回归。

这些算法都可以用来拟合线性回归模型,但在不同的数据集和应用场景下,它们的表现可能会有所不同。

15. 模型的保存和加载

学习目标

  • 知道 sklearn 中模型的保存和加载

15.1 sklearn模型的保存和加载API

joblib 是一个用于保存和加载 Python 对象的库。它可以用来保存 scikit-learn 模型,以便在以后重新加载并使用。

需要注意的是,sklearn.externals.joblib 已经在 scikit-learn 0.23 版本中被弃用,并且将在未来版本中被删除。建议直接从 joblib 库中导入 joblib,而不是从 sklearn.externals 中导入。例如:

import joblib

joblib 库提供了两个主要函数:dumpload

dump 英[dʌmp] 美[dʌmp]
vt. 倾倒; 抛弃; (尤指在不合适的地方)丢弃; 扔掉; 丢下; 推卸; (向国外)倾销; 抛售; 随便堆放; 与(某人)结束恋爱关系; (内存信息)转储,转存;
n. 垃圾场; 废物堆; 尾矿堆; 脏地方; 邋遢场所; 令人讨厌的地方; 军需品临时存放处; 转储;

  1. dump 函数用于将 Python 对象保存到磁盘文件中。它的语法为:
joblib.dump(value, filename, compress=0, protocol=None, cache_size=None)

其中,value 是要保存的 Python 对象,filename 是保存文件的路径。其他参数可选,用于控制压缩级别、序列化协议和缓存大小等。

  1. load 函数用于从磁盘文件中加载 Python 对象。它的语法为:
joblib.load(filename, mmap_mode=None)

其中,filename 是要加载的文件的路径。其他参数可选,用于控制内存映射模式等。

这两个函数都没有返回值。它们分别用于保存和加载 Python 对象。

注意:与深度学习中一般只保存模型参数(或我们指定的内容)不同,joblib.dump 函数会将模型的所有内容保存到磁盘文件中。这包括模型的参数、超参数和其他属性。因此当我们使用 joblib.load 函数从文件中加载模型时,会得到一个与原始模型完全相同的模型对象。您可以使用这个对象进行预测、评估或进一步训练


举例

# 保存文件
joblib.dump(estimator, "model_name.pkl")

# 加载文件
estimator = joblib.load("model_name.pkl")

15.2 线性回归的模型保存加载案例

import pandas as pd
import numpy as np
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
from sklearn.linear_model import Ridge
from sklearn.metrics import mean_squared_error
import joblib


# 1. 获取数据
data_url = "http://lib.stat.cmu.edu/datasets/boston"
raw_df = pd.read_csv(data_url, sep="\s+", skiprows=22, header=None)
data = np.hstack([raw_df.values[::2, :], raw_df.values[1::2, :2]])
target = raw_df.values[1::2, 2]

# 2. 数据集划分
x_train, x_test, y_train, y_test = train_test_split(data, target, test_size=0.2, random_state=2)

# 3. 特征工程:标准化
transformer = StandardScaler()
x_train = transformer.fit_transform(x_train)
x_test = transformer.transform(x_test)

# 4. 机器学习:线性回归(岭回归)
## 4.1 定义模型
estimator = Ridge(alpha=1)  # alpha为正则化力度系数

## 4.2 训练模型
estimator.fit(X=x_train, y=y_train)

# 5. 模型保存
joblib.dump(value=estimator, filename="./岭回归模型.pkl")  # 模型的所有都会保存
print("模型已保存!")

# 6. 模型加载
# 这里我们为了方便演示,先将之前训练好的模型删除
del estimator  
# 正式加载模型
estimator = joblib.load("./岭回归模型.pkl")
print("模型加载成功!")

# 7. 模型评估
## 7.1 获取系数
y_predict_1 = estimator.predict(X=x_test)
# print(f"预测值为:\r\n{y_predict_1}")
# print(f"模型系数为:\r\n{estimator.coef_}")
# print(f"模型偏置为:{estimator.intercept_:.4f}")

## 7.2 误差
error = mean_squared_error(y_true=y_test, y_pred=y_predict_1)
print(f"MSE误差为:{
      
      error:.4f}")

结果如下:

模型已保存!
模型加载成功!
MSE误差为:18.4768

与此同时,在脚本文件的目录下会生成一个模型文件 岭回归模型.pkl

在这里插入图片描述

在这里插入图片描述


小结

  • import joblib【知道】
    • 保存:joblib.dump(estimator, "model_path.pkl")
    • 加载:estimator = joblib.load("model_path.pkl")
    • 注意:
      1. 保存文件,后缀名是 **.pkl
      2. 加载模型是需要通过一个变量进行承接

猜你喜欢

转载自blog.csdn.net/weixin_44878336/article/details/130618775