梯度下降法:机器学习如何一步步找到最优解
在机器学习中,我们经常会说一句话:模型训练的目标,是让损失函数尽可能小(在深度学习中也经常使用梯度下降)。
但问题是:参数那么多,损失函数又可能很复杂,模型到底应该怎样找到那个“比较好”的参数呢?
梯度下降法就是最常用的答案之一。它的思想并不神秘:先站在一个位置,看哪里下降最快,然后朝那个方向走一小步;重复很多次以后,就逐渐接近损失函数的低谷。
1. 为什么需要梯度下降
以线性回归为例,模型可以写成:
$$
\hat{y} = wx + b
$$
其中:
- $x$ 是输入特征。
- $\hat{y}$ 是模型预测值。
- $w$ 是权重。
- $b$ 是偏置。
我们希望预测值 $\hat{y}$ 尽量接近真实值 $y$,因此可以使用均方误差作为损失函数:
$$
J(w, b) = \frac{1}{m}\sum_{i=1}^{m}(\hat{y}^{(i)} - y^{(i)})^2
$$
因为 $\hat{y}^{(i)} = wx^{(i)} + b$,所以也可以写成:
$$
J(w, b) = \frac{1}{m}\sum_{i=1}^{m}(wx^{(i)} + b - y^{(i)})^2
$$
训练模型的过程,其实就是寻找一组 $w$ 和 $b$,让 $J(w,b)$ 尽可能小。
如果只有一个参数,损失函数可能像一条碗形曲线;如果有很多参数,它就会变成高维空间里的曲面。梯度下降法的任务,就是在这张曲面上一步步走向低处。
2. 从“下山”理解梯度下降
可以把损失函数想象成一座山:
- 当前参数,就是你站在山上的位置。
- 损失函数值,就是你所在位置的海拔。
- 训练目标,就是走到尽可能低的山谷。
如果你想下山,最自然的做法是观察当前坡度,然后朝下降最快的方向走一步。
梯度下降法也是这样:
- 随机初始化参数。
- 计算当前参数位置的梯度。
- 朝梯度的反方向更新参数。
- 重新计算损失。
- 重复以上过程,直到损失不再明显下降。
这里有一个关键点:梯度指向函数上升最快的方向,所以我们要朝梯度的反方向走。
3. 什么是梯度
在一元函数中,导数表示函数在某一点的变化率。
例如:
$$
f(x) = x^2
$$
它的导数是:
$$
f’(x) = 2x
$$
当 $x = 3$ 时:
$$
f’(3) = 6
$$
这说明在 $x=3$ 附近,函数值随着 $x$ 增大而快速上升。为了让函数值变小,我们应该让 $x$ 减小。
在多参数模型中,参数不止一个,例如 $w$ 和 $b$。这时我们需要分别计算损失函数对每个参数的偏导数:
$$
\frac{\partial J}{\partial w}, \quad \frac{\partial J}{\partial b}
$$
这些偏导数组成的向量,就是梯度:
$$
\nabla J(w,b) =
\left[
\frac{\partial J}{\partial w},
\frac{\partial J}{\partial b}
\right]
$$
梯度告诉我们:如果想让损失函数上升得最快,参数应该往哪个方向变。梯度下降则反过来,朝它的相反方向更新参数。
4. 梯度下降的更新公式
梯度下降的通用更新公式是:
$$
\theta := \theta - \alpha \nabla J(\theta)
$$
其中:
- $\theta$ 表示模型参数,可以是一个参数,也可以是一组参数。
- $\alpha$ 是学习率,控制每次迈多大的步子。
- $\nabla J(\theta)$ 是损失函数对参数的梯度。
对于线性回归中的 $w$ 和 $b$,更新公式可以写成:
$$
w := w - \alpha \frac{\partial J}{\partial w}
$$
$$
b := b - \alpha \frac{\partial J}{\partial b}
$$
这两个式子非常重要。机器学习中的很多优化过程,本质上都可以看成它的变体。
5. 在线性回归中推导梯度
线性回归的损失函数为:
$$
J(w,b) = \frac{1}{m}\sum_{i=1}^{m}(wx^{(i)} + b - y^{(i)})^2
$$
为了简化表达,令第 $i$ 个样本的误差为:
$$
e^{(i)} = wx^{(i)} + b - y^{(i)}
$$
那么损失函数可以写成:
$$
J(w,b) = \frac{1}{m}\sum_{i=1}^{m}(e^{(i)})^2
$$
对 $w$ 求偏导:
$$
\frac{\partial J}{\partial w}
= \frac{2}{m}\sum_{i=1}^{m}(wx^{(i)} + b - y^{(i)})x^{(i)}
$$
对 $b$ 求偏导:
$$
\frac{\partial J}{\partial b}
= \frac{2}{m}\sum_{i=1}^{m}(wx^{(i)} + b - y^{(i)})
$$
所以线性回归的参数更新过程为:
$$
w := w - \alpha \frac{2}{m}\sum_{i=1}^{m}(wx^{(i)} + b - y^{(i)})x^{(i)}
$$
$$
b := b - \alpha \frac{2}{m}\sum_{i=1}^{m}(wx^{(i)} + b - y^{(i)})
$$
从这个推导可以看出,梯度下降并不是凭感觉调参数,而是根据损失函数的变化方向,有依据地调整参数。
6. 学习率为什么重要
学习率 $\alpha$ 决定每一步走多远。
如果学习率太小:
- 每次更新很保守。
- 损失会下降,但速度很慢。
- 可能需要训练很多轮。
如果学习率合适:
- 损失能够稳定下降。
- 收敛速度比较快。
- 参数逐渐接近较优解。
如果学习率太大:
- 参数可能直接越过最低点。
- 损失可能来回震荡。
- 严重时损失会越来越大,导致发散。
所以在训练模型时,学习率通常是最重要的超参数之一。
7. 三种常见梯度下降方式
根据每次更新参数时使用多少样本,梯度下降可以分为三类。
7.1 批量梯度下降
批量梯度下降,也叫 Batch Gradient Descent。它每次使用全部训练样本来计算梯度。
优点:
- 梯度方向比较稳定。
- 损失下降曲线通常比较平滑。
缺点:
- 数据量很大时,单次更新计算成本高。
- 参数更新频率低。
适合数据量不大、希望训练过程稳定的场景。
7.2 随机梯度下降
随机梯度下降,也叫 Stochastic Gradient Descent,简称 SGD。它每次只随机使用一个样本来更新参数。
优点:
- 单次更新很快。
- 可以更频繁地更新参数。
- 有时能跳出局部较差的位置。
缺点:
- 梯度方向噪声较大。
- 损失曲线会明显波动。
适合大规模数据或在线学习场景。
7.3 小批量梯度下降
小批量梯度下降,也叫 Mini-batch Gradient Descent。它每次使用一小批样本来计算梯度,例如 32、64 或 128 个样本。
这是深度学习中最常见的训练方式。
它在稳定性和计算效率之间取得了平衡:
- 比批量梯度下降更新更频繁。
- 比随机梯度下降方向更稳定。
- 更适合 GPU 并行计算。
8. 为什么特征缩放会影响梯度下降
假设一个房价预测模型有两个特征:
- 面积:几十到几百。
- 距离地铁距离:几百到几千。
如果不同特征的数值范围差异很大,损失函数的曲面可能会变得很“狭长”。梯度下降在这种曲面上容易来回摆动,收敛速度变慢。
因此,使用梯度下降训练模型前,经常会对特征做标准化:
$$
z = \frac{x - \mu}{\sigma}
$$
其中:
- $\mu$ 是特征均值。
- $\sigma$ 是特征标准差。
标准化以后,不同特征处在更接近的尺度上,梯度下降通常会更容易收敛。
9. Python 手写实现
下面用一元线性回归演示梯度下降的完整过程。
import numpy as np
# 构造一组简单数据:大致满足 y = 2x + 1
x = np.array([1, 2, 3, 4, 5], dtype=float)
y = np.array([3, 5, 7, 9, 11], dtype=float)
# 初始化参数。这里从 0 开始,实际任务中也可以使用随机初始化
w = 0.0
b = 0.0
# 学习率控制每次参数更新的步长
learning_rate = 0.01
# 训练轮数表示完整更新多少次参数
epochs = 1000
# 样本数量用于计算平均梯度
m = len(x)
for epoch in range(epochs):
# 根据当前参数计算预测值
y_pred = w * x + b
# 计算预测误差
error = y_pred - y
# 分别计算损失函数对 w 和 b 的梯度
dw = (2 / m) * np.sum(error * x)
db = (2 / m) * np.sum(error)
# 沿着梯度的反方向更新参数
w = w - learning_rate * dw
b = b - learning_rate * db
# 每隔 100 轮观察一次损失,确认模型是否在逐渐收敛
if epoch % 100 == 0:
loss = np.mean(error ** 2)
print(f"epoch={epoch}, loss={loss:.6f}, w={w:.4f}, b={b:.4f}")
print(f"最终参数:w={w:.4f}, b={b:.4f}")
如果学习率设置合理,你会看到损失值逐渐减小,$w$ 接近 2,$b$ 接近 1。
10. 如何判断梯度下降是否正常工作
训练时可以重点观察损失曲线:
- 如果损失稳定下降,说明学习率和数据处理通常比较合理。
- 如果损失下降很慢,可能是学习率太小,或者特征没有缩放。
- 如果损失剧烈震荡,可能是学习率偏大。
- 如果损失变成
nan,通常说明学习率过大、数据异常或数值溢出。
还可以观察参数变化:
- 如果参数几乎不变,更新步子可能太小。
- 如果参数绝对值越来越大,模型可能正在发散。
11. 梯度下降一定能找到全局最优解吗
不一定。
对于线性回归的均方误差损失函数,它通常是凸函数。在这种情况下,只要学习率合适,梯度下降可以收敛到全局最优解。
但在神经网络中,损失函数往往是非凸的,曲面会更复杂,可能存在很多局部低谷、鞍点和平坦区域。此时梯度下降通常不能保证找到全局最优解,但在实践中仍然可以找到效果足够好的参数。
这也是为什么深度学习中会出现很多优化器,例如 Momentum、RMSprop、Adam 等。它们都可以看成是在基础梯度下降上做改进。
12. 总结
梯度下降法的核心可以概括为一句话:
根据损失函数对参数的梯度,沿着让损失下降的方向,不断更新参数。
理解梯度下降时,可以抓住四个关键词:
- 损失函数:告诉模型现在错得有多离谱。
- 梯度:告诉参数往哪个方向变化会让损失上升最快。
- 反方向:为了减小损失,需要朝梯度的反方向更新。
- 学习率:控制每次更新走多大一步。
线性回归、逻辑回归、神经网络虽然模型形式不同,但背后都离不开“定义损失函数,再用优化算法降低损失”这个基本思路。学会梯度下降以后,再看很多机器学习算法的训练过程,就会清晰很多。
