所以我正在使用 np.load() 加载一些大小在 150MB 到 250MB 范围内的文件。每个文件包含一个数组,该数组有 5 个带有一些数据的子数组。有些文件在一秒钟内加载完毕,而另一些文件最多需要 5 秒才能加载,而且由于我有很多这些文件,由于加载时间慢,它们需要很长时间才能处理它们。但是,我注意到,如果我将文件分成 5 个较小的文件(每个文件 1 个子数组),那么每 5 个文件的加载时间总是很容易低于一秒。
这可能是什么原因造成的?如何在不将每个文件拆分为更小文件的情况下加快 np.load() 的速度?
问题的根源在于numpy中并没有真正的子数组的概念。
考虑这个例子:
import numpy as np
a1 = np.ones(2**17)
a2 = np.arange(2**18)
a3 = np.random.randn(2**19)
a = np.array([a1, a2, a3])
print(a.dtype) # object
如果你把数组放入一个数组中,numpy 并不知道它们是数组。相反,它将它们视为通用 Python 对象。这就是documentation of np.save
不得不说:
allow_pickle : bool, optional
Allow saving object arrays using Python pickles. [...] Default: True
所以发生的事情是子数组由 pickler 处理,这是非常低效的。显然,单独保存数组时不会发生这种情况。现在它们被有效地存储为 numpy 数组。不幸的是,您不能简单地设置 allow_pickle=False
,因为这样它就不允许您存储对象数组。
解决方案是使用np.savez
存储多个数组。这是与上述数组的时间比较:
np.save('test.npy', a)
%timeit np.load('test.npy') # 10 loops, best of 3: 40.4 ms per loop
np.savez('test2.npz', a1, a2, a3)
%timeit np.load('test2.npz') # 1000 loops, best of 3: 339 µs per loop
您可以使用以下命令检索数组
x = np.load('test2.npz')
a1 = x['arr_0']
a2 = x['arr_1']
# ...
将数组作为关键字参数传递给 savez
可能更好,这样您就可以给它们命名:
np.savez('test3.npz', a1=a1, a2=a2, timmy=a3)
x = np.load('test3.npz')
a1 = x['a1']
a2 = x['a2']
a3 = x['timmy']