2

如何使用 1000 个不同的(8,16)权重矩阵将 8 个元素向量中的 100 个转换为 10 个 16 个元素向量?10 个输出向量中的每一个都是 100 个点积的总和:

A = np.random.randn(100,8)
W = np.random.randn(1000,8,16)
B = []

for i in range(10):
  sum = np.zeros((1,16))
  for j in range(100):
    sum += np.dot(A[j], W[i*100+j])   
  B.append(sum)
B = np.asarray(B)     #B.shape=(10,16)

Numpy 或 TensorFlow 中是否有用于此功能的功能?我在 Numpy 中查看了 dot、tensordot、einsum 和 matmul,但我仍然不确定哪个是正确的选择。

编辑:我刚刚意识到我实际上想在对点积求和之前产生一个中间结果:(100,8)x(10,100,8,16) -> (10,100,16)。

我猜这可以通过重塑(100,8)到(1,100,1,8)和(1000,8,16)到(10,100,8,16)来完成,np.einsum('ijkl,ijlm->ijm', A, B) 但我不确定它是否将正确广播 1 到 10。

根据@Divakarnp.einsum('jk,ijkl->ijl', V, W.reshape(10,100,8,16))的评论,可以解决问题。

4

2 回答 2

4

在一行中,它是

B1 = np.einsum('ij,ikjl', A, np.reshape(W, (100, 10, 8, 16), order='F'))

np.allclose(B.squeeze(), B1)在你需要的地方测试它,.squeeze因为你的 B 有一个额外的尺寸 1。

说明:您的 W 形状难看,其大小为 1000 的第一个维度实际上应该分成 10 个大小为 100 的块(就像您实际上在循环中对索引操作所做的那样)。这就是重塑的目的。需要 Fortran 样式的顺序是因为我们想通过最快地更改第一个索引来取出 W 的元素。

之后是简单的爱因斯坦求和:在 j 上进行矩阵乘法,在 i 上添加 100 个结果。

于 2017-11-16T00:46:35.757 回答
2

您可以使用基于张量的乘法,np.tensordot-

def tensordot_app(A, W):
    m,n,r = W.shape
    Wr = W.reshape(-1,A.shape[0],n,r)
    return np.tensordot(A,Wr, axes=((0,1),(1,2)))

相关帖子了解tensordot

运行时测试 -

In [62]: A = np.random.randn(100,8)
    ...: W = np.random.randn(1000,8,16)

In [63]: %%timeit 
    ...: B = []
    ...: for i in range(10):
    ...:   sum = np.zeros((1,16))
    ...:   for j in range(100):
    ...:     sum += np.dot(A[j], W[i*100+j])   
    ...:   B.append(sum)
    ...: B = np.asarray(B)     #B.shape=(10,16)
1000 loops, best of 3: 1.81 ms per loop

# Other post's einsum soln
In [64]: %timeit np.einsum('ij,ikjl',A,np.reshape(W,(100,10,8,16), order='F'))
10000 loops, best of 3: 83.4 µs per loop

# Other post's einsum soln without fortran re-ordering
In [65]: %timeit np.einsum('jk,ijkl', A, np.reshape(W, (10, 100, 8, 16)))
10000 loops, best of 3: 83.3 µs per loop

In [66]: %timeit tensordot_app(A, W)
10000 loops, best of 3: 193 µs per loop
于 2017-11-16T03:43:30.953 回答