昨天,我看到了一个有关解决方案的练习。
文本:
您的代码将接受形状(n,iC,H,W)的输入张量输入和带有形状的内核(oC、iC、kH、kW )。然后,它需要在输入上应用2D卷积,以核作为核张量,没有偏差,使用1的步幅,不膨胀,不分组,不填充,并将结果存储在外面。输入和内核都有dtype torch.float32。
解决方案:
#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)) #??我的问题是:
为什么我们用这种方式解压()?
我知道解压缩()是如何工作的,但我不知道我们用这个解压缩()解决的问题。
只是为了对卷积作直观的参考:

谢谢!
发布于 2022-03-25 22:21:01
因此,我对我所作的评论作了一些阐述。
首先,如果我们一直展开它,我们可以用下面的七重嵌套for-循环来表示这个卷积运算。
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,并在所需的输出通道将其乘以内核的一个片段。
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_idx和out_ch的所有组合来填充输出。当我们看到这种类型的模式时,应该会想到广播业。
首先,如果我们只是在所有批处理(例如input[:, :, out_i:out_i + kH, out_j:out_j + kW])上获取输入片段,那么这将是一个[n, iC, kH, kW]张量。由于内核是形状的[oC, iC, kH, kW],所以不能在一起广播,因为它们在第一个维度上不一致。为了处理这个问题,我们需要插入一些幺正维,所以它们在任何地方都是一致的,这两者都有非酉维。
由于我们希望减少广播产品的输出,并将其存储在形状为out的[n, oC, ...]中,因此我们希望按以下方式插入酉维数:
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中的内容的转换。
https://stackoverflow.com/questions/71620848
复制相似问题