梯度下降基础与挑战
梯度下降是深度学习的核心优化算法,通过迭代调整参数最小化损失函数:
θ=θ−α∇θJ(θ) \theta = \theta - \alpha \nabla_\theta J(\theta) θ=θ−α∇θJ(θ)
其中 α\alphaα 是学习率,∇θJ(θ)\nabla_\theta J(\theta)∇θJ(θ) 是损失函数梯度。
梯度下降面临的主要问题
1. 梯度消失/爆炸:
在梯度函数上出现的以指数级递增或者递减的情况分别称为梯度爆炸或者梯度消失。
假设g(z)=z,b[l]=0g(z)=z,b^{[l]}=0g(z)=z,b[l]=0,对于目标输出有:y^=W[L]W[L−1]...W2W1X\hat{y}=W^{[L]}W^{[L-1]}...W^{2}W^{1}Xy^=W[L]W[L−1]...W2W1X
- 对于W[l]W^{[l]}W[l]的值大于 1 的情况,激活函数的值将以指数级递增;
- 对于W[l]W^{[l]}W[l]的值小于 1 的情况,激活函数的值将以指数级递减。
在计算梯度时,根据不同情况梯度函数也会以指数级递增或递减,导致训练导数难度上升,梯度下降算法的步长会变得非常小,需要训练的时间将会非常长。
3. 局部最优与鞍点:

- 鞍点是函数上导数为零,但不是轴上局部极值的点。通常梯度为零的点 。通常梯度为零的点是下图所示的鞍点,而非局部最小值。减少损失的难度也来自误差曲面中的鞍点,而不是局部最低点。
鞍点示意图
- 鞍点附近的平稳段会使学习非常缓慢,而这也需要后面的优化算法加速学习的原因,它们能帮助尽早走出平稳段
4. 震荡问题:
- 在陡峭方向更新过大,平缓方向更新不足
- 导致优化路径曲折,收敛缓慢
梯度下降变体算法
1. 批梯度下降 (Batch Gradient Descent)
原理:使用整个训练集计算梯度
更新公式:
θ=θ−α∇θJ(θ)\theta = \theta - \alpha \nabla_\theta J(\theta)θ=θ−α∇θJ(θ)
特点:
- 每次迭代计算整个数据集
- 收敛稳定但计算成本高
- 不适合大数据集
def batch_gradient_descent(X, y, theta, alpha, num_iters):
m = len(y)
cost_history = []
for i in range(num_iters):
# 计算梯度
gradient = (1/m) * X.T.dot(X.dot(theta) - y)
# 更新参数
theta = theta - alpha * gradient
# 记录损失
cost = np.mean((X.dot(theta) - y)**2)
cost_history.append(cost)
return theta, cost_history
2. Mini-Batch 梯度下降
原理:将数据集分成小批量进行更新
更新公式:
θ=θ−α∇θJ(θ;Xi:i+n;yi:i+n)\theta = \theta - \alpha \nabla_\theta J(\theta; X^{{i:i+n}}; y^{{i:i+n}})θ=θ−α∇θJ(θ;Xi:i+n;yi:i+n)
特点:
- 平衡计算效率和收敛稳定性
- 常用批量大小:32, 64, 128
- 默认推荐算法
- 当 mini-batch 的大小为 1 ,即是随机梯度下降法(stochastic gradient descent)
def minibatch_gd(X, y, theta, alpha, num_iters, batch_size=32):
m = len(y)
cost_history = []
for i in range(num_iters):
# 随机打乱数据
permutation = np.random.permutation(m)
X_shuffled = X[permutation]
y_shuffled = y[permutation]
for j in range(0, m, batch_size):
# 获取当前batch
X_batch = X_shuffled[j:j+batch_size]
y_batch = y_shuffled[j:j+batch_size]
# 计算梯度
gradient = (1/batch_size) * X_batch.T.dot(X_batch.dot(theta) - y_batch)
# 更新参数
theta = theta - alpha * gradient
# 记录损失
cost = np.mean((X.dot(theta) - y)**2)
cost_history.append(cost)
return theta, cost_history
3.批梯度下降与 Mini-Batch梯度下降的区别
批梯度下降法与 Mini-Batch梯度下降法代价函数的变化趋势如下:

批梯度下降代价函数的变化图

- 如果训练样本的大小比较小,如m<=2000时,选择 batch 梯度下降法;
- 如果训练样本的大小比较大, 选择 Mini-Batch梯度下降法。为了和计算机的存储方式相适应,代码在 Mini-batch 大小为 2 的幂次时运行要快一些。
高级优化算法
3. 指数加权平均 (Exponentially Weighted Averages)
指数加权平均是一种常用的序列数据处理方式。通常用在序列场景如金融分析,温度变化序列分析。
假定给定一个序列,例如某地一年中每天的气温值,下图中蓝色的点代表真实数据。

那么这样的气温值变化可以理解成优化的过程波动较大,异常较多。那么怎么平缓一些呢,这时候就要用到加权平均了。
平缓气温变化图1
这条红线通过指数加权的公式计算得出:
St={Y1t=1βSt−1+(1−β)Ytt>1S_t=\begin{cases} Y_1 & t=1 \\ \beta S_{t-1}+(1-\beta)Y_t & t>1 \end{cases}St={Y1βSt−1+(1−β)Ytt=1t>1
其中YtY_tYt为ttt下的实际值,StS_tSt为ttt下加权平均后的值,β\betaβ为权重值。
上图红线中,β\betaβ为0.9。下图中,当取权重值β=0.98\beta=0.98β=0.98时,可以得到图中更为平滑的绿色曲线。β\betaβ越大相当于求取平均利用的天数越多,曲线自然也就越平滑而且越滞后。

公式:
vt=βvt−1+(1−β)θtv_t = \beta v_{t-1} + (1-\beta)\theta_tvt=βvt−1+(1−β)θt
特点:
- β\betaβ 控制平均范围(通常0.9)
- 偏差校正:vtcorrected=vt1−βtv_t^{corrected} = \frac{v_t}{1-\beta^t}vtcorrected=1−βtvt
4. 动量梯度下降 (Momentum)
原理:引入速度变量,积累历史梯度 ,是计算梯度的指数加权平均数数值。
更新公式:
vt=βvt−1+(1−β)∇θJ(θ)θ=θ−αvt \begin{align*} v_t &= \beta v_{t-1} + (1-\beta)\nabla_\theta J(\theta) \\ \theta &= \theta - \alpha v_t \end{align*} vtθ=βvt−1+(1−β)∇θJ(θ)=θ−αvt
效果:
- 当前后梯度方向一致时,动量梯度下降能够加速学习
- 而前后梯度方向不一致时,能够减少震荡
- 帮助跨越局部最优
def momentum_gd(X, y, theta, alpha, beta, num_iters):
m = len(y)
v = np.zeros_like(theta)
cost_history = []
for i in range(num_iters):
# 计算梯度
gradient = (1/m) * X.T.dot(X.dot(theta) - y)
# 更新速度
v = beta * v + (1 - beta) * gradient
# 更新参数
theta = theta - alpha * v
# 记录损失
cost = np.mean((X.dot(theta) - y)**2)
cost_history.append(cost)
return theta, cost_history
5. RMSProp (Root Mean Square Propagation)
原理:在对梯度进行指数加权平均基础上,引入平方和平方根
更新公式:
st=βst−1+(1−β)(∇θJ(θ))2θ=θ−α∇θJ(θ)st+ϵ \begin{align*} s_t &= \beta s_{t-1} + (1-\beta)(\nabla_\theta J(\theta))^2 \\ \theta &= \theta - \alpha \frac{\nabla_\theta J(\theta)}{\sqrt{s_t} + \epsilon} \end{align*} stθ=βst−1+(1−β)(∇θJ(θ))2=θ−αst+ϵ∇θJ(θ)
其中ϵ\epsilonϵ时一个非常小的数,防止分母太小导致不稳定。
特点:
- 在陡峭方向减小步长
- 在平缓方向增大步长
- 解决学习率敏感问题
def rmsprop(X, y, theta, alpha, beta, epsilon, num_iters):
m = len(y)
s = np.zeros_like(theta)
cost_history = []
for i in range(num_iters):
# 计算梯度
grad = (1/m) * X.T.dot(X.dot(theta) - y)
# 更新累积平方梯度
s = beta * s + (1 - beta) * grad**2
# 更新参数
theta = theta - alpha * grad / (np.sqrt(s) + epsilon)
# 记录损失
cost = np.mean((X.dot(theta) - y)**2)
cost_history.append(cost)
return theta, cost_history
6. Adam (Adaptive Moment Estimation)
原理:结合动量和RMSProp
更新公式:
mt=β1mt−1+(1−β1)∇θJ(θ)vt=β2vt−1+(1−β2)(∇θJ(θ))2m^t=mt1−β1tv^t=vt1−β2tθ=θ−αm^tv^t+ϵ \begin{align*} m_t &= \beta_1 m_{t-1} + (1-\beta_1)\nabla_\theta J(\theta) \\ v_t &= \beta_2 v_{t-1} + (1-\beta_2)(\nabla_\theta J(\theta))^2 \\ \hat{m}_t &= \frac{m_t}{1-\beta_1^t} \\ \hat{v}_t &= \frac{v_t}{1-\beta_2^t} \\ \theta &= \theta - \alpha \frac{\hat{m}_t}{\sqrt{\hat{v}_t} + \epsilon} \end{align*} mtvtm^tv^tθ=β1mt−1+(1−β1)∇θJ(θ)=β2vt−1+(1−β2)(∇θJ(θ))2=1−β1tmt=1−β2tvt=θ−αv^t+ϵm^t
特点:
- 目前最常用的优化算法
- 自适应学习率
- 内置偏差校正
def adam(X, y, theta, alpha, beta1, beta2, epsilon, num_iters):
m = len(y)
mt = np.zeros_like(theta)
vt = np.zeros_like(theta)
cost_history = []
for t in range(1, num_iters+1):
# 计算梯度
grad = (1/m) * X.T.dot(X.dot(theta) - y)
# 更新一阶矩估计
mt = beta1 * mt + (1 - beta1) * grad
# 更新二阶矩估计
vt = beta2 * vt + (1 - beta2) * grad**2
# 偏差校正
mt_hat = mt / (1 - beta1**t)
vt_hat = vt / (1 - beta2**t)
# 更新参数
theta = theta - alpha * mt_hat / (np.sqrt(vt_hat) + epsilon)
# 记录损失
cost = np.mean((X.dot(theta) - y)**2)
cost_history.append(cost)
return theta, cost_history
优化技巧
7. 学习率衰减
原理:随训练过程降低学习率
常用策略:
- 时间衰减:α=α01+decay_rate×epoch_num\alpha = \frac{\alpha_0}{1 + \text{decay\_rate} \times \text{epoch\_num}}α=1+decay_rate×epoch_numα0,其中decay_rate\text{decay\_rate}decay_rate为衰减率,epoch_num\text{epoch\_num}epoch_num为将所以样本完整过一遍的次数
- 指数衰减:α=α0×e−decay_rate×epoch_num\alpha = \alpha_0 \times e^{-\text{decay\_rate} \times \text{epoch\_num}}α=α0×e−decay_rate×epoch_num
- 阶梯衰减:每N个epoch减半
def learning_rate_decay(initial_lr, epoch, decay_type='time', decay_rate=0.1):
if decay_type == 'time':
return initial_lr / (1 + decay_rate * epoch)
elif decay_type == 'exponential':
return initial_lr * np.exp(-decay_rate * epoch)
elif decay_type == 'step':
return initial_lr * (0.5 ** (epoch // 10))
else:
return initial_lr
8. 输入标准化
原理:使特征具有零均值和单位方差
公式:
x′=x−μσx' = \frac{x - \mu}{\sigma}x′=σx−μ
效果:
- 加速收敛
- 减少震荡
- 提高优化稳定性
def normalize_input(X):
mu = np.mean(X, axis=0)
sigma = np.std(X, axis=0)
X_norm = (X - mu) / (sigma + 1e-8)
return X_norm, mu, sigma
优化算法对比分析
算法 | 收敛速度 | 内存需求 | 超参数敏感性 | 适用场景 |
---|---|---|---|---|
Batch GD | 慢 | 高 | 低 | 小数据集 |
Mini-Batch GD | 中等 | 中等 | 中等 | 通用 |
Momentum | 快 | 低 | 中等 | 有噪声梯度 |
RMSProp | 快 | 低 | 高 | 非平稳目标 |
Adam | 很快 | 低 | 低 | 深度学习通用 |

优化算法选择指南
实际应用建议
- 默认选择:Adam优化器
- 学习率设置:
- 初始值:0.001(Adam),0.01(SGD)
- 配合学习率衰减
- 批大小:
- 小数据集:32-128
- 大数据集:256-1024
- 输入处理:
- 必须标准化输入
- 考虑批归一化层
- 监控训练:
- 绘制损失曲线
- 跟踪验证集性能
- 使用早停策略
总结
梯度下降优化是深度学习成功的关键:
- 基础算法:BGD、SGD、Mini-batch GD
- 高级优化:Momentum、RMSProp、Adam
- 关键技巧:
- 学习率衰减
- 输入标准化
- 批归一化
- 实践准则:
- Adam作为默认选择
- 配合学习率衰减
- 标准化输入数据
理解这些优化算法的原理和实现,对于构建高效、稳定的深度学习模型至关重要。