高维度的 Einsum
Einsum for high dimensions
考虑以下 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 在哪里?在这种情况下,为什么我们必须在 4 个维度上转换 W?
2- 'ijk,lk->il' - 我也不明白这个。
i - 指的是行,
j - 列,
k-每个元素,
l - 'l' 指的是什么?。
如果有人能理解这一点并向我解释,我将不胜感激。
提前致谢。
让我们通过删除一维并使用易于手动验证的值来简化问题:
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/row 它对 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
select
和mult
的计算可以与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 列)。
考虑以下 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 在哪里?在这种情况下,为什么我们必须在 4 个维度上转换 W? 2- 'ijk,lk->il' - 我也不明白这个。
i - 指的是行, j - 列, k-每个元素, l - 'l' 指的是什么?。 如果有人能理解这一点并向我解释,我将不胜感激。 提前致谢。
让我们通过删除一维并使用易于手动验证的值来简化问题:
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/row 它对 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
select
和mult
的计算可以与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 列)。