FPN

为了增强语义性,传统的物体检测模型通常只要在深度卷积网络的最后一个特诊图上进行后续操作,而这一层对应的下采样率(图像缩小的倍数)通常又比较大,如16、32,造成小物体在特征图上的有效信息较少,小物体的检测性能会急剧下降,这个问题也被称为多尺度问题。
解决多尺度问题的关键在于如何提取多尺度的特征。传统的方法有图像金字塔(Image Pyramid),主要思路是将输入图片做成多个尺度,不同尺寸的图像生成不同尺寸的特征,这种方法简单有效,大量使用在了COCO竞赛上,但缺点是非常耗时,计算量也很大
卷积神经网络不同层的大小与语义信息不同,本身就类似一个金字塔结构,2017年的FPN(Fearure Pyramid Network)方法融合了不同层的特征,较好地改善了多尺度问题
FPN总体框架如下图所示,主要包含自下而上网络、自上而下网络、横向连接与卷积融合4个部分

  • 自下而上:最左侧为普通的卷积网络,默认使用ResNet结构,用作语义信息。C1代表了ResNet的前几个卷积与池化层,而C2至C5分别为不同的ResNet卷积组,这些卷积组包含了多个Bottleneck结构,组内的特征图大小相同,组间大小递减
  • 自上而下:首先对C5进行1×1卷积降低通道数得到P5,然后依次进行上采样得到P4、P3和P2,目的是得到与C4、C3与C2长宽相同的特征,以方便下一步进行逐元素相加。这里采用2倍最近邻上采样,即直接对临近元素进行复制,而非线性插值
  • 横向连接(Lateral Connection):目的是为了将上采样后的高语义特征与浅层的定位细节特征进行融合。高语义特征经过上采样后,其长宽对应的浅层特征相同,而通道数固定为256,因此需要对底层特征C2至C4进行1×1卷积使得其通道数变为256,然后两者进行逐元素相加得到P4、P3与P2。由于C1的特征图尺寸较大且语义信息不足,因此没有把C1放到横向连接中
  • 卷积融合:在得到相加后的特征后,利用3×3卷积对生成的P2至P4再进行融合,目的是消除上采样过程带来的重叠效应,以生成最终的特征图

对于实际的物体检测算法,需要在特征图上进行RoI(Region of Interests,感兴趣区域)提取,而FPN有4个输出的特征图,选择哪一个特征图上的特征也是个问题。FPN给出的解决方法是,对于不同大小的RoI,使用不同的特征图,大尺度的RoI在深层的特征图上进行提取,如P5,小尺度的RoI在浅层的特征图上进行提取,如P2,具体方法,可自行查看
FPN将深层的语义信息传到底层,来补充浅层的语义信息,从而获得了高分辨率、强语义的特征,在小物体检测、实例分割等领域有着非常不俗的表现

使用PyTorch搭建一个完整的FPN网络:

import torch.nn as nn
import torch.nn.functional as F
import math#ResNet基本Bottleneck类
class Bottleneck(nn.Module):expansion = 4    #通道倍增数def __init__(self, in_planes, planes, stride=1, downsample=None):super(Bottleneck, self).__init__()self.bottleneck = nn.Sequential(nn.Conv2d(in_planes, planes, 1, bias=False),nn.BatchNorm2d(planes),nn.ReLU(inplace=True),nn.Conv2d(planes, planes, 3, stride, 1, bias=False),nn.BatchNorm2d(planes),nn.ReLU(inplace=True),nn.Conv2d(planes, self.expansion * planes, 1, bias=False),nn.BatchNorm2d(self.expansion * planes),)self.relu = nn.ReLU(inplace=True)self.downsample = downsampledef forward(self, x):identity = xout = self.bottleneck(x)if self.downsample is not None:identity = self.downsample(x)out += identityout = self.relu(out)return out#FPN类,初始化需要一个list,代表ResNet每一个阶段的Bottleneck的数量
class FPN(nn.Module):def __init__(self, layers):super(FPN, self).__init__()self.inplanes = 64#处理输入的C1模块self.conv1 = nn.Conv2d(3, 64, 7, 2, 3, bias=False)self.bn1 = nn.BatchNorm2d(64)self.relu = nn.ReLU(inplace=True)self.maxpool = nn.MaxPool2d(3, 2, 1)#搭建自下而上的C2、C3、C4、C5self.layer1 = self._make_layer(64, layers[0])self.layer2 = self._make_layer(128, layers[1], 2)self.layer3 = self._make_layer(256, layers[2], 2)self.layer4 = self._make_layer(512, layers[3], 2)#对C5减少通道数,得到P5self.toplayer = nn.Conv2d(2048, 256, 1, 1, 0) #3×3卷积融合特征self.smooth1 = nn.Conv2d(256, 256, 3, 1, 1)self.smooth2 = nn.Conv2d(256, 256, 3, 1, 1)self.smooth3 = nn.Conv2d(256, 256, 3, 1, 1)#横向连接,保证通道数相同self.latlayer1 = nn.Conv2d(1024, 256, 1, 1, 0)self.latlayer2 = nn.Conv2d( 512, 256, 1, 1, 0)self.latlayer3 = nn.Conv2d( 256, 256, 1, 1, 0)#构建C2到C5,注意区分stride值为1和2的情况def _make_layer(self, planes, blocks, stride=1):downsample  = Noneif stride != 1 or self.inplanes != Bottleneck.expansion * planes:downsample  = nn.Sequential(nn.Conv2d(self.inplanes, Bottleneck.expansion * planes, 1, stride, bias=False),nn.BatchNorm2d(Bottleneck.expansion * planes))layers = []layers.append(Bottleneck(self.inplanes, planes, stride, downsample))self.inplanes = planes * Bottleneck.expansionfor i in range(1, blocks):layers.append(Bottleneck(self.inplanes, planes))return nn.Sequential(*layers)#自上而下的采样模块def _upsample_add(self, x, y):_,_,H,W = y.shapereturn F.interpolate(x, size=(H,W), mode='bilinear') + ydef forward(self, x):#自下而上c1 = self.maxpool(self.relu(self.bn1(self.conv1(x))))c2 = self.layer1(c1)c3 = self.layer2(c2)c4 = self.layer3(c3)c5 = self.layer4(c4)#自上而下p5 = self.toplayer(c5)p4 = self._upsample_add(p5, self.latlayer1(c4))p3 = self._upsample_add(p4, self.latlayer2(c3))p2 = self._upsample_add(p3, self.latlayer3(c2))#卷积融合,平滑处理p4 = self.smooth1(p4)p3 = self.smooth2(p3)p2 = self.smooth3(p2)return p2, p3, p4, p5

在终端调用FDN模块:

>>> import torch
>>> from fpn import FPN
#利用list来初始化FPN网络
>>> net_fpn = FPN([3, 4, 6, 3])
>>> net_fpn.conv1    #查看FPN的第一个卷积层
Conv2d(3, 64, kernel_size=(7, 7), stride=(2, 2), padding=(3, 3), bias=False)
>>> net_fpn.bn1    #查看FPN的第一个BN层
BatchNorm2d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
>>> net_fpn.relu    #查看FPN的第一个ReLU层
ReLU(inplace=True)
>>> net_fpn.maxpool    #查看FPN的第一个池化层,使用最大池化
MaxPool2d(kernel_size=3, stride=2, padding=1, dilation=1, ceil_mode=False)
>>> net_fpn.layer1    #查看FPN的第一个layer,即前面的C2,包含了3个Bottleneck
Sequential(#layer1中第一个Bottleneck模块(0): Bottleneck((bottleneck): Sequential((0): Conv2d(64, 64, kernel_size=(1, 1), stride=(1, 1), bias=False)(1): BatchNorm2d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)(2): ReLU(inplace=True)(3): Conv2d(64, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)(4): BatchNorm2d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)(5): ReLU(inplace=True)(6): Conv2d(64, 256, kernel_size=(1, 1), stride=(1, 1), bias=False)(7): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True))#这里存在一个通道增加模块(relu): ReLU(inplace=True)(downsample): Sequential((0): Conv2d(64, 256, kernel_size=(1, 1), stride=(1, 1), bias=False)(1): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)))#layer1中第2个Bottleneck模块(1): Bottleneck((bottleneck): Sequential((0): Conv2d(256, 64, kernel_size=(1, 1), stride=(1, 1), bias=False)(1): BatchNorm2d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)(2): ReLU(inplace=True)(3): Conv2d(64, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)(4): BatchNorm2d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)(5): ReLU(inplace=True)(6): Conv2d(64, 256, kernel_size=(1, 1), stride=(1, 1), bias=False)(7): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True))(relu): ReLU(inplace=True))#layer1中第3个Bottleneck模块(2): Bottleneck((bottleneck): Sequential((0): Conv2d(256, 64, kernel_size=(1, 1), stride=(1, 1), bias=False)(1): BatchNorm2d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)(2): ReLU(inplace=True)(3): Conv2d(64, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)(4): BatchNorm2d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)(5): ReLU(inplace=True)(6): Conv2d(64, 256, kernel_size=(1, 1), stride=(1, 1), bias=False)(7): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True))(relu): ReLU(inplace=True))
)
>>> net_fpn.layer2    #查看fpn的layer2,即上面的C3,包含了4个Bottleneck
Sequential(#layer2中第1个Bottleneck(0): Bottleneck((bottleneck): Sequential((0): Conv2d(256, 128, kernel_size=(1, 1), stride=(1, 1), bias=False)(1): BatchNorm2d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)(2): ReLU(inplace=True)(3): Conv2d(128, 128, kernel_size=(3, 3), stride=(2, 2), padding=(1, 1), bias=False)(4): BatchNorm2d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)(5): ReLU(inplace=True)(6): Conv2d(128, 512, kernel_size=(1, 1), stride=(1, 1), bias=False)(7): BatchNorm2d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True))(relu): ReLU(inplace=True)(downsample): Sequential((0): Conv2d(256, 512, kernel_size=(1, 1), stride=(2, 2), bias=False)(1): BatchNorm2d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)))#layer2中第2个Bottleneck(1): Bottleneck((bottleneck): Sequential((0): Conv2d(512, 128, kernel_size=(1, 1), stride=(1, 1), bias=False)(1): BatchNorm2d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)(2): ReLU(inplace=True)(3): Conv2d(128, 128, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)(4): BatchNorm2d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)(5): ReLU(inplace=True)(6): Conv2d(128, 512, kernel_size=(1, 1), stride=(1, 1), bias=False)(7): BatchNorm2d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True))(relu): ReLU(inplace=True))#layer2中第3个Bottleneck(2): Bottleneck((bottleneck): Sequential((0): Conv2d(512, 128, kernel_size=(1, 1), stride=(1, 1), bias=False)(1): BatchNorm2d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)(2): ReLU(inplace=True)(3): Conv2d(128, 128, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)(4): BatchNorm2d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)(5): ReLU(inplace=True)(6): Conv2d(128, 512, kernel_size=(1, 1), stride=(1, 1), bias=False)(7): BatchNorm2d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True))(relu): ReLU(inplace=True))#layer2中第4个Bottleneck(3): Bottleneck((bottleneck): Sequential((0): Conv2d(512, 128, kernel_size=(1, 1), stride=(1, 1), bias=False)(1): BatchNorm2d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)(2): ReLU(inplace=True)(3): Conv2d(128, 128, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)(4): BatchNorm2d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)(5): ReLU(inplace=True)(6): Conv2d(128, 512, kernel_size=(1, 1), stride=(1, 1), bias=False)(7): BatchNorm2d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True))(relu): ReLU(inplace=True))
)
>>> net_fpn.toplayer    #1×1的卷积,以得到P5
Conv2d(2048, 256, kernel_size=(1, 1), stride=(1, 1))
>>> net_fpn.smooth1    #对P4进行平滑的卷积层
Conv2d(256, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
>>> net_fpn.latlayer1    #对C4进行横向处理的卷积层
Conv2d(1024, 256, kernel_size=(1, 1), stride=(1, 1))
>>> input = torch.randn(1, 3, 224, 224)
>>> output = net_fpn(input)
#返回的P2、P3、P4、P5,这4个特征图通道数相同,但特征尺寸递减
>>> output[0].shape
torch.Size([1, 256, 56, 56])
>>> output[1].shape
torch.Size([1, 256, 28, 28])
>>> output[2].shape
torch.Size([1, 256, 14, 14])
>>> output[3].shape
torch.Size([1, 256, 7, 7])

卷积神经网络:FPN相关推荐

  1. 卷积神经网络——FPN论文翻译

    论文链接: https://arxiv.org/abs/1612.03144 Feature Pyramid Networks for Object Detection 0.摘要 特征金字塔是识别系统 ...

  2. 卷积神经网络——FPN(Feature Pyramid Networks)介绍

    FPN(Feature Pyramid Networks):特征金字塔网络,是用来提取不同尺度特征图的,提供给后面的网络执行预测任务. 为什么需要FPN呢?简要介绍一下,在目标检测的网络中,要识别不同 ...

  3. 9 大主题卷积神经网络(CNN)的 PyTorch 实现

    Datawhale干货 作者:红色石头,北京大学,Datawhale伙伴 大家还记得这张图吗? 之前,红色石头发文介绍过一份很不错的资源: 52 个深度学习目标检测模型汇总,论文.源码一应俱全! 深度 ...

  4. 轻量级卷积神经网络的设计技巧

    点击上方"视学算法",星标公众号 重磅干货,第一时间送达 作者:zhouyuangan 来源:CVer 这篇文章将从一个证件检测网络(Retinanet)的轻量化谈起,简洁地介绍, ...

  5. 【CMU】图卷积神经网络中的池化综述,Pooling in Graph Convolutional Neural Network

    关注上方"深度学习技术前沿",选择"星标公众号", 资源干货,第一时间送达! 图卷积神经网络(GCNNs)是深度学习技术在图结构数据问题上的一种强大的扩展.我们 ...

  6. 【深度学习】9 大主题卷积神经网络(CNN)的 PyTorch 实现

    大家还记得这张图吗? 之前,红色石头发文介绍过一份很不错的资源: 52 个深度学习目标检测模型汇总,论文.源码一应俱全! 深度系统介绍了 52 个目标检测模型,纵观 2013 年到 2020 年,从最 ...

  7. 基于深度卷积神经网络的目标检测研究综述

    基于深度卷积神经网络的目标检测研究综述 人工智能技术与咨询 来自<光学精密工程> ,作者范丽丽等 摘要:作为计算机视觉中的基本视觉识别问题,目标检测在过去的几十年中得到了广泛地研究.目标检 ...

  8. 分组卷积计算量_轻量级卷积神经网络的设计技巧

    作者 | zhouyuangan 来源 | CVer 这篇文章将从一个证件检测网络(Retinanet)的轻量化谈起,简洁地介绍,我在实操中使用到的设计原则和idea,并贴出相关的参考资料和成果供读者 ...

  9. 基于卷积神经网络和小波变换的视频监控中的火灾探测

    1. 文章信息 本次介绍的文章是2022年发表在EngineeringApplications of Artificial Intelligence上的一篇关于火灾检测的文章,作者团队来自清华大学. ...

最新文章

  1. 【终极办法】windows下安装完MySQL,为什么cmd不识别命令?
  2. Python元组是什么
  3. 面试:InnoDB 并发控制
  4. 《spring-boot学习》-07-spring data jpa
  5. 利用函数重载编写函数max_彻底理清重载函数匹配
  6. 如何在苹果Mac上右键点击?
  7. 学习软件评测师教程计划
  8. python调用讯飞付费版语音转写
  9. U-Net在2022年相关研究的论文推荐
  10. 硬件工程师成长之路(2)——电路设计
  11. atomic 原子操作
  12. Qt QtabWidget设置背景色 设置标题栏颜色
  13. 小盒即时通讯IM-全套开源-开箱即用
  14. 7000字和你聊聊如何设计一款高并发架构(建议收藏)
  15. 《海边的卡夫卡》摘抄
  16. 成人注意缺陷多动障碍在内外源冲突时的表现
  17. BGP在数据中心的应用1——数据中心网络介绍
  18. 无线局域网技术(二)无线传输技术基础
  19. 层次分析法 AHP
  20. 网易云音乐搜索引擎 python+whoosh---(2)搜索引擎

热门文章

  1. 清理buff/cache
  2. 查询高考录取结果显示服务器错误,又有一大批考生被录取!你还显示 “未查询到录取信息”吗?...
  3. logging模块学习
  4. further occurrences of HTTP request parsing errors will be logged at DEBUG level。
  5. 10种最不健康的生活方式(转)
  6. oracle 新建一个用户和赋予权限
  7. C语言编程的书写规则,关于C语言编程书写规范的规则和建议.doc
  8. 线性回归均方误差(MSE)的推导
  9. AEB E-NCAP测试项目及发展趋势
  10. asp.net学生家校联系系统