由于模型训练初期:
- 梯度大
- 模型需要快速学习
- 高学习率有利于跳过糟糕的局部最小值
中后期:
- 参数需要更精细调整
- 大学习率会震荡
- 小学习率更稳定
因此需要对学习率做调整,使其逐渐衰减,有助于模型更好收敛。
1. 等间隔学习率衰减
1.1 公式定义
等间隔学习率衰减(StepLR Step Learning Rate Decay)是一种简单有效的学习率调整策略
\[\text{lr}*{new} = \text{lr}*{old} \times \gamma \]
每隔固定的训练步数(或 epoch),就把当前学习率乘以一个衰减系数 \(\gamma\),衰减系数恒定。
lr
│
│ ──────────┐
│ └───────┐
│ └───────┐
│ └───────┐
└────────────────────────────────────────── epoch
50 100 150 2001.2 计算示例
使用等间学习率衰减对比不衰减学习率的情况:
python
import numpy as np
import matplotlib.pyplot as plt
# Simple quadratic function
def loss(w):
return (w - 3)**2
def grad(w):
return 2*(w - 3)
def gd(w0, lr, steps):
w = w0
traj = []
for t in range(steps):
w = w - lr * grad(w)
traj.append(loss(w))
return np.array(traj)
def gd_step_decay(w0, lr, steps, step_size, gamma):
w = w0
traj = []
current_lr = lr
for t in range(steps):
if t > 0 and t % step_size == 0:
current_lr *= gamma
w = w - current_lr * grad(w)
traj.append(loss(w))
return np.array(traj)
# parameters
w0 = 20.0
lr = 0.1
steps = 200
step_size = 50
gamma = 0.5
loss_no_decay = gd(w0, lr, steps)
loss_step_decay = gd_step_decay(w0, lr, steps, step_size, gamma)
plt.figure(figsize=(7,5))
plt.semilogy(loss_no_decay, label="No LR Decay")
plt.semilogy(loss_step_decay, label="Step LR Decay")
plt.xlabel("Iteration")
plt.ylabel("Loss")
plt.title("Comparison of Learning Rate Decay")
plt.legend()
plt.grid(True)
plt.show()
{
"final_loss_no_decay": float(loss_no_decay[-1]),
"final_loss_step_decay": float(loss_step_decay[-1])
}2. 指定间隔学习率衰减
2.1 公式定义
指定间隔学习率衰减(MultiStep LR)可以在不同的区间轮次上指定学习率:
- 每隔固定 step_size(例如每 50 epoch)衰减一次
- 指定多个不等间隔的衰减点
调度方式:
\[lr_{t+1} = \begin{cases} lr_t \cdot \gamma, & t \in \text{milestones}, \\ lr_t, & \text{otherwise}. \end{cases} \]
- 当训练步数 \(t\) 到达预设的值 \(\text{milestones}\) 时,学习率乘以一个衰减因子 \(\gamma\)。
- 在其他步数,学习率保持不变
python
optim.lr_scheduler.MultiStepLR(
optimizer,
milestones = [50, 125, 160],
gamma = 0.1
)2.2 计算示例
指定间隔,在 milestones=[50, 125, 160] 分别衰减,对比等间隔衰减:
python
import numpy as np
import matplotlib.pyplot as plt
def loss(w):
return (w - 3)**2
def grad(w):
return 2 * (w - 3)
def gd_step_lr(w0, lr, steps, step_size, gamma):
w = w0
traj_loss = []
traj_lr = []
current_lr = lr
for t in range(steps):
# 等间隔衰减
if t > 0 and t % step_size == 0:
current_lr *= gamma
w = w - current_lr * grad(w)
traj_loss.append(loss(w))
traj_lr.append(current_lr)
return np.array(traj_loss), np.array(traj_lr)
def gd_multi_step_lr(w0, lr, steps, milestones, gamma):
w = w0
traj_loss = []
traj_lr = []
current_lr = lr
for t in range(steps):
# 指定间隔衰减
if t in milestones:
current_lr *= gamma
w = w - current_lr * grad(w)
traj_loss.append(loss(w))
traj_lr.append(current_lr)
return np.array(traj_loss), np.array(traj_lr)
w0 = 20.0
lr = 0.1
steps = 200
step_size = 50
gamma = 0.5
milestones = [50, 125, 160] # 指定衰减时刻
loss_step, lr_step = gd_step_lr(w0, lr, steps, step_size, gamma)
loss_multi, lr_multi = gd_multi_step_lr(w0, lr, steps, milestones, gamma)
plt.figure(figsize=(8,5))
plt.semilogy(loss_step, label="StepLR")
plt.semilogy(loss_multi, label="MultiStepLR")
plt.xlabel("Iteration")
plt.ylabel("Loss")
plt.title("Loss Curve: StepLR vs MultiStepLR")
plt.legend()
plt.grid(True)
plt.show()
plt.figure(figsize=(8,5))
plt.plot(lr_step, label="StepLR")
plt.plot(lr_multi, label="MultiStepLR")
plt.xlabel("Iteration")
plt.ylabel("Learning Rate")
plt.title("Learning Rate Schedule")
plt.legend()
plt.grid(True)
plt.show()3. 指数学习率衰减
3.1 公式定义
指数学习率衰减(Exponential LR)是指对于每个训练周期(epoch)按照指数形式衰减,其特点为:
- 学习率按指数函数平滑下降
- 每个 epoch 都以相同比例衰减
- 理论上学习率无限趋近于0但永不等于0
公式为:
\[lr_t = lr_0 \cdot \gamma^t \]
其中:
- \(lr_t\) 是第 \(t\) 个 epoch 的学习率
- \(lr_0\) 是初始学习率
- \(\gamma\) 是衰减率(\(0 < \gamma < 1\))
- \(t\) 是当前 epoch 数(从0开始)
3.2 计算示例
对比等间衰减和指数学习率在函数 \(L(w) = (w - 3)^2\) 上的衰减:
python
import numpy as np
import matplotlib.pyplot as plt
def loss(w):
return (w - 3)**2
def grad(w):
return 2 * (w - 3)
def gd_step_lr(w0, lr, steps, step_size, gamma):
w = w0
traj_loss = []
traj_lr = []
current_lr = lr
for t in range(steps):
# StepLR:每 step_size 步衰减
if t > 0 and t % step_size == 0:
current_lr *= gamma
w = w - current_lr * grad(w)
traj_loss.append(loss(w))
traj_lr.append(current_lr)
return np.array(traj_loss), np.array(traj_lr)
def gd_exp_lr(w0, lr, steps, gamma):
w = w0
traj_loss = []
traj_lr = []
current_lr = lr
for t in range(steps):
# ExponentialLR:每一步都衰减 lr = lr * gamma
if t > 0:
current_lr *= gamma
w = w - current_lr * grad(w)
traj_loss.append(loss(w))
traj_lr.append(current_lr)
return np.array(traj_loss), np.array(traj_lr)
w0 = 20.0
lr = 0.1
steps = 200
step_size = 50
gamma_step = 0.5 # StepLR 的衰减系数
gamma_exp = 0.98 # ExponentialLR 的指数底
loss_step, lr_step = gd_step_lr(w0, lr, steps, step_size, gamma_step)
loss_exp, lr_exp = gd_exp_lr(w0, lr, steps, gamma_exp)
plt.figure(figsize=(8,5))
plt.semilogy(loss_step, label="StepLR (等间隔衰减)")
plt.semilogy(loss_exp, label="ExponentialLR (指数衰减)")
plt.xlabel("Iteration")
plt.ylabel("Loss")
plt.title("Loss Curve: StepLR vs ExponentialLR")
plt.legend()
plt.grid(True)
plt.show()
plt.figure(figsize=(8,5))
plt.plot(lr_step, label="StepLR")
plt.plot(lr_exp, label="ExponentialLR")
plt.xlabel("Iteration")
plt.ylabel("Learning Rate")
plt.title("Learning Rate Schedule Comparison")
plt.legend()
plt.grid(True)
plt.show()