【论文笔记】:Pyramid Feature Attention Network for Saliency detection

&Title

在这里插入图片描述

&Summary

在显著性检测算法中,如何提取到更有效的特征是该任务的难点之一

为了解决该问题,我们通过金字塔特征注意网络来注意高语义特征和低维空间结构特征
首先,使用上下文感知金字塔特征提取(Context-aware PyramidFeatureExtraction,CPFE)模块对高级多尺度信息捕捉丰富的语义信息。

其次,在CPFE模块后加入通道注意模块( channel-wise attention,CA)以及低级特征中提取的空间注意模块( spatial attention,SA),然后将CA和SA融合。

最后,我们提出边缘保护损失,使网络学习到更多的边界定位的细节信息。

根据不同级别特征的特征,我们对高级特征采用通道注意,对于低级特征采用空间注意,以选择有效特征。另外,我们不对高级功能使用空间关注,因为高级功能包含高度的抽象语义,因此无需过滤空间信息。同时,我们不对低级功能使用通道注意,因为低级功能的不同通道之间几乎没有语义上的区别。

contributions:

  1. 我们提出了一种用于图像显着性检测的金字塔特征注意(PFA)网络。 对于高级功能,我们采用了上下文感知金字塔特征提取模块和通道级注意模块来捕获丰富的上下文信息。 对于低级特征,我们采用空间关注模块过滤掉一些背景细节。
  2. 我们设计了一种新颖的边缘保留损失,以指导网络学习边界定位方面的更多详细信息。
  3. 所提出的模型在一些具有挑战性的数据集上达到了最新水平。 实验证明了该方法的有效性和优越性。

&Research Objective

提取到更有效的特征。

&Problem Statement

如今算法多通过融合多尺度特征来提取更有效的特征,但并不是所有的特征都是有效的,而且有些可能导致相反的效果。

  • 最有效的显著性目标检测算法都是基于FCN网络,由多个卷积层和池化层堆叠来逐渐增加感受野大小以及获得更高级的语义信息。但池化层减小了特征映射的大小并恶化了显著对象的边界。
  • 一些研究者通过手动提取特征将显著性目标的边界特征进行保存。但这种方法难以有效地融合单独提取的互补特征,并且很耗时。
  • 研究者们又通过融合多尺度特征来进行显著性目标检测。深层的特征通常包含全局上下文感知信息,这些信息适合于正确定位显著区域。 浅层的特征包含空间结构细节,适合于定位边界。这些方法的缺点是,融合不同尺度的特征但是并未考虑到每一层不同的贡献

&Method(s)

提出了一种新的显着性检测方法,该方法包含一个上下文感知金字塔特征提取模块和一个通道感知模块:以捕获上下文感知多尺度多接收场高级特征;空间注意力:用于低级特征图的模块以精炼显着的对象细节;有效的边缘保留损失,以指导网络学习边界定位中的更多详细信息。 总体架构如下图所示:
在这里插入图片描述

ef VGG16(img_input, dropout=False, with_CPFE=False, with_CA=False, with_SA=False, droup_rate=0.3):
    # Block 1  Conv 1-2
    x = Conv2D(64, (3, 3), activation='relu', padding='same', name='block1_conv1')(img_input)
    x = Conv2D(64, (3, 3), activation='relu', padding='same', name='block1_conv2')(x)
    C1 = x
    x = MaxPooling2D((2, 2), strides=(2, 2), name='block1_pool')(x)
    if dropout:
        x = Dropout(droup_rate)(x)
        
    # Block 2  Conv 2-2
    x = Conv2D(128, (3, 3), activation='relu', padding='same', name='block2_conv1')(x)
    x = Conv2D(128, (3, 3), activation='relu', padding='same', name='block2_conv2')(x)
    C2 = x
    x = MaxPooling2D((2, 2), strides=(2, 2), name='block2_pool')(x)
    if dropout:
        x = Dropout(droup_rate)(x)
        
    # Block 3  Conv 3-3
    x = Conv2D(256, (3, 3), activation='relu', padding='same', name='block3_conv1')(x)
    x = Conv2D(256, (3, 3), activation='relu', padding='same', name='block3_conv2')(x)
    x = Conv2D(256, (3, 3), activation='relu', padding='same', name='block3_conv3')(x)
    C3 = x
    x = MaxPooling2D((2, 2), strides=(2, 2), name='block3_pool')(x)
    if dropout:
        x = Dropout(droup_rate)(x)
        
    # Block 4   Conv 4-3
    x = Conv2D(512, (3, 3), activation='relu', padding='same', name='block4_conv1')(x)
    x = Conv2D(512, (3, 3), activation='relu', padding='same', name='block4_conv2')(x)
    x = Conv2D(512, (3, 3), activation='relu', padding='same', name='block4_conv3')(x)
    C4 = x
    x = MaxPooling2D((2, 2), strides=(2, 2), name='block4_pool')(x)
    if dropout:
        x = Dropout(droup_rate)(x)
        
    # Block 5   Conv 5-3
    x = Conv2D(512, (3, 3), activation='relu', padding='same', name='block5_conv1')(x)
    x = Conv2D(512, (3, 3), activation='relu', padding='same', name='block5_conv2')(x)
    x = Conv2D(512, (3, 3), activation='relu', padding='same', name='block5_conv3')(x)
    if dropout:
        x = Dropout(droup_rate)(x)
    C5 = x
    
	# 上面是backbone中五个conv的实现 下面就是 PFAN的核心的地方  对照着上面的图就很好理解了
    C1 = Conv2D(64, (3, 3), padding='same', name='C1_conv')(C1)
    C1 = BN(C1, 'C1_BN')
    C2 = Conv2D(64, (3, 3), padding='same', name='C2_conv')(C2)
    C2 = BN(C2, 'C2_BN')
    # CPFE 实现
    if with_CPFE:
        C3_cfe = CFE(C3, 32, 'C3_cfe')
        C4_cfe = CFE(C4, 32, 'C4_cfe')
        C5_cfe = CFE(C5, 32, 'C5_cfe')
        # CFE的实现后面会提到
        C5_cfe = BilinearUpsampling(upsampling=(4, 4), name='C5_cfe_up4')(C5_cfe)
        C4_cfe = BilinearUpsampling(upsampling=(2, 2), name='C4_cfe_up2')(C4_cfe)
        C345 = Concatenate(name='C345_aspp_concat', axis=-1)([C3_cfe, C4_cfe, C5_cfe])
        # 通道注意力 用来注意高层特征图的,关注高级语义信息
        if with_CA:
            C345 = ChannelWiseAttention(C345, name='C345_ChannelWiseAttention_withcpfe')
    C345 = Conv2D(64, (1, 1), padding='same', name='C345_conv')(C345)
    C345 = BN(C345,'C345')
    C345 = BilinearUpsampling(upsampling=(4, 4), name='C345_up4')(C345)
	# 空间注意力,用在底层特征图上
    if with_SA:
        SA = SpatialAttention(C345, 'spatial_attention')
        C2 = BilinearUpsampling(upsampling=(2, 2), name='C2_up2')(C2)
        C12 = Concatenate(name='C12_concat', axis=-1)([C1, C2])
        C12 = Conv2D(64, (3, 3), padding='same', name='C12_conv')(C12)
        C12 = BN(C12, 'C12')
        C12 = Multiply(name='C12_atten_mutiply')([SA, C12])
        # 用的是乘,attention map用的高层特征图C345
    fea = Concatenate(name='fuse_concat',axis=-1)([C12, C345])
    sa = Conv2D(1, (3, 3), padding='same', name='sa')(fea)

    model = Model(inputs=img_input, outputs=sa, name="BaseModel")
    return model

CPFE
CPFE结构如下所示,输入时VGG的三个高层特征图,然后对其分别进行三个具有不同扩张率的3×3卷积(丰富感受野)和一个1×1卷积,然后采样到相同size的map特征图,最后cat连接作为最终输出。
在这里插入图片描述

为了使最终提取的高级特征包含比例尺和形状不变性的特征,我们采用具有不同扩张率的无穷卷积,将其设置为3、5和7来捕获多接收场上下文信息。 然后,通过跨通道级联,将来自不同的多孔卷积层的特征图和1×1降维特征进行组合。 此后,我们获得了三个具有上下文感知信息的不同尺度特征,我们将两个较小的特征上采样为最大的特征。 最后,我们通过跨通道串联将它们组合起来,作为上下文感知金字塔特征提取模块的输出。

代码如下:

def CFE(input_tensor, filters, block_id):
    rate = [3, 5, 7]  ## 三种膨胀率
    cfe0 = Conv2D(filters, (1, 1), padding='same', use_bias=False, name=block_id + '_cfe0')(
        input_tensor)
    cfe1 = AtrousBlock(input_tensor, filters, rate[0], block_id + '_cfe1')
    cfe2 = AtrousBlock(input_tensor, filters, rate[1], block_id + '_cfe2')
    cfe3 = AtrousBlock(input_tensor, filters, rate[2], block_id + '_cfe3')
    cfe_concat = Concatenate(name=block_id + 'concatcfe', axis=-1)([cfe0, cfe1, cfe2, cfe3])
    cfe_concat = BN(cfe_concat, block_id)
    return cfe_concat

Channel-wise attention
在上下文感知金字塔特征提取后,我们将通道方式注意(CA)模块添加到加权的多尺度多接收场高级特征中。 CA将对显示对突出对象有高响应的通道分配更大的权重。

两个连续的全连接(FC)层完全捕获了通道相关性(请参见图4)。 为了限制模型的复杂性和辅助概括,我们通过在非线性周围形成具有两个FC层的瓶颈来编码通道方式特征向量。 然后,通过使用S形运算,对映射到[0,1]的编码后的通道特征向量采取归一化处理措施。

在这里插入图片描述

def SpatialAttention(inputs,name):
    k = 9
    H, W, C = map(int,inputs.get_shape()[1:])
    attention1 = Conv2D(C / 2, (1, k), padding='same', name=name+'_1_conv1')(inputs)
    attention1 = BN(attention1,'attention1_1')
    attention1 = Conv2D(1, (k, 1), padding='same', name=name + '_1_conv2')(attention1)
    attention1 = BN(attention1, 'attention1_2')
    attention2 = Conv2D(C / 2, (k, 1), padding='same', name=name + '_2_conv1')(inputs)
    attention2 = BN(attention2, 'attention2_1')
    attention2 = Conv2D(1, (1, k), padding='same', name=name + '_2_conv2')(attention2)
    attention2 = BN(attention2, 'attention2_2')
    attention = Add(name=name+'_add')([attention1,attention2])
    attention = Activation('sigmoid')(attention)
    attention = Repeat(repeat_list=[1, 1, 1, C])(attention)
    return attention

def ChannelWiseAttention(inputs,name):
    H, W, C = map(int, inputs.get_shape()[1:])
    attention = GlobalAveragePooling2D(name=name+'_GlobalAveragePooling2D')(inputs)
    attention = Dense(C / 4, activation='relu')(attention)
    attention = Dense(C, activation='sigmoid',activity_regularizer=l1_reg)(attention)
    # Dense是全连接层
    attention = Reshape((1, 1, C),name=name+'_reshape')(attention)
    attention = Repeat(repeat_list=[1, H, W, 1],name=name+'_repeat')(attention)
    attention = Multiply(name=name + '_multiply')([attention, inputs])
    return attention
#这两个返回的attention不一样,SA返回的是attention weight。CA返回的是attention后的特征图

Spacial attention Natural
自然图像通常包含大量的前景和复杂背景细节。我们通过在底层特征图应用SA模块,用来注意前景区域。图4 SA模块中,Y means context-aware high-level feature after CA in this paper。即CA之后的高级特征图。就是用的上层语义来注意底层特征中需要关注的特征。上层语义包含的背景信息较少。
PS:这种利用上层语义来做attention map,使用sigmoid来归一化的操作,我也做过,但实验效果基本上没有。

从图1可以看出,来自低层特征的显着性图包含许多细节,很容易带来不良结果。 在显着性检测中,我们希望获得显着物体与背景之间的详细边界,而没有其他会分散人类注意力的纹理。 因此,我们不是在均等地考虑所有空间位置,而是将空间注意力更多地集中在前景区域上,这有助于生成用于显着性预测的有效特征。
在这里插入图片描述

Loss function
使用拉普拉斯算子来获取地面实况和网络输出的显着性图的边界,然后使用交叉熵损失来监督显着对象边界的生成。
在这里插入图片描述
拉普拉斯算子是n维欧几里德空间中的二阶微分算子,定义为梯度的散度(∆f)。 由于二阶导数可用于检测边缘,因此我们使用Laplace运算符来获取显着的对象边界。 二维的拉普拉斯算子由公式8给出,其中x和y是xy平面的标准笛卡尔座标。最后,我们使用交叉熵损失来监督显着物体边界公式10的生成。
在这里插入图片描述
总损失:
在这里插入图片描述

def EdgeHoldLoss(y_true, y_pred):
    y_pred2 = tf.sigmoid(y_pred)
    y_true_edge = tfLaplace(y_true)
    # tfLaplace 为拉普拉斯算子,具体代码看github
    y_pred_edge = tfLaplace(y_pred2)
    # 获取边界
    y_pred_edge = logit(y_pred_edge)
    edge_loss = K.mean(tf.nn.sigmoid_cross_entropy_with_logits(labels=y_true_edge,logits=y_pred_edge), axis=-1)
    saliency_pos = 1.12
    saliency_loss = K.mean(tf.nn.weighted_cross_entropy_with_logits(y_true,y_pred,saliency_pos), axis=-1)
    return 0.7*saliency_loss+0.3*edge_loss # 总loss,论文设置 α 为 0.7

&Evaluation

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

&Thinks

  • 注意力加损失函数的改进,不算是特别好的idea了,这个loss的设计可能是本文的比较大的亮点吧,有空研究研究。
  • 文中使用的空间注意力,之前有做过类似的实验,也是用两层的融合来做attention map,用sigmoid来归一化处理,然后mul。效果基本没提升。之前分析是:利用上层语义 map 来做注意力(mul), 使得剃度回传的时候变小,从而使得上层与 下层的联系没有直接使用加法来得更紧密,sigmoid 会进一步缩小梯度。但是本文这样操作跟我差不多啊,就是我没有用BN而已,有空重新做一下实验。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章