点积之和
Sum of dot products
如何使用 1000 个不同的 (8,16) 权重矩阵将 100 of 8 元素向量转换为 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。
根据@Divakar 的评论,np.einsum('jk,ijkl->ijl', V, W.reshape(10,100,8,16))
可以解决问题。
在一行中,它是
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个结果相加。
您可以使用基于张量的乘法,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)))
.
运行时测试 -
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
如何使用 1000 个不同的 (8,16) 权重矩阵将 100 of 8 元素向量转换为 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。
根据@Divakar 的评论,np.einsum('jk,ijkl->ijl', V, W.reshape(10,100,8,16))
可以解决问题。
在一行中,它是
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个结果相加。
您可以使用基于张量的乘法,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)))
运行时测试 -
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