关于numpy文件存储的读取速度,与pickle对比

文章通过实验对比了numpy的npz和pickle在序列化和反序列化numpy数组时的速度。虽然np.load在单次加载上似乎更快,但当从npz文件读取数组时,由于numpy的懒读取机制,实际读取数据时耗时较长。因此,文章指出在程序中选择序列化方法时,不应仅基于初始加载时间,而应考虑整体使用情况。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

numpy文件有两种后缀名:npy和npz。npy存储的是一个单独的numpy数组,而npz存储的是多个numpy数组。

python中pickle库也可以用于序列化numpy数组。下面做一个实验对比pickle文件和npz文件的读取速度:

import numpy as np
import pickle
import time

class Timer:
    def __init__(self):
        self.start = 0
        self.end = 0
        self.interval = 0
    def __enter__(self):
        self.start = time.time()
        return self
    def __exit__(self, *args):
        self.end = time.time()
        self.interval = self.end - self.start

shape = (1000,1000,500)

arr = np.random.rand(*shape)

name_pickle = "test.pkl"
with open(name_pickle, "wb") as f:
    with Timer() as t:
        pickle.dump(arr, f)
    print(f"pickle dump time: {t.interval}s")


with open(name_pickle, "rb") as f:
    with Timer() as t:
        x = pickle.load(f)
    print(f"pickle load time: {t.interval}s")

print(type(x))
print("\n\n")


name = "test.npz"

with Timer() as t:
    np.savez(name, arr)
print(f"np.savez time: {t.interval}s")

with Timer() as t:
    x = np.load(name, allow_pickle=True)
print(f"np.load time: {t.interval}s")

输出结果是:

pickle dump time: 3.172863721847534s
pickle load time: 1.2087347507476807s
<class 'numpy.ndarray'>



np.savez time: 3.008016347885132s
np.load time: 0.2541012763977051s

看起来np.load比pickle快了一个数量级,简直令人兴奋。不过别急,我们再补一个实验,让它打印npz文件中数组:

with Timer() as t:
    print(type(x))
    print(type(x['arr_0'])) 
print(f"read time: {t.interval}")
print("\n\n")

得到输出:

<class 'numpy.lib.npyio.NpzFile'>
<class 'numpy.ndarray'>
read time: 2.2933874130249023

什么玩意儿?将从npz文件读取到的东西打印出来要两秒多种,都比之前pickle.load从硬盘读文件还慢了。

这只能解释为numpy对npz文件的load函数是一种“懒读取”,返回的只是数组在文件中的句柄,只有在用这个句柄去索引数组时,才会真正读取文件。

所以,np.load并没有比pickle.load快一个数量级,在程序中,没有必要刻意用np.load去替换pickle.load!

另外,numpy的懒读取仅针对npz格式文件,对npy格式文件是立刻读取的。

### 解决Python `pickle`模块和不同版本的NumPy之间兼容性问题 当使用`pickle`序列化包含NumPy数组的对象时,可能会遇到由于NumPy版本差异引起的反序列化失败问题。为了确保跨环境的一致性和稳定性,建议采取以下措施: #### 方法一:指定协议版本 通过设置较高的`protocol`参数值,在保存模型或对象时可以提高向后的兼容性。较高版本的协议通常具有更好的性能以及更广泛的兼容性。 ```python import pickle with open('model.pkl', 'wb') as f: pickle.dump(obj, f, protocol=pickle.HIGHEST_PROTOCOL) # 使用最高支持的协议级别[^1] ``` #### 方法二:固定依赖库版本 在项目环境中锁定特定版本的NumPy和其他相关包,防止因更新而导致潜在的问题。这可以通过创建虚拟环境并记录所有安装软件的确切版本号来实现。 对于新项目的初始化阶段,推荐利用工具如`pipenv`, `poetry` 或者传统的`requirements.txt` 文件来管理这些依赖关系。 #### 方法三:转换为通用格式再存储 如果可能的话,考虑先将NumPy数组转化为其他更加稳定的交换格式(例如HDF5),然后再将其写入磁盘。这样做的好处在于即使未来NumPy API 发生变化,仍然能够读取旧的数据文件。 ```python import h5py # 将 NumPy 数组保存至 HDF5 文件 with h5py.File("data.hdf5", "w") as f: dset = f.create_dataset("dataset_name", data=numpy_array) # 加载 HDF5 文件中的 NumPy 数组 with h5py.File("data.hdf5", "r") as f: numpy_array_loaded = f["dataset_name"][:] ``` 这种方法不仅解决了版本控制难题,还提供了更高的灵活性和支持多种编程语言的能力[^3]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值