前言
最近,某Y在创建模型的训练数据集时遇到了一个非常头疼的问题--生成的训练数据占用了过大的存储空间致使磁盘爆满!可能因为以前都是在体量较小的数据库上进行研究,故而第一次碰到这个问题的某Y是非常崩溃的。相信被下面这句话支配的恐惧感(尤其是在没有管理员权限的服务器上)很多和某Y一样的小白都深有感触吧 😭
Unable to create xxx: Disk quota exceeded
然而也正是这句话促使了某Y认真研究起数据的存储格式以及不同存储格式之间的效率问题。
通常,由于MATLAB是科研工作者的常用工具,.mat也成了最常见的数据存储格式之一。然而,当遇到大型矩阵时,.mat究竟是不是一个最有效的存储格式呢?有没有什么更有效的存储格式呢?(结论可直接跳至这里)下面某Y通过两组实验来对比下.mat和.h5的存储效率:
Experiment 1: 大型随机矩阵
在这个实验中, 我们着重比较的是两种不同存储格式对于大型随机矩阵的存储效率。首先,我们通过numpy创建一个10000 × 10000的大型随机矩阵
import numpy as np
a = np.random.rand(10000, 10000)
随后,我们将分别利用python中提供的.mat存储工具包scipy以及.h5的存储工具h5py来存储这个大型随机矩阵。
1.1: .mat × scipy
这里我们首先导入scipy并利用其中的savemat函数进行存储。存储的文件名为matfile.mat。注意存储对象需编码成字典形式才能存储{'variable_name':data}
。此处我们将矩阵存储为变量elem
import scipy.io as sio
sio.savemat('matfile.mat', {'elem':a})
存储完毕后我们进入到数据存储位置并使用如下命令检查文件大小
ls -lh matfile.mat
可以看到系统输出的文件大小为763M。
接下来我们尝试用h5py来存储该矩阵。
1.2: .h5 × h5py
HDF的全称是Hierarchical Data Format(层级数据格式)。这是一种专门用来存储和管理大型数据的文件格式。其最初是由美国国家超级计算应用中心(NCSA)开发,现在则由非营利社团HDF Group管理运营。在这里我们提到的.h5是该格式的第五代,也就是HDF5对应的文件名后缀。python中的相关工具包为h5py。
这里我们首先导入h5py并以其中的File object对象来存储和管理数据。此处h5file.h5为待创建的存储文件。和使用savemt相同,这里我们需要先定义存储对象为elem, 再将矩阵以data=
的形式赋予该变量
import h5py
with h5py.File('h5file.h5', 'w') as hf:
hf.create_dataset('elem', data=a)
存储完毕后我们进入到数据存储位置并使用如下命令检查文件大小
ls -lh h5file.h5
文件大小依旧为763M!!某Y看到这个的时候内心是绝望的,难道说好的高效牛p都是骗人的吗?难道我的磁盘空间没救了吗?带着这个疑问,某Y又做了详细的功课并发现一个惊人的功能--压缩。是的,h5py还提供了compression
和compression_opts
这两个变量可供设置。而在compression中,共有三种压缩方式可供选择:gzip
,lzf
,szip
!这可不得了,我们赶紧试验下利用压缩后文件会有怎样的变化,这里我们选择文档中推荐的压缩方法gzip,同时compression_opts
我们尝试了默认值4以及最高值9
with h5py.File('h5file_com4.h5', 'w') as hf:
hf.create_dataset('elem', data=a, compression='gzip', compression_opts=4)
with h5py.File('h5file_com9.h5', 'w') as hf:
hf.create_dataset('elem', data=a, compression='gzip', compression_opts=9)
压缩后的文件体积缩小了!两种压缩效率得到的文件均为720M!另外值得一提的是使用默认值4的时候,存储速度明显慢于最高值9。这时候可能有人就会质疑了:会不会有信息损失呢?为了回答这个疑问,我们继续做了下面实验,从存储文件中将我们的随机矩阵读入python并与原始矩阵对比
with h5py.File('h5file_com4.h5', 'r') as hf:
a_com4 = np.array(hf['elem'])
with h5py.File('h5file_com9.h5', 'r') as hf:
a_com9 = np.array(hf['elem'])
np.sum(a_com4-a), np.sum(a_com9-a)
对比结果是
(0.0, 0.0)
等等,那么scipy在存储.mat时是否提供了压缩功能了呢?仔细查询文档后发现,scipy也提供了压缩功能!某Y真的白用了这么久的scipy(希望我不是一个人)。。。自惭形愧🙈。。。为了测试scipy的压缩效率,我们将参数do_compression
设置为True
sio.savemat('matfile_com.mat', {'elem':a}, do_compression=True)
压缩后的文件大小也为720M,与h5py压缩后的文件大小相同。
1.3: 耗时
在压缩率相同的情况下,我们进一步比较了耗时。经测试,scipy耗时49.3秒,而h5py耗时39.9秒。
不多说了,h5py用起来好嘛!
Experiment 2: 大型稀疏矩阵
某Y突然想到,自己的数据集中,样本大部分为大型的稀疏矩阵,那么不同存储方式是否会有不同的存储效率呢?为此我们设计了第二个实验。稀疏矩阵指的是大部分元素为零的矩阵。因此在存储此类矩阵时,不同的存储方式通常都会采取一定的措施大幅度压缩文件体积。这里我们在极端情况,即矩阵元素全部为0,下比较不同的存储方式之。
a = np.zeros((10000, 10000))
2.1: scipy × .mat
首先我们用scipy来对矩阵进行存储
sio.savemat('matfile.mat', {'elem':a})
存储完成后,我可以看到未经压缩的文件大小为763M。接下来我们命令scipy进行压缩存储
sio.savemat('matfile_com.mat', {'elem':a}, do_compression=True)
进行压缩存储得到的.mat文件为760K!!!
2.2: h5py × .h5
接下来我们用h5py进行压缩存储
with h5py.File('h5file.h5', 'w') as hf:
hf.create_dataset('elem', data=a, compression='gzip', compression_opts=9)
最终生成大小为1.3M的.h5文件。虽然压缩效率低于scipy,但是相较于原文件,其大小也只有最初的0.17%!
1.3: 耗时
在存储大型稀疏矩阵时,scipy耗时8.5秒,而h5py耗时4.9秒。可见h5py虽然存储效率低于scipy,但是却有着较高的压缩速度。
结论
通过上面的两组实验,我们简单总结如下:
- 如果你只在乎压缩后文件的大小,请用.mat格式进行存储。不过一定要记得
do_compression=True
! - 如果你只在乎压缩文件的耗时,请用.h5格式进行存储,并将
compression_opts
设置为9 - 如果你没有特别在乎的方面或者使用喜好的话,综合考虑可选择.h5格式进行存储(毕竟不是所有的情况都是全0矩阵)
后话
某Y发现了这个后对之前创建的数据进行了压缩存储(.h5)。通过du -lh
查看后,某Y惊喜地发现其中一个数据集从193G压缩到了16G,而另一个数据集则是从556G压缩到了48G!!!要知道服务器上分配的空间才1T!!!anyway,得救了😆😆😆;当然还有很多存储特性没有在这里分析,比如h5py的group特性。写这篇博客的初衷是希望大家不要踩我踩过的坑,希望大家科研学习生活顺利😉~~~