共计 2448 个字符,预计需要花费 7 分钟才能阅读完成。
从三维曲面看梯度下降的几何意义
想象你站在一个起伏的山丘上蒙着眼睛,目标是找到最低点。梯度下降就像用脚感受地面的倾斜方向(梯度),然后朝着最陡的下坡方向迈出一步(学习率 α 控制步长)。下图展示了一个二元函数的优化过程:
# 生成三维曲面示例代码(省略导入语句)fig = plt.figure(figsize=(10,6))
ax = fig.add_subplot(111, projection='3d')
X = np.linspace(-3,3,100)
Y = np.linspace(-3,3,100)
X, Y = np.meshgrid(X,Y)
Z = X**2 + Y**2 # 简单凸函数
ax.plot_surface(X,Y,Z, cmap='viridis', alpha=0.8)
ax.scatter([0],[0],[0], c='r', s=100) # 标记最小值点

解析解 vs 迭代解 :
– 解析解:直接令导数∇f= 0 求解(如线性回归的闭式解 $(X^TX)^{-1}X^Ty$)
– 迭代解:通过重复执行 $θ_{t+1} = θ_t – α∇f(θ_t)$ 逐步逼近最优解
数学基础:偏导数与学习率
偏导数的链式法则
对于复合函数 $f(g(x))$,其导数计算为:
$$\frac{df}{dx} = \frac{df}{dg} \cdot \frac{dg}{dx}$$
以逻辑回归为例,损失函数对权重 $w_j$ 的偏导:
$$\frac{\partial L}{\partial w_j} = \frac{\partial L}{\partial p} \cdot \frac{\partial p}{\partial z} \cdot \frac{\partial z}{\partial w_j}$$
其中 $p=\sigma(z)$ 是 sigmoid 函数,$z=w^Tx+b$
学习率收敛条件
理论证明当学习率满足 $0 < α < 2/L$ 时保证收敛(L 为 Lipschitz 常数):
$$||∇f(x)-∇f(y)|| ≤ L||x-y||$$
过大学习率会导致震荡,过小则收敛缓慢。实践中常用线性搜索或 Armijo 准则动态调整。
Python 实现三种梯度下降
1. 批量梯度下降(BGD)
每次迭代使用全量数据计算梯度:
def bgd(X, y, lr=0.01, epochs=1000):
m, n = X.shape
theta = np.zeros(n)
losses = []
for _ in range(epochs):
grad = X.T @ (X @ theta - y) / m # 关键梯度计算
theta -= lr * grad
loss = np.mean((X @ theta - y)**2)
if np.isnan(loss): raise ValueError("NaN detected!")
losses.append(loss)
return theta, losses
2. 随机梯度下降(SGD)
每次随机选取一个样本更新:
def sgd(X, y, lr=0.01, epochs=1000):
m, n = X.shape
theta = np.zeros(n)
losses = []
for _ in range(epochs):
i = np.random.randint(m)
grad = (X[i] @ theta - y[i]) * X[i] # 单样本梯度
theta -= lr * grad
loss = np.mean((X @ theta - y)**2)
losses.append(loss)
return theta, losses
3. 小批量梯度下降(MBGD)
折中方案,常用 batch_size=32/64:
def mbgd(X, y, batch_size=32, lr=0.01, epochs=1000):
m, n = X.shape
theta = np.zeros(n)
losses = []
for _ in range(epochs):
idx = np.random.choice(m, batch_size, replace=False)
X_batch, y_batch = X[idx], y[idx]
grad = X_batch.T @ (X_batch @ theta - y_batch) / batch_size
theta -= lr * grad
loss = np.mean((X @ theta - y)**2)
losses.append(loss)
return theta, losses
工程实践技巧
特征标准化实验
比较标准化前后收敛速度差异:
from sklearn.preprocessing import StandardScaler
X_raw = np.random.rand(100,2)*10 # 原始特征
X_norm = StandardScaler().fit_transform(X_raw) # 标准化后
# 分别训练并绘制 loss 曲线
_, loss_raw = bgd(X_raw, y)
_, loss_norm = bgd(X_norm, y)
plt.plot(loss_raw, label='Raw')
plt.plot(loss_norm, label='Normalized')
学习率衰减策略
常用三种衰减方式对比:
1. 线性衰减:$α_t = α_0/(1+kt)$
2. 指数衰减:$α_t = α_0 γ^t$
3. 余弦衰减:$α_t = \frac{1}{2}α_0(1+\cos(\frac{tπ}{T}))$
常见问题与解决方案
鞍点识别
通过梯度幅值和 Hessian 矩阵判断:
– 梯度接近零但非极值点
– Hessian 矩阵有正有负特征值
解决方法:
– 使用动量(Momentum)加速穿越平坦区
– 引入随机噪声跳出鞍点
梯度爆炸 / 消失
预防措施:
– 梯度裁剪:grad = np.clip(grad, -1, 1)
– 权重初始化:Xavier/He 初始化
– 批归一化(BatchNorm)
思考题
- 如何实现带动量的梯度下降?提示:$v_t = βv_{t-1} + (1-β)∇f(θ_t)$
- Adam 优化器相比传统 SGD,通过哪些机制提升性能?(一阶矩估计、二阶矩估计、偏差修正)
希望这篇笔记能帮助你真正理解梯度下降的底层原理!在实践中遇到问题时,不妨回到这些基础概念寻找答案。
