给定张量
x
和 y
,每个形状为 (num_batches, d)
,如何使用 PyTorch 计算批次中 x
和 y
的每个组合的总和?
这与外积类似,只不过我们不想相乘,而是sum。 (这意味着我可以通过求幂、外积和取对数来解决这个问题,但这当然有数值和性能上的缺点)。
可以通过笛卡尔积来完成,然后对每个组合求和。
本质上,我想要
osum[b, i, j] == x[b, i] + y[b, j]
。 PyTorch 可以在张量中做到这一点,而不需要循环吗?
这可以通过将单例维度引入
x
和 y
以及沿这些单例维度进行 广播 来轻松完成:
osum = x[..., None] + y[:, None, :]
例如:
x = torch.arange(6).view(2,3)
y = x * 10
osum = x[..., None] + y[:, None, :]
结果:
tensor([[[ 0, 10, 20], [ 1, 11, 21], [ 2, 12, 22]], [[33, 43, 53], [34, 44, 54], [35, 45, 55]]])
更新(7 月 14 日): 它是如何工作的?
您有两个张量,
x
和 y
,形状为 b
xn
,并且您想要计算:
osum[b,i,j] = x[b, i] + y[b, j]
从概念上讲,我们可以通过沿第三个维度重复
xx
和 yy
的每个元素来创建新变量 x
和 y
,这样:
xx[b, i, j] == x[b, i] # for all j
yy[b, i, j] == y[b, j] # for all i
通过这些新变量,很容易看出:
osum = xx + yy
从那时起,根据定义
osum[b, i, j] == xx[b, i, j] + yy[b, i, j] == x[b, i] + y[b, j]
torch.expand
或 torch.repeat
等命令来显式地创建 xx
和 yy
- 但为什么还要麻烦呢?由于它们的元素只是沿特定维度的元素的简单重复,因此广播会隐式地为您完成此操作。
您可以使用广播来执行此类操作:
>>> x = torch.randint(0,10,(2,4))
tensor([[0, 6, 5, 8],
[3, 0, 7, 5]])
>>> y = torch.randint(0,10,(2,5))
tensor([[6, 9, 9, 8, 7],
[0, 4, 6, 2, 5]])
>>> x[:,:,None].shape
(2, 4, 1)
>>> y[:,None].shape
(2, 1, 5])
向不同的维度添加单例可确保“外部”操作 已执行。
>>> osum = x[:,:,None] + y[:,None]
tensor([[[ 6, 9, 9, 8, 7],
[12, 15, 15, 14, 13],
[11, 14, 14, 13, 12],
[14, 17, 17, 16, 15]],
[[ 3, 7, 9, 5, 8],
[ 0, 4, 6, 2, 5],
[ 7, 11, 13, 9, 12],
[ 5, 9, 11, 7, 10]]])
也许有点晚了,但是如果你将结构化张量放在列表中,你可以做这样的事情。
def outer_sum(x: List[torch.Tensor]) -> torch.Tensor:
"""
Returns the outer sum of a list of one-dimensional arrays
Example:
x = [a, b, c]
out = a[..., None, None] + b[..., None] + c
"""
def _sum(a, b):
return a[..., None] + b
return reduce(_sum, x)
使用
reduce
中的 functools
可以得到更优雅的解决方案。