使numpy.savez确定性

2024-03-19 02:49:07 发布

您现在位置:Python中文网/ 问答频道 /正文

我很惊讶地发现,如果您使用numpy.savez将同一个numpy对象保存到file中,那么所创建的文件是不确定的。 例如

import numpy
x = numpy.random.rand(1000, 1000)
numpy.savez('foo.npz', x)
numpy.savez('bar.npz', x)

然后呢

^{2}$

读取this看起来与npzzip文件中的时间戳有关。在

出于测试目的,我想验证代码创建的数据文件是否相同。我通常用pickle文件的校验和来完成这个任务,例如

import cPickle as pickle
with open('foo.pkl', 'wb') as f:
    pickle.dump(x, f, protocol=2)

with open('bar.pkl', 'wb') as f:
    pickle.dump(x, f, protocol=2)

然后呢

 md5sum foo.pkl bar.pkl
 3139d9142d57bdde0970013f39b4854f  foo.pkl
 3139d9142d57bdde0970013f39b4854f  bar.pkl

numpy.savez做同样的事情有什么解决办法吗?在


Tags: 文件importnumpyfooaswithbaropen
2条回答

这可以通过numpy记录/结构来实现,唯一的限制是每个数组的第一个维度必须相同。在

下面是使其与numpy记录一起工作的代码片段,以便您甚至可以使用kwargs。在

import numpy as np
import time
import hashlib

def md5(fname):
    hash_md5 = hashlib.md5()
    with open(fname, "rb") as f:
        for chunk in iter(lambda: f.read(4096), b""):
            hash_md5.update(chunk)
    return hash_md5.hexdigest()

def mysavez(outfile,**kwargs):
    # sort the keys
    _sorted_keys = list(kwargs.keys())
    _sorted_keys.sort()

    # len of first element ... and check if it is same for all elements
    _len = kwargs[_sorted_keys[0]].shape[0]
    for k, v in kwargs.items():
        if v.shape[0] != _len:
            raise Exception(
                f"While creating numpy struct all arrays must have same length."
                f"invalid shape {v.shape} for item {k}"
            )

    # create numpy record buffer
    npy_record = np.zeros(
        _len,
        dtype=[
            (k, kwargs[k].dtype,  kwargs[k].shape[1:])
            for k in _sorted_keys
        ],
    )

    # fill up the elements
    for k, v in kwargs.items():
        npy_record[k] = v

    # save
    with open(outfile, 'wb') as outf:
        np.save(outf, npy_record)

a = np.random.rand(1000,1000)
b = np.random.rand(1000,1000)

# new one
mysavez('foo.nopz', a=a, b=b)
time.sleep(2) # make sure there's a difference in timestamp
mysavez('bar.nopz', a=a, b=b)

# check hash
print('foo.nopz', md5('foo.nopz'))
print('bar.nopz', md5('bar.nopz'))

输出:

^{pr2}$

如果您确实没有将关键字参数传递给^{}(也就是说,实际上只是序列化数据,而不想以后根据键引用这些项),那么可以使用^{}将多个数组转储到同一个文件中:

import numpy as np
import time

def mysavez(outfile,*args):
    with open(outfile,'wb') as outf:
        for arg in args:
            np.save(outf,arg)

x = np.random.rand(1000,1000)

# control group
np.savez('foo.npz', *[x]*5)
time.sleep(2) # make sure there's a difference in timestamp
np.savez('bar.npz', *[x]*5)

# new one
mysavez('foo.nopz', *[x]*5)
time.sleep(2) # make sure there's a difference in timestamp
mysavez('bar.nopz', *[x]*5)

生成的新文件具有相同的哈希值,甚至与原始文件的大小完全相同:

^{pr2}$

只要从文件中按顺序读取变量,就不会注意到函数上的差异。当然,您将需要一个myload来生成保存的数组,直到它们全部消失(或者更花哨地保存一个初始的整数头,告诉您保存到文件中的数组的数量)。诚然,这种方法是愚蠢的,但它可能会削减它取决于您的具体用例。在

如果您想使用键访问保存的变量,您仍然可以考虑编写一个用于测试的辅助函数,该函数读取“production”.npz文件,迭代它们的有序键,使用上面的mysavez函数按顺序保存它们,然后计算这些“扁平化”pickle文件的哈希值。当然,您可能不需要np.save来实现这一点:pickle也可以为您做同样的事情(尽管^{} might not)。在

相关问题 更多 >