Dropout 怎么做?为什么要乘 1/(1−p)?
train 和 eval 有什么区别?

英文问法What is dropout and why does it work? · Why do we scale by 1/(1−p)? · What changes at inference time? · Implement a dropout layer.

考过★ 作者本人微软 AS 电面 · 面经:微软 D&AS Bing Ads 2022(店面在编辑器里实现 dropout)· NVIDIA 框架岗 2023 onsite 第 2 轮(知乎)

先弄清楚:每个概念是什么

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)\):

手撕代码(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

必答点

追问

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)