本地关系网络
局部关系网络是由清华大学和微软研究院的研究人员提出的。本文背后的想法是,卷积层多年来一直是计算机视觉领域的主要特征提取器。然而,这种空间聚合过程只是一种模式匹配过程,对于对具有变化空间分布的视觉元素进行建模的视觉过程是低效的。
为了解决这种低效率问题,本文提出了一种新的图像特征提取器,称为局部关系层,它根据局部像素对的组合关系自适应地确定聚合权重。通过这种关系方法,它可以以更有效的方式将视觉元素组合到更高级别的实体中,从而有利于语义推理。局部关系层可以直接用来代替卷积层,但有一些开销。
建筑学:
在本节中,我们将描述基于局部关系层的特征提取器的一般公式:
让我们考虑一个层的输入和输出x∈R*C*H*W y∈R*C*H*W ,其中C和C'是输入/输出特征的通道, h, w, h', w'是输入/输出空间分辨率。现有的基本图像特征一般通过对先前特征进行加权聚合来产生输出特征,可以用下面的等式表示:
其中c, c'和p = (h, w), p' = (h', w' ) 分别索引输入和输出通道以及特征图位置; Ω c 和Ω p 分别表示在通道 c' 和位置 p' 产生输出特征值时输入特征的通道和空间聚合的范围; ω(c', c, p', p)表示从 c, p 到 c', p'的聚合权重。
- 参数化方法:定义了要学习的模型权重,最常见的参数化方法是直接学习权重w。有一些方法也可以学习元网络权重(∅)。
- 聚合范围:定义了聚合计算涉及的通道范围和空间位置。对于通道范围,常规卷积在计算每个通道输出时包括所有输入通道。为了提高效率,一些方法只考虑一个或一组输入通道来产生更大的输出特征的一个通道。
- 聚合权重:这些通常是在网络参数中学习的,或者是从这些参数中计算出来的。几乎所有卷积网络的变体都是自上而下的计算机,它们要么跨位置固定,要么由跨位置输入特征的元网络确定。
本地关系层
局部关系层可以用以下表达式表示:
在哪里, 是目标像素 p' 和其范围内的像素 p 之间可组合性的度量,基于它们在变换后的外观和 .
- 地点: The 自下而上 特征 通常在完整图像上方聚合输入特征。但是,局部关系层将聚合计算限制在一个小的局部区域,即 7×7 邻域。事实证明,这种方法在利用大内核方面更有效,并提供稳定的准确度变化,而 ConvNets 的准确度随着训练步骤的增加而饱和。这可能是因为 ConvNets 的能力受到过滤器数量的限制。
- 外观可组合性:作者遵循深度学习中的常用方法,即计算外观可组合性:
- 其中x p和 x p' 投影到查询和键嵌入空间。虽然在以前的模型中,这些被用作向量,但在这里作者将它们用作缩放器,并认为它们具有更好的速度到准确度。作者使用以下实例化∅。
- 平方差:
- 绝对差异:
- 乘法:
- 几何先验:区分局部关系层与其他卷积层的一个重要方面是几何先验的使用。几何先验由一个小网络在 p 到 p' 的相对位置上编码。这个小型网络由双通道转换层组成,它们之间有一个 ReLU 激活。作者认为,小型网络比直接学习几何先验值更好,尤其是当邻域大小很大时。
- 权重归一化:该层使用 softmax 进行权重归一化。
- 通道共享:在每个局部关系层之后,作者在聚合计算中使用通道共享,其中多个通道共享相同的聚合权重。虽然这会减少一些计算,但不会显着影响准确性。
局部关系层的总复杂度可以计算为:
其中, H×W是每次聚合计算的输入特征图、 k×k空间邻域、C 个通道和 m 个通道的维度。
本地关系网络:
LR-Net 与 ResNet 架构类似,只是 ResNet 中的所有卷积层都被局部关系层取代。下面是局部关系网络(LR-Net)的架构: 1×1, 64 7×7 LR, 64, stride, 2 1×1, 64 3×3 conv, 64 1×1, 256 1×1, 100 7×7 LR, 100 1×1, 256 1×1, 128 3×3 conv, 128 1×1, 512 1×1, 200 7×7 LR, 200 1×1, 512 1×1, 256 3×3 conv, 256 1×1, 1024 1×1, 400 7×7 LR, 400 1×1, 1024 1×1, 256 3×3 conv, 512 1×1, 2048 1×1, 800 7×7 LR, 800 1×1, 2048 global average pool 1000-d fc, softmax global average pool 1000-d fc, softmaxStage Output ResNet-50 LR-Net-50 (7×7, m=8) res1 112*112 7×7 conv, 64, stride, 2 res2 (x3) 56*56 3×3 max pool, stride, 2 3×3 max pool, stride, 2 res3 (x4) 28*28 res4 (x6) 14×14 res5 (x3) 7×7 1×1 # params 25.5 x 106 23.3 x 106 FLOPs 4.3 x 109 4.3 x 109
执行
在这个实现中,我们将使用 PyTorch 和 Torchvision 库。这些库预先安装在 Colaboratory 中。要在本地安装这些模块,请查看本指南:
Python3
import torch
class GeometricPriori(torch.nn.Module):
def __init__(self, k, channels, multiplier=0.5):
super(GeometricPriori, self).__init__()
self.channels = channels
self.k = k
self.position = 2 * torch.rand(1, 2, k, k, requires_grad=True) - 1
self.l1 = torch.nn.Conv2d(2, int(multiplier * channels), 1)
self.l2 = torch.nn.Conv2d(int(multiplier * channels), channels, 1)
def forward(self, x):
x = self.l2(torch.nn.functional.relu(self.l1(self.position)))
return x.view(1, self.channels, 1, self.k ** 2)
class KeyandQueryMap(torch.nn.Module):
def __init__(self, channels, m):
super(KeyandQueryMap, self).__init__()
self.l = torch.nn.Conv2d(channels, channels // m, 1)
def forward(self, x):
return self.l(x)
class AppearanceComposability(torch.nn.Module):
def __init__(self, k, padding, stride):
super(AppearanceComposability, self).__init__()
self.k = k
self.unfold = torch.nn.Unfold(k, 1, padding, stride)
def forward(self, x):
key_map, query_map = x
k = self.k
key_map_unfold = self.unfold(key_map)
query_map_unfold = self.unfold(query_map)
key_map_unfold = key_map_unfold.view(
key_map.shape[0], key_map.shape[1],
-1,
key_map_unfold.shape[-2] // key_map.shape[1])
query_map_unfold = query_map_unfold.view(
query_map.shape[0], query_map.shape[1],
-1,
query_map_unfold.shape[-2] // query_map.shape[1])
return key_map_unfold * query_map_unfold[:, :, :, k**2//2:k**2//2+1]
def combine_priors(appearance_kernel, geometry_kernel):
return torch.nn.functional.softmax(appearance_kernel + geometry_kernel,
dim=-1)
class LocalRelationLayer(torch.nn.Module):
"""
Define Local Relational Layer as given in the paper
"""
def __init__(self, channels, k, stride=1, m=None, padding=0):
super(LocalRelationalLayer, self).__init__()
self.channels = channels
self.k = k
self.stride = stride
self.m = 8
if(m != 8 and m != None):
self.m =m
self.padding = padding
self.kmap = KeyandQueryMap(channels, k)
self.qmap = KeyandQueryMap(channels, k)
self.ac = AppearanceComposability(k, padding, stride)
self.gp = GeometricPriori(k, channels//m)
self.unfold = torch.nn.Unfold(k, 1, padding, stride)
self.final1x1 = torch.nn.Conv2d(channels, channels, 1)
def forward(self, x):
gpk = self.gp(0)
km = self.kmap(x)
qm = self.qmap(x)
ak = self.ac((km, qm))
ck = combine_priors(ak, gpk)[:, None, :, :, :]
x_unfold = self.unfold(x)
x_unfold = x_unfold.view(x.shape[0], self.m, x.shape[1] // m,
-1, x_unfold.shape[-2] // x.shape[1])
pre_output = (ck * x_unfold).view(x.shape[0], x.shape[1],
-1, x_unfold.shape[-2] // x.shape[1])
h_out = (x.shape[2] + 2 * self.padding - 1 * (self.k - 1) - 1) // \
self.stride + 1
w_out = (x.shape[3] + 2 * self.padding - 1 * (self.k - 1) - 1) // \
self.stride + 1
pre_output = torch.sum(pre_output, axis=-1).view(x.shape[0], x.shape[1],
h_out, w_out)
return self.final1x1(pre_output)
layer = LocalRelationalLayer(channels=64,k=7,stride=1,m=8)
print(layer)
LocalRelationalLayer(
(kmap): KeyQueryMap(
(l): Conv2d(64, 9, kernel_size=(1, 1), stride=(1, 1))
)
(qmap): KeyQueryMap(
(l): Conv2d(64, 9, kernel_size=(1, 1), stride=(1, 1))
)
(ac): AppearanceComposability(
(unfold): Unfold(kernel_size=7, dilation=1, padding=0, stride=1)
)
(gp): GeometryPrior(
(l1): Conv2d(2, 4, kernel_size=(1, 1), stride=(1, 1))
(l2): Conv2d(4, 8, kernel_size=(1, 1), stride=(1, 1))
)
(unfold): Unfold(kernel_size=7, dilation=1, padding=0, stride=1)
(final1x1): Conv2d(64, 64, kernel_size=(1, 1), stride=(1, 1))
)
参考:
- LR-Net纸