前 言
大多数网络都是由较高的分辨率特征图开始,通过步长为2的卷积块,甚至是池化操作,来逐渐缩小特征图大小,丰富各个通道的信息,最后再通过一个全局池化,输出通道信息。于是HRNet的作者就在思考能否通过并行,来融合多个尺度特征图信息来提高网络的性能,事实上也证明了这种方法的有效。这篇SOTA的模型也常用于目标检测,姿势估计等复杂任务,且表现都十分不错
网络结构
这是论文里面的一幅图片,看上去十分清楚,整个网络有多个分支,经过一定卷积操作后,上面的分支通过下采样来缩小特征图大小,融合进下面的分支,而下面的分支则通过上采样来恢复原特征图大小,融合进上面的分支
这里上采样模块,作者使用的是最近邻元素填充的方式
代码分析
作者已经开源了该代码
https://github.com/leoxiaobin/deep-high-resolution-net.pytorch/blob/master/lib/models/pose_hrnet.py
残差块构造
网络中的卷积操作,还是以残差块的思想,所以开头两段module是残差块的构造,包含基本块和bottleneck块
class BasicBlock(nn.Module):
"""
基本块构造
"""
expansion = 1
def __init__(self, inplanes, planes, stride=1, downsample=None):
super(BasicBlock, self).__init__()
self.conv1 = conv3x3(inplanes, planes, stride)
self.bn1 = nn.BatchNorm2d(planes, momentum=BN_MOMENTUM)
self.relu = nn.ReLU(inplace=True) # 使用inplace直接代替原先计算的值,减少内存消耗
self.conv2 = conv3x3(planes, planes)
self.bn2 = nn.BatchNorm2d(planes, momentum=BN_MOMENTUM)
self.downsample = downsample
self.stride = stride
def forward(self, x):
"""
构造残差连接
:param x:
:return:
"""
residual = x
out = self.conv1(x)
out = self.bn1(out)
out = self.relu(out)
out = self.conv2(out)
out = self.bn2(out)
if self.downsample is not None:
# 如果downsample不为None,则进行下采样
residual = self.downsample(x)
out += residual
out = self.relu(out)
return out
class Bottleneck(nn.Module):
"""
残差块的bottleneck部分
"""
expansion = 4
def __init__(self, inplanes, planes, stride=1, downsample=None):
super(Bottleneck, self).__init__()
self.conv1 = nn.Conv2d(inplanes, planes, kernel_size=1, bi