• PyTorch 卷积网络正则化 DropBlock


    论文地址:https://arxiv.org/pdf/1810.12890.pdf

    论文概要

    DropBlock 是一种类似于 dropout 的简单方法,它与 dropout 的主要区别在于,它从层的特征图中抹除连续区域,而不是抹除独立的随机单元

    类似地,DropBlock 通过随机地置零网络的响应,实现了通道之间的解耦,缓解了网络的过拟合现象

    这个算法的伪代码如下:

    • x:特征图,shape 为 [bs, ch, h, w]
    • block_size:抹除连续区域的尺寸
    • γ:伯努利分布的均值,用于选中抹除区域的中心点
    • trainning:布尔类型,表明是 train 模式还是 eval 模式
    1. def DropBlock(x, block_size, γ, trainning):
    2. if trainning:
    3. # 选中要抹除区域的中心点
    4. del_mask = bernoulli(x, γ)
    5. # 抹除相应的区域
    6. x = set_zero(x, del_mask, block_size)
    7. # 特征图标准化
    8. keep_mask = 1 - del_mask
    9. x *= count(x) / count_1(keep_mask)
    10. return x
    11. # eval 模式下没有任何行为
    12. return x

    但是在具体实现的过程中,还有很多需要补充的细节

    γ 的确定是通过 keep_prob 参数确定的,keep_prob 表示激活单元 (即输出大于 0) 被保留的概率,feat_size 为特征图的尺寸:

    \gamma = \frac{1-keep\_prob}{block\_size^2}\cdot \frac{feat\_size^2}{(feat\_size-block\_size+1)^2}

    因为在训练刚开始时,较小的 keep_prob 会影响网络的收敛,所以令 keep_prob 从 1.0 渐渐降为 0.9

    从实验结果可以看到,ResNet-50 在使用了 DropBlock 后在验证集上的准确率有一定的提升

     以下是不同的 DropBlock 追加位置、不同的处理方法、不同 block_size 对验证集准确率的影响:

    • 按行:DropBlock 追加在 ResNet-50 的第 4 组卷积后;DropBlock 追加在 ResNet-50 的第3、第 4 组卷积后
    • 按列:只在卷积分支上追加;在卷积分支、残差连接分支上追加;在卷积分支、残差连接分支上追加,并使用 keep_prob 衰减的方法

    在论文中,最优的超参数是 block_size = 7, keep_prob = 0.9,但实际使用时仍需要根据 Loss 的变化情况做出调整

    DropBlock 复现

    在实现 DropBlock 时,有以下几个细节:

    • keep_prob 是动态变化的,令每次 eval 时进行更新
    • 抹除区域的中心点是在激活单元中选择的 (即输出大于 0),令 1 表示被选中,使用 max_pool2d 可以实现连续区域的选中,以生成 del_mask
    • 标准化系数 = 原图面积 / 保留区域面积
    1. class DropBlock(nn.Module):
    2. ''' block_size: 抹除区域的尺寸
    3. keep_prob_init: keep_prob 的初始值
    4. keep_prob_tar: keep_prob 的目标值
    5. keep_prob_decay: keep_prob 的衰减速度'''
    6. def __init__(self, block_size=7, keep_prob_init=1.,
    7. keep_prob_tar=0.9, keep_prob_decay=1e-2):
    8. super(DropBlock, self).__init__()
    9. self.block_size = block_size
    10. assert self.block_size & 1, 'block_size 需为奇数'
    11. # keep_prob 相关参数
    12. self.keep_prob = keep_prob_init
    13. self._keep_prob_tar = keep_prob_tar
    14. self._keep_prob_decay = keep_prob_decay
    15. # 伯努利分布的均值
    16. self.gamma = None
    17. def forward(self, x):
    18. # 训练模式下
    19. if self.training:
    20. *bs_ch, height, width = x.shape
    21. square = height * width
    22. # 当 γ 为空时设置
    23. if self.gamma is None:
    24. self.gamma = (1 - self.keep_prob) * square / self.block_size ** 2
    25. for f_size in (height, width):
    26. self.gamma /= f_size - self.block_size + 1
    27. # 在激活区域中, 选择抹除区域的中心点
    28. del_mask = torch.bernoulli((x > 0) * self.gamma)
    29. keep_mask = 1 - torch.max_pool2d(
    30. del_mask, kernel_size=self.block_size,
    31. stride=1, padding=self.block_size // 2
    32. )
    33. # 特征图标准化
    34. gain = square / keep_mask.view(*bs_ch, -1).sum(2).view(*bs_ch, 1, 1)
    35. return keep_mask * gain * x
    36. # 验证模式下, 更新参数
    37. self.keep_prob = max([
    38. self._keep_prob_tar,
    39. self.keep_prob * (1 - self._keep_prob_decay)
    40. ])
    41. self.gamma = None
    42. return x

    代码测试

    1. # 利用灰度图, 将亮度低的像素置为 0
    2. image = cv.imread('YouXiZi.jpg')
    3. mask = cv.cvtColor(image, cv.COLOR_BGR2GRAY) > 100
    4. for i in range(3):
    5. image[..., i] *= mask
    6. cv.imshow('debug', image)
    7. cv.waitKey(0)
    8. # 转化为 tensor, 使用 DropBlock
    9. tensor = tf.ToTensor()(image)
    10. db = DropBlock(block_size=31, keep_prob_init=0.9)
    11. image = db(tensor.unsqueeze(0))[0]
    12. image = image.permute(1, 2, 0).data.numpy()
    13. cv.imshow('debug', image)
    14. cv.waitKey(0)

    利用灰度图将亮度暗的像素置零,亮区即为激活单元 

    抹除区域的中心点均出现在亮区内,而且图像的亮度相较于原图有一定提升 (标准化系数 = 原图面积 / 保留区域面积 > 1)

  • 相关阅读:
    微信小程序接入NFC,使用HCE模拟主机卡完成NFC刷卡发送消息
    初探富文本之CRDT协同算法
    ES6对象
    MindSpore使用Summary收集数据(MindInsight)警告已有相同的值
    2、shell文本处理工具
    Istio微服务治理网格流量管理核心资源控制器详解
    十二张图:从0开始理解对称/非对称加密、CA认证、以及K8S各组件颁发证书原由
    Go net http包
    pytest教程-13-conftest.py文件
    3、TypeScript高级数据类型
  • 原文地址:https://blog.csdn.net/qq_55745968/article/details/125568203