【问题标题】:Einsum for high dimensions用于高维度的 Einsum
【发布时间】:2018-10-05 20:06:10
【问题描述】:

考虑下面的3个数组:

np.random.seed(0)

X = np.random.randint(10, size=(4,5))
W = np.random.randint(10, size=(3,4))
y = np.random.randint(3, size=(5,1))

我想将矩阵 X 的每一列添加和求和到 W 的行,由 y 作为索引给出。因此,例如,如果 y 中的第一个元素是 3 ,我会将 X 的第一列添加到 W 的第四行(python 中的索引 3)并求和。我会一遍又一遍地做,直到 X 的所有列都添加到 W 的特定行并求和。 我可以用不同的方式做到这一点: 1-使用for循环:

for i,j in enumerate(y):
    W[j]+=X[:,i] 

2- 使用 add.at 函数

np.add.at(W,(y.ravel()),X.T)

3- 但我不明白如何使用 einsum 来做到这一点。 我得到了一个解决方案,但真的无法理解。

N = y.max()+1
W[:N] += np.einsum('ijk,lk->il',(np.arange(N)[:,None,None] == y.ravel()),X) 

谁能解释一下这个结构? 1 - (np.arange(N)[:,None,None] == y.ravel(),X)。我想这部分是指根据 y 将 X 的列与 W 的特定行相加。但是 W 在哪里?在这种情况下,为什么我们必须将 W 转换为 4 维? 2- 'ijk,lk->il' - 我也不明白这一点。

i - 指行, j - 列, k- 每个元素, l - 'l' 也指什么? 如果有人能理解这一点并向我解释,我将不胜感激。 提前致谢。

【问题讨论】:

    标签: python linear-algebra numpy-einsum


    【解决方案1】:

    让我们通过删除一维并使用易于手动验证的值来简化问题:

    W = np.zeros(3, np.int)
    y = np.array([0, 1, 1, 2, 2])
    X = np.array([1, 2, 3, 4, 5])
    

    向量W 中的值通过y 查找从X 获取附加值:

    for i, j in enumerate(y):
        W[j] += X[i]
    

    W 计算为[1, 5, 9],(手动快速检查)。

    现在,如何对这段代码进行矢量化?我们不能做一个简单的W[y] += X[y],因为y 中有重复的值,并且不同的和会在索引 1 和 2 处相互覆盖。

    可以做的是将这些值广播到len(y) 的新维度,然后对这个新创建的维度求和。

    N = W.shape[0]
    select = (np.arange(N) == y[:, None]).astype(np.int)
    

    W[0, 1, 2])的索引范围,并将它们与y匹配的值在新维度中设置为1,否则为0。select包含此数组:

    array([[1, 0, 0],
           [0, 1, 0],
           [0, 1, 0],
           [0, 0, 1],
           [0, 0, 1]])
    

    它有len(y) == len(X) 行和len(W) 列,并为每个y/行显示它对W 的索引有什么贡献。

    让我们将 X 与这个数组相乘,mult = select * X[:, None]

    array([[1, 0, 0],
           [0, 2, 0],
           [0, 3, 0],
           [0, 0, 4],
           [0, 0, 5]])
    

    我们有效地将 X 展开到一个新的维度,并以一种我们可以通过对新创建的维度求和的方式将其排序为 W 形状。行的总和是我们要添加到W的向量:

    sum_Xy = np.sum(mult, axis=0)  # [1, 5, 9]
    W += sum_Xy
    

    selectmult的计算可以和np.einsum结合:

    # `select` has shape (len(y)==len(X), len(W)), or `yw`
    # `X` has shape len(X)==len(y), or `y`
    # we want something `len(W)`, or `w`, and to reduce the other dimension
    sum_Xy = np.einsum("yw,y->w", select, X)
    

    这就是一维示例。对于问题中提出的二维问题,方法完全相同:引入一个附加维度,广播y索引,然后用einsum减少附加维度。

    如果您将一维示例的每个步骤内部化,我相信您可以弄清楚代码是如何在二维中执行的,因为这只是正确获取索引的问题(W 行, X 列)。

    【讨论】:

    • 非常感谢您的出色解释。我终于明白了。
    猜你喜欢
    • 1970-01-01
    • 2023-03-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-06-02
    相关资源
    最近更新 更多