如何快速存储 numpy 对象(矩阵)?

Posted

技术标签:

【中文标题】如何快速存储 numpy 对象(矩阵)?【英文标题】:How to store numpy objects ( matrices ) fast? 【发布时间】:2017-08-18 08:57:05 【问题描述】:

我必须存储 numpy 矩阵的列表。我尝试了两种方法:

1.创建一个list并附加到它:

ls_ws=[]
for ind in range(iterations):
    ...
    ls_ws.append(ls_w) # ls_w is a matrix of size 6,1

问题:开始时很快,结束时变得很慢。

2.创建一个零矩阵并修改它:

for ind in range(iterations):
    ...
    ls_ws=np.matrix(np.zeros((6,iterations))) 

问题:我不太确定,但这似乎与每次迭代的速度都相同。 我得到的奇怪的事情是,如果我将迭代设置为小数字,它会很快。但是如果我将它设置为像 1500 这样的大数字,那么每次迭代(甚至是第一次迭代)都会非常慢。


第二种方法应该很快,但如果我将迭代次数设置为很大,则速度很慢。为什么?如何解决?

【问题讨论】:

您是否介意量化一下,并说明理想过程应该花费多少纳秒 [ns](从开始到结束,包括内存分配),例如,1.000 的中等大小.000 个元素,不要称它为慢(或者,非常慢),什么是区域,在您的使用上下文中,该过程将被称为快?这很公平,不是吗? 在第一个中,您是将ls_ws 转换为最后的数组,还是将其保留为列表? 您是在测量 iterations 的不同值的时间,还是测量每次迭代的时间? 众所周知,追加到列表涉及 malloc(),在big-o 表示法中是 O(n)。因此迭代地附加 n 项是 O(n^2)。速度太慢了。 【参考方案1】:

我认为您在这里几乎有一个很好的解决方案,但是您对列与行的处理可能会阻碍性能:

ls_ws=np.matrix(np.zeros((6,iterations))) 
for ind in range(iterations):
    ...
    ls_ws[:,ind]=ls_w

您应该在第一个维度而不是最后一个维度内进行迭代。您可以交换尺寸,或者只是这样做:

ls_ws=np.matrix(np.zeros((6,iterations), order='F')) 

当数组很大时,这可能会有所帮助,因为您将访问 6 个元素的连续块,而不是每次迭代中的分散位。

【讨论】:

np.array,请不要 np.matrix @ev-br 什么意思? @AbhishekBhatia:您在这里的评论中提到了compute_error(),但在您的问题中却没有! 你的内存是一维的,但可以处理二维数组。数组np.array([[1,2,3],[4,5,6],[7,8,9]]) 可以像1 2 3 4 5 6 7 8 9 那样存储,它是像语言C 中的行主要(order='C'),或者可以像1 4 7 2 5 8 3 6 9 那样存储,它是像Fortran 中的列主要(order='F')。参见例如Wiki。就像提到的答案一样,访问内存中的连续位置更快。 @AbhishekBhatia: order='F' 重新排列内存中的数据以形成您的访问模式,以便您对元素 0-5、6-11、12-17 进行操作,而不是原来您正在操作0、1000、2000、3000、4000、5000,然后是 1、1001、2001、3001、4001、5001 等。由于多种原因,按顺序执行会更快。【参考方案2】:

使用准系统版本的列表追加:

def foo1(N):
    alist = []
    for _ in range(N):
        alist.append([1,2,3,4,5,6])
    return np.array(alist)

与 N 平滑地缩放时间

In [4]: timeit foo1(100)
10000 loops, best of 3: 123 µs per loop
In [5]: timeit foo1(1000)
1000 loops, best of 3: 1.23 ms per loop
In [6]: timeit foo1(10000)
100 loops, best of 3: 12.3 ms per loop
In [7]: timeit foo1(100000)
10 loops, best of 3: 129 ms per loop
In [8]: timeit foo1(1000000)
1 loop, best of 3: 1.29 s per loop
In [9]: timeit foo1(10000000)
1 loop, best of 3: 12.9 s per loop

列表附加可能会因列表较大而减慢速度。列表使用缓冲区来保存指针,以及一些增长空间。当它超出那个空间时,它必须得到更多。如果它不能“就地”扩展,它将不得不请求新空间并复制所有指针。对于接近填充内存的非常大的列表,这可能会减慢速度。随着时间安排从 123 到 129,我可能会看到这方面的暗示

def foo2(N):
    out = np.zeros((N,6),int)
    for i in range(N):
        out[i,:] = [1,2,3,4,5,6]
    return out

时间也随 N 缩放,是列表情况的两倍:

In [15]: timeit foo2(100)
1000 loops, best of 3: 242 µs per loop
In [16]: timeit foo2(1000)
100 loops, best of 3: 2.52 ms per loop
In [17]: timeit foo2(10000)
10 loops, best of 3: 24.6 ms per loop
In [18]: timeit foo2(100000)
1 loop, best of 3: 249 ms per loop
In [19]: timeit foo2(1000000)
1 loop, best of 3: 2.52 s per loop
In [20]: timeit foo2(10000000)
1 loop, best of 3: 25.2 s per loop

在最后一个维度上迭代在时间上没有区别

def foo3(N):
    out = np.zeros((6,N),int)
    for i in range(N):
        out[:,i] = [1,2,3,4,5,6]
    return out

在一次只分配/创建 6 个元素的同时迭代 1000 次是很糟糕的 numpy 实践。如果必须迭代,最好循环几次,每次都进行大型操作。例如 6 个循环,包含 1000 个项目数组。

如果我将迭代向下移动到已编译的代码,时间会快得多

def foo4(N):
    out = np.zeros((N,6),int)
    out[...] = [1,2,3,4,5,6]
    return out

In [28]: timeit foo4(1000)
.... cached.
10000 loops, best of 3: 20.4 µs per loop
In [29]: timeit foo4(100000)
1000 loops, best of 3: 1.45 ms per loop

您谈论保存矩阵,甚至分配np.matrix。那么让我们看看有什么效果:

def foo1m(N):
    alist = []
    for _ in range(N):
        alist.append(np.matrix([1,2,3,4,5,6]).T)
    return np.concatenate(alist, axis=1)

def foo2m(N):
    out = np.matrix(np.zeros((6,N),int))
    for i in range(N):
        out[:,i] = np.matrix([1,2,3,4,5,6]).T
    return out

In [62]: timeit foo1(1000)
1000 loops, best of 3: 1.32 ms per loop
In [63]: timeit foo1m(1000)
100 loops, best of 3: 18 ms per loop
In [64]: timeit foo2(1000)
100 loops, best of 3: 2.75 ms per loop
In [65]: timeit foo2m(1000)
10 loops, best of 3: 28.2 ms per loop

这使得每次/迭代时间增加了 10 倍。 matrixndarray 的子类,需要更多处理(强制其为 2d 等)。

【讨论】:

以上是关于如何快速存储 numpy 对象(矩阵)?的主要内容,如果未能解决你的问题,请参考以下文章

python系列26:numpy稀疏矩阵笔记

在 HDF5 (PyTables) 中存储 numpy 稀疏矩阵

numpy快速入门

numpy快速入门

如何将 numpy 数组存储在 Pandas 数据框的列中?

如何将 numpy 数组存储在 Pandas 数据框的列中?