【问题标题】:Most Pythonic way to multiply these two vectors?将这两个向量相乘的大多数 Pythonic 方法?
【发布时间】:2023-03-26 17:52:02
【问题描述】:

我有两个带形状的 ndarray:

A = (32,512,640)

B = (4,512)

我需要将 A 和 B 相乘以得到一个新的 ndarray:

C = (4,32,512,640)

另一种思考方式是向量 B 的每一行沿 A 的轴 =-2 相乘,得到一个新的 1,32,512,640 立方体。 B 的每一行可以循环形成 1,32,512,640 个立方体,然后可以使用 np.concatenatenp.vstack 来构建 C,例如:

# Sample inputs, where the dimensions aren't necessarily known
a = np.arange(32*512*465, dtype='f4').reshape((32,512,465))
b = np.ones((4,512), dtype='f4')

# Using a loop
d = []
for row in b:
    d.append(np.expand_dims(row[None,:,None]*a, axis=0))

# Or using list comprehension
d = [np.expand_dims(row[None,:,None]*a,axis=0) for row in b]

# Stacking the final list
result = np.vstack(d)

但我想知道是否可以使用np.einsumnp.tensordot 之类的东西在一行中将此矢量化。我还在学习如何使用这两种方法,所以我不确定这里是否合适。

谢谢!

【问题讨论】:

  • 你已经尝试了什么?它在工作吗?如果不是,它以何种方式未能按预期执行?
  • 添加了我已经尝试过的内容,尽管在下面发布了有用的答案之后。它按预期执行,但我怀疑可能有更好或更有效的方法来做到这一点(通过性能或更少的代码行)。
  • @wolfblade87 btw、ABC 不是向量;它们分别是 3D、2D 和 4D 数组。

标签: python numpy vectorization numpy-einsum tensordot


【解决方案1】:

我们可以在将B 的维度扩展为None/np.newaxis 之后利用broadcasting -

C = A * B[:,None,:,None]

使用einsum,它将是 -

C = np.einsum('ijk,lj->lijk',A,B)

这里没有减少和,所以einsum 不会比explicit-broadcasting 更好。但是因为,我们正在寻找 Pythonic 解决方案,一旦我们通过了它的字符串表示法,就可以使用它。

让我们安排一些时间来完成任务 -

In [15]: m,n,r,p = 32,512,640,4
    ...: A = np.random.rand(m,n,r)
    ...: B = np.random.rand(p,n)

In [16]: %timeit A * B[:,None,:,None]
10 loops, best of 3: 80.9 ms per loop

In [17]: %timeit np.einsum('ijk,lj->lijk',A,B)
10 loops, best of 3: 109 ms per loop

# Original soln
In [18]: %%timeit
    ...: d = []
    ...: for row in B:
    ...:     d.append(np.expand_dims(row[None,:,None]*A, axis=0))
    ...: 
    ...: result = np.vstack(d)
10 loops, best of 3: 130 ms per loop

利用multi-core

我们可以利用适合arithmetic operationslarge datamulti-core capability of numexpr,从而在此处获得一些性能提升。让我们慢慢来-

In [42]: import numexpr as ne

In [43]: B4D = B[:,None,:,None] # this is virtually free

In [44]: %timeit ne.evaluate('A*B4D')
10 loops, best of 3: 64.6 ms per loop

一行代码为:ne.evaluate('A*B4D',{'A':A,'B4D' :B[:,None,:,None]})

Related post 了解如何控制多核功能。

【讨论】:

  • 为了更清楚地表达意图,请使用np.newaxis而不是None
  • 感谢您的回答!我不知道你可以在这样的广播中扩展维度!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-07-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-10-13
相关资源
最近更新 更多