【问题标题】:Slicing without views (or: shuffling multiple arrays)没有视图的切片(或:改组多个数组)
【发布时间】:2016-10-15 16:16:07
【问题描述】:

我有两个不同的 numpy 数组,我想以异步方式对它们进行洗牌。

当前的解决方案取自https://www.tensorflow.org/versions/r0.8/tutorials/mnist/pros/index.html,如下:

perm = np.arange(self.no_images_train)
np.random.shuffle(perm)
self.images_train = self.images_train[perm]
self.labels_train = self.labels_train[perm]

问题是每次我这样做都会使内存翻倍。不知何故,旧数组没有被删除,可能是因为切片运算符创建了我猜的视图。出于纯粹的绝望,我尝试了以下更改:

perm = np.arange(self.no_images_train)
np.random.shuffle(perm)

n_images_train = self.images_train[perm]
n_labels_train = self.labels_train[perm]            

del self.images_train
del self.labels_train
gc.collect()

self.images_train = n_images_train
self.labels_train = n_labels_train

还是一样,内存泄漏,我在几次操作后内存不足。

顺便说一句,这两个数组的秩分别为 100000,224,244,1 和 100000,1。

我知道这已在此处处理 (Better way to shuffle two numpy arrays in unison),但答案对我没有帮助,因为提供的解决方案需要再次切片。

感谢您的帮助。

【问题讨论】:

  • 那些不是视图。您可能在某处对原始数组有其他引用。
  • "...因为切片运算符创建视图我猜。"切片确实创建视图,但您显示的代码不是切片.当您写a[perm] 时,会生成一份副本。 “切片”是指使用冒号的操作:start:end:step,例如0:44:
  • "... 以异步方式。" 我认为您缺少一个空格。根据以下内容,我认为您的意思是“以同步方式”。
  • "...rank 100000,224,244,1..." 差不多是 5.5 GB(假设数据类型是 8 位)。即使在您的“绝望”代码中,有时self.images_train 和 `n_images_train" 也会同时存在,这将需要 11 GB。这不是内存“泄漏”。
  • 我认为这个问题的一个更好的标题是“如何将相同的随机排列应用于两个数组而不制作数组的临时副本?”

标签: python arrays numpy memory-leaks slice


【解决方案1】:

以同步方式就地置换两个大型数组的一种方法是保存随机数生成器的状态,然后对第一个数组进行洗牌。然后恢复状态并打乱第二个数组。

例如,这是我的两个数组:

In [48]: a
Out[48]: array([ 0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15])

In [49]: b
Out[49]: array([ 0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15])

保存随机数生成器当前的内部状态:

In [50]: state = np.random.get_state()

就地随机播放a

In [51]: np.random.shuffle(a)

恢复随机数发生器的内部状态:

In [52]: np.random.set_state(state)

就地随机播放b

In [53]: np.random.shuffle(b)

检查排列是否相同:

In [54]: a
Out[54]: array([13, 12, 11, 15, 10,  5,  1,  6, 14,  3,  9,  7,  0,  8,  4,  2])

In [55]: b
Out[55]: array([13, 12, 11, 15, 10,  5,  1,  6, 14,  3,  9,  7,  0,  8,  4,  2])

对于您的代码,如下所示:

state = np.random.get_state()
np.random.shuffle(self.images_train)
np.random.set_state(state)
np.random.shuffle(self.labels_train)

【讨论】:

  • 确实有帮助,谢谢。不过,我其实找到了更好的办法:规避问题。我决定不定期对数据进行洗牌,而只重新创建一个置换向量并使用它进行采样。我仍然想知道为什么原来的解决方案会失败。​​
  • 但是这个解决方案需要两次调用随机数生成器,这可能会成为性能瓶颈。您可以使用不同的随机数生成器来减少这种影响。
  • @Guillaum 是的,对随机数生成器的两次调用(生成相同的序列!)可能是一个问题,因此建议进行一些性能测试。使用不同的生成器会有什么帮助?
  • @WarrenWeckesser 据我所知,numpy 的随机生成器是 Mersenne Twister。它存在具有不同行为(质量和速度)的随机数生成器。例如,使用 C++ std::mt19937_64 (Mersenne Twister) 或 std::minstd_rand (一种更简单的方法) 生成 1000 万个随机数在我的计算机上运行时间为 8.3 秒而不是 1.0 秒。但是我认为 numpy 带有不同的生成器,但我错了。
【解决方案2】:

其实我认为 numpy 或 python 没有任何问题。 Numpy 使用系统 malloc / free 分配数组,这会导致内存碎片(参见Memory Fragmentation on SO)。

所以我猜如果可能的话,当系统能够减少碎片时,您的内存配置文件可能会增加并突然下降。

【讨论】:

  • 内存以 6GB 的步长增加,在 230GB 时,我用 64GB 的物理内存终止了我机器上的进程。我不确定这是否可以完全归因于碎片化。特别是因为没有真正的理由应该在较长时间内使用超过 6GB 的内存(除了用于复制的临时分配等)。
猜你喜欢
  • 2012-11-10
  • 1970-01-01
  • 1970-01-01
  • 2016-04-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-04-29
  • 2011-10-07
相关资源
最近更新 更多