如何压缩.npy数据节省磁盘空间?寻求比np.save更省空间的存储方案
针对你提到的1.5TB分块numpy数据集(Python 2.7环境,每个.npy为(number_of_examples,224,224,19)的float数组),我分两部分解答你的问题:
1. 高效的数据集压缩方法
这里有几个实用的压缩方案,兼顾压缩率和易用性:
利用numpy内置的压缩功能
你可以直接用np.savez_compressed替代np.save,它会对数组进行无损压缩,不需要额外依赖。对于float类型的数据,尤其是存在冗余的场景,压缩率通常能达到2-5倍。示例代码:import numpy as np # 假设你的数据数组是data np.savez_compressed('data_1_compressed.npz', data=data) # 读取时 loaded = np.load('data_1_compressed.npz') data = loaded['data']如果你想保留单个文件的结构,也可以手动对.npy文件进行压缩(比如用
gzip或zstd命令行工具),但savez_compressed更方便,能直接处理数组读写。使用高速压缩库(Zstandard/LZ4)
如果你追求更快的压缩/解压速度和更高的压缩率,可以用Zstandard(zstd)或LZ4这类现代压缩库。以Zstandard为例,Python 2.7可以安装对应版本的zstandard库,然后手动处理数组的字节流压缩:import numpy as np import zstandard as zstd # 保存 data = np.random.rand(100,224,224,19).astype(np.float32) compressor = zstd.ZstdCompressor(level=10) # 压缩级别可调,越高压缩率越高 compressed_data = compressor.compress(data.tobytes()) with open('data_1.zst', 'wb') as f: f.write(compressed_data) # 读取 with open('data_1.zst', 'rb') as f: compressed_data = f.read() decompressor = zstd.ZstdDecompressor() decompressed_data = decompressor.decompress(compressed_data) data = np.frombuffer(decompressed_data, dtype=np.float32).reshape((100,224,224,19))这种方式的压缩速度比numpy内置的更快,压缩率也更优,适合大规模数据集。
批量打包压缩
如果你有多个子数据集文件,可以用tar结合Zstandard或XZ进行批量打包压缩,命令行操作如下:# 把所有data_*.npy打包成tar并以zstd压缩 tar -I zstd -cf dataset.tar.zst data_*.npy # 解压 tar -I zstd -xf dataset.tar.zst这种方式适合归档存储,能减少文件系统的碎片化,同时利用批量压缩的优势。
2. 比np.save更省空间的存储方式
除了压缩,换用更高效的存储格式也能大幅减少磁盘占用:
HDF5格式(推荐)
HDF5是专为大型科学数据设计的存储格式,支持内置压缩、分块存储和按需读取,非常适合你的场景。Python 2.7可以使用h5py库来操作,示例代码:import h5py import numpy as np # 保存单个数据集,启用gzip压缩(也可以用'lzf'、'zstd'等) data = np.random.rand(100,224,224,19).astype(np.float32) with h5py.File('data_1.h5', 'w') as f: # 设置压缩参数,compression_opts越高压缩率越高 f.create_dataset('data', data=data, compression='gzip', compression_opts=9) # 读取时可以按需加载部分数据,不用全读入内存 with h5py.File('data_1.h5', 'r') as f: # 只加载前10个样本 partial_data = f['data'][:10, ...]HDF5的压缩率通常比
np.save高很多,而且支持复杂的数据结构,还能避免多个小文件的问题(你可以把多个子数据集放到同一个HDF5文件里)。降低数据精度(如果允许)
如果你当前用的是float64(双精度),可以先把数组转换为float32(单精度),这能直接减少一半的磁盘占用,而且很多深度学习或数据分析场景下,单精度已经足够满足需求。转换代码很简单:data = data.astype(np.float32)配合上面的压缩或HDF5存储,能进一步节省空间。
自定义二进制格式
如果你追求极致的空间优化,可以把数组直接转成二进制字节流,再用压缩库处理。这种方式最灵活,但需要自己维护数据的形状、 dtype等元信息,示例:import numpy as np import zstandard as zstd import json # 保存:先写元信息(形状、dtype),再写压缩后的字节流 data = np.random.rand(100,224,224,19).astype(np.float32) meta = {'shape': data.shape, 'dtype': str(data.dtype)} meta_str = json.dumps(meta).encode('utf-8') compressor = zstd.ZstdCompressor(level=10) compressed_data = compressor.compress(data.tobytes()) with open('data_1.bin.zst', 'wb') as f: # 先写元信息长度,再写元信息,再写压缩数据 f.write(len(meta_str).to_bytes(4, byteorder='big')) f.write(meta_str) f.write(compressed_data) # 读取 with open('data_1.bin.zst', 'rb') as f: meta_len = int.from_bytes(f.read(4), byteorder='big') meta_str = f.read(meta_len).decode('utf-8') meta = json.loads(meta_str) compressed_data = f.read() decompressor = zstd.ZstdDecompressor() decompressed_data = decompressor.decompress(compressed_data) data = np.frombuffer(decompressed_data, dtype=meta['dtype']).reshape(meta['shape'])这种方式的存储开销最小,但需要自己处理元信息的序列化和反序列化,适合对存储有极致要求的场景。
内容的提问来源于stack exchange,提问作者eric lardon

