人人都能学编程
English 开始学习
专项 · 人工智能第 3 课 / 共 6 课免费阅读

梯度下降 · 人工智能基础

模型怎么一步一步地改进自己?

课程目录

为什么需要它

上一课的公式只对简单的模型存在。神经网络有成百上千万个数,没有公式,但它们都是用一个简单的想法训练出来的:梯度下降。看看往哪个方向走损失会变小,朝那边迈一小步,然后重复。理解了它,你就明白每个训练脚本里都会遇到的那些设置,比如学习率和训练轮数。

直觉理解

把损失想象成一个山谷,权重 w 是你的位置。你看不到谷底,但能感觉到脚下的坡度。损失的坡度叫做梯度。梯度为正,说明往右走损失会升高,所以往左走;为负就往右走:

w = w - 学习率 × 梯度

对于损失 (w − 3)²,梯度是 2 × (w − 3),谷底在 w = 3。

学习率决定步子有多大:

  • 太小:进展非常慢
  • 太大:每一步都冲过谷底,落在对面更高的地方,损失越来越大

对于直线 w × x + b 加上 MSE 损失,(在 n 个点上取平均的)梯度是:

dw = (2 / n) × Σ (预测值 − y) × x
db = (2 / n) × Σ (预测值 − y)

把所有数据过一遍叫做一个轮次(epoch)。先把两个梯度都算出来,再同时更新 w 和 b。

定义

梯度

The slope of the loss: which way, and how steeply, it rises as a weight changes.
损失的坡度:当权重变化时,损失朝哪个方向上升、升得有多陡。

梯度下降

Repeatedly moving the weights a small step against the gradient to lower the loss.
反复把权重朝梯度的反方向挪一小步,让损失降低。

学习率

The size of each step. Too big diverges, too small crawls.
每一步的大小。太大会发散,太小走得很慢。

轮次

One pass of training over all the training examples.
在所有训练样本上训练一遍。

示例

走下山谷

动手试试

每一步都让 w 更靠近 3,损失也更低。

def loss(w):
    return (w - 3) ** 2

def grad(w):
    return 2 * (w - 3)

w = 0.0
lr = 0.1
for step in range(5):
    w = w - lr * grad(w)
    print(step + 1, round(w, 3), round(loss(w), 3))
输出
1 0.6 5.76
2 1.08 3.686
3 1.464 2.359
4 1.771 1.51
5 2.017 0.966
逐步执行
  1. L4(w − 3)² 的坡度。在 w = 0 处是 −6:往右走损失会下降。
  2. L10逆着坡度走一步:0 − 0.1 × (−6) = 0.6。
  3. L11越靠近谷底坡度越平,步子也越小。

三种学习率

动手试试

同一个山谷,各走 20 步。最后一个炸了。

for lr in [0.1, 0.5, 1.1]:
    w = 0.0
    for _ in range(20):
        w = w - lr * 2 * (w - 3)
    print(lr, round(w, 3))
输出
0.1 2.965
0.5 3.0
1.1 -112.013
逐步执行
  1. L1试一个小的、一个刚好的、一个太大的步长。
  2. L50.1 接近了;0.5 正好落在 3;1.1 每次都冲得更远。

训练一条直线

动手试试

梯度下降找到的直线和公式一样:w = 2,b = 1。

xs = [1, 2, 3, 4]
ys = [3, 5, 7, 9]
w, b = 0.0, 0.0
lr = 0.05
n = len(xs)
for epoch in range(1, 1001):
    dw = sum(2 * (w * x + b - y) * x for x, y in zip(xs, ys)) / n
    db = sum(2 * (w * x + b - y) for x, y in zip(xs, ys)) / n
    w, b = w - lr * dw, b - lr * db
    if epoch in (1, 10, 100, 1000):
        print(epoch, round(w, 3), round(b, 3))
输出
1 1.75 0.6
10 2.084 0.754
100 2.022 0.937
1000 2.0 1.0
逐步执行
  1. L7w 变化时损失怎么变,在所有点上取平均。
  2. L9同时更新两个数,用的是同一组 w 和 b 算出的梯度。
  3. L10打印几个轮次,看它慢慢稳定下来。

常见错误

学习率太大

损失每个轮次都在变大,甚至变成 nan。把学习率除以 10 再试。

特征的量级不同

一个以千计的特征梯度巨大,会逼着所有东西都用很小的学习率。先缩放特征(第 1 课)。

先更新一个权重,再算另一个的梯度

如果在算 db 之前 w 就已经变了,db 用的就是错误的 w。先把所有梯度算完,再更新。

小测验

1. 这段代码会输出什么?

w = 10
for _ in range(2):
    w = w - 0.25 * 2 * (w - 2)
print(w)
  1. A
    4.0
  2. B
    6.0
  3. C
    2.0
查看答案

答案:A

10 − 0.5 × 8 = 6.0;6 − 0.5 × 4 = 4.0。

2. 损失上下乱跳,而且每个轮次都更大。最可能的原因是什么?

  1. A学习率太大
  2. B学习率太小
  3. C轮次太多
查看答案

答案:A

每一步冲过谷底的距离都比上一步更大。

动手练习

3 道练习,分三层:先预测程序的输出,再按新需求改程序,最后从零写一个。代码在浏览器里运行,提交马上判题。

  1. 预测输出三步

    准确写出它打印的内容。

  2. 改需求上坡

    descend 应该朝 3((w − 3)² 的谷底)走,可它却越跑越远,再也回不来。修正更新的那一行。

  3. 从零写自己来训练

    写一个 train(xs, ys, lr, epochs),用梯度下降拟合 w × x + b,返回 (w, b)。

    • 从 w = 0 和 b = 0 开始
    • 每个轮次,用本课的公式计算 dw 和 db,两个都算完再改任何一个
    • 然后 w -= lr * dw,b -= lr * db
    train([1], [2], 0.25, 1)   # (1.0, 1.0)

在互动课里练这一课

打开互动课,这些例子都能直接改、直接运行,练习题提交马上判题。免费开始,不用注册。

互动课按顺序解锁:前面的课还没学,会先带你到课程地图。

本课术语

术语中文英文定义
Gradient梯度The slope of the loss with respect to a weight.
Gradient descent梯度下降Lowering the loss by stepping against the gradient.
Learning rate学习率How big each gradient step is.
Epoch轮次One pass over the training data.