共计 2376 个字符,预计需要花费 6 分钟才能阅读完成。
为什么梯度下降总是不收敛?
刚开始接触机器学习时,我花了整整一周调试不收敛的梯度下降代码。后来发现是学习率设成了 1.0(教科书示例值),而我的数据范围却是 0 -10000。这种数值尺度差异会导致参数更新像炮弹一样飞出合理范围。

更隐蔽的问题是未做特征缩放。假设有两个特征:
- 房间面积(单位:平方米,范围 50-200)
- 房间数量(单位:个,范围 1 -5)
它们的代价函数等高线会变成极窄的椭圆,导致需要非常小的学习率才能收敛。
不同类型的梯度下降怎么选?
- 批量梯度下降 (BGD)
- 每次迭代用全部数据计算梯度
- 稳定但计算量大,适合小型数据集
-
数学表达:$\theta = \theta – \eta \cdot \nabla_\theta J(\theta)$
-
随机梯度下降 (SGD)
- 每次随机选一个样本更新
- 波动大但能跳出局部最优
-
适合在线学习场景
-
小批量梯度下降 (MBGD)
- 折中方案,通常 batch 取 32-256
- GPU 并行计算效率高
- 现代深度学习的主流选择
用 AE 制作梯度下降可视化
在 After Effects 中创建二维网格作为参数空间,用表达式实现动态更新:
// AE 表达式代码(注释版)[width, height] = [thisComp.width, thisComp.height];
// 定义二次代价函数 f(x,y) = (x-100)^2 + (y-150)^2
function cost(x, y) {return Math.pow(x-100, 2) + Math.pow(y-150, 2);
}
// 梯度计算(数值微分简化版)function gradient(x, y, delta=0.01) {dx = (cost(x+delta, y) - cost(x-delta, y))/(2*delta);
dy = (cost(x, y+delta) - cost(x, y-delta))/(2*delta);
return [dx, dy];
}
// 参数更新(学习率 eta=0.1)if (time > 0) {[gx, gy] = gradient(x, y);
x -= 0.1 * gx;
y -= 0.1 * gy;
}
// 返回当前参数位置
[x, y]
通过 AE 的图形图层可以直观看到参数点如何 ” 滚下山坡 ”。建议开启运动模糊效果,能清晰展示优化路径。
Python 实现带动量的自适应梯度下降
import numpy as np
import matplotlib.pyplot as plt
class MomentumGradientDescent:
def __init__(self, lr=0.01, momentum=0.9):
self.lr = lr
self.momentum = momentum
self.velocity = None
def update(self, params, grads):
if self.velocity is None:
self.velocity = np.zeros_like(params)
self.velocity = self.momentum * self.velocity - self.lr * grads
return params + self.velocity
# 示例:优化二次函数 f(x) = x^2 + 5sin(x)
def func(x):
return x**2 + 5*np.sin(x)
x = np.linspace(-10, 10, 100)
plt.plot(x, func(x), label='Function')
optimizer = MomentumGradientDescent(lr=0.1)
params = np.array([-8.0]) # 初始点
for i in range(50):
grad = 2*params + 5*np.cos(params) # 解析梯度
params = optimizer.update(params, grad)
plt.scatter(params, func(params), c='red', alpha=0.3)
plt.legend()
plt.show()
必须绕开的六个大坑
- 特征缩放测试
- 对波士顿房价数据分别尝试:
- 原始数据(RM 范围 3 -9,AGE 范围 2 -100)
- MinMax 标准化到 [0,1]
- Z-score 标准化
-
比较三者收敛速度差异
-
梯度检查技巧
-
用数值梯度验证解析梯度:
def numerical_gradient(f, x, eps=1e-4): grad = np.zeros_like(x) for i in range(len(x)): tmp = x[i] x[i] = tmp + eps fxh1 = f(x) x[i] = tmp - eps fxh2 = f(x) grad[i] = (fxh1 - fxh2) / (2*eps) x[i] = tmp return grad -
学习率动态调整
- 初始学习率设为 0.1
- 每 10 轮乘以 0.9
-
或用 AdaGrad 等自适应方法
-
早停机制
-
验证集误差连续 3 次不下降时终止
-
梯度裁剪
-
当梯度范数超过阈值时缩放:
max_norm = 5.0 grad_norm = np.linalg.norm(grad) if grad_norm > max_norm: grad = grad * max_norm / grad_norm -
随机种子固定
- 调试时设置
np.random.seed(42)
从二维到神经网络的进阶思考
在 AE 中实现三维参数空间可视化的主要挑战是:
- 需要处理 z 轴深度遮挡
- 等高线渲染消耗大量 GPU 资源
- 摄像机运动轨迹设计复杂
一个取巧的方案是分图层显示:
- 底层:参数空间的热力图
- 中层:当前参数位置的投影
- 上层:实时损失值曲线
这种方法虽然不能完全展示三维结构,但能有效表达高维优化过程。对于真正的神经网络可视化,建议使用 TensorBoard 或 PyTorch 的 Visdom 工具。
经过两个月的实践,我发现梯度下降就像骑自行车——理论上看很容易摔倒,但找到平衡点后就会变得自然。建议初学者先用 AE 制作二维可视化,建立几何直觉后再接触更复杂的优化器。记住:没有万能的优化算法,只有最适合当前问题的解决方案。
