在 Python 的 Numpy 中,点积不等同于 einsum,我不确定为什么不
In Python's Numpy, a dot product isn't equivalent to an einsum, and I'm not sure why not
但显然我做错了什么。
找了一晚上的bug,终于解决了。考虑:
xs = np.arange(100 * 3).reshape(100, 3)
W = np.arange(3 * 17).reshape(3, 17)
a = np.einsum('df, hg -> dg', xs, W)
b = np.dot(xs, W)
在上面a != b
.
我发现的问题是在 einsum 中,我说 df, hg -> dg
,但是如果我将 h
换成 f
,它会按预期工作:
a = np.einsum('df, fg -> dg', xs, W)
b = np.dot(xs, W)
现在,a == b
.
两种情况下的求和有何不同,我希望它们相同?
以下是等效的基于广播的表达式,也许它们可以帮助您理解其中的区别:
dffg = (xs[:,:,None]*W[None,:,:]).sum(1)
dfhg = (xs[:,:,None,None]*W[None,None,:,:]).sum((1,2))
(a==dfhg).all()
# True
(b==dffg).all()
# True
在 dfhg 的情况下,数据轴实际上并不重叠;因此可以分别对每一项进行求和:
dfhg_ = (xs.sum(1)[:,None]*W.sum(0)[None,:])
(a==dfhg_).all()
# True
将此与 dffg 情况进行对比,其中在 xs 的每一行和 W 的每一列之间形成点积。
使用 np.einsum
进行矩阵乘法的正确方法是重复 "middle" 索引(表示对行时间列求和),如您所见:
a = np.array([[1,2],[3,4]])
b = np.array([[1,-2],[-0.4,3]])
np.einsum('df,fg->dg', a, b)
array([[ 0.2, 4. ],
[ 1.4, 6. ]])
a.dot(b)
array([[ 0.2, 4. ],
[ 1.4, 6. ]])
如果不这样做,您会得到 a
的每个值 乘以 b
:
np.einsum('df, hg -> dfhg', a, b)
array([[[[ 1. , -2. ],
[ -0.4, 3. ]],
[[ 2. , -4. ],
[ -0.8, 6. ]]],
[[[ 3. , -6. ],
[ -1.2, 9. ]],
[[ 4. , -8. ],
[ -1.6, 12. ]]]])
与
相同
a[:,:, None, None] * b
当您在使用显式运算符 ->
时省略中间索引时,您对这些轴求和:
np.einsum('df, hg -> dg', a, b)
array([[ 1.8, 3. ],
[ 4.2, 7. ]])
等同于:
np.einsum('df, hg -> dfhg', a, b).sum(axis=1).sum(axis=1)
但显然我做错了什么。
找了一晚上的bug,终于解决了。考虑:
xs = np.arange(100 * 3).reshape(100, 3)
W = np.arange(3 * 17).reshape(3, 17)
a = np.einsum('df, hg -> dg', xs, W)
b = np.dot(xs, W)
在上面a != b
.
我发现的问题是在 einsum 中,我说 df, hg -> dg
,但是如果我将 h
换成 f
,它会按预期工作:
a = np.einsum('df, fg -> dg', xs, W)
b = np.dot(xs, W)
现在,a == b
.
两种情况下的求和有何不同,我希望它们相同?
以下是等效的基于广播的表达式,也许它们可以帮助您理解其中的区别:
dffg = (xs[:,:,None]*W[None,:,:]).sum(1)
dfhg = (xs[:,:,None,None]*W[None,None,:,:]).sum((1,2))
(a==dfhg).all()
# True
(b==dffg).all()
# True
在 dfhg 的情况下,数据轴实际上并不重叠;因此可以分别对每一项进行求和:
dfhg_ = (xs.sum(1)[:,None]*W.sum(0)[None,:])
(a==dfhg_).all()
# True
将此与 dffg 情况进行对比,其中在 xs 的每一行和 W 的每一列之间形成点积。
使用 np.einsum
进行矩阵乘法的正确方法是重复 "middle" 索引(表示对行时间列求和),如您所见:
a = np.array([[1,2],[3,4]])
b = np.array([[1,-2],[-0.4,3]])
np.einsum('df,fg->dg', a, b)
array([[ 0.2, 4. ],
[ 1.4, 6. ]])
a.dot(b)
array([[ 0.2, 4. ],
[ 1.4, 6. ]])
如果不这样做,您会得到 a
的每个值 乘以 b
:
np.einsum('df, hg -> dfhg', a, b)
array([[[[ 1. , -2. ],
[ -0.4, 3. ]],
[[ 2. , -4. ],
[ -0.8, 6. ]]],
[[[ 3. , -6. ],
[ -1.2, 9. ]],
[[ 4. , -8. ],
[ -1.6, 12. ]]]])
与
相同a[:,:, None, None] * b
当您在使用显式运算符 ->
时省略中间索引时,您对这些轴求和:
np.einsum('df, hg -> dg', a, b)
array([[ 1.8, 3. ],
[ 4.2, 7. ]])
等同于:
np.einsum('df, hg -> dfhg', a, b).sum(axis=1).sum(axis=1)