小小白祈祷中...

梯度下降(Gradient Descent)是一种最常用的优化算法,广泛应用于机器学习和深度学习模型的训练中。它的主要目的是在参数空间中找到使损失函数最小化的参数集合。下面将详细介绍梯度下降的原理、公式以及Python中的代码实现。

梯度下降的原理

梯度下降是一种基于一阶导数的信息,沿着负梯度方向搜索最小值的迭代优化算法。直观上,可以将其想象成一个人在山坡上(损失函数表面)下山,每一步都按照当前位置的坡度(负梯度)选择下坡的方向,以达到最低点(全局最小值)。

梯度下降的核心思想是:在参数空间中,从初始点开始,沿着损失函数梯度的反方向迭代地更新参数,使得每一步都朝着使损失函数值减小的方向前进,直到达到损失函数的最小值或收敛于某个值

梯度下降的数学公式

假设我们有一个需要最小化的目标函数(损失函数)J(θ)J(\theta),其中 θ\theta 是参数向量。

梯度下降算法的更新规则为:

θ(t+1)=θ(t)αJ(θ(t))\theta^{(t+1)} = \theta^{(t)} - \alpha \nabla J(\theta^{(t)})

其中:

  • θ(t)\theta^{(t)} 表示第 tt 次迭代时的参数。
  • α\alpha 是学习率(步长),决定了每次更新的幅度。
  • J(θ(t))\nabla J(\theta^{(t)}) 是损失函数关于参数的梯度向量,在 θ(t)\theta^{(t)} 处计算。

梯度 J(θ)\nabla J(\theta) 表示函数在参数 θ\theta 处的导数向量,指向函数增长最快的方向。由于我们希望找到使损失函数最小化的参数,因此需要沿着负梯度方向更新参数。

梯度下降的类型

梯度下降根据使用的数据量不同,可以分为以下三种类型:

  1. 批量梯度下降(Batch Gradient Descent)

    • 在每一次参数更新时,使用所有训练数据计算梯度。

    • 更新规则:

      θ=θα1mi=1mJ(i)θ\theta = \theta - \alpha \cdot \frac{1}{m} \sum_{i=1}^{m} \nabla J^{(i)}\theta

      其中 mm 为训练样本总数。

    • 优点:方向更新稳定。

    • 缺点:每次更新计算开销大,尤其是大数据集。

  2. 随机梯度下降(Stochastic Gradient Descent, SGD)

    • 在每一次参数更新时,仅使用一个样本计算梯度。

    • 更新规则:

      θ=θαJ(i)θ\theta = \theta - \alpha \cdot \nabla J^{(i)}\theta

    • 优点:计算速度快,能跳出局部最小值。

    • 缺点:容易引起损失函数的波动,收敛路径不稳定。

  3. 小批量梯度下降(Mini-batch Gradient Descent)

    • 在每一次参数更新时,使用一个小批量(batch)的样本计算梯度。

    • 更新规则:

      θ=θα1bi=1bJ(i)θ\theta = \theta - \alpha \cdot \frac{1}{b} \sum_{i=1}^{b} \nabla J^{(i)}\theta

      其中 bb 是batch的大小。

  • 综合了批量和随机梯度下降的优点,常用的batch大小为32、64、128等。

梯度下降的收敛性与学习率

学习率 α\alpha 是梯度下降中的一个关键超参数,决定了每次参数更新的步长。

  • 学习率过大:可能导致越过最优点,甚至导致发散。
  • 学习率过小:收敛速度慢,可能陷入局部最小值或鞍点。

因此,常常需要通过实验或使用自适应的学习率调整方法(如AdagradRMSPropAdam等)来选择合适的学习率。

梯度下降的Python代码实现

以下以简单的线性回归为例,使用Python实现梯度下降算法。

问题描述

假设我们有一组一维数据点,想要拟合一条直线 y=wx+by = wx + b,需要通过最小化均方误差(MSE)损失函数来求解参数 wwbb

损失函数:

J(w,b)=12mi=1m(y(i)(wx(i)+b))2J(w, b) = \frac{1}{2m} \sum_{i=1}^{m} (y^{(i)} - (w x^{(i)} + b))^2

实现步骤

  • 初始化参数 wwbb
  • 定义损失函数 J(w,b)J(w, b)
  • 计算损失函数对 wwbb 的梯度。
  • 迭代更新参数,直到满足停止条件(如达到最大迭代次数损失函数收敛)。

代码实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
import numpy as np
import matplotlib.pyplot as plt

# 生成模拟数据
np.random.seed(42)
m = 100 # 样本数量
X = 2 * np.random.rand(m, 1)
true_w = 3.5
true_b = 1.2
y = true_w * X + true_b + np.random.randn(m, 1) # 添加一些噪声

# 可视化数据
plt.scatter(X, y)
plt.xlabel('x')
plt.ylabel('y')
plt.show()

# 梯度下降算法实现
def gradient_descent(X, y, lr=0.1, n_iterations=1000):
m = len(y)
# 初始化参数
w = np.random.randn(1)
b = 0

loss_history = []

for iteration in range(n_iterations):
# 计算模型预测
y_pred = w * X + b
# 计算损失
loss = (1 / (2 * m)) * np.sum((y_pred - y) ** 2)
loss_history.append(loss)
# 计算梯度
dw = (1 / m) * np.sum((y_pred - y) * X)
db = (1 / m) * np.sum(y_pred - y)
# 更新参数
w = w - lr * dw
b = b - lr * db

if iteration % 100 == 0:
print(f"Iteration {iteration}: Loss = {loss}, w = {w[0]}, b = {b}")

return w, b, loss_history

# 设置超参数
learning_rate = 0.1
n_iters = 1000

# 运行梯度下降
w_opt, b_opt, loss_hist = gradient_descent(X, y, lr=learning_rate, n_iterations=n_iters)

print(f"Optimized parameters: w = {w_opt[0]}, b = {b_opt}")

# 绘制损失函数变化
plt.plot(loss_hist)
plt.xlabel('Iteration')
plt.ylabel('Loss')
plt.title('Loss over iterations')
plt.show()

# 绘制拟合结果
plt.scatter(X, y, label='Data')
plt.plot(X, w_opt * X + b_opt, color='red', label='Fitted line')
plt.xlabel('x')
plt.ylabel('y')
plt.legend()
plt.show()

代码解释

输出:

1
2
3
4
5
6
7
8
9
10
11
Iteration 0: Loss = 12.288322478218705, w = 0.5482405149791566, b = 0.4477958365103154
Iteration 100: Loss = 0.406609440292755, w = 3.141471680138024, b = 1.560792222398323
Iteration 200: Loss = 0.40340042917083907, w = 3.246885681182567, b = 1.4414032188965682
Iteration 300: Loss = 0.4032958078369932, w = 3.265919363304535, b = 1.419846193018257
Iteration 400: Loss = 0.40329239693466856, w = 3.2693561084441964, b = 1.4159538298150631
Iteration 500: Loss = 0.4032922857312052, w = 3.2699766513890296, b = 1.4152510199102504
Iteration 600: Loss = 0.40329228210570994, w = 3.2700886973891645, b = 1.4151241196862692
Iteration 700: Loss = 0.4032922819875102, w = 3.2701089285532627, b = 1.4151012064251764
Iteration 800: Loss = 0.4032922819836566, w = 3.270112581517226, b = 1.4150970691784694
Iteration 900: Loss = 0.40329228198353106, w = 3.2701132411009084, b = 1.415096322152097
Optimized parameters: w = 3.270113359743099, b = 1.4150961877812085
  • 数据生成部分:模拟生成一组线性关系的数据,并添加了噪声。

  • gradient_descent 函数:实现梯度下降算法,包含参数初始化、损失计算、梯度计算和参数更新。

  • 参数更新规则

    {w=wα1mi=1m(wx(i)+by(i))x(i)b=bα1mi=1m(wx(i)+by(i))\begin{cases} w = w - \alpha \cdot \frac{1}{m} \sum_{i=1}^{m} (w x^{(i)} + b - y^{(i)}) x^{(i)} \\ b = b - \alpha \cdot \frac{1}{m} \sum_{i=1}^{m} (w x^{(i)} + b - y^{(i)}) \end{cases}

  • 结果可视化:绘制损失函数随迭代次数的下降曲线,以及最终拟合的直线与原始数据的对比。

结果分析

运行上述可视化图,可以观察到:

  • 损失函数随着迭代次数的增加逐渐减小,算法收敛。
  • 最终得到的参数 wwbb 与真实参数接近。
  • 拟合的直线较好地描述了数据的分布。

总结

梯度下降作为一种基础的优化算法,具有简单易实现的特点,但在实际应用中也存在一些缺点,例如收敛速度慢、容易陷入局部最小值、对学习率敏感等。为了提高算法性能,出现了许多改进算法,如动量法、自适应学习率方法等。可参考:

动量优化器
动量优化器
在深度学习和机器学习的优化过程中,梯度下降算法是最基本也是最常用的优化算法之一。然而,标准的梯度下降算法在某些情况下收敛速度较慢,或者在接近最优点时产生振荡。为了加速收敛和减少振荡,引入了动量(`Momentum`)优化器。 下面,我们将详细介绍动量优化器的原理、数学公式,以及Python代码实现。 --- # 背景与动机 在标准的梯度下降(`Gradient Descent`,GD)算
自适应学习率优化器
自适应学习率优化器
在深度学习和机器学习的模型训练过程中,优化算法起着至关重要的作用。除了标准的梯度下降和动量优化器之外,还有许多`自适应学习率`的优化算法,如 `Adagrad`、`Adadelta` 和 `RMSProp`。它们通过自适应地调整学习率,以提升收敛速度和稳定性。 以下将详细介绍这三个优化算法,包括公式、原理,以及 Python 代码实现。 --- # Adagrad 算法 ## 背景与动机
Adam优化器
Adam优化器
在深度学习和机器学习的模型训练过程中,优化算法起着关键作用。 **Adam**(Adaptive Moment Estimation)优化器是目前最受欢迎和广泛使用的优化算法之一。它结合了`动量优化器`和 `RMSProp` 的优势,能够在训练过程中`自适应地调整学习率`,实现`高效`和`稳健`的梯度更新。 下面,我们将详细介绍 Adam 优化器的原理、数学公式,以及 Python 代码实现