Numpy“:”运算符广播问题
Numpy ":" operator broadcasting issues
在下面的代码中,我编写了 2 个方法,理论上(在我看来)应该做同样的事情。不幸的是他们没有,我无法找出他们为什么不按照 numpy 文档做同样的事情。
import numpy as np
dW = np.zeros((20, 10))
y = [1 for _ in range(100)]
X = np.ones((100, 20))
# ===================
# Method 1 (works!)
# ===================
for i in range(len(y)):
dW[:, y[i]] -= X[i]
# ===================
# Method 2 (does not work)
# ===================
dW[:, y] -= X.T
选项 1:
for i in range(len(y)):
dW[:, y[i]] -= X[i]
这是可行的,因为您正在循环并更新上次更新的值。
选项 2:
dW[:, [1,1,1,1,....1,1,1]] -= [[1,1,1,1...1],
[1,1,1,1...1],
.
.
[1,1,1,1...1]]
它不起作用,因为更新同时发生在第一个索引上是并行的,而不是串行的。最初全部为 0,因此减去结果为 -1。
如前所述,由于 NumPy 中缓冲的工作原理,原则上您不能在一次操作中对同一元素进行多次操作。为此,有 at
function, which can be used on about any standard NumPy function (add
, subtract
,等等)。对于您的情况,您可以这样做:
import numpy as np
dW = np.zeros((20, 10))
y = [1 for _ in range(100)]
X = np.ones((100, 20))
# at modifies in place dW, does not return a new array
np.subtract.at(dW, (slice(None), y), X.T)
这是 问题的 column-wise 版本。
那里的答案可以按如下方式改编column-wise:
方法一:np.<ufunc>.at
>>> np.subtract.at(dW, (slice(None), y), X.T)
方法二:np.bincount
>>> m, n = dW.shape
>>> dW -= np.bincount(np.add.outer(np.arange(m) * n, y).ravel(), (X.T).ravel(), dW.size).reshape(m, n)
请注意,基于 bincount
的解决方案 - 尽管它涉及更多步骤 - 速度提高了约 6 倍。
>>> from timeit import repeat
>>> kwds = dict(globals=globals(), number=5000)
>>>
>>> repeat('np.subtract.at(dW, (slice(None), y), X.T); np.add.at(dW, (slice(None), y), X.T)', **kwds)
[1.590626839082688, 1.5769231889862567, 1.5802007300080732]
>>> repeat('_= dW; _ -= np.bincount(np.add.outer(np.arange(m) * n, y).ravel(), (X.T).ravel(), dW.size).reshape(m, n); _ += np.bincount(np.add.outer(np.arange(m) * n, y).ravel(), (X.T).ravel(), dW.size).reshape(m, n)', **kwds)
[0.2582490430213511, 0.25572817400097847, 0.25478115503210574]
我找到了这个问题的第三个解决方案。普通矩阵乘法:
ind = np.zeros((X.shape[0],dW.shape[1]))
ind[range(X.shape[0]),y] = -1
dW = X.T.dot(ind)
我使用上面提出的方法在一些神经网络数据上做了一些实验。在我的示例中 X.shape = (500,3073)
、W.shape = (3073,10)
和 ind.shape = (500,10)
.
减法版本大约需要 0.2 秒(最慢)。矩阵乘法0.01秒(最快)。正常循环 0.015 然后 bincount
方法 0.04 秒。请注意,问题中 y
是一个的向量。这不是我的情况。只有一个的情况可以通过简单的求和来解决。
在下面的代码中,我编写了 2 个方法,理论上(在我看来)应该做同样的事情。不幸的是他们没有,我无法找出他们为什么不按照 numpy 文档做同样的事情。
import numpy as np
dW = np.zeros((20, 10))
y = [1 for _ in range(100)]
X = np.ones((100, 20))
# ===================
# Method 1 (works!)
# ===================
for i in range(len(y)):
dW[:, y[i]] -= X[i]
# ===================
# Method 2 (does not work)
# ===================
dW[:, y] -= X.T
选项 1:
for i in range(len(y)):
dW[:, y[i]] -= X[i]
这是可行的,因为您正在循环并更新上次更新的值。
选项 2:
dW[:, [1,1,1,1,....1,1,1]] -= [[1,1,1,1...1],
[1,1,1,1...1],
.
.
[1,1,1,1...1]]
它不起作用,因为更新同时发生在第一个索引上是并行的,而不是串行的。最初全部为 0,因此减去结果为 -1。
如前所述,由于 NumPy 中缓冲的工作原理,原则上您不能在一次操作中对同一元素进行多次操作。为此,有 at
function, which can be used on about any standard NumPy function (add
, subtract
,等等)。对于您的情况,您可以这样做:
import numpy as np
dW = np.zeros((20, 10))
y = [1 for _ in range(100)]
X = np.ones((100, 20))
# at modifies in place dW, does not return a new array
np.subtract.at(dW, (slice(None), y), X.T)
这是
那里的答案可以按如下方式改编column-wise:
方法一:np.<ufunc>.at
>>> np.subtract.at(dW, (slice(None), y), X.T)
方法二:np.bincount
>>> m, n = dW.shape
>>> dW -= np.bincount(np.add.outer(np.arange(m) * n, y).ravel(), (X.T).ravel(), dW.size).reshape(m, n)
请注意,基于 bincount
的解决方案 - 尽管它涉及更多步骤 - 速度提高了约 6 倍。
>>> from timeit import repeat
>>> kwds = dict(globals=globals(), number=5000)
>>>
>>> repeat('np.subtract.at(dW, (slice(None), y), X.T); np.add.at(dW, (slice(None), y), X.T)', **kwds)
[1.590626839082688, 1.5769231889862567, 1.5802007300080732]
>>> repeat('_= dW; _ -= np.bincount(np.add.outer(np.arange(m) * n, y).ravel(), (X.T).ravel(), dW.size).reshape(m, n); _ += np.bincount(np.add.outer(np.arange(m) * n, y).ravel(), (X.T).ravel(), dW.size).reshape(m, n)', **kwds)
[0.2582490430213511, 0.25572817400097847, 0.25478115503210574]
我找到了这个问题的第三个解决方案。普通矩阵乘法:
ind = np.zeros((X.shape[0],dW.shape[1]))
ind[range(X.shape[0]),y] = -1
dW = X.T.dot(ind)
我使用上面提出的方法在一些神经网络数据上做了一些实验。在我的示例中 X.shape = (500,3073)
、W.shape = (3073,10)
和 ind.shape = (500,10)
.
减法版本大约需要 0.2 秒(最慢)。矩阵乘法0.01秒(最快)。正常循环 0.015 然后 bincount
方法 0.04 秒。请注意,问题中 y
是一个的向量。这不是我的情况。只有一个的情况可以通过简单的求和来解决。