文章目录
- 1 概述
- 1.1 案例
- 1.2 分析
- 2 代价函数
- 2.1 代价函数公式
- 2.2 理解代价函数
- 2.3 可视化代价函数
- 3 梯度下降
- 3.1 实现步骤
- 3.2 理解梯度下降
- 3.3 学习率
- 4 最佳实践
- 4.1 导入数据
- 4.2 代码实现
- 4.3 可视化
1 概述
线性回归模型是使用最广泛的学习算法,让我们从一个可以使用线性回归解决的问题开始。
1.1 案例
下图展示了美国波特兰市的房屋大小和价格数据集,其中横轴是以平方英尺为单位的房屋大小,纵轴是以千美元为单位的房屋价格,每个数据点使用小十字架表示。

现在,假设您是波特兰的一名房地产经纪人,您正在帮助一位客户出售她的房子。她问你这套房子能卖多少钱?该数据集会帮助您估算她可以获得的价格:
- 你先测量房子的大小,结果房子是 1,250 平方英尺。
- 接下来,使用数据集构建一个线性回归模型,将数据拟合一条直线,如下图所示。
- 根据这条与数据拟合的直线,1,250 平方英尺对应的价格大约 220,000 美元。,

除了将数据可视化为图表外,还有另一种查看有用数据的方法,即数据表。
例如,表格的第一行是一个面积为 2,104 平方英尺的房子,售价是 400,000 美元,也就是在这附近。表格的第一行绘制为此处的数据点。

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)。

注意:
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
线性函数只是直线的一个奇特术语,由于其相对简单且易于使用,因此使用直线作为基础,最终逐渐学习并理解更复杂的非线性模型。
这个特殊的模型有一个名字,叫做线性回归。更具体地说,这是具有一个变量的线性回归,其中“一个变量”表示只有一个输入变量或特征 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 的:

对于线性回归,我们的目标是选择合适的参数 w w w 和 b b b ,使得函数 f f f 生成的直线能够很好地拟合训练数据。为了衡量直线与数据的拟合程度,我们需要构建代价函数(也称为损失函数):通过比较预测值 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=1∑m(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 最小化代价函数,从而使模型的预测更加准确。

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)

为了更直观地理解代价函数,我们暂时简化模型,仅考虑参数 w w w,即假设 b = 0 b=0 b=0。此时,模型变为:
f w ( x ) = w ⋅ x f_{w}(x)=w\cdot x fw(x)=w⋅x
相应的代价函数也简化为:
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=1∑m(w⋅x(i)−y(i))2
假设我们有一个简单的训练集,包含三个数据点:(1, 1)、(2, 2) 和 (3, 3)。
- w = 1 w=1 w=1 时
- 模型 f w ( x ) f_{w}(x) fw(x) 是一条斜率为 1 的直线,完美地通过所有数据点。
- 计算代价函数 J(w):J(1) = 0,表示模型完美拟合数据。

- 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,表示模型拟合效果较差。

- w = 0 w=0 w=0 时
- 模型 f w ( x ) f_{w}(x) fw(x) 是一条水平线,完全偏离数据。
- 计算代价函数 J(w):J(0) ≈ 2.33,表示模型拟合效果非常差。

这就是在线性回归中如何使用代价函数来找到使 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,并可视化代价函数来深入理解其作用。

假设我们有一个房价预测模型,输入特征 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.06⋅x+50
这个模型对房价的预测效果较差,因为它始终低估了房价。

在此之前,我们将参数 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) 成为一个三维的曲面。

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

需要注意的是,梯度下降可能会收敛到局部最小值,而不是全局最小值。局部最小值是指某个区域内代价函数的最小值,但不一定是整个函数的最小值。这种现象在复杂的代价函数中尤为常见。
3.1 实现步骤
梯度下降的核心思想是通过迭代更新参数 w 和 b,使得代价函数 J(w,b) 逐渐减小。具体来说,梯度下降的更新规则如下:
-
更新参数 w w w:
w : = w − α ⋅ ∂ J ( w , b ) ∂ w w:=w-\alpha\cdot\frac{\partial J(w,b)}{\partial w} w:=w−α⋅∂w∂J(w,b)其中,α 是学习率, ∂ J ( w , b ) ∂ w \frac{\partial J(w,b)}{\partial w} ∂w∂J(w,b) 是代价函数对 w 的偏导数。
-
更新参数 b b b:
b : = b − α ⋅ ∂ J ( w , b ) ∂ b b:=b-\alpha\cdot\frac{\partial J(w,b)}{\partial b} b:=b−α⋅∂b∂J(w,b)其中, ∂ J ( w , b ) ∂ w \frac{\partial J(w,b)}{\partial w} ∂w∂J(w,b) 是代价函数对 b 的偏导数。
-
赋值运算符 :=
- 在编程中,:= 表示赋值操作。例如, w : = w − α ⋅ ∂ J ( w , b ) ∂ w w:=w-\alpha\cdot\frac{\partial J(w,b)}{\partial w} w:=w−α⋅∂w∂J(w,b) 表示将 w 更新为右侧表达式的值。
- 这与数学中的等号 = 不同,后者通常用于表示真值断言。
-
学习率 α
- 学习率 α 是一个介于 0 和 1 之间的正数,通常设置为 0.01。
- 学习率决定了每次更新参数的步长。较大的学习率意味着更激进的更新,而较小的学习率则意味着更谨慎的更新。
-
偏导数
-
偏导数表示代价函数在 w 和 b 方向上的变化率。
-
偏导数的作用是告诉我们参数应该朝哪个方向更新,以最快地降低代价函数。
-

在实现梯度下降时,一个关键细节是同步更新参数 w 和 b。这意味着在每次迭代中,我们需要同时计算 w 和 b 的更新值,然后再同时更新它们。上图左边展示了正确的更新步骤,右边则是不推荐的错误做法。
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 应该如何更新,以最快地降低代价函数。

- 导数为正时
- 如果导数为正,意味着代价函数在当前 w 处是上升的。
- 根据梯度下降的更新规则,w 会减小(即 w:=w−α⋅正数)。
- 在图形上,w 会向左移动,代价函数 J(w) 会逐渐减小。
- 导数为负时
- 如果导数为负,意味着代价函数在当前 w 处是下降的。
- 根据梯度下降的更新规则,w 会增加(即 w:=w−α⋅负数)。
- 在图形上,w 会向右移动,代价函数 J(w) 会逐渐减小。
通过这种方式,导数项引导 w 朝着代价函数的最小值方向移动。
3.3 学习率
(1)学习率过小
如果学习率 α 过小,梯度下降的更新步长会非常小。具体来说:
- 更新步长小:每次更新 w 时,w 的变化量非常小。
- 收敛速度慢:虽然梯度下降最终会收敛到最小值,但需要非常多的迭代步骤。
- 效率低下:计算成本高,尤其是在大规模数据集上。
示例:
假设学习率 α α α = 0.0000001,每次更新 w 的步长非常小。虽然 w 会逐渐接近最小值,但需要大量的迭代步骤才能达到目标。
(2)学习率过大
如果学习率 α 过大,梯度下降的更新步长会非常大。具体来说:
- 更新步长大:每次更新 w 时,w 的变化量非常大。
- 可能无法收敛:梯度下降可能在最小值附近振荡,甚至偏离最小值。
- 发散风险:在某些情况下,梯度下降可能完全无法收敛,导致代价函数值不断增加。
示例:
假设学习率 α α α = 10,每次更新 w 的步长非常大。梯度下降可能会从最小值的一侧跳到另一侧,甚至偏离最小值,导致代价函数值不断增加。

(3)学习率的自动调整
一个有趣的现象是,即使学习率 α 保持不变,梯度下降在接近最小值时也会自动减小更新步长。这是因为:
- 导数变小:当 w 接近最小值时,导数 d J ( w ) d w \frac{d J(w)}{d w} dwdJ(w) 会逐渐变小。
- 更新步长减小:由于更新步长 α ⋅ d J ( w ) d w \alpha\cdot\frac{d J(w)}{d w} α⋅dwdJ(w) 中的导数项变小,更新步长也会自动减小。
- 稳定收敛:这使得梯度下降在接近最小值时能够稳定地收敛,而不会在最小值附近振荡。

(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 的值,算法会保持稳定。

4 最佳实践
4.1 导入数据
首先导入相关库:
import math, copy
import numpy as np
import matplotlib.pyplot as plt
math
和copy
用于数学运算和深拷贝。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)代价函数
w
和b
是线性模型的参数。m
是样本数量。f_wb
是模型的预测值。total_cost
是成本值。
# Function to calculate the cost
def compute_cost(x, y, w, b):m = x.shape[0]cost = 0for i in range(m):f_wb = w * x[i] + bcost = cost + (f_wb - y[i]) ** 2total_cost = 1 / (2 * m) * costreturn total_cost
(2)计算梯度
dj_dw
和dj_db
分别是 w 和 b 的梯度。
def compute_gradient(x, y, w, b):"""Computes the gradient for linear regression Args:x (ndarray (m,)): Data, m examples y (ndarray (m,)): target valuesw,b (scalar) : model parameters Returnsdj_dw (scalar): The gradient of the cost w.r.t. the parameters wdj_db (scalar): The gradient of the cost w.r.t. the parameter b """# Number of training examplesm = x.shape[0]dj_dw = 0dj_db = 0for i in range(m):f_wb = w * x[i] + bdj_dw_i = (f_wb - y[i]) * x[i]dj_db_i = f_wb - y[i]dj_db += dj_db_idj_dw += dj_dw_idj_dw = dj_dw / mdj_db = dj_db / mreturn dj_dw, dj_db
(3)梯度下降算法
alpha
是学习率,num_iters
是迭代次数。- 每次迭代中,更新 w 和 b,并记录成本值和参数历史。
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 alphaArgs:x (ndarray (m,)) : Data, m examples y (ndarray (m,)) : target valuesw_in,b_in (scalar): initial values of model parameters alpha (float): Learning ratenum_iters (int): number of iterations to run gradient descentcost_function: function to call to produce costgradient_function: function to call to produce gradientReturns:w (scalar): Updated value of parameter after running gradient descentb (scalar): Updated value of parameter after running gradient descentJ_history (List): History of cost valuesp_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 laterJ_history = []p_history = []b = b_inw = w_infor i in range(num_iters):# Calculate the gradient and update the parameters using gradient_functiondj_dw, dj_db = gradient_function(x, y, w, b)# Update Parameters using equation (3) aboveb = b - alpha * dj_dbw = w - alpha * dj_dw# Save cost J at each iterationif i < 100000: # prevent resource exhaustionJ_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 < 10if 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)运行梯度下降
- 初始化参数 w 和 b 为 0。
- 设置学习率
tmp_alpha
和迭代次数iterations
。 - 运行梯度下降算法,得到优化后的参数 w 和 b。
# 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})")

(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()

(6)预测
- 使用优化后的参数 w 和 b 进行预测。
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")

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为0z = np.zeros_like(b0)# 遍历w和b的网格,计算每个点的costfor 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为pointbase = pointreturndef 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、vfor 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的值为100fix_b = 100# 创建一个从-70000到70000,步长为1000的数组w_array = np.arange(-70000, 70000, 1000)# 创建一个与w_array相同长度的数组,用于存储costcost = np.zeros_like(w_array)# 遍历w_array,计算costfor 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相同大小的数组,用于存储costz = np.zeros_like(tmp_b)# 遍历tmp_b、tmp_w,计算costfor 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 angleax.view_init(elev=20., azim=-65)ax.plot(x, y, v, c=dlmagenta)return
(1)梯度下降路径
等高线图展示了 cost(w,b) 在 w 和 b 一定范围内的变化。成本水平通过环形等高线表示。叠加在等高线图上的红色箭头表示梯度下降的路径。
- 路径朝着目标稳步(单调)前进。
- 初始步骤的步长比接近目标时的步长要大得多。
fig, ax = plt.subplots(1, 1, figsize=(12, 6))
plt_contour_wgrad(x_train, y_train, p_hist, ax)

(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()

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