酸洗大型 NumPy 数组

Posted

技术标签:

【中文标题】酸洗大型 NumPy 数组【英文标题】:Pickling large NumPy array 【发布时间】:2015-04-14 18:01:52 【问题描述】:

我想保留一个大型 3d numpy 数组。我的第一种方法是简单地使用pickle,但这似乎导致了一个解释不清的错误。

test_rand = np.random.random((100000,200,50))
with open('models/test.pkl', 'wb') as save_file:
    pickle.dump(test_rand, save_file, -1)

---------------------------------------------------------------------------
error                                     Traceback (most recent call last)
<ipython-input-18-511e30b08440> in <module>()
      1 with open('models/test.pkl', 'wb') as save_file:
----> 2         pickle.dump(test_rand, save_file, -1)
      3 

C:\Users\g1dak02\AppData\Local\Continuum\Anaconda\lib\pickle.pyc in dump(obj, file, protocol)
   1368 
   1369 def dump(obj, file, protocol=None):
-> 1370     Pickler(file, protocol).dump(obj)
   1371 
   1372 def dumps(obj, protocol=None):

C:\Users\g1dak02\AppData\Local\Continuum\Anaconda\lib\pickle.pyc in dump(self, obj)
    222         if self.proto >= 2:
    223             self.write(PROTO + chr(self.proto))
--> 224         self.save(obj)
    225         self.write(STOP)
    226 

C:\Users\g1dak02\AppData\Local\Continuum\Anaconda\lib\pickle.pyc in save(self, obj)
    329 
    330         # Save the reduce() output and finally memoize the object
--> 331         self.save_reduce(obj=obj, *rv)
    332 
    333     def persistent_id(self, obj):

C:\Users\g1dak02\AppData\Local\Continuum\Anaconda\lib\pickle.pyc in save_reduce(self, func, args, state, listitems, dictitems, obj)
    417 
    418         if state is not None:
--> 419             save(state)
    420             write(BUILD)
    421 

C:\Users\g1dak02\AppData\Local\Continuum\Anaconda\lib\pickle.pyc in save(self, obj)
    284         f = self.dispatch.get(t)
    285         if f:
--> 286             f(self, obj) # Call unbound method with explicit self
    287             return
    288 

C:\Users\g1dak02\AppData\Local\Continuum\Anaconda\lib\pickle.pyc in save_tuple(self, obj)
    560         write(MARK)
    561         for element in obj:
--> 562             save(element)
    563 
    564         if id(obj) in memo:

C:\Users\g1dak02\AppData\Local\Continuum\Anaconda\lib\pickle.pyc in save(self, obj)
    284         f = self.dispatch.get(t)
    285         if f:
--> 286             f(self, obj) # Call unbound method with explicit self
    287             return
    288 

C:\Users\g1dak02\AppData\Local\Continuum\Anaconda\lib\pickle.pyc in save_string(self, obj, pack)
    484                 self.write(SHORT_BINSTRING + chr(n) + obj)
    485             else:
--> 486                 self.write(BINSTRING + pack("<i", n) + obj)
    487         else:
    488             self.write(STRING + repr(obj) + '\n')

error: integer out of range for 'i' format code

所以我的两个问题如下:

这个错误究竟发生了什么? 我应该如何将阵列保存到磁盘?

我正在使用 Python 2.7.8 和 NumPy 1.9.0。

【问题讨论】:

刚刚遇到***.com/questions/9619199/…,它给出了#2 的答案:numpy:savez。 使用np.save 代替:docs.scipy.org/doc/numpy/reference/generated/numpy.save.html 但是,我不知道您为什么会收到该错误。酸洗大型数组可以正常工作(给定足够的内存来保存中间字符串表示),但为了提高效率,您应该使用pickle.HIGHEST_PROTOCOL numpy.save 和 numpy.savez 有什么区别? savez 用于保存多个数组。 save 用于保存单个数组。如果您只想转储单个数组,savez 使用起来会更复杂。不过,它们在引擎盖下或多或少是相同的。 savez 将每个变量保存到不同的文件中,并将它们打包为 zip 存档。 【参考方案1】:

关于#1,这是一个错误……而且是一个老错误。有一个启发性的,尽管令人惊讶的古老讨论在这里:http://python.6.x6.nabble.com/test-gzip-test-tarfile-failure-om-AMD64-td1830323.html

错误原因在这里:http://www.littleredbat.net/mk/files/grimoire.html#contents_item_2.1

最简单最基本的类型是整数,用整数表示 作为C长。因此,它们的大小取决于您所在的平台 使用;在 32 位机器上,它们的范围可以从 -2147483647 到 2147483647. Python 程序可以通过查看 sys.maxint 来确定整数的最大可能值;可能的最低值 通常是 -sys.maxint - 1。

这个错误并不常见,因为大多数人在面对非常大的numpy 数组时,会使用np.savenp.savez 来利用numpy 数组的缩减pickle 格式(参见__reduce__ 方法用于 numpy 数组,这是 np.save 在幕后调用的)。

为了表明这只是数组对于pickle来说太大了...

>>> import numpy as np
>>> import pickle
>>> test_rand = np.random.random((100000,200,50))
>>> x = pickle.dumps(test_rand[:20000], -1)
>>> x = pickle.dumps(test_rand[:30000], -1)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/Users/mmckerns/lib/python2.7/site-packages/dill-0.2.3.dev0-py2.7.egg/dill/dill.py", line 194, in dumps
    dump(obj, file, protocol, byref, fmode)#, strictio)
  File "/Users/mmckerns/lib/python2.7/site-packages/dill-0.2.3.dev0-py2.7.egg/dill/dill.py", line 184, in dump
    pik.dump(obj)
  File "/opt/local/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/pickle.py", line 224, in dump
    self.save(obj)
  File "/opt/local/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/pickle.py", line 286, in save
    f(self, obj) # Call unbound method with explicit self
  File "/Users/mmckerns/lib/python2.7/site-packages/dill-0.2.3.dev0-py2.7.egg/dill/dill.py", line 181, in save_numpy_array
    pik.save_reduce(_create_array, (f, args, state, npdict), obj=obj)
  File "/opt/local/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/pickle.py", line 401, in save_reduce
    save(args)
  File "/opt/local/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/pickle.py", line 286, in save
    f(self, obj) # Call unbound method with explicit self
  File "/opt/local/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/pickle.py", line 562, in save_tuple
    save(element)
  File "/opt/local/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/pickle.py", line 286, in save
    f(self, obj) # Call unbound method with explicit self
  File "/opt/local/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/pickle.py", line 562, in save_tuple
    save(element)
  File "/opt/local/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/pickle.py", line 286, in save
    f(self, obj) # Call unbound method with explicit self
  File "/opt/local/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/pickle.py", line 486, in save_string
    self.write(BINSTRING + pack("<i", n) + obj)
struct.error: 'i' format requires -2147483648 <= number <= 2147483647
>>> 

但是,这适用于整个数组...

>>> x = test_rand.__reduce__()
>>> type(x)
<type 'tuple'>
>>> x[0]     
<built-in function _reconstruct>
>>> x[1]
(<type 'numpy.ndarray'>, (0,), 'b')
>>> x[2][0:3]
(1, (100000, 200, 50), dtype('float64'))
>>> len(x[2][4])
8000000000
>>> x[2][4][:100]
'Y\xa4\xdf\x84\xdf\xe1?\xfe\x1fd\xe3\xf2\xab\xe2?\x80\xe4\xfe\x17\xfb\xd6\xc2?\xd73\x92\xc9N]\xe8?\x90\xbc\xe3@\xdcO\xc9?\x18\x9dX\x12MG\xc4?(\x0f\x8f\xf9\xf6\xb1?\xd0\x90O\xe2\x9b\xf1\xed?_\x99\x06\xacY\x9e\xe2?\xe7\xf8\x15\xa8\x13\x91\xe2?\x96\xffH\xda\xc3\xd4?@\t\xae_"\xe0\xda?y<%\x8a'

如果你想烧掉你的粉丝,print x

您还会注意到x[0] 中的函数与数据一起保存。它是一个独立的函数,可以从腌制的数据中生成一个 numpy 数组。

【讨论】:

【参考方案2】:

作为pickle 的替代方案,尤其是对于非常大的数据集,您可能希望考虑使用 Python 接口来连接二进制数据格式,例如 HDF5(例如,h5py)。有关其优缺点的讨论,请参阅this question 和第一个答案。

【讨论】:

【参考方案3】:

要回答第一个问题,“这个错误到底发生了什么?”,这是我的猜测

Pickle 正在尝试将您的 NumPy 数组保存为打包的二进制数据。它将每个整数保存为一个四字节有符号整数(i 代码)。但是,numpy.random.random 创建浮点数(应该是八字节 ds 而不是四字节 is)。我不知道为什么泡菜会这样做。 i 实际上也完全有可能用于保存一些其他信息,而不是数组的值之一。我只是猜测出现错误是因为您的数组的值不适合四个字节。

您使用的是什么版本的 Python 和 NumPy?

【讨论】:

使用 Python 2.78 和 NumPy 1.9.0。 是保存数据的问题还是维度的问题? '

以上是关于酸洗大型 NumPy 数组的主要内容,如果未能解决你的问题,请参考以下文章

对于小型/大型 numpy 数组,释放的处理方式是不是不同?

为啥 numpy.any 在大型数组上如此缓慢?

Python:两个大型numpy数组之间的余弦相似度

处理大型 Numpy 数组的技术? [复制]

访问大型 numpy 数组,同时保留其顺序

在多处理进程之间共享大型只读 Numpy 数组