Dans l'exemple de code ci-dessous, j'essayais d'adapter la réponse acceptée dans ce fil. Le but est d'utiliser le multi-traitement pour générer des nombres normaux aléatoires indépendants (dans l'exemple ci-dessous, je veux juste 3 nombres aléatoires). Ceci est une version bébé de tout code plus compliqué où un générateur de nombres aléatoires est utilisé pour définir la fonction d'essai.

Exemple de code

import multiprocessing

def trial(procnum, return_dict):
    p = np.random.randn(1)
    num = procnum
    return_dict[procnum] = p, num

if __name__ == '__main__':
    manager = multiprocessing.Manager()
    return_dict = manager.dict()
    jobs = []
    for i in range(5):
        p = multiprocessing.Process(target=trial, args=(i,return_dict))
        jobs.append(p)
        p.start()

    for proc in jobs:
        proc.join()
    print(return_dict.values())

Cependant, la sortie me donne le même nombre aléatoire à chaque fois, plutôt qu'un nombre aléatoire indépendant pour chaque entrée dans return_dict.

Sortie

[(array([-1.08817286]), 0), (array([-1.08817286]), 1), (array([-1.08817286]), 2)]

J'ai l'impression que c'est une erreur vraiment stupide. Quelqu'un peut-il exposer ma bêtise s'il vous plaît :)

1
Zhengyan Shi 31 août 2020 à 20:54

2 réponses

Meilleure réponse

Ce n'est pas une erreur stupide, et cela a à voir avec la façon dont numpy effectue le transfert entre les cœurs. En savoir plus ici: https://discuss.pytorch.org/t/why-does-numpy-random-rand-produce-the-same-values-in-different-cores/12005

Mais la solution est de donner à numpy une graine aléatoire d'une large plage:

import multiprocessing
import numpy as np
import random

def trial(procnum, return_dict):
    np.random.seed(random.randint(0,100000))
    p = np.random.randn()
    return_dict[procnum] = p

if __name__ == '__main__':
    manager = multiprocessing.Manager()
    return_dict = manager.dict()
    jobs = []
    for i in range(3):
        p = multiprocessing.Process(target=trial, args=(i,return_dict))
        jobs.append(p)
        p.start()

    for proc in jobs:
        proc.join()
    print(return_dict.values())
2
Aziz Sonawalla 31 août 2020 à 18:03

Il suffit d'ajouter un gloss à la réponse de @Aziz Sonawalla: pourquoi cela fonctionne-t-il?

Parce que le module random de Python fonctionne différemment. Sous Windows, le multitraitement engendre de nouveaux processus, et chacun est une instance fraîchement créée qui effectue son propre amorçage à partir de zéro à partir des sources d'entropie du système d'exploitation.

Sous Linux, le multitraitement utilise par défaut fork() pour créer de nouveaux processus, et ceux-ci héritent de l'état entier du processus principal, en mode copie sur écriture. Cela inclut l'état du générateur de nombres aléatoires. Ainsi, vous obtiendriez les mêmes nombres aléatoires à travers les processus de travail de Python également, sauf que, au moins depuis Python 3.7, Python explicitement (mais sous les couvertures - invisiblement) réamorce son générateur de nombres aléatoires après { {X1}}.

Je ne sais pas quand, mais pendant un certain temps avant la version 3.7, l'implémentation multiprocesseur Process a également réamorcé le générateur de Python dans les processus enfants qu'il a créés via fork() (mais Python lui-même ne l'a pas si vous avez appelé fork() vous-même).

Tout cela est juste pour expliquer pourquoi l'appel de Python random.randrange() renvoie des résultats différents dans différents processus de travail. C'est pourquoi c'est un moyen efficace de générer des graines différentes pour numpy à utiliser dans ce contexte.

1
Tim Peters 31 août 2020 à 20:23