【问题标题】:How to vectorize this multiplication?如何向量化这个乘法?
【发布时间】:2013-03-12 20:37:39
【问题描述】:

我有一个形状为 (ni*43*91)x67 的 X 矩阵和一个形状为 67x43x91 的 W 张量。不一样

我需要通过将 X 的前 ni 行与 W 的第一列点来获得一个 (ni*43*91) 向量 y,以获得 y 的前 ni 个元素和 X 的第二个 ni 行与第二列W 得到 y 的第二个 ni 元素,以此类推。当 W 中的列用完时,我会转到下一个维度继续。

我有两个面罩 dim2 和 dim3,它们的形状都是 (ni*43*91),按顺序排列。现在这就是我正在做的(简化),而且速度很慢

for d3 in range(91):
  for d2 in range(43):
    mask = ((dim3 == d3) & (dim2 == d2))
    curr_X = X[mask, :]
    curr_W = W[:,d2,d3]
    curr_y = numpy.dot(curr_X,curr_W)
    y[mask] = curr_y

如果没有 for 循环,这可能吗?

【问题讨论】:

标签: python numpy vectorization


【解决方案1】:

我不完全了解您的 dim2 和 dim3 数组是什么,以及 mask 是如何构造的,但是根据您的描述,您想要接近这个的东西:

ni = 10
a, b, c = 43, 91, 67
X = np.random.rand(ni*a*b, c)
W = np.random.rand(c, a, b)

X = X.reshape(ni, a*b, c)
W = W.reshape(c, a*b)

y = np.einsum('ijk, kj -> ij', X, W)
y = y.reshape(-1)

如果您使用工作代码更新您的问题,即 dim2 和 dim3 的完整描述,我们可以对其进行微调以返回完全相同的内容(如果尚未返回)。

【讨论】:

  • ni 不是恒定的而是变化的。 dim2 和 dim3 是掩码,因此我可以获取对应于 W 的 d2 和 d3 维度的 ni 行。它们就像 dim2 = [0,0,0,1,1,2,3,3,0,0, 1,2,2,3] dim3 = [0,0,0,0,0,0,0,0,1,1,1,1,1,1] n1=3 n2=2 n3=1 n4= 2 ...
  • @bizso09 您是否尝试过答案中的代码是否提供与循环相同的结果?
  • 你把ni固定为10,所以没法比较。
【解决方案2】:

首先,不清楚您要做什么,因为您的代码不起作用。我只能猜你想这样做:

from numpy import *
from numpy.random import rand

ni=12
A=67
B=43
C=91


X = rand(ni*B*C,A) 
W = rand(A,B,C)

y = zeros((ni*B*C))

for k in xrange(len(y)):
    b = (k/ni)/C
    c = (k/ni) % C

    #print 'y[%i] = dot(X[%i,:],W[:,%i,%i])'%(k,k,b,c)

    y[k] = dot(X[k,:],W[:,b,c])

如果您只是将A,B,C,ni 设置为一些较低的值并取消注释print-行,您将很快看到该算法的作用。

如果这是你想要的,那么你可以用这个单线更快地做到这一点:

y2 = sum(X * (W.reshape((A,B*C)).swapaxes(0,1).repeat(ni,axis=0)),axis=1)

尽管进行了一些索引重新排列,但这里的关键技巧是使用repeat,因为在循环中,b,c 的索引“冻结”了ni,而k 增长了。

我现在有点着急,但是如果您需要进一步的解释,请发表评论。

【讨论】:

  • ni 是变化的,而不是恒定的。你不能在上面声明它
  • 这个我不明白,X“变化”的元素个数怎么会变化?当ni“变化”时,公式形状为 (ni*43*91)x67 的 X 矩阵是什么意思?
  • X 中的元素个数不变,只有 ni。如果 X 有 m 行,则将这些 m 行划分为 43*91 块。第一个块的大小是 n1 ... 第 (43*91) 个块的大小是 n(43*91)。在我的示例中,您通过掩码获得了每个块。
【解决方案3】:

从这个问题中很难理解想要的结果是什么,但是我认为你想要的结果可以很容易地像这样获得:

y = (X.T * W[:,dim2,dim3]).sum(axis=0)

比较正确性和速度:

import numpy as np

# some test data, the sorting isn't really necessary
N1, N2, N3 = 67, 43, 91
ni_avg = 1.75
N = int(ni_avg * N2 * N3)

dim2 = np.random.randint(N2, size=N)
dim3 = np.sort(np.random.randint(N3, size=N))
for d3 in range(N3):
    dim2[dim3==d3].sort()

X = np.random.rand(N, N1)
W = np.random.rand(N1, N2, N3)

# original code
def original():
    y = np.empty(X.shape[0])
    for d2 in range(W.shape[1]):
        for d3 in range(W.shape[2]):
            mask = ((dim3 == d3) & (dim2 == d2))
            curr_X = X[mask, :]
            curr_W = W[:,d2,d3]
            curr_y = numpy.dot(curr_X,curr_W)
            y[mask] = curr_y
    return y

# comparison
%timeit original()
# 1 loops, best of 3: 672 ms per loop
%timeit (X.T * W[:,dim2,dim3]).sum(axis=0)
# 10 loops, best of 3: 31.8 ms per loop
np.allclose(original(), np.sum(X.T * W[:,dim2,dim3], axis=0))
# True

还是要快一点使用

y = np.einsum('ij,ji->i', X, W[:,dim2,dim3])

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-05-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多