【问题标题】:How to batch matrix-vector multiplication (one matrix, many vectors) in pytorch without duplicating the matrix in memory如何在pytorch中批量矩阵向量乘法(一个矩阵,多个向量)而不在内存中复制矩阵
【发布时间】:2021-09-12 11:40:00
【问题描述】:

我有n 大小为d 的向量和一个d x d 矩阵J。我想计算J 与每个n 向量的n 矩阵向量乘法。

为此,我使用 pytorch 的expand() 来获得J广播,但似乎在计算矩阵向量积时,pytorch 实例化了一个完整的n x d x d 张量记忆。例如以下代码

device = torch.device("cuda:0")
n = 100_000_000
d = 10

x = torch.randn(n, d, dtype=torch.float32, device=device)
J = torch.randn(d, d, dtype=torch.float32, device=device).expand(n, d, d)
y = torch.sign(torch.matmul(J, x[..., None])[..., 0])

加注

RuntimeError: CUDA out of memory. Tried to allocate 37.25 GiB (GPU 0; 11.00 GiB total capacity; 3.73 GiB already allocated; 5.69 GiB free; 3.73 GiB reserved in total by PyTorch)

这意味着 pytorch 不必要地尝试为矩阵 Jn 副本分配空间

如何在不耗尽 GPU 内存的情况下以向量化方式(矩阵很小,所以我不想循环每个矩阵向量乘法)执行此任务?

【问题讨论】:

    标签: python pytorch torch


    【解决方案1】:

    我认为这会解决它:

    import torch
    x = torch.randn(n, d)
    J = torch.randn(d, d) # no need to expand
    
    y = torch.matmul(J, x.T).T
    

    使用您的表达式进行验证:

    Jex = J.expand(n, d, d)
    y1 = torch.matmul(Jex, x[..., None])[..., 0]
    y = torch.matmul(J, x.T).T
    
    torch.allclose(y1, y) # using allclose for float values
    # tensor(True)
    

    【讨论】:

    • 谢谢!后续问题:如果 J 确实由 n 个不同的矩阵组成,而原始矩阵对两种情况都是正确的(将所有 n 个向量乘以相同的矩阵或乘以 n 个不同的矩阵),那么您给出的习语不会给出正确的答案。有没有办法在不广播的情况下也拥有这个功能?
    • 另一种选择是torch.matmul(J, x[..., None]).squeeze(-1),但您必须在此处广播x 才能执行批量矩阵向量乘法。我假设J 的形状为 n x d x dn x dx。 matmul 返回一个形状为 n x d x 1 的张量,这就是为什么我添加了一个 squeeze() 来删除多余的最后一个维度。
    • 还要注意,在上面的表达式中,不需要转置,因为我们在形状 nxdxd 和 _n xdx 1_(@ 987654328@)。这一切都归结为您如何将张量传递给matmul 函数。
    • 我刚刚意识到torch.matmul(J, x[..., None]).squeeze(-1) 正是您在问题中所做的!哈哈,我猜x这里的广播是不可避免的!或者您可以将其作为一个单独的问题提出。