深度学习100问
Author:louwill
Machine Learning Lab
所谓深监督(Deep Supervision),就是在深度神经网络的某些中间隐藏层加了一个辅助的分类器作为一种网络分支来对主干网络进行监督的技巧,用来解决深度神经网络训练梯度消失和收敛速度过慢等问题。
深监督作为一个训练trick在2014年就已经通过DSN(Deeply-Supervised Nets)提出来了。具体参见DSN论文。
DSN的一个缺点在于使用的网络结构不够深,且辅助的分类器为传统的SVM模型。2015年的一篇Training Deeper Convolutional Networks with Deep Supervision的论文尝试了在更深层结构的网络中使用深监督技巧。
深监督的结构
通常而言,增加神经网络的深度可以一定程度上提高网络的表征能力,但随着深度加深,会逐渐出现神经网络难以训练的情况,其中就包括像梯度消失和梯度爆炸等现象。为了更好的训练深度网络,我们可以尝试给神经网络的某些层添加一些辅助的分支分类器来解决这个问题。这种辅助的分支分类器能够起到一种判断隐藏层特征图质量好坏的作用。
既然明确了要通过深监督来解决深度网络难以训练的问题,那么这个作为监督分支结构应该加在神经网络的什么位置?论文作者根据一些经验法则和实验给出了结论。作者先是把深监督放在网络最后一层,然后跑10-50次迭代,绘制出中间层的平均梯度值。最后作者将监督分支添加在平均梯度消失(原文中平均梯度小于10_(-7))的那一层。随迭代次数变化的各卷积层的平均梯度值如下图所示。可以看到,Conv1-4层在迭代次数增加时平均梯度值都要小于10_(-7)。
带有深监督的一个8层深度卷积网络结构如下图所示。
带有深监督的一个13层深度卷积网络结构如下图所示。
其中各个模块含义如下:
可以看到,图中在第四个卷积块之后添加了一个监督分类器作为分支。Conv4输出的特征图除了随着主网络进入Conv5之外,也作为输入进入了分支分类器。如图所示,该分支分类器包括一个卷积块、两个带有Dropout和ReLu的全连接块和一个纯全连接块。
损失函数
以W和Ws分别表示主干网络和深监督分支的权重,则有:
输出层softmax表示为:
主干网络的损失函数为:
深监督分支的softmax输出表示为:
深监督分支的损失函数为:
可以看到深监督分支的损失函数取决于W,而不是Ws,因为分支结构中倒数第二层的S8特征图关联到主干网络的卷积权重W1-4。
所以,联合损失函数可以表示为:
其中α_t可以表示为随训练epoch t衰减的一个值:
看到这个联合损失函数是不是有种正则化的味道,实际也正是如此,辅助loss能够起到一种类似正则化的效果。可以看到,Conv4在加深监督和不加深监督的平均梯度差异。如下图所示:
Torch示例
下面以Torch为例实现一个带深度监督的卷积模块。先定义卷积块:
import torch.nn as nn
# 定义卷积块
# 包含3x3卷积+BN+relu
def conv3x3_bn_relu(in_planes, out_planes, stride=1):
"3x3 convolution + BN + relu"
return nn.Sequential(
nn.Conv2d(in_planes, out_planes, kernel_size=3,
stride=stride, padding=1, bias=False),
BatchNorm2d(out_planes),
nn.ReLU(inplace=True),
)
带有深监督的卷积模块如下:
class C1DeepSup(nn.Module):
def __init__(self, num_class=150, fc_dim=2048, use_softmax=False):
super(C1DeepSup, self).__init__()
self.use_softmax = use_softmax
self.cbr = conv3x3_bn_relu(fc_dim, fc_dim // 4, 1)
self.cbr_deepsup = conv3x3_bn_relu(fc_dim // 2, fc_dim // 4, 1)
# 最后一层卷积
self.conv_last = nn.Conv2d(fc_dim // 4, num_class, 1, 1, 0)
self.conv_last_deepsup = nn.Conv2d(fc_dim // 4, num_class, 1, 1, 0)
# 前向计算流程
def forward(self, conv_out, segSize=None):
conv5 = conv_out[-1]
x = self.cbr(conv5)
x = self.conv_last(x)
if self.use_softmax: # is True during inference
x = nn.functional.interpolate(
x, size=segSize, mode='bilinear', align_corners=False)
x = nn.functional.softmax(x, dim=1)
return x
# 深监督模块
conv4 = conv_out[-2]
_ = self.cbr_deepsup(conv4)
_ = self.conv_last_deepsup(_)
# 主干卷积网络softmax输出
x = nn.functional.log_softmax(x, dim=1)
# 深监督分支网络softmax输出
_ = nn.functional.log_softmax(_, dim=1)
return (x, _)
在U-Net等经典语义分割网络中,使用深监督技巧是一个通用做法。以上就是本文内容。
参考资料:
Deeply-Supervised Nets
Training Deeper Convolutional Networks with Deep Supervision
往期精彩:
深度学习100问-14:图像语义分割有哪些经典的上采样方法?
深度学习100问-13:深度学习如何制作个人数据集?
深度学习100问-12:深度学习有哪些经典数据集?
深度学习100问-11:什么是学习率衰减?
深度学习100问-10:如何部署一个轻量级的深度学习项目?
深度学习100问-9:为什么EfficientNet号称是最好的分类网络?
深度学习100问-8:什么是Batch Normalization?
深度学习100问-7:dropout有哪些细节问题?
深度学习100问-6:有哪些经典的卷积类型?
深度学习100问-5:如何阅读一份深度学习项目代码?
深度学习100问-4:深度学习应遵循怎样的论文研读路线?
深度学习100问-3:深度学习应掌握哪些Linux开发技术?
深度学习100问-2:深度学习应掌握哪些Git开发技术?
深度学习100问-1:深度学习环境配置有哪些坑?
一个算法工程师的成长之路
长按二维码.关注机器学习实验室