使用 torch.matmul 将 3d 张量与 2d 矩阵相乘
Multiply a 3d tensor with a 2d matrix using torch.matmul
我在 PyTorch 中有两个张量,z
是形状为 (n_samples, n_features, n_views)
的 3d 张量,其中 n_samples
是数据集中的样本数,n_features
是每个样本的特征数量,n_views
是描述相同 (n_samples, n_features)
特征矩阵但具有其他值的不同视图的数量。
我有另一个二维张量 b
,形状为 (n_samples, n_views)
,其目的是重新调整样本在不同视图中的所有特征。换句话说,它封装了同一样本的每个视图的特征的重要性。
例如:
import torch
z = torch.Tensor(([[2,3], [1,1], [4,5]],
[[2,2], [1,2], [7,7]],
[[2,3], [1,1], [4,5]],
[[2,3], [1,1], [4,5]]))
b = torch.Tensor(([1, 0],
[0, 1],
[0.2, 0.8],
[0.5, 0.5]))
print(z.shape, b.shape)
>>>torch.Size([4, 3, 2]) torch.Size([4, 2])
我想通过 z
和 b
之间的运算获得形状为 (n_samples, n_features)
的第三个张量 r
。
一种可能的解决方案是:
b = b.unsqueeze(1)
r = z * b
r = torch.sum(r, dim=-1)
print(r, r.shape)
>>>tensor([[2.0000, 1.0000, 4.0000],
[2.0000, 2.0000, 7.0000],
[2.8000, 1.0000, 4.8000],
[2.5000, 1.0000, 4.5000]]) torch.Size([4, 3])
是否可以使用 torch.matmul()
获得相同的结果?。我多次尝试置换两个向量的维度,但无济于事。
是的,这是可能的。如果您在两个操作中都有多个批次维度,则可以使用广播。在这种情况下,每个操作数的最后两个维度被解释为 矩阵 大小。 (我建议在 documentation 中查找它。)
所以你需要为你的向量增加一个维度b
,使它们成为n x 1
“矩阵”(列向量):
# original implementation
b1 = b.unsqueeze(1)
r1 = z * b1
r1 = torch.sum(r1, dim=-1)
print(r1.shape)
# using torch.matmul
r2 = torch.matmul(z, b.unsqueeze(2))[...,0]
print(r2.shape)
print((r1-r2).abs().sum()) # should be zero if we do the same operation
另外,torch.einsum
也使这非常简单。
# using torch.einsum
r3 = torch.einsum('ijk,ik->ij', z, b)
print((r1-r3).abs().sum()) # should be zero if we do the same operation
einsum
是一个非常强大的操作,可以做很多事情:您可以排列张量维度、对它们求和或执行标量积,所有这些都可以使用或不使用广播。它来源于Einstein summation convention mostly used in physics. The rough idea is that you give every dimension of your operans a name, and then, using these names define what the output should look like. I think it is best to read the documentation。在我们的例子中,我们有一个 4 x 3 x 2
张量和一个 4 x 2
张量。所以我们称第一个张量的维度为ijk
。这里i
和k
应该被认为是第二个张量的维度相同,所以这个可以描述为ik
。最后输出应该是 ij
(它必须是 4 x 3
张量)。从这个“签名”ijk, ik -> ij
可以清楚地看出维度i
被保留了,而维度k
必须是“summe/multiplied”(标量积)。
我在 PyTorch 中有两个张量,z
是形状为 (n_samples, n_features, n_views)
的 3d 张量,其中 n_samples
是数据集中的样本数,n_features
是每个样本的特征数量,n_views
是描述相同 (n_samples, n_features)
特征矩阵但具有其他值的不同视图的数量。
我有另一个二维张量 b
,形状为 (n_samples, n_views)
,其目的是重新调整样本在不同视图中的所有特征。换句话说,它封装了同一样本的每个视图的特征的重要性。
例如:
import torch
z = torch.Tensor(([[2,3], [1,1], [4,5]],
[[2,2], [1,2], [7,7]],
[[2,3], [1,1], [4,5]],
[[2,3], [1,1], [4,5]]))
b = torch.Tensor(([1, 0],
[0, 1],
[0.2, 0.8],
[0.5, 0.5]))
print(z.shape, b.shape)
>>>torch.Size([4, 3, 2]) torch.Size([4, 2])
我想通过 z
和 b
之间的运算获得形状为 (n_samples, n_features)
的第三个张量 r
。
一种可能的解决方案是:
b = b.unsqueeze(1)
r = z * b
r = torch.sum(r, dim=-1)
print(r, r.shape)
>>>tensor([[2.0000, 1.0000, 4.0000],
[2.0000, 2.0000, 7.0000],
[2.8000, 1.0000, 4.8000],
[2.5000, 1.0000, 4.5000]]) torch.Size([4, 3])
是否可以使用 torch.matmul()
获得相同的结果?。我多次尝试置换两个向量的维度,但无济于事。
是的,这是可能的。如果您在两个操作中都有多个批次维度,则可以使用广播。在这种情况下,每个操作数的最后两个维度被解释为 矩阵 大小。 (我建议在 documentation 中查找它。)
所以你需要为你的向量增加一个维度b
,使它们成为n x 1
“矩阵”(列向量):
# original implementation
b1 = b.unsqueeze(1)
r1 = z * b1
r1 = torch.sum(r1, dim=-1)
print(r1.shape)
# using torch.matmul
r2 = torch.matmul(z, b.unsqueeze(2))[...,0]
print(r2.shape)
print((r1-r2).abs().sum()) # should be zero if we do the same operation
另外,torch.einsum
也使这非常简单。
# using torch.einsum
r3 = torch.einsum('ijk,ik->ij', z, b)
print((r1-r3).abs().sum()) # should be zero if we do the same operation
einsum
是一个非常强大的操作,可以做很多事情:您可以排列张量维度、对它们求和或执行标量积,所有这些都可以使用或不使用广播。它来源于Einstein summation convention mostly used in physics. The rough idea is that you give every dimension of your operans a name, and then, using these names define what the output should look like. I think it is best to read the documentation。在我们的例子中,我们有一个 4 x 3 x 2
张量和一个 4 x 2
张量。所以我们称第一个张量的维度为ijk
。这里i
和k
应该被认为是第二个张量的维度相同,所以这个可以描述为ik
。最后输出应该是 ij
(它必须是 4 x 3
张量)。从这个“签名”ijk, ik -> ij
可以清楚地看出维度i
被保留了,而维度k
必须是“summe/multiplied”(标量积)。