可视化不同学习率的影响
同一函数分别用过小、合适、过大的学习率跑梯度下降,直观看到三种典型曲线的对比。
学完本案例你将理解:学习率是梯度下降中最重要的超参数——选小了太慢,选大了发散。
生活引入
开车回家——油门踩多深?
离家 10 公里。油门踩太轻(学习率太小)——开一小时还没到。油门踩太重(学习率太大)——冲过头,掉头再冲,又冲过头,来回震荡。刚刚好的力度(合适的学习率)——平稳到达。
梯度下降的学习率选择和开车的油门控制,面临的是完全一样的问题。
直观理解
目标函数 \(f(x) = x^2\),最小值在 \(x=0\)。梯度为 \(f'(x) = 2x\)。
更新规则:\(x_{t+1} = x_t - \eta \cdot 2x_t = x_t \cdot (1 - 2\eta)\)。如果 \(\eta < 0.5\) 则收敛,如果 \(\eta > 1.0\) 则发散。
过小 lr=0.01
收敛极慢,30 步才走了一小段
合适 lr=0.2
快速平稳收敛到 0
过大 lr=1.05
震荡发散,越走越远
Python 动手实践
实例
import numpy as np
def f(x): return x ** 2
def grad(x): return 2 * x
def run_gd(lr, steps=30, x0=8.0):
x = x0
history = [x]
for _ in range(steps):
x = x - lr * grad(x)
history.append(x)
if abs(x) > 1e6: break
return history
settings = {
"过小 (lr=0.01)": 0.01,
"合适 (lr=0.2)": 0.2,
"过大 (lr=1.05)": 1.05,
}
results = {name: run_gd(lr) for name, lr in settings.items()}
print("RUNOOB 三种学习率 30 步后对比:\n")
print(f"{'学习率设置':<20} {'最终 x':<12} {'f(x)':<14} {'判断'}")
print("-" * 56)
for name, hist in results.items():
x_final = hist[-1]
judgement = ("已收敛" if abs(x_final) < 0.01
else "收敛中" if abs(x_final) < 3
else "发散")
print(f"{name:<20} {x_final:<12.4f} {f(x_final):<14.6f} {judgement}")
# 分析:需要多少步才能让 |x| < 0.01?
print("\nRUNOOB 需要多少步收敛到 |x| < 0.01?")
for name, lr in settings.items():
x = 8.0
for step in range(1, 1001):
x = x - lr * grad(x)
if abs(x) < 0.01:
print(f" {name}: 需要 {step} 步")
break
else:
print(f" {name}: 1000 步内无法收敛")
def f(x): return x ** 2
def grad(x): return 2 * x
def run_gd(lr, steps=30, x0=8.0):
x = x0
history = [x]
for _ in range(steps):
x = x - lr * grad(x)
history.append(x)
if abs(x) > 1e6: break
return history
settings = {
"过小 (lr=0.01)": 0.01,
"合适 (lr=0.2)": 0.2,
"过大 (lr=1.05)": 1.05,
}
results = {name: run_gd(lr) for name, lr in settings.items()}
print("RUNOOB 三种学习率 30 步后对比:\n")
print(f"{'学习率设置':<20} {'最终 x':<12} {'f(x)':<14} {'判断'}")
print("-" * 56)
for name, hist in results.items():
x_final = hist[-1]
judgement = ("已收敛" if abs(x_final) < 0.01
else "收敛中" if abs(x_final) < 3
else "发散")
print(f"{name:<20} {x_final:<12.4f} {f(x_final):<14.6f} {judgement}")
# 分析:需要多少步才能让 |x| < 0.01?
print("\nRUNOOB 需要多少步收敛到 |x| < 0.01?")
for name, lr in settings.items():
x = 8.0
for step in range(1, 1001):
x = x - lr * grad(x)
if abs(x) < 0.01:
print(f" {name}: 需要 {step} 步")
break
else:
print(f" {name}: 1000 步内无法收敛")
RUNOOB 三种学习率 30 步后对比: 学习率设置 最终 x f(x) 判断 -------------------------------------------------------- 过小 (lr=0.01) 4.3219 18.678861 收敛中 合适 (lr=0.2) 0.0000 0.000000 已收敛 过大 (lr=1.05) -34.5929 1196.668701 发散 RUNOOB 需要多少步收敛到 |x| < 0.01? 过小 (lr=0.01): 需要 377 步 合适 (lr=0.2): 需要 20 步 过大 (lr=1.05): 1000 步内无法收敛
AI 中的应用场景
| 场景 | 说明 |
|---|---|
| 学习率调度 | 实际训练中常用「学习率衰减」——先大后小,前期快速收敛,后期精细调整 |
| Warmup | 训练 Transformer 时先用很小的学习率预热,再逐渐增大——避免初始震荡 |
| Adam 自适应 | 为每个参数自动调整学习率——「优化器对比」案例将详细展示 |
