AI数学基础教程:用NumPy和Matplotlib把梯度下降跑明白
AI数学基础教程最容易走偏的地方,是先花几周看高数视频,结果还是不知道导数在模型里干什么。
更有效的方式是选一个最小可运行的任务,边写代码边对照数学公式。
下面用 NumPy 和 Matplotlib 实现梯度下降,拟合一条直线,把导数、偏导、学习率和损失函数串起来。
先明确这个任务要解决什么
目标不是训练一个能上线的模型,而是让你亲眼看到:模型参数怎样根据误差一步步调整。
具体任务是给定一组带噪声的散点,用 y = w * x + b 去拟合,通过梯度下降找到合适的 w 和 b。
整个过程只依赖两个库,不需要 GPU,一台普通电脑几分钟就能跑完。
准备条件:
- Python 3.8 及以上,能用
python --version确认版本。 - 安装 NumPy 和 Matplotlib:
pip install numpy matplotlib。 - 会写最基础的 Python 循环和函数即可。
如果你还没有 Python 环境,先在终端确认 python 或 python3 命令可用,再执行安装。
国内网络下如果 pip 很慢,可以临时指定镜像源,例如 pip install numpy matplotlib -i https://pypi.tuna.tsinghua.edu.cn/simple。
用代码把数学概念对应起来
先看损失函数。
这里用均方误差:loss = (1/n) * Σ(y_pred - y_true)^2。
它衡量预测值和真实值差多少,数值越小说明拟合越好。
梯度下降的核心更新公式是:
w = w - learning_rate * dL/dwb = b - learning_rate * dL/db
其中 dL/dw 和 dL/db 是损失函数对两个参数的偏导数,表示参数往哪个方向调、调多少。
对均方误差求偏导后,代码里的梯度可以写成:
import numpy as np
import matplotlib.pyplot as plt
np.random.seed(42)
x = np.linspace(0, 10, 100)
y = 2.5 * x + 1.0 + np.random.randn(100) * 2.0
w, b = 0.0, 0.0
learning_rate = 0.01
epochs = 1000
n = len(x)
losses = []
for i in range(epochs):
y_pred = w * x + b
loss = np.mean((y_pred - y) ** 2)
losses.append(loss)
dw = (2 / n) * np.sum((y_pred - y) * x)
db = (2 / n) * np.sum(y_pred - y)
w -= learning_rate * dw
b -= learning_rate * db
print(f"w={w:.4f}, b={b:.4f}, loss={losses[-1]:.4f}")
把这段代码保存为 gradient_descent.py,运行 python gradient_descent.py。
预期输出中 w 接近 2.5,b 接近 1.0,损失值会明显下降。
如果结果差很远,先检查学习率是否过大或过小,以及数据生成时随机种子是否固定。
把训练过程画出来,验证是否真的在收敛
只看最后一行输出不够直观。
加上几行 Matplotlib 代码,把损失曲线和拟合直线画出来:
plt.figure(figsize=(10, 4))
plt.subplot(1, 2, 1)
plt.plot(losses)
plt.xlabel("epoch")
plt.ylabel("loss")
plt.title("Loss curve")
plt.subplot(1, 2, 2)
plt.scatter(x, y, s=10, label="data")
plt.plot(x, w * x + b, color="red", label="fit")
plt.legend()
plt.tight_layout()
plt.show()
验证标准有三个:损失曲线整体下降并逐渐变平;
红色拟合线穿过散点中心;w 和 b 与生成数据时的真实参数接近。
如果损失曲线上下震荡甚至变大,通常是学习率设得太大,把 learning_rate 改成 0.001 再试。
如果损失几乎不下降,可能是学习率太小,或者循环次数不够。
容易踩的坑
- 学习率不是越大越好:太大会跳过最优点,太小则收敛慢。先用
0.01跑通,再手动改成0.1和0.001对比曲线。 - 忘记除以样本数:梯度里漏掉
1/n会让更新幅度随数据量放大,换成大数据集时直接发散。 - 循环里覆盖了梯度:每次迭代都要重新计算
dw和db,不要写在循环外面。 - 画图时重复调用
plt.show():多次调用可能只显示一个窗口,统一在最后调用一次。 - Mac 上 Matplotlib 弹窗不显示:如果使用虚拟环境且没有图形界面,可以先把
plt.show()改成plt.savefig("result.png"),再打开图片查看。
从直线推广到更复杂的模型
直线拟合只有两个参数,但梯度下降的流程和神经网络训练是一致的:前向计算预测值、计算损失、反向求梯度、按学习率更新参数。
把这里的 w 和 b 换成矩阵,把均方误差换成交叉熵,就是逻辑回归和简单神经网络的基本训练循环。
所以这份 AI数学基础教程的重点不是背公式,而是建立“损失函数衡量误差、偏导数指示方向、学习率控制步长”的对应关系。
跑通之后,可以试着把数据换成二次函数,模型改成 y = w1 * x^2 + w2 * x + b,手动推导梯度并更新三个参数。
能独立完成这一步,说明你已经把梯度下降的核心逻辑用代码走通了。
常见疑问
需要先学完线性代数吗?
这个例子只用到向量化写法,不要求先学完整门课。
遇到矩阵乘法时再补对应概念,效率更高。
为什么梯度前面有系数 2?
对平方项求导会产生系数 2,它不影响下降方向,只影响步长。
实际工程中常被学习率吸收,但手写推导时保留更清楚。
损失降到多少才算训练好?
没有统一数值,取决于数据噪声和任务。
看损失曲线是否趋于平稳、拟合线是否合理,比盯一个固定阈值更可靠。