np.load()爆内存因默认全量加载,不支持流式读取;应改用np.memmap()(需转裸数据)、np.fromfile()(无头二进制)或dask.array(惰性计算),并注意存储顺序与字节序。

为什么np.load()直接加载大文件会爆内存?
因为np.load()默认把整个文件一次性读进内存,哪怕你只想要其中几列或某一段。比如一个 20GB 的 .npy 文件,不管后续是否切片,它都会先全量载入——这和 Python 的 open() 行读取完全不同。
常见错误现象:MemoryError、系统卡死、numpy.core._exceptions._ArrayMemoryError(NumPy 1.23+ 新错误类型)。
- 使用场景:处理遥感影像、基因测序矩阵、日志聚合特征矩阵等单文件 >5GB 的数组
- 关键限制:不是磁盘空间不够,而是 RAM 不够;即使有 64GB 内存,加载 40GB 数组也大概率失败
- 根本原因:
np.load()不支持内存映射以外的“流式”加载逻辑
用np.memmap()替代np.load()加载超大数组
np.memmap() 把文件当“虚拟内存”用,只在真正访问某块数据时才从磁盘读取,不占实际 RAM。前提是文件必须是二进制格式(如 .npy 或自定义 .dat),且形状/类型已知。
实操要点:
- 不能直接用
np.memmap("big.npy")——.npy有头部元信息,np.memmap()不识别;需先用np.load()读一次获取shape和dtype,再转存为裸数据(.dat) - 推荐流程:
arr = np.load("big.npy"); arr.tofile("big.dat"),然后用np.memmap("big.dat", dtype=arr.dtype, shape=arr.shape) - 如果必须保留
.npy格式,可用np.lib.format.open_memmap()(但仅支持创建,不支持读已有.npy) - 注意权限:Windows 下打开
memmap后文件会被锁住,别在多进程里反复 open/close
分块读取 + np.fromfile() 处理无头二进制数据
当数据是纯二进制(如 C/Fortran 输出的 .bin)、没有 shape 信息时,np.fromfile() + 手动 reshape 是最轻量的方案,比 memmap 更省系统资源。
典型使用场景:传感器原始采样流、HDF5 中某个 dataset 导出的 flat buffer、模型权重 bin 文件。
- 先确认字节序(
dtype带或 <code>>,如np.float32默认小端,设备输出可能是大端) - 计算单次读取长度:
chunk_size = 1000000个元素 →bytes_per_chunk = chunk_size * arr.dtype.itemsize - 用
with open("data.bin", "rb") as f:配合f.seek()和np.frombuffer(f.read(bytes_per_chunk), dtype=np.float32)流式读取 - 避免用
np.fromfile()直接读整个文件——它内部仍会尝试分配大内存
用dask.array做惰性计算,绕过内存瓶颈
如果你后续要做统计、矩阵运算或需要分布式扩展,dask.array 是更工程化的解法。它不真正加载数据,而是构建计算图,直到 .compute() 才触发执行。
但要注意兼容性陷阱:
-
dask.array.from_npy_stack()只支持目录下多个小.npy文件,不支持单个大文件 - 对单个大
.npy,得先用np.memmap加载后转成dask.array.from_array(memmap_arr, chunks=(1000, -1)) - 某些 NumPy 函数(如
np.linalg.svd)在 dask 中对应的是da.linalg.svd,行为不完全一致 - 调试时别忘加
optmethod="none"关闭图优化,否则报错堆栈难定位
真正容易被忽略的是:所有方案都依赖「数据是否连续存储」。如果原始数组是 order="F"(Fortran order),而你用 C-order 的 memmap 或 fromfile 解析,结果会完全错位——务必先查清 arr.flags。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











