2025-03-15 吴恩达机器学习2——线性回归(1):案例入门

Python3.8

Python3.8

Conda
Python

Python 是一种高级、解释型、通用的编程语言,以其简洁易读的语法而闻名,适用于广泛的应用,包括Web开发、数据分析、人工智能和自动化脚本

1 概述

​ 线性回归模型是使用最广泛的学习算法,让我们从一个可以使用线性回归解决的问题开始。

1.1 案例

​ 下图展示了美国波特兰市的房屋大小和价格数据集,其中横轴是以平方英尺为单位的房屋大小,纵轴是以千美元为单位的房屋价格,每个数据点使用小十字架表示。

image-20250315110931623

​ 现在,假设您是波特兰的一名房地产经纪人,您正在帮助一位客户出售她的房子。她问你这套房子能卖多少钱?该数据集会帮助您估算她可以获得的价格:

  1. 你先测量房子的大小,结果房子是 1,250 平方英尺。
  2. 接下来,使用数据集构建一个线性回归模型,将数据拟合一条直线,如下图所示。
  3. 根据这条与数据拟合的直线,1,250 平方英尺对应的价格大约 220,000 美元。,
image-20250315111150181

​ 除了将数据可视化为图表外,还有另一种查看有用数据的方法,即数据表。

​ 例如,表格的第一行是一个面积为 2,104 平方英尺的房子,售价是 400,000 美元,也就是在这附近。表格的第一行绘制为此处的数据点。

image-20250315111431986

1.2 分析

​ 在机器学习中,

  • x x x:表示输入的标准符号,称之为输入变量,也称为特征或输入特征。

    例如,对于训练集中的第一个房子, x x x 是房子的大小,因此 x x x 等于 2,104。

  • y y y:尝试预测的输出变量(有时也称为目标变量)。

    在本案例中, y y y 是房子的价格,对于第一个训练示例,它等于 400,所以 y y y 等于 400。

  • m m m:表示训练示例的总数。

    在本案例中, m m m 等于 47。

  • ( x , y ) (x, y) (x,y):表示单个训练样本。

    对于第一个训练示例 ( x , y ) (x, y) (x,y),这对数字是 ( 2104 , 400 ) (2104, 400) (2104,400)

image-20250315112018729

注意:

X ( i ) X^{(i)} X(i) 中的上标 i i i 不是求幂,而是表示第 i i i 组数据。

​ 监督学习中的训练集包括输入特征(例如房屋大小)和输出目标(例如房屋价格)。输出目标是我们将从中学习的模型的正确答案。要训练模型,需要将训练集(包括输入特征和输出目标)提供给学习算法。我们将这个算法函数写成小写的 f f f,其中 f f f 代表函数。

历史上,函数 f f f 曾经被称为假设,但我在这个类中只是将它称为函数 f f f

f f f 的工作是采用新的输入 x x x 和输出并进行估计或预测,我将其称为 y ^ \hat{y} y^(y-hat)。

​ 在机器学习中,

  • x x x:称为输入或输入特征。
  • f f f:称为模型。
  • y y y:指目标,即训练集中的实际真实值。
  • y ^ \hat{y} y^(y-hat): y y y 的估计或预测。

​ 一个关键问题是,我们将如何表示函数 f f f

​ 在线性回归模型中, f f f 是一条直线。函数可以写成
f w , b ( x ) = w x + b f_{w,b}(x)=wx+b fw,b(x)=wx+b
该公式表示 f f f 是一个以 x x x 作为输入的函数,并根据 w w w b b b 的值,输出预测 y ^ \hat{y} y^ 值。

​ 因此,只要知道 w w w b b b,即可根据输入特征 x x x 确定预测 y ^ \hat{y} y^。有时我们会只写 f 而没有明确地将 w w w b b b 包含在下标中,但其含义与 f w , b f_{w,b} fw,b 完全相同:
f ( x ) = w x + b f(x)=wx+b f(x)=wx+b
image-20250315113740583

​ 线性函数只是直线的一个奇特术语,由于其相对简单且易于使用,因此使用直线作为基础,最终逐渐学习并理解更复杂的非线性模型。

​ 这个特殊的模型有一个名字,叫做线性回归。更具体地说,这是具有一个变量的线性回归,其中“一个变量”表示只有一个输入变量或特征 x x x,即房屋的大小。

2 代价函数

2.1 代价函数公式

​ 假设我们有一个包含输入特征 x x x 和输出目标 y y y 的训练集。用于拟合这个训练集的模型是一个线性函数:
f w , b ( x ) = w x + b f_{w,b}(x)=wx+b fw,b(x)=wx+b
其中, w w w b b b 被称为模型的参数。在机器学习中,模型的参数是在训练期间可以调整的变量,以改进模型的性能。有时, w w w b b b 也被称为系数权重

​ 回顾线性模型,了解参数 w w w b b b 是如何确定 f f f 的:

image-20250315122106573

​ 对于线性回归,我们的目标是选择合适的参数 w w w b b b ,使得函数 f f f 生成的直线能够很好地拟合训练数据。为了衡量直线与数据的拟合程度,我们需要构建代价函数(Cost Function,也称为成本函数):通过比较预测值 y ^ \hat{y} y^ 和实际目标值 y y y 来计算误差。具体来说,误差是预测值与实际值之间的差值:
e r r o r = y ^ − y error=\hat{y}-y error=y^y
​ 为了消除误差的正负影响,我们通常计算误差的平方:
e r r o r = ( y ^ − y ) 2 error=(\hat{y}-y)^2 error=(y^y)2
​ 接下来,我们对训练集中的所有样本计算平方误差,并取其平均值:
J ( w , b ) = 1 2 m ∑ i = 1 m ( y ^ ( i ) − y ( i ) ) 2 J(w,b)=\frac{1}{2m}\sum^{m}_{i=1}(\hat{y}^{(i)}-y^{(i)})^2 J(w,b)=2m1i=1m(y^(i)y(i))2

  • 我们将从 i i i 等于 1,2,3 一直加到 m m m,并记住 m m m 是训练示例的数量,对于这个数据集来说是 47。
  • 额外除以 2 只是为了让我们后面的一些计算看起来更整洁,但无论是否包含此除以 2,代价函数仍然有效。

​ 代价函数 J ( w , b ) J(w,b) J(w,b) 衡量了模型预测值与实际值之间的平均误差。我们的目标是通过调整来 w w w b b b 最小化代价函数,从而使模型的预测更加准确。

image-20250315123020219

2.2 理解代价函数

​ 代价函数用于衡量模型预测值与实际值之间的差异。具体来说,线性回归的目标是找到参数 w w w b b b,使得代价函数 J ( w , b ) J(w,b) J(w,b) 最小化。数学上,我们表示为:
minimize  w , b J ( w , b ) \mathop{\text{minimize}\ }\limits_{w,b}J(w,b) w,bminimize J(w,b)

image-20250315125129317

​ 为了更直观地理解代价函数,我们暂时简化模型,仅考虑参数 w w w,即假设 b = 0 b=0 b=0。此时,模型变为:
f w ( x ) = w ⋅ x f_{w}(x)=w\cdot x fw(x)=wx
​ 相应的代价函数也简化为:
J ( w ) = 1 2 m ∑ i = 1 m ( w ⋅ x ( i ) − y ( i ) ) 2 J(w)=\frac{1}{2m}\sum^{m}_{i=1}(w\cdot x^{(i)}-y^{(i)})^2 J(w)=2m1i=1m(wx(i)y(i))2
​ 假设我们有一个简单的训练集,包含三个数据点:(1, 1)、(2, 2) 和 (3, 3)。

  1. w = 1 w=1 w=1
    • 模型 f w ( x ) f_{w}(x) fw(x) 是一条斜率为 1 的直线,完美地通过所有数据点。
    • 计算代价函数 J(w):J(1) = 0,表示模型完美拟合数据。
image-20250315130227577
  1. w = 0.5 w=0.5 w=0.5
    • 模型 f w ( x ) f_{w}(x) fw(x) 是一条斜率为 0.5 的直线,未能完全拟合数据。
    • 计算代价函数 J(w):J(0.5) ≈ 0.58,表示模型拟合效果较差。
image-20250315130336836
  1. w = 0 w=0 w=0
    • 模型 f w ( x ) f_{w}(x) fw(x) 是一条水平线,完全偏离数据。
    • 计算代价函数 J(w):J(0) ≈ 2.33,表示模型拟合效果非常差。
image-20250315130444739

​ 这就是在线性回归中如何使用代价函数来找到使 J J J 最小化的 w w w 值。在更一般的情况下,我们有参数 w w w b b b 而不仅仅是 w w w,需要找到使 J J J 最小化的 w w w b b b 的值。

2.3 可视化代价函数

​ 现在,我们将回到完整的线性回归模型,同时考虑参数 w w w b b b,并可视化代价函数来深入理解其作用。

image-20250315130959705

​ 假设我们有一个房价预测模型,输入特征 x x x 表示房屋的大小,输出目标 y y y 表示房屋的价格。我们选择 w = 0.06 w=0.06 w=0.06 b = 50 b=50 b=50,则模型函数为:
f w , b ( x ) = 0.06 ⋅ x + 50 f_{w,b}(x)=0.06\cdot x+50 fw,b(x)=0.06x+50
​ 这个模型对房价的预测效果较差,因为它始终低估了房价。

image-20250315131312096

​ 在此之前,我们将参数 b b b 设为 0 来简化模型,这使得代价函数 J ( w ) J(w) J(w) 成为一个二维的 U 形曲线,形状类似于一个“汤碗”。然而,在完整的线性回归模型中,我们需要同时考虑参数 w w w b b b,这使得代价函数 J ( w , b ) J(w,b) J(w,b) 成为一个三维的曲面。

image-20250315131911894

3 梯度下降

​ 梯度下降是一种通用的优化算法,适用于最小化任何函数,而不仅仅是线性回归的代价函数。为了更全面地讨论梯度下降,我们将其推广到更一般的函数。

​ 例如,假设我们有一个代价函数 J J J,它是参数 w 1 , w 2 , ⋯   , w n w_1,w_2,\cdots,w_n w1,w2,,wn b b b 的函数。我们的目标是通过调整这些参数,使得代价函数 J J J最小化。

  1. 初始化参数:首先,我们需要为参数 wb 选择初始值。在线性回归中,初始值的选择并不重要,通常可以将它们都设为 0。例如,w=0,b=0。
  2. 逐步调整参数:梯度下降的核心思想是通过多次迭代,逐步调整参数 wb,以降低代价函数 J(w,b) 的值。每次迭代中,算法会根据当前参数值计算代价函数的梯度(即函数的变化率),并沿着梯度的反方向更新参数。
  3. 收敛到最小值:通过不断迭代,梯度下降算法会逐渐接近代价函数的最小值。最终,参数 wb 会稳定在或接近最优值。
image-20250315144657194

需要注意的是,梯度下降可能会收敛到局部最小值,而不是全局最小值。局部最小值是指某个区域内代价函数的最小值,但不一定是整个函数的最小值。这种现象在复杂的代价函数中尤为常见。

3.1 实现步骤

​ 梯度下降的核心思想是通过迭代更新参数 wb,使得代价函数 J(w,b) 逐渐减小。具体来说,梯度下降的更新规则如下:

  1. 更新参数 w w w
    w : = w − α ⋅ ∂ J ( w , b ) ∂ w w:=w-\alpha\cdot\frac{\partial J(w,b)}{\partial w} w:=wαwJ(w,b)

    其中,α 是学习率, ∂ J ( w , b ) ∂ w \frac{\partial J(w,b)}{\partial w} wJ(w,b) 是代价函数对 w 的偏导数。

  2. 更新参数 b b b
    b : = b − α ⋅ ∂ J ( w , b ) ∂ b b:=b-\alpha\cdot\frac{\partial J(w,b)}{\partial b} b:=bαbJ(w,b)

    其中, ∂ J ( w , b ) ∂ w \frac{\partial J(w,b)}{\partial w} wJ(w,b) 是代价函数对 b 的偏导数。

  • 赋值运算符 :=

    • 在编程中,:= 表示赋值操作。例如, w : = w − α ⋅ ∂ J ( w , b ) ∂ w w:=w-\alpha\cdot\frac{\partial J(w,b)}{\partial w} w:=wαwJ(w,b) 表示将 w 更新为右侧表达式的值。
    • 这与数学中的等号 = 不同,后者通常用于表示真值断言。
  • 学习率 α

    • 学习率 α 是一个介于 0 和 1 之间的正数,通常设置为 0.01。
    • 学习率决定了每次更新参数的步长。较大的学习率意味着更激进的更新,而较小的学习率则意味着更谨慎的更新。
  • 偏导数

    • 偏导数表示代价函数在 wb 方向上的变化率。

    • 偏导数的作用是告诉我们参数应该朝哪个方向更新,以最快地降低代价函数。

image-20250315145450045

​ 在实现梯度下降时,一个关键细节是同步更新参数 wb。这意味着在每次迭代中,我们需要同时计算 wb 的更新值,然后再同时更新它们。上图左边展示了正确的更新步骤,右边则是不推荐的错误做法。

3.2 理解梯度下降

​ 为了更好地理解梯度下降,我们暂时简化问题,仅考虑一个参数 w。此时,代价函数 J(w) 是一个关于 w 的一维函数,其图形是一条曲线。梯度下降的更新规则简化为:
w : = w − α ⋅ d J ( w ) d w w:=w-\alpha\cdot\frac{d J(w)}{d w} w:=wαdwdJ(w)
​ 其中,α 是学习率, d J ( w ) d w \frac{d J(w)}{d w} dwdJ(w) 是代价函数 J(w) 对 w 的导数,表示代价函数在 w 方向上的变化率。具体来说,导数告诉我们 w 应该如何更新,以最快地降低代价函数。

image-20250315150126513
  1. 导数为正时
    • 如果导数为正,意味着代价函数在当前 w 处是上升的。
    • 根据梯度下降的更新规则,w 会减小(即 w:=wα⋅正数)。
    • 在图形上,w 会向左移动,代价函数 J(w) 会逐渐减小。
  2. 导数为负时
    • 如果导数为负,意味着代价函数在当前 w 处是下降的。
    • 根据梯度下降的更新规则,w 会增加(即 w:=wα⋅负数)。
    • 在图形上,w 会向右移动,代价函数 J(w) 会逐渐减小。

​ 通过这种方式,导数项引导 w 朝着代价函数的最小值方向移动。

3.3 学习率

(1)学习率过小

​ 如果学习率 α 过小,梯度下降的更新步长会非常小。具体来说:

  1. 更新步长小:每次更新 w 时,w 的变化量非常小。
  2. 收敛速度慢:虽然梯度下降最终会收敛到最小值,但需要非常多的迭代步骤。
  3. 效率低下:计算成本高,尤其是在大规模数据集上。

示例
假设学习率 α α α = 0.0000001,每次更新 w 的步长非常小。虽然 w 会逐渐接近最小值,但需要大量的迭代步骤才能达到目标。

(2)学习率过大

​ 如果学习率 α 过大,梯度下降的更新步长会非常大。具体来说:

  1. 更新步长大:每次更新 w 时,w 的变化量非常大。
  2. 可能无法收敛:梯度下降可能在最小值附近振荡,甚至偏离最小值。
  3. 发散风险:在某些情况下,梯度下降可能完全无法收敛,导致代价函数值不断增加。

示例
​ 假设学习率 α α α = 10,每次更新 w 的步长非常大。梯度下降可能会从最小值的一侧跳到另一侧,甚至偏离最小值,导致代价函数值不断增加。

image-20250315150651959

(3)学习率的自动调整

​ 一个有趣的现象是,即使学习率 α 保持不变,梯度下降在接近最小值时也会自动减小更新步长。这是因为:

  1. 导数变小:当 w 接近最小值时,导数 d J ( w ) d w \frac{d J(w)}{d w} dwdJ(w) 会逐渐变小。
  2. 更新步长减小:由于更新步长 α ⋅ d J ( w ) d w \alpha\cdot\frac{d J(w)}{d w} αdwdJ(w) 中的导数项变小,更新步长也会自动减小。
  3. 稳定收敛:这使得梯度下降在接近最小值时能够稳定地收敛,而不会在最小值附近振荡。
image-20250315150908804

(4)局部最小值

​ 当 w 处于局部最小值时,导数 d J ( w ) d w \frac{d J(w)}{d w} dwdJ(w) 为零。此时,梯度下降的更新规则变为:
w : = w − α ⋅ 0 = w w:=w-\alpha\cdot0=w w:=wα0=w
​ 这意味着,如果 w 已经处于局部最小值,梯度下降不会改变 w 的值,算法会保持稳定。

image-20250315150816286

4 最佳实践

4.1 导入数据

​ 首先导入相关库:

import math, copy
import numpy as np
import matplotlib.pyplot as plt
  • mathcopy 用于数学运算和深拷贝。
  • numpy 用于科学计算,特别是数组操作。
  • matplotlib.pyplot 用于绘图。

​ 方便起见,本次数据集中有两个样本,特征 x_train 是房屋的面积(1000平方英尺),目标值 y_train 是房屋的价格(千美元)。

x_train = np.array([1.0, 2.0])   # 特征
y_train = np.array([300.0, 500.0])   # 目标值

4.2 代码实现

(1)代价函数

  • wb 是线性模型的参数。
  • m 是样本数量。
  • f_wb 是模型的预测值。
  • total_cost 是成本值。
# Function to calculate the cost
def compute_cost(x, y, w, b):
    m = x.shape[0]
    cost = 0

    for i in range(m):
        f_wb = w * x[i] + b
        cost = cost + (f_wb - y[i]) ** 2
    total_cost = 1 / (2 * m) * cost

    return total_cost

(2)计算梯度

  • dj_dwdj_db 分别是 wb 的梯度。
def compute_gradient(x, y, w, b):
    """
    Computes the gradient for linear regression 
    Args:
      x (ndarray (m,)): Data, m examples 
      y (ndarray (m,)): target values
      w,b (scalar)    : model parameters  
    Returns
      dj_dw (scalar): The gradient of the cost w.r.t. the parameters w
      dj_db (scalar): The gradient of the cost w.r.t. the parameter b     
     """

    # Number of training examples
    m = x.shape[0]
    dj_dw = 0
    dj_db = 0

    for i in range(m):
        f_wb = w * x[i] + b
        dj_dw_i = (f_wb - y[i]) * x[i]
        dj_db_i = f_wb - y[i]
        dj_db += dj_db_i
        dj_dw += dj_dw_i
    dj_dw = dj_dw / m
    dj_db = dj_db / m

    return dj_dw, dj_db

(3)梯度下降算法

  • alpha 是学习率,num_iters 是迭代次数。
  • 每次迭代中,更新 wb,并记录成本值和参数历史。
def gradient_descent(x, y, w_in, b_in, alpha, num_iters, cost_function, gradient_function):
    """
    Performs gradient descent to fit w,b. Updates w,b by taking 
    num_iters gradient steps with learning rate alpha
    
    Args:
      x (ndarray (m,))  : Data, m examples 
      y (ndarray (m,))  : target values
      w_in,b_in (scalar): initial values of model parameters  
      alpha (float):     Learning rate
      num_iters (int):   number of iterations to run gradient descent
      cost_function:     function to call to produce cost
      gradient_function: function to call to produce gradient
      
    Returns:
      w (scalar): Updated value of parameter after running gradient descent
      b (scalar): Updated value of parameter after running gradient descent
      J_history (List): History of cost values
      p_history (list): History of parameters [w,b] 
      """

    w = copy.deepcopy(w_in)  # avoid modifying global w_in
    # An array to store cost J and w's at each iteration primarily for graphing later
    J_history = []
    p_history = []
    b = b_in
    w = w_in

    for i in range(num_iters):
        # Calculate the gradient and update the parameters using gradient_function
        dj_dw, dj_db = gradient_function(x, y, w, b)

        # Update Parameters using equation (3) above
        b = b - alpha * dj_db
        w = w - alpha * dj_dw

        # Save cost J at each iteration
        if i < 100000:  # prevent resource exhaustion
            J_history.append(cost_function(x, y, w, b))
            p_history.append([w, b])
        # Print cost every at intervals 10 times or as many iterations if < 10
        if i % math.ceil(num_iters / 10) == 0:
            print(f"Iteration {i:4}: Cost {J_history[-1]:0.2e} ",
                  f"dj_dw: {dj_dw: 0.3e}, dj_db: {dj_db: 0.3e}  ",
                  f"w: {w: 0.3e}, b:{b: 0.5e}")

    return w, b, J_history, p_history  #return w and J,w history for graphing

(4)运行梯度下降

  • 初始化参数 wb 为 0。
  • 设置学习率 tmp_alpha 和迭代次数 iterations
  • 运行梯度下降算法,得到优化后的参数 wb
# initialize parameters
w_init = 0
b_init = 0
# some gradient descent settings
iterations = 10000
tmp_alpha = 1.0e-2
# run gradient descent
w_final, b_final, J_hist, p_hist = gradient_descent(x_train, y_train, w_init, b_init, tmp_alpha,
                                                    iterations, compute_cost, compute_gradient)
print(f"(w,b) found by gradient descent: ({w_final:8.4f},{b_final:8.4f})")
image-20250315153437405

(5)代价函数与迭代次数

  • 绘制代价函数随迭代次数的变化图,分为初始阶段(前100次)和结束阶段(后9000次)。
# plot cost versus iteration  
fig, (ax1, ax2) = plt.subplots(1, 2, constrained_layout=True, figsize=(12, 4))
ax1.plot(J_hist[:100])
ax2.plot(1000 + np.arange(len(J_hist[1000:])), J_hist[1000:])
ax1.set_title("Cost vs. iteration(start)");
ax2.set_title("Cost vs. iteration (end)")
ax1.set_ylabel('Cost');
ax2.set_ylabel('Cost')
ax1.set_xlabel('iteration step');
ax2.set_xlabel('iteration step')
plt.show()
image-20250315153651953

(6)预测

  • 使用优化后的参数 wb 进行预测。
print(f"1000 sqft house prediction {w_final*1.0 + b_final:0.1f} Thousand dollars")
print(f"1200 sqft house prediction {w_final*1.2 + b_final:0.1f} Thousand dollars")
print(f"2000 sqft house prediction {w_final*2.0 + b_final:0.1f} Thousand dollars")
image-20250315154026430

4.3 可视化

​ 可视化函数:

def plt_contour_wgrad(x, y, hist, ax, w_range=[-100, 500, 5], b_range=[-500, 500, 5],
                      contours=[0.1, 50, 1000, 5000, 10000, 25000, 50000],
                      resolution=5, w_final=200, b_final=100, step=10):
    # 创建w和b的网格
    b0, w0 = np.meshgrid(np.arange(*b_range), np.arange(*w_range))
    # 初始化z为0
    z = np.zeros_like(b0)
    # 遍历w和b的网格,计算每个点的cost
    for i in range(w0.shape[0]):
        for j in range(w0.shape[1]):
            z[i][j] = compute_cost(x, y, w0[i][j], b0[i][j])

    # 绘制等高线图
    CS = ax.contour(w0, b0, z, contours, linewidths=2,
                    colors=[dlblue, dlorange, dldarkred, dlmagenta, dlpurple])
    # 添加等高线标签
    ax.clabel(CS, inline=1, fmt='%1.0f', fontsize=10)
    # 设置x轴和y轴标签
    ax.set_xlabel("w");
    ax.set_ylabel("b")
    # 设置标题
    ax.set_title('Contour plot of cost J(w,b), vs b,w with path of gradient descent')
    # 设置w和b的初始值
    w = w_final;
    b = b_final
    # 绘制w和b的初始值
    ax.hlines(b, ax.get_xlim()[0], w, lw=2, color=dlpurple, ls='dotted')
    ax.vlines(w, ax.get_ylim()[0], b, lw=2, color=dlpurple, ls='dotted')

    # 设置起始点
    base = hist[0]
    # 遍历hist,绘制梯度下降路径
    for point in hist[0::step]:
        # 计算两点之间的距离
        edist = np.sqrt((base[0] - point[0]) ** 2 + (base[1] - point[1]) ** 2)
        # 如果距离大于resolution或者point是hist的最后一个点,则绘制箭头
        if (edist > resolution or point == hist[-1]):
            # 如果point在ax的范围内,则绘制箭头
            if inbounds(point, base, ax.get_xlim(), ax.get_ylim()):
                plt.annotate('', xy=point, xytext=base, xycoords='data',
                             arrowprops={'arrowstyle': '->', 'color': 'r', 'lw': 3},
                             va='center', ha='center')
            # 更新base为point
            base = point
    return

def plt_divergence(p_hist, J_hist, x_train, y_train):
    # 初始化x、y、v三个数组,长度为p_hist的长度
    x = np.zeros(len(p_hist))
    y = np.zeros(len(p_hist))
    v = np.zeros(len(p_hist))
    # 遍历p_hist,将p_hist中的值赋给x、y、v
    for i in range(len(p_hist)):
        x[i] = p_hist[i][0]
        y[i] = p_hist[i][1]
        v[i] = J_hist[i]

    # 创建一个大小为12x5的图形
    fig = plt.figure(figsize=(12, 5))
    # 设置子图之间的间距
    plt.subplots_adjust(wspace=0)
    # 添加一个1行5列的网格
    gs = fig.add_gridspec(1, 5)
    # 设置图形的标题
    fig.suptitle(f"Cost escalates when learning rate is too large")
    # ===============
    #  First subplot
    # ===============
    # 添加一个子图
    ax = fig.add_subplot(gs[:2], )

    # Print w vs cost to see minimum
    # 设置b的值为100
    fix_b = 100
    # 创建一个从-70000到70000,步长为1000的数组
    w_array = np.arange(-70000, 70000, 1000)
    # 创建一个与w_array相同长度的数组,用于存储cost
    cost = np.zeros_like(w_array)

    # 遍历w_array,计算cost
    for i in range(len(w_array)):
        tmp_w = w_array[i]
        cost[i] = compute_cost(x_train, y_train, tmp_w, fix_b)

    # 绘制w vs cost的图像
    ax.plot(w_array, cost)
    # 绘制p_hist中的点
    ax.plot(x, v, c=dlmagenta)
    # 设置子图的标题
    ax.set_title("Cost vs w, b set to 100")
    # 设置y轴的标签
    ax.set_ylabel('Cost')
    # 设置x轴的标签
    ax.set_xlabel('w')
    # 设置x轴的刻度
    ax.xaxis.set_major_locator(MaxNLocator(2))

    # ===============
    # Second Subplot
    # ===============

    # 创建一个从-35000到35000,步长为500的数组
    tmp_b, tmp_w = np.meshgrid(np.arange(-35000, 35000, 500), np.arange(-70000, 70000, 500))
    # 创建一个与tmp_b、tmp_w相同大小的数组,用于存储cost
    z = np.zeros_like(tmp_b)
    # 遍历tmp_b、tmp_w,计算cost
    for i in range(tmp_w.shape[0]):
        for j in range(tmp_w.shape[1]):
            z[i][j] = compute_cost(x_train, y_train, tmp_w[i][j], tmp_b[i][j])

    # 添加一个3D子图
    ax = fig.add_subplot(gs[2:], projection='3d')
    # 绘制3D图像
    ax.plot_surface(tmp_w, tmp_b, z, alpha=0.3, color=dlblue)
    # 设置x轴的刻度
    ax.xaxis.set_major_locator(MaxNLocator(2))
    # 设置y轴的刻度
    ax.yaxis.set_major_locator(MaxNLocator(2))

    # 设置x轴的标签
    ax.set_xlabel('w', fontsize=16)
    # 设置y轴的标签
    ax.set_ylabel('b', fontsize=16)
    # 设置z轴的标签
    ax.set_zlabel('\ncost', fontsize=16)
    # 设置子图的标题
    plt.title('Cost vs (b, w)')
    # Customize the view angle
    ax.view_init(elev=20., azim=-65)
    ax.plot(x, y, v, c=dlmagenta)

    return

(1)梯度下降路径

​ 等高线图展示了 cost(w,b) 在 wb 一定范围内的变化。成本水平通过环形等高线表示。叠加在等高线图上的红色箭头表示梯度下降的路径。

  • 路径朝着目标稳步(单调)前进。
  • 初始步骤的步长比接近目标时的步长要大得多。
fig, ax = plt.subplots(1, 1, figsize=(12, 6))
plt_contour_wgrad(x_train, y_train, p_hist, ax)
image-20250315154317763

(2)增加学习率

​ 大幅增加学习率后,观察梯度下降的收敛性和发散性。

# initialize parameters
w_init = 0
b_init = 0
# set alpha to a large value
iterations = 10
tmp_alpha = 8.0e-1
# run gradient descent
w_final, b_final, J_hist, p_hist = gradient_descent(x_train, y_train, w_init, b_init, tmp_alpha,
                                                    iterations, compute_cost, compute_gradient)
plt_divergence(p_hist, J_hist, x_train, y_train)
plt.show()
image-20250315154555996

​ 在上图中,wb 在正负之间来回波动,且绝对值随着每次迭代而增大。此外,每次迭代中 d J ( w ) d w \frac{d J(w)}{d w} dwdJ(w) 的符号都会改变,而成本值不断增加而不是减少。这表明学习率过大,导致解发散,如下图所示。

image-20250315154608679

您可能感兴趣的与本文相关的镜像

Python3.8

Python3.8

Conda
Python

Python 是一种高级、解释型、通用的编程语言,以其简洁易读的语法而闻名,适用于广泛的应用,包括Web开发、数据分析、人工智能和自动化脚本

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

蔗理苦

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

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

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

打赏作者

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

抵扣说明:

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

余额充值