单神经元梯度下降实验

Guo Xiaosheng Lv1

背景

最近在着手学习深度学习相关的知识。准备用C语言实现一个 MLP 网络实现手写数字的识别。在学习梯度下降的过程中,遇到了梯度爆炸的问题。于是写篇博客记录一下。

模型与推导

考虑如下这样一个十分简单的函数:

我们假设训练集只有这样的一个样本: , 。不难发现我们所期望的 应该为3。

损失用均方误差(MSE):

用链式法则求梯度(中间变量为误差 ):

更新规则(学习率 ):

实验

我们考虑一下三组实验:

  1. 、学习率 ,迭代 10 次:
0 2.400000 36.000000
1 2.880000 1.440000
2 2.976000 0.057600
3 2.995200 0.002304
4 2.999040 0.000092
5 2.999808 0.000004
6 2.999962 0.000000
7 2.999992 0.000000
8 2.999999 0.000000
9 3.000000 0.000000

损失函数值顺利地从 36 一路降到 0, 收敛到 3.000000。符合梯度下降的预期。然后下面做了第二组实验。

2.把数据改成 (等比例放大 100 倍,正确权重仍是 3), 不变:

0 24000.000000 360000.000000
1 -191952000.000000 23034240958464.000000
2 1535424069632.000000 1473822681305364561920.000000
3 -12281858414870528.000000 94301087412151397100942262272.000000
4 98242586598715686912.000000 6033761425490425962899905671893876736.000000
5 -785842538524696815599616.000000 inf
6 6285955011855612635475083264.000000 inf
7 -50281352547214949123377342709760.000000 inf
8 402200578581225195828561960721448960.000000 inf
9 -inf inf

不难发现, 在正负之间交替,绝对值指数级放大,损失变成 inf。这就是典型的发散震荡:步长超过谷底宽度,第一步就冲过最低点,之后每次矫枉过正、越震越大。

不难发现,在梯度公式中: 梯度与 的平方大致成正比 放大 100 倍,梯度放大 10000 倍。如果想要让 的收敛轨迹不发生变化,那么 应当变为原来的.

3.保持 ,把 缩到 :震荡消失,但收敛很慢,10 轮后 只到 1.7 左右)。

再把 升到 :收敛节奏与实验一完全一致,每一轮 的值逐位相同:

iter w L
0 2.400000 360000.000000
1 2.880000 14400.007812
2 2.976000 576.000000
3 2.995200 23.040468
4 2.999040 0.921642
5 2.999808 0.036870
6 2.999962 0.001474
7 2.999992 0.000059
8 2.999999 0.000002
9 3.000000 0.000000

总结

收敛速度由 决定,每次更新后,误差 按同一个因子收缩。推导如下:

记当前权重为 ,误差为 。梯度下降更新一步:

更新后误差也随之变化:

两个实验的收敛因子相同,所以 的轨迹完全相同。

在公式中出现两次:一次在梯度本身(),一次在误差传递(权重更新后再经 放大成误差变化)。因此 放大 100 倍, 要缩小 10000 倍(不是 100 倍)才能保持原收敛节奏。

这次实验也说明了为什么训练前数据要归一化。MNIST手写数字识别的 MLP 有 784 个像素输入,每个像素的量级各不相同,不可能为每个输入单独配学习率。归一化把输入统一缩到 0~1,让所有输入的梯度量级一致,一个 η 即可稳定训练。

实验代码(C)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
int main(void)
{
float x = 2.0f; //输入
float y = 6.0f; //期望
float w = 0.0f; //初值
float n = 0.1f; //学习率

for(int i = 0;i < 10;i ++){
float y_hat = w * x; // 前向:预测
float L = (y_hat - y) * (y_hat - y); // 损失
float dLdw = 2 * (y_hat - y) * x; // 梯度
w = w - n * dLdw; // 更新
printf("iter %d: w=%.6f L=%.6f\n", i, w, L);
}

return 0;
}
  • 标题: 单神经元梯度下降实验
  • 作者: Guo Xiaosheng
  • 创建于 : 2026-08-06 10:00:00
  • 更新于 : 2026-08-06 17:14:34
  • 链接: https://serendipityb612.com/2026/08/06/2026-08-06-gradient-descent-experiment/
  • 版权声明: 本文章采用 CC BY-NC-SA 4.0 进行许可。
目录
单神经元梯度下降实验