paper: Strip Pooling: Rethinking Spatial Pooling for Scene Parsing
code: https://github.com/Andrew-Qibin/SPNet
Abstract
- CVPR2020的一篇文章,通过改进空间池化层来优化场景分割的任务。其出发点是,传统的标准pooling多是方形,而实际场景中会有一些物体是长条形,需要模型能够尽可能捕获一个long-range的dependencies。因此,作者引入来一种long but narrow kernel。
- 本文主要的几个关注点是:
- 引入strip pooling,即前面说的long but narrow kernel
- 在上面的基础上,构造来strip pooling module(SPM),使得该结构在现有的网络结构中能够即插即用
- 进一步将strip pooling和standard spatial pooling组合,提出mixed pooling module,即综合标准的spatial pooling和strip pooling,以兼顾各种shape的物体的分割
- 基于上面的所有改进,提出来SPNet,验证前面几点的有效性
Details
- strip pooling如何实现?
- 实际物体中有不少物体依赖long-range的dependencies,那么从pooling的角度解决这一的问题,最直接的方式应该就是把pooling操作改成long but narrow的形状,即本文说的strip pooling
- 实现上,很简单,相当于把标准的spatial pooling的kernel的宽或高置为1,然后每次取所有水平元素或垂直元素相加求平均(average pooling,按文中公式的表述,每次取的所有水平元素或垂直元素的个数分别为输入的tensor的w和h),即只是改变了spatial pooling中采样的方式,对应论文中的公式2和3
- 在作者公布的PyTorch源码中,作者是通过AdaptiveAvgPooling实现的
- PyTorch的AdaptiveAvgPooling操作,是由用户指定输入和输出的size,该操作内部会去计算对应的stride和kernel size,可以参考这里
- 利用strip pooling和spatial pooling的效果对比可以看下图
- strip pooling module
- 为了能让strip pooling实现在现有的不同网络结构中即插即用,作者设计了strip pooling module,即将strip pooling封装到该模块内部,保证对于输入特征图,经过SPM模块后,是已经执行过strip pooling操作的特征图
- 如下图:
- 对于一个输入tensor,用两个pathway分别处理水平和垂直的strip pooling,然后再expand到输入tensor的原尺寸(看下文中的代码,这个expand应该是通过上采样的插值实现的interprolate)
- 然后将两个pathway的结果相加进行融合;之后再添加一个
1x1 conv
(改变channel个数),然后加上sigmoid激活函数 - 同时SPM中有一个类似residual的identity map操作,将上面两个pathway融合后经过sigmoid的结果直接通过element-wise的乘法融合到一起。这里相当于上一步sigmoid得到的是一个权重矩阵,得到输入tensor中每个位置的特征的重要性,因此,上面2路的pathway其实是可以不用重新训练的,有点像attention机制
- mixed pooling module
- 如果因为上面的考虑将网络中的所有pooling全部换成strip pooling操作,则必然会影响原来的非长条物体的效果,就得不偿失了。因此,作者将strip pooling和pyramid pooling都加入进来,构造成mixed pooling module
- 其中,strip pooling用于解决long-range dependencies,而轻量级的pyramid pooling用于解决short-range dependencies
- 实现代码
- SPNet部分,其backbone是resnet系列
- 参考源码StripPooling部分
### 通过AdaptiveAvgPool2d实现strip pooling self.pool1 = nn.AdaptiveAvgPool2d(pool_size[0]) self.pool2 = nn.AdaptiveAvgPool2d(pool_size[1]) self.pool3 = nn.AdaptiveAvgPool2d((1, None)) self.pool4 = nn.AdaptiveAvgPool2d((None, 1)) ## SPM模块 def forward(self, x): _, _, h, w = x.size() x1 = self.conv1_1(x) x2 = self.conv1_2(x) x2_1 = self.conv2_0(x1) x2_2 = F.interpolate(self.conv2_1(self.pool1(x1)), (h, w), **self._up_kwargs) x2_3 = F.interpolate(self.conv2_2(self.pool2(x1)), (h, w), **self._up_kwargs) x2_4 = F.interpolate(self.conv2_3(self.pool3(x2)), (h, w), **self._up_kwargs) x2_5 = F.interpolate(self.conv2_4(self.pool4(x2)), (h, w), **self._up_kwargs) x1 = self.conv2_5(F.relu_(x2_1 + x2_2 + x2_3)) x2 = self.conv2_6(F.relu_(x2_5 + x2_4)) out = self.conv3(torch.cat([x1, x2], dim=1)) return F.relu_(x + out)
Conclusions
- 个人感觉strip pooling这个思路还是比较有道理,且实现也比较简单,对于交通场景中的,路障,车道线这类物体,应该是会有所帮助的。
- 再者,其中strip pooling这个思路,如果和非对称卷积结合到一起,有没有可能会进一步提升文中阐述的需要long-range dependencies的物体的效果呢?如ACNet的做法