为了防止训练过拟合,在训练过程中随机丢弃一部分输出(经常有人说随机丢弃掉一些神经元,感觉不是很准确)
左图不使用dropout
对于第 l l l层的输出 y i l {y}_{i}^{l} yil,直接进行线性变化,然后经过激活函数,得到第 l + 1 l+1 l+1层的输出 y i l + 1 {y}_{i}^{l+1} yil+1
z i ( l + 1 ) = w i ( l + 1 ) y i l + b i ( l + 1 ) , y i ( l + 1 ) = f ( z i ( l + 1 ) ) , \begin{aligned} z_{i}^{(l+1)} &=\mathbf{w}_{i}^{(l+1)} \mathbf{y}_{i}^{l}+b_{i}^{(l+1)}, \\ y_{i}^{(l+1)} &=f\left(z_{i}^{(l+1)}\right), \end{aligned} zi(l+1)yi(l+1)=wi(l+1)yil+bi(l+1),=f(zi(l+1)),
右图使用dropout
先按照伯努利分布,概率为 p p p采样,得到 r i ( l ) r_{i}^{(l)} ri(l), 与第 l l l层的输出 y i l {y}_{i}^{l} yil维度相同。两者对应位置相乘,对 y i l {y}_{i}^{l} yil中的部分位置置0,得到dropout后的 y ~ i l \widetilde{\mathbf{y}}_{i}^{l} y il. 最后进行线性变化,经过激活函数,得到第 l + 1 l+1 l+1层的输出 y i l + 1 {y}_{i}^{l+1} yil+1
r i ( l ) ∼ Bernoulli ( p ) y ~ i ( l ) = r i ( l ) ∗ y i ( l ) z i ( l + 1 ) = w i ( l + 1 ) y ~ i l + b i ( l + 1 ) y i ( l + 1 ) = f ( z i ( l + 1 ) ) \begin{aligned} r_{i}^{(l)} & \sim \operatorname{Bernoulli}(p) \\ \widetilde{\mathbf{y}}_{i}^{(l)} &=\mathbf{r}_{i}^{(l)} * \mathbf{y}_{i}^{(l)} \\ z_{i}^{(l+1)} &=\mathbf{w}_{i}^{(l+1)} \widetilde{\mathbf{y}}_{i}^{l}+b_{i}^{(l+1)} \\ y_{i}^{(l+1)} &=f\left(z_{i}^{(l+1)}\right) \end{aligned} ri(l)y i(l)zi(l+1)yi(l+1)∼Bernoulli(p)=ri(l)∗yi(l)=wi(l+1)y il+bi(l+1)=f(zi(l+1))
实现代码
import numpy as np
def dropout(x, prob):
if prob < 0. or prob >= 1: # prob概率值,在0~1之间
raise Exception('Dropout prob must be in interval [0, 1[.')
retain_prob = 1. - prob
# 通过binomial函数,生成与x一样的维数向量
sample = np.random.binomial(n=1, p=retain_prob, size=x.shape)
# 二项分布,当n=1时,即为伯努利分布
# 即将生成一个0、1分布的向量,0表示丢掉对应位置x的数值
print(sample)
x *= sample
print(x)
x /= retain_prob
# infer时不进行dropout,从而保持输出的结果稳定
return x
x = np.array([1, 2, 3, 4, 5, 6, 7, 8, 9, 10], dtype=np.float32)
y = dropout(x, 0.4)
print(y)
个人理解
其中,代码第14行 x /= retain_prob
是对原始的x进行rescale,那么为什么要进行 rescale呢?可能是为了保持训练和测试的时候网络输出的期望一致
假设丢弃概率是p, 原始论文中的dropout(vanilla版本)在测试时做rescale
- 训练时,使用dropout,对于网络中的某个输出值x,期望变为
(1-p)x+p0=(1-p)x
- 为了保持测试过程(没有dropout)期望一致,在测试时,对于相应层输出乘以
1-p
inverted 版本,在训练时做rescale
- 训练时,使用dropout,对于网络中的某个输出值x,期望变为
(1-p)x+p0=(1-p)x
- 为了保持测试过程(没有dropout)期望一致,在训练时, 对包含dropout的输出乘
1/(1-p)
, 使得期望仍然为1/(1-p)*(1-p)x = x
,即代码中所使用的方法。从而保证测试时不需要对代码进行修改
参考
https://blog.csdn.net/hjimce/article/details/50413257