在进行矩阵工作时如何理解循环和额外 numpy 维度之间的权衡?

Posted

技术标签:

【中文标题】在进行矩阵工作时如何理解循环和额外 numpy 维度之间的权衡?【英文标题】:How to understand the trade-off between looping and extra numpy dimensions when doing matrix work? 【发布时间】:2022-01-19 03:15:33 【问题描述】:

我发现自己的一个常见模式如下:

matrix_list1 = [...]  # list of N ndarrays of shape (100,100)
matrix_list2 = [...]  # list of N ndarrays of shape (100, 100)
result = []
for i in range(N):
    M = matrix_list1[i] * matrix_list2[i]
    result.append(M)

或者,我们可以通过堆叠两个列表中的矩阵来完成此操作而无需循环:

M1 = np.stack(matrix_list1)
M2 = np.stack(matrix_list2)
result = M1*M2

假设我们可以将这些堆栈保存在内存中,第二种方法总是更快吗?它仅仅是速度与内存的权衡吗?我怎样才能更好地理解这一点?

【问题讨论】:

ipython 中使用%timeit 运行基准测试。 如果您在两种情况下都需要一个数组,您可能还希望在时序循环中包含一个np.stack(result)。我的一般经验法则是,如果内存使用量变大,复杂计算的适度迭代次数会更快。较小计算的许多迭代总是较慢。但是没有硬性的权衡。 @hpaulj。到那时,通常会成为一个问题,即您是否可以将数据直接生成到数组中或必须进行转换。一旦开销离开图片,它就不是一场比赛了 【参考方案1】:

M1M2 都是一个(小)元数据对象和一个连续的内存块。它们每个占用的空间都比原来的 matrix_list1matrix_list2 少。后者需要存储多组元数据和列表结构本身。切换到单个数组时不会牺牲大小。

向量化乘法运算在一个连续块上的速度可能比在多个单独的段上要快一些,即使不考虑循环的开销。如果块不是加载到处理器缓存中的大小的倍数,则尤其如此。

在执行足够复杂操作的管道时,有时运行 python 级别的循环会更快,以消除一些昂贵的操作。如果迭代次数非常少,则尤其如此。在您的情况下,只有一个操作与循环竞争:转换为数组。对于少数矩阵,循环可能会稍微快一些。

如果您完全删除数组转换,则可以通过在连续缓冲区中使用向量化来加快整个操作。具体来说,如果您可以将矩阵加载到 3D 数组中而不是使用列表开始,那么消除循环将是最佳解决方案。在您弄清楚如何做到这一点之前,对这两个选项进行基准测试是确定哪个更快的唯一方法。

【讨论】:

以上是关于在进行矩阵工作时如何理解循环和额外 numpy 维度之间的权衡?的主要内容,如果未能解决你的问题,请参考以下文章

cupy或numpy中"数组"与"矩阵"的区别

NumPy之:理解广播

用矩阵对 numpy 向量进行排序

Python 2D NumPy 数组理解

用分隔符python连接字符矩阵

2D 数组每列的外积形成 3D 数组 - NumPy