无法分配具有形状和数据类型的数组

我在 Ubuntu18上分配大型数组时遇到了一个问题,而在 MacOS 上却没有遇到同样的问题。

我试图为一个形状为 (156816, 36, 53806)的数字数组分配内存 和

np.zeros((156816, 36, 53806), dtype='uint8')

当我在 Ubuntu 操作系统上遇到一个错误时

>>> import numpy as np
>>> np.zeros((156816, 36, 53806), dtype='uint8')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
numpy.core._exceptions.MemoryError: Unable to allocate array with shape (156816, 36, 53806) and data type uint8

我在 MacOS 上看不到:

>>> import numpy as np
>>> np.zeros((156816, 36, 53806), dtype='uint8')
array([[[0, 0, 0, ..., 0, 0, 0],
[0, 0, 0, ..., 0, 0, 0],
[0, 0, 0, ..., 0, 0, 0],
...,
[0, 0, 0, ..., 0, 0, 0],
[0, 0, 0, ..., 0, 0, 0],
[0, 0, 0, ..., 0, 0, 0]],


[[0, 0, 0, ..., 0, 0, 0],
[0, 0, 0, ..., 0, 0, 0],
[0, 0, 0, ..., 0, 0, 0],
...,
[0, 0, 0, ..., 0, 0, 0],
[0, 0, 0, ..., 0, 0, 0],
[0, 0, 0, ..., 0, 0, 0]],


[[0, 0, 0, ..., 0, 0, 0],
[0, 0, 0, ..., 0, 0, 0],
[0, 0, 0, ..., 0, 0, 0],
...,
[0, 0, 0, ..., 0, 0, 0],
[0, 0, 0, ..., 0, 0, 0],
[0, 0, 0, ..., 0, 0, 0]],


...,


[[0, 0, 0, ..., 0, 0, 0],
[0, 0, 0, ..., 0, 0, 0],
[0, 0, 0, ..., 0, 0, 0],
...,
[0, 0, 0, ..., 0, 0, 0],
[0, 0, 0, ..., 0, 0, 0],
[0, 0, 0, ..., 0, 0, 0]],


[[0, 0, 0, ..., 0, 0, 0],
[0, 0, 0, ..., 0, 0, 0],
[0, 0, 0, ..., 0, 0, 0],
...,
[0, 0, 0, ..., 0, 0, 0],
[0, 0, 0, ..., 0, 0, 0],
[0, 0, 0, ..., 0, 0, 0]],


[[0, 0, 0, ..., 0, 0, 0],
[0, 0, 0, ..., 0, 0, 0],
[0, 0, 0, ..., 0, 0, 0],
...,
[0, 0, 0, ..., 0, 0, 0],
[0, 0, 0, ..., 0, 0, 0],
[0, 0, 0, ..., 0, 0, 0]]], dtype=uint8)

我在某处读到过,np.zeros实际上不应该为数组分配所需的整个内存,而只是为非零元素分配内存。尽管 Ubuntu 机器有64gb 的内存,而我的 MacBook Pro 只有16gb。

版本:

Ubuntu
os -> ubuntu mate 18
python -> 3.6.8
numpy -> 1.17.0


mac
os -> 10.14.6
python -> 3.6.4
numpy -> 1.17.0

另外,在 Google Colab 上也失败了

670587 次浏览

这可能是由于您的系统的 超负荷处理模式。

在默认模式下,0,

启发式过度提交处理。地址空间的明显过载被拒绝。用于典型的系统。它确保严重的野生分配失败,同时允许过度提交以减少交换使用。在这种模式下,允许根分配稍微多一点的内存。这是默认设置。

确切的启发式使用在这里没有很好地解释,但这是在 Linux 优于提交启发式在这一页上讨论更多。

可以通过运行

$ cat /proc/sys/vm/overcommit_memory
0

在这种情况下,您将分配

>>> 156816 * 36 * 53806 / 1024.0**3
282.8939827680588

~ 282GB,内核说很明显我不可能把那么多物理页面提交给它,它拒绝分配。

如果(作为 root 用户)运行:

$ echo 1 > /proc/sys/vm/overcommit_memory

这将启用“总是超量提交”模式,您会发现,无论分配有多大(至少在64位内存寻址中) ,系统确实允许您进行分配。

我自己在一台有32GB 内存的机器上测试了它。在超额提交模式 0中,我也得到了一个 MemoryError,但是在将它改回 1之后,它就可以工作了:

>>> import numpy as np
>>> a = np.zeros((156816, 36, 53806), dtype='uint8')
>>> a.nbytes
303755101056

然后,您可以继续写入数组中的任何位置,当您显式写入物理页时,系统将只分配物理页。因此,可以谨慎地对稀疏数组使用这种方法。

我在“窗口”上遇到了同样的问题,并且遇到了这个解决方案。所以如果有人在 Windows 中遇到这个问题,我的解决方案是增加 页面文件的大小,因为这对我来说也是一个内存超量使用的问题。

视窗8

  1. 在键盘上按 WindowsKey + X,然后在弹出菜单中单击 System
  2. 点击或单击高级系统设置。您可能会被要求输入管理员密码或确认您的选择
  3. 在“高级”选项卡上的“性能”下,点击或单击“设置”。
  4. 单击或单击“高级”选项卡,然后在“虚拟内存”下单击或单击“更改”
  5. 清除“自动管理所有驱动器的分页文件大小”复选框。
  6. 在“驱动器[卷标]”下,点击或单击包含要更改的分页文件的驱动器
  7. 单击或单击“自定义大小”,在“初始大小(MB)”或“最大大小(MB)”框中输入以兆字节为单位的新大小,单击或单击“设置”,然后单击或单击“确定”
  8. 重启你的系统

视窗10

  1. 按下 Windows 键
  2. 类型 SystemPropertiesAdvanced
  3. 单击“以管理员身份运行”
  4. 在“性能”下,单击“设置”
  5. 选择 Advanced 选项卡
  6. 选择改变..。
  7. 取消选中自动管理所有驱动器的分页文件大小
  8. 然后选择“自定义大小”并填入适当的大小
  9. 按下 Set 然后按下 OK 然后退出虚拟内存、性能选项和系统属性对话框
  10. 重启你的系统

注意: 在这个示例中,我的系统上没有足够的内存来容纳大约282GB,但是对于我的特殊情况,这是可行的。

剪辑

给你的建议页面文件大小:

有一个计算正确页文件大小的公式。初始大小是系统总内存量的1.5倍。最大大小为初始大小的3倍。假设您有4 GB (1 GB = 1,024 MB x 4 = 4,096 MB)的内存。初始大小为1.5 x 4,096 = 6,144 MB,最大大小为3 x 6,144 = 18,432 MB。

给你中需要记住的一些事情:

但是,这并没有考虑到其他重要因素和系统设置可能是唯一的计算机。同样,让 Windows 选择使用什么,而不是依赖于在不同计算机上工作的任意公式。

另外:

增加页面文件大小可能有助于防止 Windows 中的不稳定性和崩溃。但是,硬盘的读/写时间比数据在计算机内存中的时间要慢得多。有一个较大的页面文件将增加额外的工作,你的硬盘驱动器,导致其他一切运行得更慢。页面文件大小只有在遇到内存不足错误时才应该增加,而且只能作为临时修复。一个更好的解决方案是给计算机增加更多的内存。

我在 Windows 上也遇到过这个问题。我的解决方案是 从32位版本切换到64位版本的 Python。事实上,一个32位的软件,比如一个32位的 CPU,可以寻址一个内存(2 ^ 32)的 最大容量为4GB。因此,如果您有超过4GB 的内存,32位版本不能利用它。

对于64位版本的 Python (下载页面中标记为 X86-64的版本) ,问题就消失了。

你可以通过输入解释器来检查你使用的是哪个版本,我使用64位版本,现在有: Python 3.7.5rc1 (tags/v3.7.5rc1:4082f600a5, Oct 1 2019, 20:28:14) [MSC v.1916 64 bit (AMD64)],其中[ MSC v. 191664位(AMD64)]的意思是“64位 Python”。

资料来源:

在我的例子中,添加一个 dtype 属性将数组的 dtype 更改为一个更小的类型(从 float64变为 uint8) ,从而减小了数组大小,足以不在 Windows 中抛出 MemoryError (64位)。

来自

mask = np.zeros(edges.shape)

mask = np.zeros(edges.shape,dtype='uint8')

将数据类型改为使用较少内存的数据类型。对于我来说,我将数据类型更改为 numpy.uint8:

data['label'] = data['label'].astype(np.uint8)

我在 EC2的一个码头上运行熊猫时遇到了同样的问题。我尝试了上面的解决方案,允许通过 sysctl -w vm.overcommit_memory=1(关于这个 给你的更多信息)过度分配内存,但是这仍然没有解决问题。

我没有深入研究 Ubuntu/EC2的内存分配内容,而是开始寻找并行化 DataFrame 的选项,并发现使用 黑暗在我的案例中很有效:

import dask.dataframe as dd
df = dd.read_csv('path_to_large_file.csv')
...

你的经验可能会有所不同,请注意 dask API 非常相似,但不完全喜欢的熊猫/麻木(例如,你可能需要在一些地方根据你对数据做了一些代码更改)。

我在尝试使用 图像尺寸为600x600(360K)时遇到了这个麻木的问题,我决定使用 减少到224x224(~ 50k),这样可以减少7倍的内存使用。

X_set = np.array(X_set).reshape(-1 , 600 * 600 * 3)

就是现在

X_set = np.array(X_set).reshape(-1 , 224 * 224 * 3)

希望这能帮上忙