AI 学习课程 / LESSON 02

第二节:模型怎样学习

第一节解决了“数据怎样组织”。这一节解决“模型预测错了,怎样知道该改哪个参数、改多少”。沿着实际数值,把损失、梯度和反向传播连接起来。

逐个样本计算批次训练实验反向传播图解

01 / 从输入到参数更新

模型学习,就是反复更新参数

用线性回归举例:ŷ = wx + b。数据 x、y 已给定,模型要学习的是参数 w、b。通常不直接给模型正确参数,而是让它根据预测误差逐步调整。

① 前向计算输入 x,得到预测 ŷ
② 计算损失比较 ŷ 与真实 y
③ 求梯度损失对各参数的导数
④ 更新参数沿负梯度方向移动
本节目标:你能手算一次参数更新,说明不同批次怎样影响更新,并沿链式法则推导一个小型神经网络的梯度。

02 / 回归:预测值偏离目标多少?

均方误差:逐个平方,再取平均

先看一个样本:真实值 y=3,预测值 ŷ=1。误差是 1−3=−2,平方误差是 (−2)²=4。平方让正负误差都产生非负损失,也让较大的误差受到更大的惩罚。

对于 n 个样本、每个样本一个输出,均方误差(MSE)定义为:

L = (e₁² + e₂² + … + eₙ²) / n eᵢ = ŷᵢ − yᵢ

这里没有额外的 1/2 系数,因此后面的梯度会出现系数 2。不同教材可能采用不同系数,推导时要与损失定义保持一致。

实验 A:逐个样本算损失

三组数据满足 y=2x+1。拖动参数,观察误差怎样变化。

x真实 y预测 wx+b误差误差²

试着把 w 调到 2、b 调到 1。再保持 b=1,只改变 w,观察误差是否会随 x 改变。

03 / 分类:给正确答案多少概率?

交叉熵:惩罚低估正确答案的概率

二分类中,标签 y 是 0 或 1;模型输出 p,表示“标签为 1”的预测概率。例如 y=1 时,预测 p=0.9 比 p=0.1 更合理。

二元交叉熵 BCE = −[y ln(p) + (1−y) ln(1−p)] y=1 时:L = −ln(p) y=0 时:L = −ln(1−p)

ln 是自然对数。先把这条规则理解为:模型给真实类别的概率越小,损失越大;自信地答错,会受到很大的惩罚。两个公式分支分别对应真实类别 1 与 0。

实验 B:改变预测概率

多分类时呢?实际代码为什么常使用 logits?

多分类、单个正确类别时,交叉熵也是 −ln(正确类别的预测概率)。例如猫、狗、鸟的概率为 [0.1,0.7,0.2],真实类别是狗,则损失为 −ln(0.7)≈0.3567。

逻辑回归通过 sigmoid 把原始分数转为二分类概率;多分类常使用 softmax。训练库常提供直接接收原始分数 logits 的损失函数,以改善数值稳定性。此处用概率讲解含义,具体 API 在 PyTorch 部分再学习。

04 / 损失对参数的变化率

梯度告诉你局部该往哪个方向改

先只看参数 w。若导数为正,局部增大 w 会增大损失,所以应减小 w;若导数为负,则应增大 w。多个参数的偏导数组成梯度。

w新 = w旧 − η · ∂L/∂w b新 = b旧 − η · ∂L/∂b

η(eta)是学习率,控制每次移动多远。梯度只描述局部变化,学习率过大时,一次更新可能跨过低损失区域,甚至导致发散。

用一个样本手算

已知 x=2,y=5,w=1,b=0:

  1. 预测:ŷ=1×2+0=2。
  2. 误差:e=2−5=−3;损失:L=e²=9。
  3. 梯度:∂L/∂w=2ex=−12,∂L/∂b=2e=−6。

实验 C:同一个梯度,不同学习率

多个样本的线性回归梯度

损失是样本平方误差的平均,因此梯度也是各个样本梯度的平均:

∂L/∂w = (2/n) Σ eᵢxᵢ ∂L/∂b = (2/n) Σ eᵢ

对一个输出、二维数组 X:(n,F)、W:(F,1)、y:(n,1),代码为:

error = X @ W + b - y              # (n, 1)
grad_W = 2 / len(X) * X.T @ error  # (F, 1)
grad_b = 2 * error.mean(axis=0)    # (1,)

预测和标签的形状必须一致,避免第一节提到的意外广播。

05 / 用哪些样本决定这次更新?

全批量、随机梯度与小批量

对完整训练集平均求梯度,叫全批量梯度下降;每次随机选一个样本更新,叫随机梯度下降(SGD);每次使用一小组样本,叫小批量梯度下降。实际使用中,人们也常把小批量方式统称为 SGD。

方法这里的批次大小每次更新的数据
全批量6全部 6 个样本
小批量22 个样本的平均梯度
随机梯度11 个样本的梯度
一个 epoch 表示完整遍历一次训练集;一个 step 表示做一次参数更新。本例有 6 个样本,batch size=2 时,一个 epoch 有 3 个 step。

实验 D:看这一批数据怎样推动模型

六个样本大致满足 y=2x+1,并带有固定的小扰动。每个 epoch 使用固定种子的随机打乱顺序;重新开始会重置参数和样本顺序。

改变批次大小会重新开始;改变学习率影响接下来的更新,点击“重新开始”可从同一起点比较。

模型直线与目标数据

完整六个样本上的 MSE

小批次的梯度可能让完整数据上的损失暂时上升,不一定是程序错误。这里的曲线始终画完整数据的损失。比较不同批次时,也要比较 epoch 数或处理样本数:同样 6 次更新,使用的数据量可能不同。

06 / 让优化更容易进行

特征缩放与参数初始化

为什么单位会影响训练?

同一个长度可以用米或毫米表示,数值相差 1000 倍。预测含义不必改变,但损失对参数的变化速度会改变,原来的学习率可能不再合适。

米: x=2, w=3 → wx=6 毫米: x=2000,w=0.003 → wx=6

在线性回归的平方损失中,w 方向的二阶导数包含 x²。输入数值放大 1000 倍,这个方向的曲率会放大 100 万倍,使优化更容易出现不稳定或不同参数方向进展不均衡。

一种常见处理是标准化:x标准化=(x−均值)/标准差。均值与标准差在训练数据上计算;常数特征需要单独处理,避免除以零。标准化通常能改善优化,但不保证任意学习率都稳定。

为什么神经网络通常用随机初始化?

线性回归可以从 w=0、b=0 开始。但如果同一隐藏层的多个神经元拥有完全相同的输入权重、偏置和对称的连接,它们会得到相同的输出与更新,难以学出不同特征。

随机初始化用来打破神经元之间的对称性;数值规模也需要合适,过大或过小都可能影响信号与梯度。具体初始化方法在后面的深度学习课程展开。

下方只有一个隐藏神经元,用固定参数方便手算,不用于说明多神经元的随机初始化效果。

07 / 沿计算关系往回求导

反向传播:一条链,四个参数

考虑一个隐藏神经元和一个线性输出的小网络。输入 x=2,目标 y=1;初始参数为 w₁=0.5、b₁=0、w₂=1、b₂=0。

z₁ = w₁x + b₁ h = tanh(z₁) ŷ = w₂h + b₂ L = (ŷ − y)²

tanh 把输入平滑映射到 −1 与 1 之间;本例只需要知道它的导数是 1−tanh(z₁)²=1−h²。

实验 E:逐步展开计算图

每步使用同一组初始参数,显示值四舍五入,实际计算保留完整精度。

反向传播从损失的导数出发,通过每步的局部导数,计算损失对更早变量和参数的影响。所有参数的梯度先基于同一组旧参数算好,再一起更新。它不是把前向运算逐个求逆。

08 / 给推导找一个独立检查方法

用数值差分验证梯度

把某个参数 θ 增大一点、减小一点,分别重新计算损失,估计导数:

数值梯度 ≈ [L(θ+ε)−L(θ−ε)] / (2ε) 本例 ε = 0.00001

每次只改变一个参数,其他参数保持不变。手推梯度与数值估计相近,会给推导多一项证据。ε 太大近似不准,太小又容易受浮点误差影响。

同一小网络:解析梯度 vs 数值梯度

参数解析梯度数值梯度绝对差

解析梯度来自链式法则;数值梯度通过扰动参数、重新运行前向计算得到。

配套 NumPy 代码

下载第二节练习代码。包含 MSE、BCE、手写反向传播、数值梯度检查,以及三种批次大小的线性回归训练。在终端进入项目根目录后运行:

uv run python lessons/lesson-02-practice.py

运行后输出梯度对比,并在代码文件旁的 lesson-02-output 目录导出训练记录 CSV。本页提供训练曲线,配套代码只依赖 NumPy。

本节练习

  1. 手算 x=2、y=5、w=1、b=0 时的损失和两个梯度,验证 η=0.1 的更新结果。
  2. 解释 y=0 时,为什么 p=0.99 会产生很大的交叉熵。
  3. 用 batch size=1、2、6 训练相同的 epoch 数,观察完整数据上的损失。
  4. 遮住答案,沿计算图推导 w₁、b₁、w₂、b₂ 的梯度。
  5. 修改配套小网络的参数或样本,用数值差分检查梯度公式。

09 / 完成标准

检查你是否能解释训练过程

1. ∂L/∂w=−4,η=0.1,w=1,更新后 w 是多少?

2. y=1 时,哪个预测的交叉熵更小?

3. 12 个样本,batch size=3,一个 epoch 有几次更新?

4. 反向传播计算参数的梯度,主要使用什么?

5. 为什么多个对称的隐藏神经元通常不全部用相同参数初始化?

下一节:第三节:判断模型是否有效。我们会学习训练集、验证集、测试集,以及过拟合和分类指标。