如何在pytorch中实现Conv2d的棋盘大步? [英] How can I implement a checkerboard stride for Conv2d in pytorch?

查看:138
本文介绍了如何在pytorch中实现Conv2d的棋盘大步?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试使用pytorch创建一个convnet来处理2d矩阵的输入.我正在使用3x5滤镜,但我希望它具有以下自定义步幅-在偶数行号上,我希望滤镜从位置0(图像中的红色)处的元素开始,在奇数行号上,我希望它开始在位置1的元素上(图像中为蓝色),并且在两种情况下在x方向上的步幅均为2.这意味着,如果我有一个图像中的矩阵作为输入,我希望过滤器的中心只有0.我知道这在卷积网络中非常不寻常,但这实际上是物理学中的问题,因此准确的步幅很重要.

I am trying to create a convnet using pytorch to work on an input of 2d matrices. I am using a 3x5 filter and I want it to have a custom stride as follows - on even line numbers I want the filter to start from the element at position 0 (red in the image), on odd line numbers I want it to start on the element of position 1 (blue in the image), and in both cases have a stride of 2 on the x direction. That means that if I have a matrix as in the image as my input, I want the filter to have only 0s in its center. I know this is very unusual in convnets but this is actually a problem in physics so the exact stride is important.

推荐答案

下面的自定义conv2d层按照棋盘上的步幅实现卷积,如原始问题所示.此处的困难在于pytorch并不真正支持这样的不一致步幅.也就是说,我们可以将该操作分为两个独立的跨步卷积,一个用于偶数行,一个用于奇数行.之后,我们可以将结果交织在一起.下面的代码中有一些详细信息,可确保我们正确填充(如果需要).另外,该层完全支持反向传播.

The following custom conv2d layer implements convolutions in a checkerboard stride as indicated in the original question. The difficulty here lies with the fact that pytorch doesn't really support inconsistent strides like this. That said we can break this operation into two separate strided convolutions, one for the even rows, and one for the odd rows. After that we can just interleave the results back together. There are some details in the code below which ensure we pad correctly (if desired). Also, this layer fully supports back-propagation.

import torch.nn as nn
import torch.nn.functional as F

class AMNI_Conv2d(nn.Module):
    def __init__(self, in_channels, out_channels, kernel_size, padding=0, bias=True):
        super().__init__()
        self.conv = nn.Conv2d(in_channels, out_channels, kernel_size, bias=bias, padding=padding)
        self.crow = self.conv.kernel_size[0] // 2
        self.ccol = self.conv.kernel_size[1] // 2

        # this module only works with odd sized kernels
        assert self.conv.kernel_size[0] % 2 == 1 and self.conv.kernel_size[1] % 2 == 1

    def forward(self, x):
        # currently only padding with zeros is supported
        if self.conv.padding[0] != 0 or self.conv.padding[1] != 0:
            x = F.pad(x, pad=(self.conv.padding[1], self.conv.padding[1], self.conv.padding[0], self.conv.padding[0]))

        # center filters on the "zeros" according to the diagram by AMNI, starting column for even/odd rows may need to change depending on padding/kernel size
        if (self.crow + self.ccol + self.conv.padding[0] + self.conv.padding[1]) % 2 == 0:
            x_even = F.conv2d(x[:, :, :-1, 1:], self.conv.weight, self.conv.bias, stride=2)
            x_odd = F.conv2d(x[:, :, 1:, :-1], self.conv.weight, self.conv.bias, stride=2)
        else:
            x_even = F.conv2d(x[:, :, :-1, :-1], self.conv.weight, self.conv.bias, stride=2)
            x_odd = F.conv2d(x[:, :, 1:, 1:], self.conv.weight, self.conv.bias, stride=2)
        b, c, h, w = x_even.shape

        # interleave even and odd rows back together
        return torch.stack((x_even, x_odd), dim=3).contiguous().view(b, c, -1, w)


示例

该层基本上像普通的Conv2d一样,但具有棋盘格跨度.

This layer basically acts like a normal Conv2d but with the checkerboard stride.

>>> x = torch.arange(64).view(1, 1, 8, 8).float()
tensor([[[[ 0.,  1.,  2.,  3.,  4.,  5.,  6.,  7.],
          [ 8.,  9., 10., 11., 12., 13., 14., 15.],
          [16., 17., 18., 19., 20., 21., 22., 23.],
          [24., 25., 26., 27., 28., 29., 30., 31.],
          [32., 33., 34., 35., 36., 37., 38., 39.],
          [40., 41., 42., 43., 44., 45., 46., 47.],
          [48., 49., 50., 51., 52., 53., 54., 55.],
          [56., 57., 58., 59., 60., 61., 62., 63.]]]])


>>> layer = AMNI_Conv2d(1, 1, (3, 5), bias=False)

# set kernels to delta functions to demonstrate kernel centers
>>> with torch.no_grad():
...     layer.conv.weight.zero_()
...     layer.conv.weight[:,:,1,2] = 1

>>> result = layer(x)
tensor([[[[10., 12.],
          [19., 21.],
          [26., 28.],
          [35., 37.],
          [42., 44.],
          [51., 53.]]]], grad_fn=<ViewBackward>)

您也可以通过填充来做到这一点,以使原始图中的每个零"都可以得到

You could also do this with padding to get every "zero" in the original diagram

>>> layer = AMNI_Conv2d(1, 1, (3, 5), padding=(1, 2), bias=False)

# set kernels to delta functions to demonstrate kernel centers
>>> with torch.no_grad():
...     layer.conv.weight.zero_()
...     layer.conv.weight[:,:,1,2] = 1

>>> result = layer(x)
tensor([[[[ 1.,  3.,  5.,  7.],
          [ 8., 10., 12., 14.],
          [17., 19., 21., 23.],
          [24., 26., 28., 30.],
          [33., 35., 37., 39.],
          [40., 42., 44., 46.],
          [49., 51., 53., 55.],
          [56., 58., 60., 62.]]]], grad_fn=<ViewBackward>)

这篇关于如何在pytorch中实现Conv2d的棋盘大步?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆