【问题标题】:How to efficiently multiply a list of NumPy views with corresponding weights?如何有效地将 NumPy 视图列表与相应的权重相乘?
【发布时间】:2016-07-02 22:09:51
【问题描述】:

我在使用 NumPy 时遇到内存和速度问题,但我的问题很简单。

  • A 是一个由 H * W 整数组成的大型 NumPy 数组。
  • V 是一个列表,其中包含 N 大数组 A 的视图,每个视图都具有相同的 (Hv, Wv) 形状。
  • K 是另一个列表,其中包含与视图对应的 N 浮动权重。

HvWv 几乎等于 HW 但更小。由于NumPy views 不是副本,这对于内存管理非常有用,即使N 很大。

现在,我想使用广播计算一个新数组以提高速度:B = V1*K1 + ... + V N*KN

这将产生一个新的Hv * Wv 加权数组。

问题是我不知道如何在不在内存中创建中间数组(当视图乘以相应的权重时会发生这种情况)并从广播操作中受益的情况下执行此类操作。

import numpy as np

H = W = 1000
Hv = Wv = 900
N = 100

A = np.arange(H * W).reshape(H, W)

V = [A[i:Hv + i, i:Wv + i] for i in range(N)]

K = np.random.rand(N)

# It neither uses speed broadcast nor low memory!
B = sum(v*k for v, k in zip(V, K))

有人可以帮我巧妙地使用 NumPy 吗?

【问题讨论】:

  • 看来A 有重量?因此,使用其中包含随机数的样本而不是仅使用 ones 会更合理,对吧?
  • 你没有过度简化:V = [A[:Hv, :Wv] for _ in range(N)] 因为 V[0]、V[1] 等会是一样的吗?
  • @Divakar 我认为这足以理解问题,但这是真的,它看起来一点也不像真实数据。我用一系列滑动对角线视图更新了我的问题,我希望它会更好(但它可以工作,因为N = H - Hv 否则会引发错误)。
  • 没有固定的方法可以用来创建V吗?所以,我想我们可以说V会被给出,我们不需要担心优化创建V
  • @Divakar 没错!我有足够的时间创建AVK,但我经常需要更新B 的一些值(这也是我使用视图的原因)。我只关心创建B时的速度和内存管理。

标签: python arrays numpy optimization memory-management


【解决方案1】:

我假设V 是作为一个列表给出的,我们无权优化创建它或者只是不需要。因此,A 不在等式中,我们只剩下VK 来获得最终输出B,因此,剩下的就是优化最后一步。

为了解决这个问题,我们可以只使用np.tensordot 来替换求和的最后一步,因为这基本上是矩阵乘法的求和。在我们的例子中,我们从K 和沿输入列表V 的长度减少第一个轴。在内部,NumPy 会将列表转换为 NumPy 张量数组,并且该长度将成为其数组版本的第一个轴。因此,我们将从这两个输入中减少第一个轴,因此实现将是 -

B = np.tensordot(K,V,axes=[0,0]) # `axes` indicates the axes to be sum-reduced

请注意,列表到 NumPy 数组的内部转换可能并不便宜,因此使用初始化作为 NumPy 数组创建 V 比在会导致列表的循环理解中更有意义.

【讨论】:

  • 哦,这看起来很复杂,我永远也想不通,谢谢。很抱歉,当我编辑我的问题以简化它时,变量 coords 不再有意义。
  • 哇,非常感谢您抽出宝贵时间,非常感谢您的解决方案和解释!但是,我仍然对 NumPy 的内存使用感到困惑。虽然使用np.tensordot 的解决方案似乎更快并且lighter (没有我想象的那么多,所以我假设将视图与浮点数相乘会产生巨大的内存使用可能是错误的),将V 初始化为NumPy 数组导致an unexpected increment of the memory usage。我在分析方面做错了什么,还是有什么我应该知道的?
  • @Delgan 啊不是那样,那会创建一个列表然后转换为数组!我的意思是 V = np.zeros((N,Hv,Wv)) 并使用适当的循环而不是循环理解在每次迭代时存储到 V[0] = .., V[1] = ... 等等上。如果由于某种原因无法使用正确的循环,请坚持使用之前的列表。
  • 这会产生weird and even worse results,但我认为这是因为我的视图对象被转换并且它们的值被复制到K。我想这也是将我的列表投射到np.array 时发生的情况。我想我应该坚持使用列表解决方案,即使内部转换很昂贵,因为我需要视图,我想在对A 进行一些更改后计算一个新的B 数组,对吧?
  • 那么,如果您继续使用循环理解创建V 并使用np.tensordot 得到B,那么在内存和性能方面与原始解决方案相比如何?跨度>
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-01-22
  • 1970-01-01
  • 2021-06-06
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多