Dropout 怎么做?为什么要乘 1/(1−p)?
train 和 eval 有什么区别?
先弄清楚:每个概念是什么
- Co-adaptation
- neuron 之间互相依赖,A 只有和 B 一起出现才有用。这往往是在记训练集的噪声,也就是 overfitting。
- Dropout
- 训练时每一步随机把一部分 neuron 的输出置 0,谁都不能指望别人在场。每种丢法是一个共享 weight 的子网络,所以也可以看成 \(2^n\) 个子网络的 ensemble。
- Mask
- 和 activation 同形状的 0/1 随机矩阵,1 = 保留(概率 \(1-p\))。每个 batch、每个样本都重新抽。
- Inverted dropout
- 保留下来的值放大 \(1/(1-p)\),把丢掉的那部分补回来,让期望不变。
- train / eval mode
- 训练时随机丢、放大,推理时 dropout 层什么都不做。PyTorch 里用
model.train()/model.eval()切换。
30 秒回答
Dropout is a regularizer. During training, each unit is zeroed independently with probability p, so the network can't rely on any particular unit and units can't co-adapt. It's like training an ensemble of exponentially many sub-networks that share weights. With inverted dropout we divide the kept units by 1 − p during training, so the expected activation is unchanged; at inference we turn dropout off and use the full network as is, which approximates averaging the ensemble. The backward pass reuses the same mask.
推导 / 关键公式
为什么乘 \(1/(1-p)\)
\[ m_i \sim \mathrm{Bernoulli}(1-p),\qquad \tilde h_i = \frac{m_i\, h_i}{1-p} \] \[ \mathbb{E}[\tilde h_i] = \frac{(1-p)\,h_i}{1-p} = h_i \]期望和不加 dropout 时一样,所以推理时不用再缩放。代价是多了 noise:
\[ \mathrm{Var}[\tilde h_i] = \frac{p}{1-p}\,h_i^2 \]backward
\[ \frac{\partial L}{\partial h_i} = \frac{m_i}{1-p}\cdot\frac{\partial L}{\partial \tilde h_i} \]用 forward 时的同一个 mask,所以被丢掉的位置 gradient 也是 0。
原论文的写法
原论文的 \(p\) 是保留概率,PyTorch 的是丢弃概率。原论文训练时不缩放,test 时 \(W_{\text{test}} = pW\),期望相同。inverted 把缩放挪到训练时,推理代码不用改,所以框架都这么实现。
手算例子
\(h = (1, 2, 3, 4)\),\(p = 0.5\),抽到 mask \((1, 0, 1, 0)\):
- train:\((1, 0, 3, 0) \div 0.5 = (2, 0, 6, 0)\),和是 8;对所有 mask 取期望,和是 10
- eval:原样 \((1, 2, 3, 4)\),和是 10,和训练时的期望对得上
- 训练时不除以 \(1-p\):平均只有 5,推理时却是 10,下一层的输入尺度对不上
手撕代码(NumPy)
def dropout_forward(x, p, training, rng):
# p = 丢弃概率(和 PyTorch 一致);返回输出和 mask
if not training or p == 0:
return x, None
if p == 1:
return np.zeros_like(x), np.zeros_like(x)
# 每个元素以概率 1-p 保留,保留的放大 1/(1-p)
mask = (rng.random(x.shape) >= p) / (1 - p)
return x * mask, mask
def dropout_backward(dout, mask):
# 用同一个 mask:被丢掉的位置 gradient 也是 0
return dout if mask is None else dout * mask
必答点
- train:每个 unit 以概率 \(p\) 置 0,保留的乘 \(1/(1-p)\)(inverted dropout)
- 为什么乘:让 \(\mathbb{E}[\tilde h] = h\),train 和 eval 的期望一致
- eval:关掉 dropout,不做任何缩放;记得切 eval mode
- 为什么有效:防 co-adaptation;近似 \(2^n\) 个共享 weight 的子网络的 ensemble
- backward 用同一个 mask
追问
p 取多少?
原论文 hidden layer 保留概率 0.5–0.8(即丢 0.2–0.5),实数输入保留 0.8 左右。Transformer base 和 BERT 都用丢弃率 0.1。
dropout 放在哪?
MLP 放在 hidden layer 的 activation 之后。Transformer 放在每个 sub-layer 输出加 residual 之前,以及 embedding + positional encoding 之后。CNN 相邻 pixel 强相关,改用 SpatialDropout 整个 channel 一起丢。
和 L2 regularization 有什么区别?
L2 直接惩罚 weight 的大小,dropout 往 activation 里乘随机 noise。只有在 linear regression 上,dropout 的期望 loss 等于加了一个按 feature 尺度加权的 L2:\(\|y - (1-p)Xw\|^2 + p(1-p)\|\Gamma w\|^2\),\(\Gamma = \operatorname{diag}(X^\top X)^{1/2}\)。深层网络上两者不等价。
和 BatchNorm 一起用有什么问题?
dropout 让 unit 的 variance 在 train 和 eval 之间不一样,BN 在 eval 用的 running variance 就对不上,叫 variance shift。所以常见做法是只在最后一个 BN 之后用 dropout。
来源Srivastava et al., JMLR 2014 §1(pp.1930–1931)、§4(pp.1933–1934)、§9.1(p.1950)、Appendix A.4(p.1953)· Goodfellow et al., Deep Learning §7.12(pp.255–265)· Murphy, PML1 §13.5.4(pp.455–457)· Dive into Deep Learning §5.6 eq. 5.6.1 · Vaswani et al. 2017 §5.4(p.8)· Li et al. 2018(variance shift)· Tompson et al. 2015 §3.2(p.3,SpatialDropout)