Non-local Neural Networks论文理解

解决什么问题

提出non-local操作来建立block,来捕捉长距离的依赖


本文创新点\贡献

提出的建立non-local block的方法能方便的插入到很多计算机视觉结构中,在视频分类、位姿估计和图像识别中都有很好的效果
在这里插入图片描述
这个图能很好说明什么叫non-local,前两个都是在中心的邻域做一些操作


前人方法

有使用很大的感受野来获取远距离依赖的尝试,之前的方法中长距离依赖只能通过重复的操作卷积、循环通过数据逐步传播信号,但是这样有局限性:

信号在多次的重复中消失?计算耗时?

  1. 计算效率低

  2. 优化困难

    作者在这里距离的是残差网络和长短期记忆网络,这俩个都对数据多次重复利用,不过残差网络也有找到response的能力?不同层数的特征图融合,能让相关的特征在一个图中显示出来?

  3. 在多次跳跃依赖的建模中有困难,比如远距离之间

    多次跳跃依赖是指关联的递进? 充电器->手机->人 这样的吗?


本文IDEA来源

没说


方法

在这里插入图片描述

方法概述

对每个位置都计算一个response,能更好的获得位置间的联系

相关

和self-attention相关,self-attention通过考虑全部位置,取其在嵌入空间中的加权平均值,来计算一个序列里某个位置的response

解决的点

长距离的相关点的response计算问题,延伸到了图像、视频上

优点

  1. 可以直接计算两个位置的交互,而不用考虑距离的问题
  2. 效率高,效果好,只用几个层
  3. 输入尺度多样,很容易和其他模型结合

原理

和其他方法的比较

  1. non-local考虑了全部的位置,而卷积和序列却不能考虑这么多信息
  2. 虽然考虑了全部的位置信息,但是又和全连接不同:
    non-local是基于不同位置之间的联系来计算response的,而全连接使用的是学习到的权重,并且直接多种输入大小,而全连接的输入大小是固定的且没有位置间的对应

    用的是“ f f ”来处理不同位置,而不是“ W x Wx

  3. 位置自由,可以放在深度神经网络的前面或后面,利于将non-local和local信息结合起来

Non-local Neural Networks

方程式

y i = 1 C ( x ) j f ( x i , x j ) g ( x j ) y_i = \frac{1}{C(x)} \sum_{\mathclap{\forall j}}f(x_i,x_j)g(x_j)
其中 i i 是输出位置的index, j j 代表全部的可能的位置, i i 位置的response是和其他的 j j 位置一起计算的
x x 是输入signal,可以是图片、序列、视频,一般是他们的特征
y y 是和 x x 一样大的输出signal
成对函数 f f 计算 i i j j 之间的标量
g g 函数计算在 j j 位置的输入signa 的表达
1 C ( x ) \frac{1}{C(x)} 用来正则化response

多种 f f 表达式

f   g f\ g 都有多种计算公式,而实验证明,不同的公式算出来的效果差别不是很大

先将 g g 假设成简单的线性嵌入:
g ( X j ) = W g x j g(X_j) = W_g x_j
其中 W g W_g 是需要学习的权重矩阵,再空间上就是个 1 × 1 1\times 1 的卷积

Gaussian
f ( x i , x j ) = e x i T x j f(x_i,x_j) = e^{x^T_i \cdot x_j}

其中 x i , x j x_i,x_j 也可以用欧式距离,但是点乘更方便深度学习计算
C ( x ) = j f ( x i , x j ) C(x) = \sum_{\forall j}f(x_i,x_j)

Embedded Gaussian:

Gaussian函数的简单拓展,能计算嵌入空间的相似性
f ( x i , x j ) = e θ ( x i ) T ϕ ( x j ) f(x_i,x_j) = e^{\theta(x_i)^T\phi(x_j)}

其中 θ ( x i ) = W θ x i \theta(x_i) = W_{\theta}x_i ϕ ( x j ) = W ϕ x j \phi(x_j) = W_{\phi}x_j 是两个嵌入

为什么又加了两个参数就能计算相似性了?

C ( x ) = j f ( x i , x j ) C(x) = \sum_{\forall j}f(x_i,x_j)

Softmax
Attention is all you need 论文中,使用了softmax,所以这也有个softmax版本的:
y = s o f t m a x ( x T W θ T W ϕ x ) g ( x ) y = softmax(x^TW^T_{\theta}W_{\phi}x)g(x)

但是作者把这个softmax版本的应用到空间/时空间的图像/视频任务上,发现作用并不明显(指softmax),然后提供了其他的版本

前面两个Gaussian可以直接写成softmax

Dot product:
f ( x i , x j ) = θ ( x i ) T ϕ ( x j ) f(x_i,x_j) = \theta(x_i)^T\phi(x_j)

这是采用嵌入的版本, C ( x ) = N C(x) = N N N 就是 x x 的位置的数量,这样能简化梯度计算,而且规范化是很有必要的,因为输入的大小是变化。

这里的 N N 是下采样后的数量

Concatenation
f ( x i , x j ) = R e L U ( w f T [ θ ( x i ) , ϕ ( x j ) ] ) f(x_i,x_j) = ReLU(w^T_f[\theta(x_i),\phi(x_j)])
其中 [ , ] [\cdot,\cdot] 表示串联,而且 w f w_f 是一个权重向量,能把串联向量映射成标量
C ( x ) = N C(x) = N

之后就适当的选择版本,当成个参数来调吧


Non-local Block

封装到block中,这样能更好的合并到其他的结构中:
z i = W z y i + x i z_i = W_z y_i + x_i
这个 + x i +x_i 就是个残差操作,这个残差的操作可以在不破坏预训练模型的情况下,将non-local block插入到任何预训练的模型中(此时将 W z W_z 初始化为0)

这要怎么操作,虽然按理来说不需要啥权重,但是要怎么让改动的结构和权重对应上?看看6DVNet怎么做的

在这里插入图片描述
N o n l o c a l B l o c k Non-local Block计算图

更高效的改动

通过参数的设置来提高效率

设置 W g , W θ , W ϕ W_g,W_{\theta},W_{\phi} 的通道数是 x x 的通道数的一半,这是根据残差网路的论文来的,这样能减少计算量。

W z W_z 的通道数和 x x 一样,因为之后还要和 x x 相加

通过下采样的trick还能用来进一步减少计算量

y y 的方程式改为:
y i = 1 C ( x ^ ) j f ( x i , x j ^ ) g ( x j ^ ) y_i = \frac{1}{C(\hat{x})}\sum_{\forall j}f(x_i,\hat{x_j})g(\hat{x_j})
其中 x ^ \hat{x} x x 的下采样版本,加个池化层就能做到,能减少
成对计算的 1 / 4 1/4 计算量。

Non-local Block计算图 ϕ \phi 后面加个池化层来完成。


训练

backbone: ResNet-50

学习率:共400K次迭代,一开始设置为0.01,每150K除10

动量:0.9

权重衰减:0.0001

dropout:全局池化后使用,比例是0.5

使用BatchNorm来未调模型,能减少过拟合,BN的缩放参数初始化成0
在这里插入图片描述
l o s s 线 loss变化曲线


实验

在这里插入图片描述
效果图
在这里插入图片描述
b l o c k 网络深度和block数量的比较
越深的网络,越多的non-local block,效果越好,作者使用了残差块做了消融实验,说明了效果提升并不是网络变深的原因,而是non-local block的功劳

在这里插入图片描述
b l o c k 增加block的位置探究
如果添加的太晚的话,特征图较小( 7 × 7 7\times 7 )的话,效果也不好,所以要看特征图的大小
在这里插入图片描述
M a s k   R C N N Mask\ RCNN的效果提升


总结

感觉主要是对[ Attention is all you need ]做了些改动, y y 的公式是相似的,作者又提出了很多版本的 y y 的公式,最终选择哪个公式还是要看应用的环境,亮点应该在与从NLP领域迁移到了CV领域,而且做成了一个容易插入的block


代码

点乘版本的代码,这是源码

import torch
from torch import nn



class NL_block(nn.Module):
    def __init__(self,input_dim,sub_sample,bn):
        super(NL_block, self).__init__()

        self.input_dim = input_dim
        self.sub_sample = sub_sample
        self.sub_dim = self.input_dim // 2
        self.bn = bn

        if self.sub_dim == 0:
            self.sub_dim = 1

        Conv = nn.Conv2d
        Maxpool = nn.MaxPool2d
        BatchNorm = nn.BatchNorm2d

        self.theta = Conv(self.input_dim,self.sub_dim,kernel_size=1,stride=1)
        self.phi = Conv(self.input_dim,self.sub_dim,kernel_size=1,stride=1)
        self.g = Conv(self.input_dim,self.sub_dim,kernel_size=1,stride=1)

        # theta的 HxW 不能取半,否则就回不到原来的维度了
        if self.sub_sample:
            self.phi = nn.Sequential(
                self.phi,
                Maxpool(kernel_size=2)
            )
            self.g = nn.Sequential(
                self.g,
                Maxpool(kernel_size=2)
            )

        if self.bn:
            self.W = nn.Sequential(
                Conv(self.sub_dim,self.input_dim,kernel_size=1,stride=1),
                BatchNorm(num_features=self.input_dim)
            )
        else:
            self.W = Conv(self.sub_dim,self.input_dim)

    def forward(self,x):
        batch = x.shape[0]
        # theta并没有下采样,现在是原版的所有元素400个,通道数减少了一般
        x_theta = self.theta(x).view((batch,-1,self.sub_dim)) # 5 400 4
        # 对phi进行下采样,x的总数变成了100
        x_phi  = self.phi(x).view((batch,self.sub_dim,-1))  # 5 4 100
        
        # 让原版的特征图的每个元素都和所有的下采样的元素对应起来,这里的100维度代表权值
        f = torch.matmul(x_theta,x_phi) # 5 400 100
        # g也下采样
        g_x = self.g(x).view(batch,-1,self.sub_dim) # 5 100 4

        N = x_phi.shape[2]
        f_div_c = f / N

        # 5 4 20 20
        # 如此每个元素都是下采样后所有元素的加权和
        y = torch.matmul(f_div_c,g_x).view(batch,self.sub_dim,*x.size()[2:])

        z = self.W(y) + x

        return z

if __name__ = "__main__":
	x = torch.randn((5,8,20,20))
	model = NL_block(x.shape[1],True,True)
	result = model(x)

发布了63 篇原创文章 · 获赞 2 · 访问量 8028

猜你喜欢

转载自blog.csdn.net/McEason/article/details/104159368