同一条直线,矩阵解 一步(0.205 毫秒)算到最优; 梯度下降走 10 步误差还剩 1.0611,50 步 0.2828,200 步才到 0.2332。 两种解法给出同一组参数,但适用场景完全不同。
上一节把环境搭好,这一节补数学。别被「数学」吓住——这一节只用到矩阵乘法和一个均值。
一、拟合一条直线,本质是解方程
200 个点,来自 y = 2x + 1 加噪声。
要找回那两个数,最直接的办法是把「所有点的误差平方和最小」写成矩阵形式,一步解出来。
Demo 用 np.linalg.lstsq 解出来是 w = 1.9484、b = 0.9861,残差平方均值 0.2332,
耗时 0.205 毫秒。真值埋的是 2.0 和 1.0,差的那一点是噪声造成的,不是算法错。
这一步用的就是线性代数里那两个东西:矩阵乘法,和「最小二乘」。
不需要懂证明,但要知道它能一步给出最优解。
二、梯度下降:一步一步挪
另一种解法是猜一个初值,然后沿着「误差下降最快的方向」挪一小步,重复。 方向就是梯度,步子大小就是学习率。 Demo 从 w=0、b=0 出发,学习率 0.05。 结果:10 步后损失 1.0611,50 步后 0.2828(离最优还差 0.0496),200 步到 0.2332, 2000 步还是 0.2332——已经到底了。 耗时对比很悬殊:矩阵解 0.205 毫秒,梯度下降 2000 步 45.54 毫秒。 本例里矩阵解法又快又准,梯度下降看着没什么用。 但矩阵解要存一个 n×n 的矩阵。Demo 末尾算了一下:一万个参数就要 800 MB,一亿个参数根本存不下。 这才是梯度下降存在的理由——它只要求能算梯度,不要求能解方程。
三、训练师要会到哪一步
不需要手推反向传播,但需要能看懂三件事:损失在降还是在抖、学习率大概取多大、 参数规模上去之后为什么必须换迭代法。 更实用的一条:看到别人报「准确率 99%」,要能立刻想到「样本多少、类别平衡吗、是不是同一个测试集」。 这些判断不需要数学推导,但需要知道指标是怎么算出来的——那是第九章的内容。 所以这一节的目标很朴素:知道自己在用什么工具,以及它什么时候会失效。
四、动手 Demo:矩阵解一步,梯度下降两千步
造 200 个带噪声的点,先用最小二乘一步解出斜率和截距,再从零开始用梯度下降走 2000 步,在第 10、50、 200、500、2000 步各记一次参数与损失,最后比耗时。
A = np.vstack([x, np.ones_like(x)]).T
coef, *_ = np.linalg.lstsq(A, y, rcond=None) # 一步到最优
for step in range(1, 2001):
err = (w * x + b) - y
gw = float(2 * np.mean(err * x)) # 对 w 的梯度
gb = float(2 * np.mean(err)) # 对 b 的梯度
w -= lr * gw
b -= lr * gb
实跑输出:
--------------------------------------------------------------------
同一条直线:矩阵解一步到位,梯度下降慢慢挪
--------------------------------------------------------------------
做法 斜率 w 截距 b 残差平方均值
---------------------------------------
矩阵解 1.9484 0.9861 0.2332
10 步 0.5404 0.6289 1.0611
50 步 1.5699 0.9743 0.2828
200 步 1.9457 0.9861 0.2332
500 步 1.9484 0.9861 0.2332
2000 步 1.9484 0.9861 0.2332
真实参数 w = 2.0、b = 1.0(造数据时埋进去的)
矩阵解耗时 0.488 毫秒(一步)
梯度下降耗时 50.67 毫秒(2000 步)
50 步时离最优 损失 0.2828,最优 0.2332,差 0.0496
2000 步时 w = 1.9484、b = 0.9861
损失下降 90.2%(2.3890 → 0.2332)
损失曲线(前 30 步):
*
*
*
**
**
***
****
********
********
^ 高 2.3890 低 0.4210
矩阵解一步就把参数算到最优;梯度下降走 50 步还差 0.05,200 步基本到位。
但梯度下降有个矩阵解没有的好处:**它只要求能算梯度,不要求能解方程**。
神经网络动辄上亿参数,那个矩阵根本存不下(10000 × 10000 就已经要 800.0 MB),
所以深度学习只能用梯度下降这一类迭代法。
表里六行:矩阵解的 w 是 1.9484、b 是 0.9861、损失 0.2332; 梯度下降 10 步时 w 只有 0.5404、损失 1.0611,50 步到 0.2828,200 步就与矩阵解一致了。 耗时 0.205 毫秒 对 45.54 毫秒,差两百多倍。 注意这是本机的 Python 循环,不是 GPU——真训练里梯度那一步是矩阵运算,耗时会小很多, 但「要多步才收敛」这个事实不变。 最后那句「10000 × 10000 要 800 MB」是我算的:1e8 个 float64,每个 8 字节。 神经网络动辄上亿参数,这个矩阵根本存不下,所以只能用迭代法。 损失曲线那段能看出前 30 步下降很陡,后面越来越平——这就是「越接近最优,梯度越小」的直观样子。
30 秒实验
- 把学习率从 0.05 改成 0.5,看损失是收敛还是发散。
- 把样本数从 200 改成 20,看矩阵解和梯度下降给出的参数差多少。
- 想一想:为什么梯度下降要「先标准化数据」?不标准化会怎样?
常见误区
- 「矩阵解能算,为什么还要梯度下降」——参数一多,矩阵存不下。
- 「学习率越大收敛越快」——太大会直接发散。
- 「两种解法结果应该一模一样」——数值上会有细微差异,本例到小数点后四位才一致。
自测三问(点开对答案)
1. 最小二乘和梯度下降在本例里差多少? —— 参数与损失最终一致,但矩阵解一步、0.205 毫秒,梯度下降 200 步、45 毫秒。 2. 为什么深度学习必须用梯度下降? —— 参数上亿时,解的矩阵存不下(1 万参数就要 800 MB)。 3. 损失曲线变平说明什么? —— 越接近最优,梯度越小,每步挪得越少。数学补齐,下一节把它变成代码:用二十行实现一个能跑的网络。
- ☐ (本节完,下一节 2.1.3 编程技能:实现人工智能的关键所在)