目录
一、RFB部分学习笔记
1.神经学基础
2.受视觉研究启发得出的RFB
3.原论文中提出的RFB结构:
4.PraNet中的RFB结构:
二、aggregation部分学习笔记
三、代码:
RFB及aggregation定义
在PraNet中所应用到部分的节选:
论文:Receptive Field Block Net for Accurate and Fast Object Detection
论文链接:https://arxiv.org/abs/1711.07767
要构建一个快速而强大的检测器,一个合理的替代方案是通过引入某些手工制作的机制来增强轻量级网络的特征表示,而不是顽固地深化模型。
另一方面,神经科学的一些发现表明,在人类的视觉皮层中,群体接受野(pRF)的大小是其视网膜位图中偏心度的函数,尽管在不同的图之间存在差异,但它随着每个图中偏心度的增加而增加,如图所示,这有助于突出靠近中心区域的重要性,并提高了对小空间转移的不敏感性。
图1图1所示。人类群体接受域特性的规律。
图(A)在一些人类视网膜定位图中,pRF(population Receptive Field)的大小作为偏心度的函数,其中有两个明显的趋势: (1)在每个图中,pRF的大小随着偏心度的增加而增加 (2)在不同的图中,pRF的大小不同。
图 (B)基于(A)中参数的pRF空间阵列.每个圆的半径是在适当的偏心距下的RF大小。
受人类视觉系统RFs结构的启发,Songtao Liu(2018)等提出了一种新的模块,即接受域块(RFB),以加强从轻量级CNN模型中学习到的深度特征,从而为快速准确的检测器做出贡献。 (具体来说,RFB利用多分支池化的不同内核对应RFs不同的大小,应用扩张卷积层来控制自己的偏心率,并重塑了他们来产生最后表示,如图2。然后我们将RFB模块组装到SSD(Single shot multibox detector)的顶部,一种具有轻量级主干的实时方法,并构建一个先进的单阶检测器(RFB网)。)
由于这个简单的模块,RFB网提供了相对不错的分数,可以与最新的基于更深层次骨干网的探测器相媲美,并保持了原始轻量级探测器的快速。此外,RFB模块是通用的,对网络架构施加的约束很少。
为了兼顾效率和精度提出的RFB是一个多分支卷积块。其内部结构可分为两部分:具有不同核的多分支卷积层和尾随的扩展池化或卷积层。前者与Inception相同,负责模拟多种尺寸的pRF,后者再现了人类视觉系统中pRF尺寸与偏心距的关系。图2显示了RFB及其相应的空间池区域图。
图2在图2的RFB结构中用3种不同大小和颜色的输出叠加来展示。
在图2的最后一列中将融合后的特征与人类视觉感受野做对比,从图可以看出是非常接近的,这也是这篇文章的出发点,换句话说就是模拟人类视觉的感受野进行RFB结构的设计。
RFB也concate了多个conv分支,与Inception结构类似,每个分支上使用不同尺度的常规卷积 + 空洞卷积,通过常规卷积的不同卷积核尺度来模拟pRFs中的不同感受野,各个分支上通过各自dilated conv所得到的偏心率,来模拟pRF的尺度与离心率的比例。
个人理解为:
通过一个特征点—
—联结多个不同大小卷积核的卷积结果(同时他们也是匹配自身核大小的空洞卷积)—
—每层的不同卷积核再抓取在输入图像上边长与自身dilation参数(间隔)相等的矩形部分的特征。(即图2中间隔为3的空洞卷积核,核中每一个格抓取3*3,间隔为5的空洞卷积核,核中每一个格抓取5*5)形成了一个严密整齐、既不重合也没有遗漏的结构。
小的卷积核模仿视觉研究中偏心度小的部分的感受野,大的卷积核模仿偏心度大的部分的感受野。
(b)中为了减少参数使用了1*n+n*1卷积代替n*n卷积操作。
shorcut为一次1*1卷积操作
(圆角矩形1,3代表一个1*3卷积操作)
图4此部分将RFB模块输出的三个结果聚合为Sg,完成了图6中PD部分的功能。
下图为aggregation操作流程图:
图5 图6class RFB_modified(nn.Module): # Receptive Field Block
def __init__(self, in_channel, out_channel):
super(RFB_modified, self).__init__()
self.relu = nn.ReLU(True)
self.branch0 = nn.Sequential(
BasicConv2d(in_channel, out_channel, 1),
)
self.branch1 = nn.Sequential(
BasicConv2d(in_channel, out_channel, 1),
BasicConv2d(out_channel, out_channel, kernel_size=(1, 3), padding=(0, 1)),
BasicConv2d(out_channel, out_channel, kernel_size=(3, 1), padding=(1, 0)),
BasicConv2d(out_channel, out_channel, 3, padding=3, dilation=3)
)
self.branch2 = nn.Sequential(
BasicConv2d(in_channel, out_channel, 1),
BasicConv2d(out_channel, out_channel, kernel_size=(1, 5), padding=(0, 2)),
BasicConv2d(out_channel, out_channel, kernel_size=(5, 1), padding=(2, 0)),
BasicConv2d(out_channel, out_channel, 3, padding=5, dilation=5)
)
self.branch3 = nn.Sequential(
BasicConv2d(in_channel, out_channel, 1),
BasicConv2d(out_channel, out_channel, kernel_size=(1, 7), padding=(0, 3)),
BasicConv2d(out_channel, out_channel, kernel_size=(7, 1), padding=(3, 0)),
BasicConv2d(out_channel, out_channel, 3, padding=7, dilation=7)
)
self.conv_cat = BasicConv2d(4*out_channel, out_channel, 3, padding=1) # 对连接后的结果卷积
self.conv_res = BasicConv2d(in_channel, out_channel, 1) # 直连层shortcut
def forward(self, x):
x0 = self.branch0(x)
x1 = self.branch1(x)
x2 = self.branch2(x)
x3 = self.branch3(x)
x_cat = self.conv_cat(torch.cat((x0, x1, x2, x3), 1)) # 按通道数连接四个分支后1*1卷积
x = self.relu(x_cat + self.conv_res(x))
return x
class aggregation(nn.Module): # PD partial decoder 聚合高级特征——图
# dense aggregation, it can be replaced by other aggregation previous, such as DSS, amulet, and so on.
# used after MSF
def __init__(self, channel):
super(aggregation, self).__init__()
self.relu = nn.ReLU(True)
self.upsample = nn.Upsample(scale_factor=2, mode='bilinear', align_corners=True) # 双线性插值法上采样
self.conv_upsample1 = BasicConv2d(channel, channel, 3, padding=1)
self.conv_upsample2 = BasicConv2d(channel, channel, 3, padding=1)
self.conv_upsample3 = BasicConv2d(channel, channel, 3, padding=1)
self.conv_upsample4 = BasicConv2d(channel, channel, 3, padding=1)
self.conv_upsample5 = BasicConv2d(2*channel, 2*channel, 3, padding=1)
self.conv_concat2 = BasicConv2d(2*channel, 2*channel, 3, padding=1)
self.conv_concat3 = BasicConv2d(3*channel, 3*channel, 3, padding=1)
self.conv4 = BasicConv2d(3*channel, 3*channel, 3, padding=1)
self.conv5 = nn.Conv2d(3*channel, 1, 1)
def forward(self, x1, x2, x3):
x1_1 = x1
x2_1 = self.conv_upsample1(self.upsample(x1)) * x2
x3_1 = self.conv_upsample2(self.upsample(self.upsample(x1))) \
* self.conv_upsample3(self.upsample(x2)) * x3
x2_2 = torch.cat((x2_1, self.conv_upsample4(self.upsample(x1_1))), 1)
x2_2 = self.conv_concat2(x2_2)
x3_2 = torch.cat((x3_1, self.conv_upsample5(self.upsample(x2_2))), 1)
x3_2 = self.conv_concat3(x3_2)
x = self.conv4(x3_2)
x = self.conv5(x)
return x
# ---- Receptive Field Block like module ---- RFB模块
self.rfb2_1 = RFB_modified(512, channel)
self.rfb3_1 = RFB_modified(1024, channel)
self.rfb4_1 = RFB_modified(2048, channel)
# ---- Partial Decoder ---- 聚合得到Sg
self.agg1 = aggregation(channel)
x2_rfb = self.rfb2_1(x2) # channel -> 32
x3_rfb = self.rfb3_1(x3) # channel -> 32
x4_rfb = self.rfb4_1(x4) # channel -> 32
ra5_feat = self.agg1(x4_rfb, x3_rfb, x2_rfb)
# PD三个RFB块得到Sg ,为(bs, 1, 44, 44)