Как обмениваться случайным состоянием nump родительского процесса с дочерними процессами?

Я установил numpy random seed в начале моей программы. Во время выполнения программы я запускаю функцию несколько раз с использованием multiprocessing.Process . Функция использует случайные функции numpy для рисования случайных чисел. Проблема в том, что Process получает копию текущей среды. Поэтому каждый процесс выполняется независимо, и все они начинаются с того же случайного семени, что и родительская среда.

Поэтому мой вопрос заключается в том, как я могу поделиться случайным состоянием numpy в родительской среде с дочерней технологической средой? Просто имейте в виду, что я хочу использовать Process for my work и вам нужно использовать отдельный class и import numpy в этом classе отдельно. Я попытался использовать multiprocessing.Manager для совместного использования случайного состояния, но кажется, что все работает не так, как ожидалось, и я всегда получаю одинаковые результаты. Кроме того, не имеет значения, перемещаю ли цикл for внутри drawNumpySamples или оставляю его в main.py ; Я все еще не могу получить разные числа, и случайное состояние всегда одно и то же. Вот упрощенная версия моего кода:

 # randomClass.py import numpy as np class myClass(self): def __init__(self, randomSt): print ('setup the object') np.random.set_state(randomSt) def drawNumpySamples(self, idx) np.random.uniform() 

И в основном файле:

  # main.py import numpy as np from multiprocessing import Process, Manager from randomClass import myClass np.random.seed(1) # set random seed mng = Manager() randomState = mng.list(np.random.get_state()) myC = myClass(randomSt = randomState) for i in range(10): myC.drawNumpySamples() # this will always return the same results 

Примечание . Я использую Python 3.5. Я также разместил вопрос на странице GitHub от Numpy. Просто отправьте ссылку на проблему здесь для дальнейшего использования.

Даже если вам удастся добиться этого, я не думаю, что он будет делать то, что вы хотите. Как только у вас есть несколько процессов, вытягивающих из одного и того же случайного состояния параллельно, он уже не является детерминированным, какой порядок они получают в состоянии, то есть ваши прогоны на самом деле не будут повторяемы. Вероятно, есть способы обойти это, но это кажется нетривиальной проблемой.

Между тем есть решение, которое должно решить как нужную проблему, так и проблему недетерминизма:

Перед нерестом дочернего процесса спросите RNG о случайном числе и передайте его ребенку. Затем ребенок может посеять это число. Каждый ребенок будет иметь другую случайную последовательность от других детей, но такую ​​же случайную последовательность, которую получил тот же ребенок, если вы запустили все приложение с фиксированным семенем.

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


Как указал senderle в комментарии: если вам не нужны несколько различных прогонов, но только один фиксированный прогон, вам даже не нужно вытаскивать семя из вашего сеянного RNG; просто используйте счетчик, начинающийся с 1, и увеличивайте его для каждого нового процесса и используйте его как семя. Я не знаю, приемлемо ли это, но если это так, сложно сделать проще.

Как отметил Амир в комментарии: лучший способ – нарисовать случайное целое число каждый раз, когда вы создаете новый процесс и передаете это случайное целое в новый процесс, чтобы установить случайное семя numpy с этим целым числом. Это целое число действительно может быть np.random.randint() из np.random.randint() .

Вам нужно обновлять состояние Manager каждый раз, когда вы получаете случайное число:

 import numpy as np from multiprocessing import Manager, Pool, Lock lock = Lock() mng = Manager() state = mng.list(np.random.get_state()) def get_random(_): with lock: np.random.set_state(state) result = np.random.uniform() state[:] = np.random.get_state() return result np.random.seed(1) result1 = Pool(10).map(get_random, range(10)) # Compare with non-parallel version np.random.seed(1) result2 = [np.random.uniform() for _ in range(10)] # result of Pool.map may be in different order assert sorted(result1) == sorted(result2) 

К счастью, согласно документации , вы можете получить доступ к полному состоянию генератора случайных чисел numpy с помощью get_state и снова установить его с помощью set_state . Сам генератор использует алгоритм Mersenne Twister (см. RandomState документации ).

Это означает, что вы можете делать все, что хотите, хотя это будет хорошо и эффективно , это совсем другой вопрос. Как указывает abarnert , независимо от того, как вы разделяете состояние родителя, это может использовать метод Alex Hall , который выглядит правильно: ваша последовательность в каждом дочернем будет зависеть от порядка, в котором каждый ребенок рисует случайные числа из конечного автомата MT.

Возможно, было бы лучше построить большой пул псевдослучайных чисел для каждого ребенка, сохранив начальное состояние всего генератора один раз в начале. Затем каждый ребенок может нарисовать значение PRNG до тех пор, пока не закончится его конкретный пул, после чего у вас есть дочерняя координата с родителем для следующего пула. Родитель перечисляет, какие дети получили номер «пул». Код будет выглядеть примерно так (обратите внимание, что было бы целесообразно превратить это в бесконечный генератор со next методом):

 class PrngPool(object): def __init__(self, child_id, shared_state): self._child_id = child_id self._shared_state = shared_state self._numbers = [] def next_number(self): if not self.numbers: self._refill() return self.numbers.pop(0) # XXX inefficient def _refill(self): # ... something like Alex Hall's lock/gen/unlock, # but fill up self._numbers with the next 1000 (or # however many) numbers after adding our ID and # the index "n" of which n-through-n+999 numbers # we took here. Any other child also doing a # _refill will wait for the lock and get an updated # index n -- eg, if we got numbers 3000 to 3999, # the next child will get numbers 4000 to 4999. 

Таким образом, связь между элементами диспетчера (состояние MT и наш идентификатор и индекс добавлены в список «used») не так велики. В конце процесса можно увидеть, какие дети использовали значения PRNG, и при необходимости генерировать эти значения PRNG (не забудьте записать полное состояние начала MT)!

Изменить для добавления: способ думать об этом выглядит так: МТ на самом деле не случайный. Это периодический период с очень длительным периодом. Когда вы используете какой-либо такой RNG, ваше семя просто отправной точкой в ​​течение периода. Чтобы получить повторяемость, вы должны использовать неслучайные числа, например набор из книги. Существует (виртуальная) книга с каждым числом, которое выходит из генератора MT. Мы собираемся записать страницы (страницы) этой книги, которые мы использовали для каждой группы вычислений, чтобы мы могли повторно открыть книгу на эти страницы позже и повторить те же вычисления.

Interesting Posts

Как «Удалить производные данные» в Xcode 6 и более поздних версиях?

Как получить символ для заданного значения ascii

Последовательно создайте один и тот же случайный массив numpy

Сеть и маршрутизация частных IP-диапазонов

«Сервер Tomcat v7.0 Сервер на локальном хосте не смог запустить» без трассировки стека, когда он работает в терминале

Как я могу предотвратить кражу данных у компании?

Почему NSWindow без styleMask: NSTitledWindowMask не может быть keyWindow?

Безопасно ли читать указатель на функцию одновременно без блокировки?

Почему строки возвращаются с помощью «объяснения», не равны count ()?

Что произойдет, если служба запущена несколько раз?

Func с параметром out

C # 4: реальный пример динамических типов

Как я могу поделиться своим проводным подключением к ноутбуку wifi?

Ubuntu 10.10: второй экземпляр Firefox не подчиняется настройкам GTK2

Как определить, что вы работаете под виртуальной машиной?

Давайте будем гением компьютера.