Einsum高尺寸

时间:2018-10-05 20:06:11

标签: python linear-algebra numpy-einsum

考虑以下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”也指什么? 如果有人能理解并向我解释,我将不胜感激。 预先感谢。

1 个答案:

答案 0 :(得分:2)

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

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

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

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列)。