在2D的数据中,dropout的效果并不好(图像具有空间局部依赖,在局部范围内,少量的像素特征值被drop掉,并不太影响整个模型的预测)
就是说,dropout只能随机的把多处的某一点神经元给丢掉,但是图像附近删除某一点,在其范围内,因为相似的比较多,在大范围看来,丢失的这一点对模型没什么影响,不会影响模型的预测,就像把一个小狗身上好多点,也不会影响别人知道他是一条小狗,DropBlock 不是随机地丢弃单个神经元,而是丢弃相邻的一整块神经元,就像一个小狗,可能把重要的一些一整块特征扔掉,就不太能认出来,如下图例子:
dropout的示例图:
DropBlock 示例图
DropBlock参考论文:
https://arxiv.org/abs/1810.12890
????????1.输入:
????????2.模式判断:
????????3.随机采样遮罩 M:
????????4.生成丢弃块:
????????5.应用遮罩到激活值:
????????6.特征标准化:
在上述算法中,γ(gamma)的值是通过一个计算得到的,该计算考虑了希望丢弃的特征比例以及特征图的大小和Block的大小。γ 的计算确保了实际丢弃的激活值数量期望接近于预定义的丢弃率。
通常,γ 的计算涉及以下步骤:
通过这种方式计算出的 γ,在每个激活值上应用伯努利分布进行采样,可以得到期望中大约有 p 比例的激活值被丢弃。这样的计算考虑到了Block的空间特性,使得在实际应用中,丢弃的激活值的比例与期望的丢弃率相匹配。
import torch
import torch.nn as nn
import torch.nn.functional as F
from torch import Tensor
class DropBlock2D(nn.Module):
def __init__(self, p: float = 0.1, block_size: int = 7, inplace: bool = False):
super(DropBlock2D, self).__init__()
if p < 0 or p > 1:
raise ValueError("DropBlock probability has to be between 0 and 1, "
"but got {}".format(p))
if block_size < 1:
raise ValueError("DropBlock block size必须大于0.")
if block_size % 2 != 1:
raise ValueError("当前代码实现的并不是特别完善,要求drop的区域大小必须是奇数")
self.p = p
self.inplace = inplace
self.block_size = block_size
# noinspection PyShadowingBuiltins
def forward(self, input: Tensor) -> Tensor:
if not self.training:
return input
N, C, H, W = input.size()
mask_h = H - self.block_size + 1
mask_w = W - self.block_size + 1
gamma = (self.p * H * W) / ((self.block_size ** 2) * mask_h * mask_w)
mask_shape = (N, C, mask_h, mask_w)
# bernoulli:伯努利数据产生器,取值只有两种:0或者1;底层每个点会产生一个随机数,随机数小于等于gamma的,对应位置就是1;否则就是0
mask = torch.bernoulli(torch.full(mask_shape, gamma, device=input.device))
# 在 mask 的四个边界上添加填充。这里 [self.block_size // 2] * 4 产生了一个长度为4的列表,
# 每个元素都是 self.block_size // 2 的值。在 PyTorch 的 F.pad 中,
# 这个列表定义了填充的大小,格式为 [左, 右, 上, 下]
mask = F.pad(mask, [self.block_size // 2] * 4, value=0) # 当前0表示保留,1表示删除
mask = F.max_pool2d(mask, (self.block_size, self.block_size), (1, 1), self.block_size // 2)
mask = 1.0 - mask # 最终的drop mask产生了, 0删除,1保留
# .numel() 方法用于返回一个张量(Tensor)中元素的总数
normalize_scale = mask.numel() / (1e-6 + mask.sum()) # 为了保证训练和推理的数据一致性
if self.inplace:
input.mul_(mask * normalize_scale)
else:
input = input * mask * normalize_scale
return input