nn.Dropout 是 torch.nn 中最常用的正则化组件:训练期按概率随机置零并放大存活单元,推理期自动退化为恒等映射。本篇讲清缩放为何在训练期完成、模式切换靠什么驱动,以及变体族与放置位置怎么选。

训练期完成缩放:inverted dropout

Dropout 的正则化直觉是阻止单元间共适应(co-adaptation):每一步都有输入随机消失,网络无法依赖任何单一通路。nn.Dropout(p) 的实现是 inverted dropout——缩放在训练期一次做完:

其中 是输入的任一元素; 是逐元素掩码,以 的概率取 1、 的概率取 0。除以 是在补偿被丢掉的期望质量——对掩码随机性取平均(记为 )后,,活动尺度与不 dropout 时一致。注意 的语义是置零概率:p=0.5 保留一半单元并放大 2 倍,不是”保留 0.5”。

把缩放挪进训练期的收益落在推理侧:推理路径是纯恒等映射,零额外计算,也不依赖”上线时记得乘 “这种脆弱约定。

模式切换与 F.dropout 的坑

恒等映射成立的前提,是模块知道自己处于哪个模式。nn.Dropout 靠 Module 的 training 标志驱动,由 train 和 eval 的 model.train() / model.eval() 统一翻转,前向时据此决定是否丢弃——不需要手动开关。

坑出在 functional 风格:F.dropout 不携带状态,必须显式传 training=self.training。漏传时默认 True,手写的 attention 或 functional 前向里,推理期就仍在随机丢——输出每次都不同且不报错,只能靠固定 seed 对比排查,是这类代码的高发 bug。

import torch
import torch.nn as nn
import torch.nn.functional as F
 
m = nn.Dropout(p=0.5)
x = torch.ones(1, 8)
 
m.train()
print(m(x))  # 存活单元放大到 2.0,期望尺度不变
m.eval()
print(m(x))  # 恒等映射,原样输出
 
# F.dropout 无状态:模式必须手动传,漏传则 eval 时仍在随机丢
out = F.dropout(x, p=0.5, training=m.training)

变体族与放置惯例

基础版逐元素置零,适合全连接与 embedding 输出;卷积特征图上相邻位置高度相关,丢单个元素几乎不损失信息,因此 Dropout1d/2d/3d 改为置零整个通道(channel-wise),按输入是 2D/3D/4D 各管一种。alpha_dropout 走另一条路:连同均值方差一起保持激活统计,专供 SELU 自归一化网络——这类结构不依赖 nn 归一化层 式的显式归一化,统计量全靠激活性质维持,普通 dropout 一旦破坏它就失效。

放置惯例比想象中具体:激活函数之后、残差分支出口处(保主干梯度通路干净)、nn.Embedding 与 nn.MultiheadAttention 的 dropout_p 参数内部。串行堆叠时直接嵌进 nn.Sequential 与 ModuleList 的层序列即可。

调参方向看征兆:训练损失远低于验证损失、验证指标抖动大,是过拟合信号,在 0.1–0.5 的常用区间内上调 p;反之欠拟合就下调或撤掉个别层。