这是篇很有意思的文章,为什么呢,我们来分析一下,
CNN是固定尺寸,大小,位置的卷积操作,但是很多情况下,图像的信息并不是固定尺寸的,然后dcn提出了,参见下文。
https://blog.csdn.net/u014119694/article/details/93210130
咋搞的呢,就是增加偏移量啊,我还是3x3的卷积,但是取不同位置,给他增加个偏移,是不是就很优秀啊,nonono,有更简单并且粗暴的方法,那就是non local
请看,g表示原图像素点的特征,f表示位置关系,相当于f表示图中的连接关系,只要点i和j有关系,其f必定很大,然后再乘个当前点的特征,相加,就是改点卷积后的特征,其实这里的f不仅仅表示是否相连,还表示怎么连的,乘上g就表示j点的信息,
ok,so easy ,这样是不是提取的特征更强烈啊。
ok,具体块吐下,w还是卷积操作,只不过卷积的是y,y这里就表示上文中得到的各个点相结合的信息。然后再来个x,表示残差。
图示也很清楚,这里唯一要解决的就是怎么计算f,原文中定义了几种不同的f,图中theta和phi的结合就表示f,然后乘个g,各个矩阵的维度很清晰。
ok,最后还是要上代码了
- 全是1x1的没什么好说的,就是提取特征,然后计算,这里不再用3x3的原因就是我已经获取了周围的信息,还用3x3的纯属浪费时间。
self.g = ConvModule(
self.in_channels,
self.inter_channels,
kernel_size=1,
activation=None)
self.theta = ConvModule(
self.in_channels,
self.inter_channels,
kernel_size=1,
activation=None)
self.phi = ConvModule(
self.in_channels,
self.inter_channels,
kernel_size=1,
activation=None)
self.conv_out = ConvModule(
self.inter_channels,
self.in_channels,
kernel_size=1,
conv_cfg=conv_cfg,
norm_cfg=norm_cfg,
activation=None)
前向操作。首先各自1x1卷积,然后把reshape一下,使theta和phi能相互操作,然后pairwise_func表示f函数,这里不再指出,最后乘个g就得到y。
def forward(self, x):
n, _, h, w = x.shape
# g_x: [N, HxW, C]
g_x = self.g(x).view(n, self.inter_channels, -1)
g_x = g_x.permute(0, 2, 1)
# theta_x: [N, HxW, C]
theta_x = self.theta(x).view(n, self.inter_channels, -1)
theta_x = theta_x.permute(0, 2, 1)
# phi_x: [N, C, HxW]
phi_x = self.phi(x).view(n, self.inter_channels, -1)
pairwise_func = getattr(self, self.mode)
# pairwise_weight: [N, HxW, HxW]
pairwise_weight = pairwise_func(theta_x, phi_x)
# y: [N, HxW, C]
y = torch.matmul(pairwise_weight, g_x)
# y: [N, C, H, W]
y = y.permute(0, 2, 1).reshape(n, self.inter_channels, h, w)
output = x + self.conv_out(y)
return output