Создание большой случайной булевой матрицы с 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)