04 - 损失函数与优化¶
损失函数¶
图注:损失曲面可视化 - 展示了神经网络训练过程中的损失地形,帮助理解优化算法如何在复杂的损失景观中寻找最优解
重要性: ⭐⭐⭐⭐⭐ 实用度: ⭐⭐⭐⭐⭐ 学习时间: 4小时 必须掌握: 是
为什么学这一章?¶
损失函数和优化算法是训练神经网络的核心。理解它们能帮助你: - 知道如何衡量模型的预测好坏 - 选择合适的优化策略加速收敛 - 调试训练过程中的问题 - 提升模型性能
学完这一章,你将能够: - ✅ 理解各种损失函数的适用场景 - ✅ 掌握梯度下降及其变种 - ✅ 选择合适的优化器和学习率 - ✅ 诊断和解决优化问题
📖 损失函数¶
什么是损失函数?¶
Text Only
┌─────────────────────────────────────────────────────────────────────┐
│ 损失函数的作用 │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ 损失函数 = 衡量预测与真实之间的差距 │
│ │
│ 训练目标:最小化损失函数 │
│ │
│ ┌─────────┐ 预测 ┌─────────┐ 比较 ┌─────────┐ │
│ │ 输入 │ ─────────────→ │ 模型 │ ─────────────→ │ 损失 │ │
│ │ x │ │ f(x) │ │ L(y,ŷ) │ │
│ └─────────┘ └─────────┘ └────┬────┘ │
│ ↑ │ │
│ │ ┌─────────┐ │ │
│ └──────────────│ 真实 │←──────────────────────────┘ │
│ │ y │ │
│ └─────────┘ │
│ │
│ 好的损失函数特点: │
│ • 可微分(能用梯度下降优化) │
│ • 凸性(最好有全局最优) │
│ • 对异常值鲁棒 │
│ │
└─────────────────────────────────────────────────────────────────────┘
回归损失函数¶
1. MSE(均方误差)¶
图注:MSE损失函数曲线 - 展示了预测值与真实值之间的平方误差关系,误差越大惩罚越重
Text Only
┌─────────────────────────────────────────────────────────────────────┐
│ MSE - Mean Squared Error │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ 公式: │
│ MSE = (1/n) Σ(yᵢ - ŷᵢ)² │
│ │
│ 特点: │
│ • 对异常值敏感(平方放大误差) │
│ • 处处可导,优化方便 │
│ • 假设误差服从高斯分布 │
│ │
│ 适用场景:房价预测、温度预测等连续值预测 │
│ │
│ 梯度(对单个样本 ŷᵢ 求偏导): │
│ ∂MSE/∂ŷᵢ = (2/n)(ŷᵢ - yᵢ) │
│ 简化写法(单样本 L=(y-ŷ)²):∂L/∂ŷ = 2(ŷ - y) │
│ │
└─────────────────────────────────────────────────────────────────────┘
2. MAE(平均绝对误差)¶
图注:MAE vs MSE对比 - 展示了两种损失函数对异常值的不同处理方式,MAE更鲁棒,MSE对大误差惩罚更重
Text Only
┌─────────────────────────────────────────────────────────────────────┐
│ MAE - Mean Absolute Error │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ 公式: │
│ MAE = (1/n) Σ|yᵢ - ŷᵢ| │
│ │
│ 特点: │
│ • 对异常值更鲁棒 │
│ • 在0点不可导(可用次梯度) │
│ • 收敛速度比MSE慢 │
│ │
│ MSE vs MAE: │
│ • MSE:对大误差惩罚重,适合异常值少的情况 │
│ • MAE:对所有误差一视同仁,适合有异常值的情况 │
│ │
└─────────────────────────────────────────────────────────────────────┘
分类损失函数¶
1. 交叉熵损失(Cross-Entropy)¶
Text Only
┌─────────────────────────────────────────────────────────────────────┐
│ 交叉熵损失 - Cross-Entropy Loss │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ 信息论基础: │
│ 交叉熵衡量两个概率分布的差异 │
│ │
│ 二分类: │
│ BCE = -[y·log(ŷ) + (1-y)·log(1-ŷ)] │
│ │
│ 多分类: │
│ CE = -Σ yᵢ·log(ŷᵢ) │
│ │
│ 特点: │
│ • 与Softmax配合,梯度形式简洁 │
│ • 对错误预测惩罚大 │
│ • 训练速度快(梯度大) │
│ │
│ 为什么不用MSE做分类? │
│ • MSE在分类问题中梯度小,收敛慢 │
│ • 交叉熵更适合概率分布的比较 │
│ │
└─────────────────────────────────────────────────────────────────────┘
🔧 优化算法¶
梯度下降家族¶
图注:梯度下降优化器对比 - 展示了不同优化算法在损失曲面上的收敛路径,可以看出Adam等自适应优化器收敛更快更稳定
Text Only
┌─────────────────────────────────────────────────────────────────────┐
│ 梯度下降算法演进 │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ 1. 批量梯度下降 (BGD) │
│ θ = θ - η·∇J(θ) # 使用全部数据 │
│ 优点:稳定,收敛到全局最优 │
│ 缺点:慢,内存占用大 │
│ │
│ 2. 随机梯度下降 (SGD) │
│ θ = θ - η·∇J(θ; xᵢ, yᵢ) # 使用单个样本 │
│ 优点:快,能跳出局部最优 │
│ 缺点:震荡大,不稳定 │
│ │
│ 3. 小批量梯度下降 (Mini-batch) ⭐ 最常用 │
│ θ = θ - η·∇J(θ; batch) # 使用小批量 │
│ 优点:平衡了速度和稳定性 │
│ 典型batch size: 32, 64, 128, 256 │
│ │
└─────────────────────────────────────────────────────────────────────┘
动量法(Momentum)¶
图注:Momentum优化效果 - 展示了动量法如何通过累积之前的梯度方向来加速收敛并减少震荡
Text Only
┌─────────────────────────────────────────────────────────────────────┐
│ Momentum - 加速收敛 │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ 思想:累积之前的梯度方向,像滚雪球一样加速 │
│ │
│ 公式: │
│ vₜ = γ·vₜ₋₁ + η·∇J(θ) # 速度更新 │
│ θ = θ - vₜ # 参数更新 │
│ │
│ γ (动量系数):通常取 0.9 │
│ │
│ 效果对比: │
│ │
│ 标准SGD Momentum │
│ ↓ ↓ │
│ ↓ 震荡大 ↓ 平滑快速 │
│ ╱╲╱╲ ═══════ │
│ │
│ 优点: │
│ • 加速收敛(尤其在相关方向) │
│ • 减少震荡 │
│ • 有助于跳出局部最优 │
│ │
└─────────────────────────────────────────────────────────────────────┘
Adam 优化器 ⭐ 最常用¶
Text Only
┌─────────────────────────────────────────────────────────────────────┐
│ Adam - Adaptive Moment Estimation │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ Adam = Momentum + RMSprop │
│ │
│ 一阶矩估计(动量): │
│ mₜ = β₁·mₜ₋₁ + (1-β₁)·gₜ # 梯度的指数移动平均 │
│ │
│ 二阶矩估计(RMSprop): │
│ vₜ = β₂·vₜ₋₁ + (1-β₂)·gₜ² # 梯度平方的指数移动平均 │
│ │
│ 偏差修正: │
│ m̂ₜ = mₜ / (1-β₁ᵗ) │
│ v̂ₜ = vₜ / (1-β₂ᵗ) │
│ │
│ 参数更新: │
│ θ = θ - η·m̂ₜ / (√v̂ₜ + ε) │
│ │
│ 默认参数: │
│ β₁ = 0.9, β₂ = 0.999, ε = 1e-8 │
│ │
│ 优点: │
│ • 自适应学习率(每个参数有自己的学习率) │
│ • 结合了Momentum和RMSprop的优点 │
│ • 对超参数不敏感 │
│ • 适合大多数情况 ⭐ │
│ │
│ 缺点: │
│ • 可能不收敛到最优解(可用AdamW改进) │
│ • 需要更多内存 │
│ │
└─────────────────────────────────────────────────────────────────────┘
学习率调度¶
图注:学习率调度策略对比 - 展示了五种常用学习率调度策略:Step Decay、Exponential Decay、Cosine Annealing、ReduceLROnPlateau和Cyclical LR
图注:Step Decay学习率调度 - 每隔固定步数将学习率乘以衰减系数,形成阶梯状下降
图注:Cosine Annealing学习率调度 - 使用余弦函数平滑地降低学习率,提供更自然的衰减曲线
Text Only
┌─────────────────────────────────────────────────────────────────────┐
│ 学习率调度策略 │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ 为什么需要学习率调度? │
│ • 初期:大学习率,快速接近最优 │
│ • 后期:小学习率,精细调整 │
│ │
│ 常用策略: │
│ │
│ 1. Step Decay │
│ 每N个epoch,学习率乘以衰减系数(如0.1) │
│ lr = lr₀ × γ^(epoch // step_size) │
│ │
│ 2. Exponential Decay │
│ 学习率指数衰减 │
│ lr = lr₀ × γ^epoch │
│ │
│ 3. Cosine Annealing │
│ 余弦退火,平滑下降 │
│ lr = lr_min + 0.5*(lr_max-lr_min)*(1+cos(π·epoch/max_epoch)) │
│ │
│ 4. ReduceLROnPlateau │
│ 验证集损失不下降时,降低学习率 │
│ │
│ 5. Warmup ⭐ 重要! │
│ 前几个epoch从小学习率逐渐增大,稳定训练 │
│ 特别适用于大模型和深层网络 │
│ linear warmup: lr = lr_max × (step / warmup_steps) │
│ │
│ 推荐组合(大模型训练): │
│ Warmup + Cosine Annealing │
│ • 前5-10%步数:线性warmup │
│ • 剩余步数:余弦退火到接近0 │
│ │
└─────────────────────────────────────────────────────────────────────┘
学习率调度代码实现¶
Python
import numpy as np
import matplotlib.pyplot as plt
class LearningRateScheduler:
"""学习率调度器"""
def __init__(self, initial_lr, total_steps): # __init__构造方法,创建对象时自动调用
self.initial_lr = initial_lr
self.total_steps = total_steps
def step_decay(self, step, drop_every=10, drop_factor=0.5):
"""阶梯式衰减"""
return self.initial_lr * (drop_factor ** (step // drop_every))
def exponential_decay(self, step, decay_rate=0.95):
"""指数衰减"""
return self.initial_lr * (decay_rate ** step)
def cosine_annealing(self, step, min_lr=0):
"""余弦退火"""
return min_lr + 0.5 * (self.initial_lr - min_lr) * \
(1 + np.cos(np.pi * step / self.total_steps))
def warmup_cosine(self, step, warmup_steps=1000, min_lr=0):
"""Warmup + 余弦退火 ⭐ 推荐"""
if step < warmup_steps:
# Linear warmup
return self.initial_lr * (step / warmup_steps)
else:
# Cosine annealing
progress = (step - warmup_steps) / (self.total_steps - warmup_steps)
return min_lr + 0.5 * (self.initial_lr - min_lr) * \
(1 + np.cos(np.pi * progress))
# 可视化不同学习率调度策略
steps = np.arange(0, 1000)
scheduler = LearningRateScheduler(initial_lr=0.1, total_steps=1000)
plt.figure(figsize=(12, 8))
plt.subplot(2, 2, 1)
lr_step = [scheduler.step_decay(s) for s in steps] # 列表推导式,简洁创建列表
plt.plot(steps, lr_step)
plt.title('Step Decay')
plt.xlabel('Step')
plt.ylabel('Learning Rate')
plt.subplot(2, 2, 2)
lr_exp = [scheduler.exponential_decay(s) for s in steps]
plt.plot(steps, lr_exp)
plt.title('Exponential Decay')
plt.xlabel('Step')
plt.ylabel('Learning Rate')
plt.subplot(2, 2, 3)
lr_cos = [scheduler.cosine_annealing(s) for s in steps]
plt.plot(steps, lr_cos)
plt.title('Cosine Annealing')
plt.xlabel('Step')
plt.ylabel('Learning Rate')
plt.subplot(2, 2, 4)
lr_warmup_cos = [scheduler.warmup_cosine(s, warmup_steps=200) for s in steps]
plt.plot(steps, lr_warmup_cos)
plt.title('Warmup + Cosine (Recommended)')
plt.xlabel('Step')
plt.ylabel('Learning Rate')
plt.tight_layout()
plt.savefig('lr_schedulers.png', dpi=150)
plt.show()
print("学习率调度策略可视化完成!")
📊 优化器对比¶
| 优化器 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|
| SGD | 简单,泛化好 | 慢,需调学习率 | 大规模数据,最终优化 |
| Momentum | 加速收敛 | 需调参 | 通用 |
| Adam | 自适应,快 | 可能过拟合 | 默认选择 ⭐ |
| AdamW | 更好的权重衰减 | 稍复杂 | 推荐替代Adam |
| RMSprop | 适合非平稳目标 | 需调参 | RNN |
💡 核心要点¶
- 损失函数选择:
- 回归:MSE(默认),MAE(有异常值)
-
分类:Cross-Entropy(配合Softmax)
-
优化器选择:
- 默认用Adam
- 追求极致性能用SGD + Momentum
-
注意学习率调度
-
学习率:
- 最重要超参数
- 太大:不收敛
- 太小:收敛慢
- 典型值:0.1, 0.01, 0.001, 0.0001
🎯 下一步¶
继续学习 05-反向传播算法,理解梯度是如何计算的。





