首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >计算机视觉.用火炬进行2D卷积

计算机视觉.用火炬进行2D卷积
EN

Stack Overflow用户
提问于 2022-03-25 17:20:59
回答 1查看 177关注 0票数 0

昨天,我看到了一个有关解决方案的练习。

文本:

您的代码将接受形状(n,iC,H,W)的输入张量输入和带有形状的内核(oC、iC、kH、kW )。然后,它需要在输入上应用2D卷积,以核作为核张量,没有偏差,使用1的步幅,不膨胀,不分组,不填充,并将结果存储在外面。输入和内核都有dtype torch.float32。

解决方案:

代码语言:javascript
复制
#set-up code
import random
import torch

n = random.randint(2, 6)
iC = random.randint(2, 6)
oC = random.randint(2, 6)
H = random.randint(10, 20)
W = random.randint(10, 20)
kH = random.randint(2, 6)
kW = random.randint(2, 6)

input = torch.rand(n, iC, H, W, dtype=torch.float32)
kernel = torch.rand(oC, iC, kH, kW, dtype=torch.float32)

#solution code
oH, oW = H-(kH-1), W-(kW-1)
out = torch.zeros((n, oC, oH, oW), dtype=torch.float32)

for i in range(oH):
    for j in range(oW):
         inp = input.unsqueeze(1)[:, :, :,i: i+kH, j : j+kW] # shape inp => (n, 1, iC, H, W)
         ker = kernel.unsqueeze(0) # shape ker => (1, oC, iC, kH, kW)
         out[:, :, i, j] = (inp*ker).sum((-1, -2, -3)) #??

我的问题是:

为什么我们用这种方式解压()?

我知道解压缩()是如何工作的,但我不知道我们用这个解压缩()解决的问题。

只是为了对卷积作直观的参考:

谢谢!

EN

回答 1

Stack Overflow用户

发布于 2022-03-25 22:21:01

因此,我对我所作的评论作了一些阐述。

首先,如果我们一直展开它,我们可以用下面的七重嵌套for-循环来表示这个卷积运算。

代码语言:javascript
复制
out = torch.zeros((n, oC, oH, oW), dtype=torch.float32)
for out_i in range(oH):
    for out_j in range(oW):
        for b_idx in range(n):
            for out_ch in range(oC):
                for in_ch in range(iC):
                    for ker_i in range(kH):
                        for ker_j in range(kW):
                            out[b_idx, out_ch, out_i, out_j] += \
                                input[b_idx, in_ch, out_i + ker_i, out_j + ker_j] \
                                * kernel[out_ch, in_ch, ker_i, ker_j]

当然,这可能会很慢。相反,我们可以将内部的三个for-循环聚合到一个操作中,该操作通过跨越所有输入通道的输入张量的kW切片获取一个kW,并在所需的输出通道将其乘以内核的一个片段。

代码语言:javascript
复制
out = torch.zeros((n, oC, oH, oW), dtype=torch.float32)
for out_i in range(oH):
    for out_j in range(oW):
        for b_idx in range(n):
            for out_ch in range(oC):
                # input_slice -> [iC, kH, kW]
                input_slice = input[b_idx, :, out_i:out_i+kH, out_j:out_j+kW]
                # kernel_slice -> [iC, kH, kW]
                kernel_slice = kernel[out_ch, :, :, :]
                out[b_idx, out_ch, out_i, out_j] = (input_slice * kernel_slice).sum()

注意到,在这个最新版本中,input_slice是从单个批处理索引(b_idx)中提取的,而kernel_slice是从单个输出通道(out_ch)获取的。我们计算b_idxout_ch的所有组合来填充输出。当我们看到这种类型的模式时,应该会想到广播业

首先,如果我们只是在所有批处理(例如input[:, :, out_i:out_i + kH, out_j:out_j + kW])上获取输入片段,那么这将是一个[n, iC, kH, kW]张量。由于内核是形状的[oC, iC, kH, kW],所以不能在一起广播,因为它们在第一个维度上不一致。为了处理这个问题,我们需要插入一些幺正维,所以它们在任何地方都是一致的,这两者都有非酉维。

由于我们希望减少广播产品的输出,并将其存储在形状为out[n, oC, ...]中,因此我们希望按以下方式插入酉维数:

代码语言:javascript
复制
out = torch.zeros((n, oC, oH, oW), dtype=torch.float32)
for out_i in range(oH):
    for out_j in range(oW):
        # input_slice -> [n, 1, iC, kH, kW]
        input_slice = input[:, :, out_i:out_i+kH, out_j:out_j+kW].unsqueeze(1)
        # kernel_slice -> [1, oC, iC, kH, kW]
        kernel_slice = kernel[:, :, :, :].unsqueeze(0)
        # broadcasted shape [n, 1, ...] times shape [1, oC, ...] -> [n, oC, ...]
        # therefore prod_slice -> [n, oC, iC, kH, kW]
        prod_slice = input_slice * kernel_slice
        # sum over last three channels producing reduced_slice -> [n, oC]
        reduced_slice = prod_slice.sum((-1, -2, -3))
        out[:, :, out_i, out_j] = reduced_slice

注意,我们可以通过在输入片上使用.unsqueeze(0)和在内核片上使用.unsqueeze(1)来实现有效的广播。然而,这将导致reduced_slice成为shape [oC, n],而不是[n, oC],后者将是我们希望存储在out中的内容的转换。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/71620848

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档