Создание большой случайной булевой матрицы с numpy
Я пытаюсь создать огромную матрицу boolean
, которая случайным образом заполнена True
и False
с заданной вероятностью p
. Сначала я использовал этот код:
N = 30000
p = 0.1
np.random.choice(a=[False, True], size=(N, N), p=[p, 1-p])
Но, к сожалению, это не заканчивается для этого большого N
. Поэтому я попытался разделить его на генерацию отдельных строк, выполнив следующее:
N = 30000
p = 0.1
mask = np.empty((N, N))
for i in range (N):
mask[i] = np.random.choice(a=[False, True], size=N, p=[p, 1-p])
if (i % 100 == 0):
print(i)
Теперь происходит нечто странное (по крайней мере, на моем устройстве): первые ~ 1100 строк генерируются очень быстро, но после этого код становится ужасно медленным. Почему это происходит? Что мне здесь не хватает? Есть ли лучшие способы создать большую матрицу, в которой есть записи True
с вероятностью p
и False
записи с вероятностью 1-p
?
Изменить: многие из вас предполагали, что с оперативной памятью возникнут проблемы: поскольку устройство, на котором будет выполняться код, имеет почти 500 ГБ оперативной памяти, это не будет проблемой.
Ответы
Ответ 1
Проблема заключается в вашей ОЗУ, значения хранятся в памяти при ее создании. Я просто создал эту матрицу, используя следующую команду:
np.random.choice(a=[False, True], size=(N, N), p=[p, 1-p])
Я использовал экземпляр AWS i3
с 64 ГБ оперативной памяти и 8 ядер. Чтобы создать эту матрицу, htop
показывает, что она занимает ~ 20 ГБ ОЗУ. Вот вам пример:
time np.random.choice(a=[False, True], size=(N, N), p=[p, 1-p])
CPU times: user 18.3 s, sys: 3.4 s, total: 21.7 s
Wall time: 21.7 s
def mask_method(N, p):
for i in range(N):
mask[i] = np.random.choice(a=[False, True], size=N, p=[p, 1-p])
if (i % 100 == 0):
print(i)
time mask_method(N,p)
CPU times: user 20.9 s, sys: 1.55 s, total: 22.5 s
Wall time: 22.5 s
Обратите внимание, что метод маски только занимает около 9 ГБ ОЗУ на пике.
Изменить: первый метод сбрасывает RAM после завершения процесса, когда метод функции сохраняет все его.
Ответ 2
Поэтому я попытался разбить его на поколение одиночных строк, выполнив следующие действия:
Как работает np.random.choice
, сначала создается float64
в [0, 1)
для каждой ячейки ваших данных, а затем преобразуется в индекс в вашем массиве с помощью np.search_sorted
. Это промежуточное представление в 8 раз больше логического массива!
Поскольку ваши данные логические, вы можете получить коэффициент в 2 раза с помощью
np.random.rand(N, N) > p
Что, естественно, вы могли бы использовать внутри своего цикла-решения
Кажется, что np.random.choice
может выполнять некоторую буферизацию здесь - вы можете захотеть записать проблему с numpy.
Другой вариант - попытаться сгенерировать float32
вместо float64
s. Я не уверен, что numpy может сделать это прямо сейчас, но вы можете запросить эту функцию.
Ответ 3
Другая возможность может заключаться в том, чтобы сгенерировать ее в пакете (т.е. вычислить множество подматриц и собрать их вместе в самом конце). Но не стоит обновлять один массив (mask
) в цикле for
, как это делает OP. Это заставит весь массив загружаться в основной памяти во время каждого обновления индексации.
Вместо этого, например: чтобы получить 30000x30000
, у вас есть 9000 100x100
отдельных массивов, обновите каждый из этих массивов 100x100
соответственно в цикле for
и, наконец, соедините эти 9000 массивов вместе в гигантском массиве. Это, безусловно, должно быть не более 4 ГБ ОЗУ и будет очень быстрым.
Минимальный пример:
In [9]: a
Out[9]:
array([[0, 1],
[2, 3]])
In [10]: np.hstack([np.vstack([a]*5)]*5)
Out[10]:
array([[0, 1, 0, 1, 0, 1, 0, 1, 0, 1],
[2, 3, 2, 3, 2, 3, 2, 3, 2, 3],
[0, 1, 0, 1, 0, 1, 0, 1, 0, 1],
[2, 3, 2, 3, 2, 3, 2, 3, 2, 3],
[0, 1, 0, 1, 0, 1, 0, 1, 0, 1],
[2, 3, 2, 3, 2, 3, 2, 3, 2, 3],
[0, 1, 0, 1, 0, 1, 0, 1, 0, 1],
[2, 3, 2, 3, 2, 3, 2, 3, 2, 3],
[0, 1, 0, 1, 0, 1, 0, 1, 0, 1],
[2, 3, 2, 3, 2, 3, 2, 3, 2, 3]])
In [11]: np.hstack([np.vstack([a]*5)]*5).shape
Out[11]: (10, 10)