Каков максимальный размер массива numpy?
Я пытаюсь создать матрицу, содержащую 2 708 000 000 элементов. Когда я пытаюсь создать массив numpy этого размера, он дает мне ошибку значения. Есть ли способ увеличить максимальный размер массива?
а = np.arange(2708000000)
Traceback ValueError (последний последний вызов)
ValueError: превышен максимально допустимый размер
Ответы
Ответ 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. Обратите внимание: если вы примените его к списку (или другому контейнеру), то возвращаемый размер не будет содержать размер содержащихся объектов списка; только структуры, используемой для хранения этих объектов. Вот некоторые значения на моей машине.