微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

通过附加数据构建 Numpy 数组事先不知道完整大小

如何解决通过附加数据构建 Numpy 数组事先不知道完整大小

我有很多文件,每个文件都被读取为形状为 (n,1000) 的矩阵,其中 n 可能因文件而异。

我想将它们全部连接成一个大的 Numpy 数组。我目前这样做:

dataset = np.zeros((100,1000))
for f in glob.glob('*.png'):
    x = read_as_numpyarray(f)    # custom function; x is a matrix of shape (n,1000)
    dataset = np.vstack((dataset,x))

但它效率低下,因为我通过将现有数组与读取的下一个文件堆叠在一起来多次重新定义 dataset

如何用 Numpy 以更好的方式做到这一点,避免整个数据集在内存中多次重写?

注意:最终的大 Numpy 数组可能需要 10 GB。

解决方法

使用原生的 list numpy 数组,然后使用 np.concatenate

原生list会在需要时乘以(by ~1.125)大小,因此不会发生太多重新分配,而且,它只会保存指向分散(内存中不连续){{1}的指针}} 保存实际数据。

只调用一次 np.arrays 即可解决您的问题。

伪代码

concatenate

注意 dataset = [] for f in glob.glob('*.png'): x = read_as_numpyarray(f) # custom function; x is a matrix of shape (n,1000) dataset.append(x) dataset_np = np.concatenate(dataset) 在内部使用 vstack


编辑以解决已编辑的问题:

假设数据的总大小为 20 GB。连接时, 系统仍必须保留 20 GB(对于每个单独的阵列)和 还为新的串联数组分配 20 GB,因此需要 40 GB RAM(数据集的两倍)。如何在不需要的情况下做到这一点 RAM的两倍? (例如:如果我们只有 32 GB,是否有解决方案? 内存?)

我将首先通过分阶段执行当前答案中提出的相同操作来解决此问题。 concatenatedataset_np1 = np.concatenate(half1_of_data) 只需要 150% 的 RAM(而不是 200%)。这可以以牺牲速度为代价递归地扩展,直到它成为问题中的命题的极限。我只能假设 dask 之类的人可以更好地处理这个问题,但我没有测试过自己。

澄清一下,在您拥有 dataset_np1 之后,您不再需要所有分片小数组的列表,并且可以释放它。只有这样你才开始加载另一半。因此,您只需要在内存中保存 50% 的额外数据。

伪代码:

dataset_np2 = np.concatenate(half2_of_data)

人们可以(轻松地或不那么容易地)将其概括为四分之一、八分之一或递归任何所需的分数,以牺牲速度来降低内存成本,无穷大的限制是问题中的原始命题。

>
  • 重要注释(参见“代码中​​的注释*):
    人们可能会注意到 def load_half(buffer: np.array,shard_path: str,shard_ind: int): half_dataset = [] for f in glob.glob(f'{shard_path}/*.png'): x = read_as_numpyarray(f) # custom function; x is a matrix of shape (n,1000) half_dataset.append(x) half_dataset_np = np.concatenate(half_dataset) # see comment * buffer[:buffer.shape[0] // 2 * (shard_ind + 1),...] = half_dataset_np half1_path = r"half1" # preprocess the shards to be found by glob or otherwise half2_path = r"half2" assert os.path.isdir(half1_path) assert os.path.isdir(half2_path) buffer = np.zeros(size_shape) half1_np = load_half(half1_path,buffer,0) # only 50% of data temporarily loaded,then freed [can be done manually if needed] half2_np = load_half(half2_path,1) # only 50% of data temporarily loaded,then freed 实际上分配了 50% 的数据集,另外 50% 分配 在碎片中,显然什么也拯救不了我们。这是正确的,我可以 找不到连接到缓冲区的方法。然而,实施这一 按照建议递归地(而不是在伪代码中显示)将保存 内存,因为一个季度每次只会使用 2* 25%。这只是一个 实施细节,但我希望要点是明确的。

换个说法,另一种方法会说明“如果数据集是 1000GB 会怎样”?那么没有 numpy 数组会做。这就是数据库存在的原因,并且可以使用 tools 非常有效地查询它们。但同样,这在某种程度上是一个研究问题,在很大程度上取决于您的特定需求。作为一个非常不知情的预感,我会检查dask

此类库显然会将此类问题作为其工作的一个子集来解决,我建议您不要自己实现这些事情,因为您花费的总时间将远远超过选择和学习库的时间。


另一方面,我想知道这是否真的必须是一个如此庞大的数组,而 maybe a slightly different design or formulation of the problem 可以让我们完全摆脱这个技术问题。

,

预先分配您的阵列。循环遍历您要预先添加的文件,并将您将从每个文件中检索的数据量相加。然后使用您需要的总大小创建您的 dataset 数组。最后再次循环遍历文件,将数据插入到已分配的数组中。这将比为每个附加文件一遍又一遍地分配新数组和从以前的文件中复制数据更有效。

或者不要构建 10GB 的阵列。考虑修改您的操作,以便它们在较小的数据块上兼容并按需读取更易于管理的数据集。

,

我认为您在这里需要 mmap 的概念。 Numpy 内置了对 mmap 的支持。我喜欢@Gulzar 提到的分片以分块处理文件的方式。我没有使用 mmap() 但如果你看到他们的文档,你似乎不需要在内存中保存整个文件。您可以通过附加模式写入它,并且可以通过这种方式对其进行操作。此外,如果您担心此操作的时间限制,因为分片会不断增加处理时间,您应该考虑此过程的分布式计算架构。通过这种方式,您可以例如使用 10 台机器,每台机器都有较小的内存,但将块附加到较大的内存中。我知道这不是一个完整的答案,这就是为什么,我最后会提供一些与我刚才提到的想法相关的资源。此外,您还可以使用 hdf5 和 zarr 来解决此过程。

我所说的算法思想:
(1) 开始
(2)通过文件1到10解析:添加它们,作为mmap引用对象存储在硬盘中。
对于从 1 到 100 的所有 n:
(3) 将n*10+1解析为(n+1)*10个文件:将它们添加到内存中,将它们附加到硬盘中的mmap引用对象中。
(4) 你把你的 numpy 数组存储在内存中。
参考文献:
(1) mmap vs zarr vs hdf5
(2) numpy mmap support
(3) what is mmap actually

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。