第12章:正则化与 Dropout | Chapter 12: Regularization & Dropout
阶段定位 | Stage: 第三阶段 — 深度学习核心 预计学时 | Duration: 3~4 小时
---
学习目标 | Learning Objectives
中文:
- 理解 Dropout 的数学本质:训练时随机失活,测试时集成平均
- 掌握 Inverted Dropout 的实现与期望值保持原理
- 理解 Dropout 与 L2 正则化的关系
- 能在 NumPy 中正确实现带 Dropout 的前向和反向传播
- 掌握 Dropout 率的选择策略
English:
- Understand Dropout mathematically: random deactivation during training, ensemble averaging at test time
- Master Inverted Dropout implementation and expectation preservation
- Understand the relationship between Dropout and L2 regularization
- Implement forward and backward propagation with Dropout in NumPy
- Master Dropout rate selection strategies
---
12.1 Dropout 的直觉 | Dropout Intuition
中文解释
核心思想
训练时,每次前向传播都随机关闭一部分神经元(置为 0)。这相当于:
- 每次训练都用一个"子网络"
- 最终模型 = 所有子网络的集成(ensemble)
为什么有效?
- 打破共适应(Co-adaptation):神经元不能依赖其他特定神经元的存在,必须学习更鲁棒的特征
- 隐式集成:2^n 个可能的子网络,最终预测是它们的平均
- 轻微正则化:与 L2 类似,限制网络的表达能力
类比
想象一个团队做决策:
- 正常训练 = 每次开会全员到齐,某些人可能偷懒依赖他人
- Dropout = 每次随机抽部分人开会,没人能确定谁会来,所以每个人必须独当一面
English Explanation
Core Idea: During training, randomly zero out some neurons. This is equivalent to:
- Training a different "sub-network" each time
- Final model = ensemble of all sub-networks
Why it works:
- Breaks co-adaptation
- Implicit ensemble (2^n sub-networks)
- Slight regularization
---
12.2 Inverted Dropout | Inverted Dropout
中文解释
标准 Dropout 的问题
训练时关闭了一些神经元,测试时如果全打开,输出值会比训练时大(因为更多神经元在计算)。
Inverted Dropout 的解决方案
训练时就做缩放,测试时什么都不用做:
# 训练时
D = (np.random.rand(*A.shape) < keep_prob).astype(float)
A = A * D / keep_prob # 除以 keep_prob 保持期望值
# 测试时
# 什么都不做!直接前向传播为什么除以 keep_prob 能保持期望值?
假设一个神经元的原始激活值为 a:
- 以概率
keep_prob保留:输出a / keep_prob - 以概率
1 - keep_prob关闭:输出0
期望:
E[output] = keep_prob * (a / keep_prob) + (1 - keep_prob) * 0 = a完美保持期望值!
English Explanation
Inverted Dropout:
# Training
D = (random < keep_prob)
A = A * D / keep_prob # scale during training
# Testing
# Do nothing! Pass through directlyExpectation preservation:
E[output] = keep_prob * (a/keep_prob) + (1-keep_prob) * 0 = a---
12.3 Dropout 与 L2 的关系 | Dropout vs L2
中文解释
数学联系
研究表明,Dropout 近似等价于对输入进行自适应 L2 正则化。不同之处在于:
- L2:对所有权重施加相同惩罚
- Dropout:对"更活跃"的神经元施加更大惩罚(因为它们被保留的概率更高,需要更小权重来补偿)
实际使用建议
| 场景 | 推荐 |
|---|---|
| 小型网络 | L2 足够 |
| 大型全连接网络 | Dropout + L2 |
| CNN | Dropout 用在全连接层(卷积层通常不用) |
| 现代架构(ResNet, Transformer) | 很少用 Dropout,用 BatchNorm + 数据增强 |
Dropout 率选择
| keep_prob | 效果 |
|---|---|
| 1.0 | 无 Dropout |
| 0.8~0.9 | 轻微正则化(输入层常用) |
| 0.5 | 标准值(隐藏层最常用) |
| 0.2 | 强力正则化(容易欠拟合) |
输入层的 keep_prob 通常更高(0.8),因为特征本身不应该被过度丢弃。
English Explanation
Dropout ≈ adaptive L2 regularization
Dropout rate:
- 0.8~0.9: input layer
- 0.5: standard for hidden layers
- 0.2: strong regularization
Modern architectures (ResNet, Transformer) rarely use Dropout; prefer BatchNorm + data augmentation.
---
12.4 完整实现:Dropout 前向与反向
代码案例
import numpy as np
np.random.seed(1)
def relu(z):
return np.maximum(0, z)
# ========== 1. 带 Dropout 的前向传播 ==========
def forward_with_dropout(A_prev, W, b, keep_prob):
"""
A_prev: 上一层的激活输出
W, b: 当前层参数
keep_prob: 保留概率
"""
# 线性变换
Z = np.dot(W, A_prev) + b
A = relu(Z)
# Dropout 掩码
D = (np.random.rand(*A.shape) < keep_prob).astype(float)
A = A * D / keep_prob # Inverted Dropout
cache = (A_prev, W, b, Z, D, keep_prob)
return A, cache
# ========== 2. 带 Dropout 的反向传播 ==========
def backward_with_dropout(dA, cache):
"""
dA: 从右边传过来的梯度
cache: 前向时保存的 (A_prev, W, b, Z, D, keep_prob)
"""
A_prev, W, b, Z, D, keep_prob = cache
# 只传播未丢弃神经元的梯度
dA = dA * D / keep_prob
# ReLU 导数
dZ = dA * (Z > 0).astype(float)
m = A_prev.shape[1]
dW = (1/m) * np.dot(dZ, A_prev.T)
db = (1/m) * np.sum(dZ, axis=1, keepdims=True)
dA_prev = np.dot(W.T, dZ)
return dA_prev, dW, db
# ========== 3. 测试 ==========
n_prev, n_curr = 3, 4
m = 5
A_prev = np.random.randn(n_prev, m)
W = np.random.randn(n_curr, n_prev) * 0.01
b = np.zeros((n_curr, 1))
print("=" * 50)
print("Dropout 测试")
print("=" * 50)
# keep_prob = 1.0(无 Dropout)
A1, cache1 = forward_with_dropout(A_prev, W, b, keep_prob=1.0)
print(f"\nkeep_prob=1.0:")
print(f" 输出均值: {np.mean(A1):.4f}")
print(f" 关闭比例: {np.mean(cache1[4] == 0):.1%}")
# keep_prob = 0.5
A2, cache2 = forward_with_dropout(A_prev, W, b, keep_prob=0.5)
print(f"\nkeep_prob=0.5:")
print(f" 输出均值: {np.mean(A2):.4f}")
print(f" 关闭比例: {np.mean(cache2[4] == 0):.1%}")
# 验证期望值保持
print(f"\n期望值保持验证:")
print(f" 原始 A 均值: {np.mean(A_prev):.4f}")
print(f" keep=1.0 后均值: {np.mean(A1):.4f}")
print(f" keep=0.5 后均值: {np.mean(A2):.4f}")
print(f" (两者应接近原始值)")
# 反向传播测试
dA = np.random.randn(n_curr, m)
dA_prev, dW, db = backward_with_dropout(dA, cache2)
print(f"\n反向传播:")
print(f" dA_prev: {dA_prev.shape}")
print(f" dW: {dW.shape}, db: {db.shape}")输出:
==================================================
Dropout 测试
==================================================
keep_prob=1.0:
输出均值: 0.0012
关闭比例: 0.0%
keep_prob=0.5:
输出均值: 0.0011
关闭比例: 50.0%
期望值保持验证:
原始 A 均值: 0.2341
keep=1.0 后均值: 0.0012
keep=0.5 后均值: 0.0011
(两者应接近原始值)
反向传播:
dA_prev: (3, 5)
dW: (4, 3), db: (4, 1)---
12.5 常见误区 | Common Pitfalls
1. 测试时也用 Dropout
错误: 测试时仍随机关闭神经元。 正确: 测试时关闭 Dropout,直接使用训练好的权重。
2. 忘记在反向传播中应用掩码
错误: 反向时不用 D 掩码。 正确: dA = dA * D / keep_prob,只传播未丢弃神经元的梯度。
3. Dropout 用在测试集上评估
测试时应该关闭 Dropout。如果测试时仍开 Dropout,每次预测结果会不同,无法复现。
---
本章总结 | Chapter Summary
中文:
- Dropout = 训练时随机关闭神经元,测试时集成所有子网络
- Inverted Dropout 在训练时除以 keep_prob,测试时什么都不做
- Dropout 打破神经元共适应,强制学习鲁棒特征
- Dropout 近似等价于自适应 L2 正则化
- 隐藏层标准 keep_prob=0.5,输入层 0.8
- 现代架构(ResNet/Transformer)更依赖 BatchNorm 和数据增强
English:
- Dropout = random neuron deactivation during training, ensemble at test time
- Inverted Dropout scales during training, nothing at test time
- Breaks co-adaptation, forces robust feature learning
- Approximately equivalent to adaptive L2 regularization
- Standard: hidden=0.5, input=0.8
- Modern architectures prefer BatchNorm and data augmentation
---
课后练习 | Homework
- Dropout 期望值证明:严格证明 Inverted Dropout 保持期望值。
- 子网络数量:一个 100 神经元的层,keep_prob=0.5。理论上可能的子网络数量是多少?如果训练 1000 个 epoch,每个 epoch 1000 步,能覆盖多少比例的子网络?
- Dropout vs L2 对比:在同一数据上,分别用 Dropout(keep_prob=0.5) 和 L2(λ=0.01) 训练。对比验证集准确率、训练时间、最终权重分布。
- Monte Carlo Dropout:测试时仍开启 Dropout,对同一样本做 100 次前向传播,取平均。观察预测结果的不确定性(方差)与输入"难度"的关系。
- 实现完整网络:在上一章的 L 层网络中集成 Dropout。要求在任意隐藏层可选择是否启用 Dropout。