Подтвердить что ты не робот

Каков максимальный размер массива numpy?

Я пытаюсь создать матрицу, содержащую 2 708 000 000 элементов. Когда я пытаюсь создать массив numpy этого размера, он дает мне ошибку значения. Есть ли способ увеличить максимальный размер массива?

а = np.arange(2708000000)

Traceback ValueError (последний последний вызов)

ValueError: превышен максимально допустимый размер

4b9b3361

Ответ 1

Вы пытаетесь создать массив с 2,7 миллиардами записей. Если вы используете 64-разрядный numpy, по 8 байт на запись, это будет всего 20 ГБ.

Так что почти наверняка у вас на компьютере осталась нехватка памяти. Общий размер массива в numpy отсутствует.

Ответ 2

Значение ValueError указывает, что размер слишком большой для распределения, а не того, что памяти недостаточно. На моем ноутбуке, используя 64-битный питон, я могу выделить его, если я уменьшу количество бит:

In [16]: a=np.arange(2708000000)
---------------------------------------------------------------------------
MemoryError                               Traceback (most recent call last)
<ipython-input-16-aaa1699e97c5> in <module>()
----> 1 a=np.arange(2708000000)

MemoryError: 

# Note I don't get a ValueError

In [17]: a = np.arange(2708000000, dtype=np.int8)

In [18]: a.nbytes
Out[18]: 2708000000

In [19]: a.nbytes * 1e-6
Out[19]: 2708.0

В вашем случае arange использует бит int64, что означает, что он в 16 раз больше или около 43 ГБ. 32-битный процесс может обрабатывать только около 4 ГБ памяти.

Основная причина - размер указателей, используемых для доступа к данным, и количество номеров, которые вы можете представить с помощью этих бит:

In [26]: np.iinfo(np.int32)
Out[26]: iinfo(min=-2147483648, max=2147483647, dtype=int32)
In [27]: np.iinfo(np.int64)
Out[27]: iinfo(min=-9223372036854775808, max=9223372036854775807, dtype=int64)

Обратите внимание, что я могу реплицировать значение ValueError, если попытаюсь создать абсурдно большой массив:

In [29]: a = np.arange(1e350)
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
<ipython-input-29-230a6916f777> in <module>()
----> 1 a = np.arange(1e350)

ValueError: Maximum allowed size exceeded

Если на вашем компьютере много памяти, как вы сказали, это будет 64 бит, поэтому вы должны установить 64 бита Python, чтобы иметь доступ к нему. С другой стороны, для таких больших наборов данных вы должны рассмотреть возможность использования вне основных вычислений.

Ответ 3

Мне удалось создать массив размером 6 миллиардов, который съел 45 ГБ памяти. По умолчанию numpy создал массив с dtype float64. Отбросив точность, я смог сэкономить много памяти.

np.arange(6000000000,dtype=np.dtype('f8'))
np.arange(6000000000,dtype=np.dtype('f4'))
#etc...

default == float64

  • np.float64 - 45.7GB

  • np.float32 - 22.9GB

  • np.int8 - 5.7GB

Очевидно, что 8-битное целое число не может хранить значение 6B. Я уверен, что максимальный размер существует в какой-то момент, но я подозреваю, что он прошел мимо всего возможного в 2016 году. Интересно, что "Python Blaze" позволяет создавать массивы numpy на диске. Я вспоминаю игру с ним некоторое время назад и создание чрезвычайно большого массива, в котором заняло 1 ТБ диска.

Ответ 4

Это действительно связано с максимальной длиной адреса системы, если говорить просто, 32-битной системой или 64-битной системой. Вот объяснение этих вопросов, первоначально от Марка Дикинсона

Короткий ответ: накладные расходы на Python убивают вас. В Python 2.x на 64-битной машине список строк потребляет 48 байт в записи списка даже до учета содержания строк. Это более 8,7 Гб накладных расходов для размера массива, который вы описываете. На 32-битной машине это будет немного лучше: всего 28 байт в записи списка.

Более длинное объяснение: вы должны знать, что сами объекты Python могут быть довольно большими: даже простые объекты, такие как int, float и strings. В вашем коде вы получаете список списков строк. На моей (64-битной) машине даже пустой объект string занимает до 40 байт, и вам нужно добавить 8 байт для указателя списка, указывающего на этот строковый объект в памяти. Так что уже 48 байт на запись или около 8,7 Гб. Учитывая, что Python выделяет память кратным 8 байтам за раз, и что ваши строки почти наверняка не пустые, вы на самом деле смотрите 56 или 64 байта (я не знаю, сколько длинных строк) для каждой записи.

Возможные решения:

(1) Вы можете сделать (немного) лучше, преобразовывая свои записи из строк в int или плавающие, если это необходимо.

(2) Вы бы сделали намного лучше, используя либо тип массива Python (не такой, как список!), либо используя numpy: тогда ваши int или float будут принимать только 4 или 8 байтов.

Начиная с Python 2.6, вы можете получить основную информацию о размерах объектов с помощью функции sys.getsizeof. Обратите внимание: если вы примените его к списку (или другому контейнеру), то возвращаемый размер не будет содержать размер содержащихся объектов списка; только структуры, используемой для хранения этих объектов. Вот некоторые значения на моей машине.