web-dev-qa-db-fra.com

Résultats reproductibles dans Tensorflow avec tf.set_random_seed

J'essaie de générer N ensembles de nombres aléatoires indépendants. J'ai un code simple qui montre le problème pour 3 séries de 10 nombres aléatoires. Je remarque que même si j'utilise le tf.set_random_seed pour définir la valeur de départ, les résultats de différentes exécutions ne se ressemblent pas. Toute aide ou commentaires sont grandement appréciés.

(py3p6) bash-3.2$ cat test.py 
import tensorflow as tf
for i in range(3):
  tf.set_random_seed(1234)
  generate = tf.random_uniform((10,), 0, 10)
  with tf.Session() as sess:
    b = sess.run(generate)
    print(b)

Ceci est la sortie du code:

# output :
[9.604688  5.811516  6.4159    9.621765  0.5434954 4.1893444 5.8865128
 7.9785547 8.296125  8.388672 ]
[8.559105  3.2390785 6.447526  8.316823  1.6297233 1.4103293 2.647568
 2.954973  6.5975866 7.494894 ]
[2.0277488 6.6134906 0.7579422 4.6359386 6.97507   3.3192968 2.866236
 2.2205782 6.7940736 7.2391043]

Je veux quelque chose comme

[9.604688  5.811516  6.4159    9.621765  0.5434954 4.1893444 5.8865128
 7.9785547 8.296125  8.388672 ]
[9.604688  5.811516  6.4159    9.621765  0.5434954 4.1893444 5.8865128
 7.9785547 8.296125  8.388672 ]
[9.604688  5.811516  6.4159    9.621765  0.5434954 4.1893444 5.8865128
 7.9785547 8.296125  8.388672 ]

Mise à jour 1: En effet, la raison pour laquelle j'ai placé l'initialiseur de départ dans la boucle for, c'est parce que je veux les configurer différemment (pensez à différentes exécutions de MCMC, par exemple). C'est mon code qui fait le travail mais je ne suis pas sûr qu'il soit efficace. Fondamentalement, je génère un couple de graines aléatoires compris entre 0 et 2 ^ 32-1 et je change la graine à chaque exécution. Toute aide ou commentaire visant à optimiser l’efficacité de la mémoire/RAM est grandement apprécié.

import numpy as np
import tensorflow as tf
global_seed = 42
N_chains = 5
np.random.seed(global_seed)
seeds = np.random.randint(0, 4294967295, size=N_chains)

for i in range(N_chains):
    tf.set_random_seed(seeds[i])
    .... some stuff ....
    kernel_initializer = tf.random_normal_initializer(seed=seeds[i])
    .... some stuff
    with tf.Session() as sess:
         .... some stuff .....
 .
 .
 .
4
Mehdi Rezaie

Dans tensorflow, une opération aléatoire repose sur deux graines différentes: une graine globale, définie par tf.set_random_seed, et une graine d'opération fournie en tant qu'argument de l'opération. Vous trouverez plus de détails sur leur relation dans la documentation .

Vous avez une graine différente pour chaque opération aléatoire car chaque opération aléatoire conserve son propre état interne pour la génération de nombres pseudo-aléatoires. La raison pour laquelle chaque générateur aléatoire maintient son propre état est d'être robuste: si s'ils partageaient le même état, l'ajout d'un nouveau générateur aléatoire dans votre graphique modifierait les valeurs produites par tous les autres générateurs, ce qui irait à l'encontre du but recherché. en utilisant une graine.

Maintenant, pourquoi avons-nous ce double système de semences mondiales et per-op? Eh bien, en réalité, la graine mondiale n'est pas nécessaire. Il est là pour plus de commodité: il permet de définir toutes les valeurs op aléatoires sur une valeur différente et déterministe (si inconnue) à la fois, sans avoir à les parcourir de manière exhaustive.

Maintenant, quand une graine globale est définie, mais pas la graine op, selon la documentation,

Le système sélectionne de manière déterministe une graine d'opération en conjonction avec la graine de niveau graphique afin d'obtenir une séquence aléatoire unique.

Pour être plus précis, le germe fourni est l'id de la dernière opération créée dans le graphique en cours. Par conséquent, les opérations aléatoires générées globalement sont extrêmement sensibles aux modifications du graphique, en particulier à celles créées avant elle-même.

Par exemple,

import tensorflow as tf
tf.set_random_seed(1234)
generate = tf.random_uniform(())
with tf.Session() as sess:
  print(generate.eval())
  # 0.96046877

Maintenant, si nous créons un noeud avant, le résultat change:

import tensorflow as tf
tf.set_random_seed(1234)
tf.zeros(()) # new op added before 
generate = tf.random_uniform(())
with tf.Session() as sess:
  print(generate.eval())
  # 0.29252338

Cependant, si un nœud est créé après, cela n'affectera pas la graine op:

import tensorflow as tf
tf.set_random_seed(1234)
generate = tf.random_uniform(())
tf.zeros(()) # new op added after
with tf.Session() as sess:
  print(generate.eval())
  # 0.96046877

Évidemment, comme dans votre cas, si vous générez plusieurs opérations, elles auront des germes différents:

import tensorflow as tf
tf.set_random_seed(1234)
gen1 = tf.random_uniform(())
gen2 = tf.random_uniform(())
with tf.Session() as sess:
  print(gen1.eval())
  print(gen2.eval())
  # 0.96046877
  # 0.85591054

Par curiosité et pour valider le fait que les graines sont simplement le dernier identifiant utilisé dans le graphique, vous pouvez aligner la graine de gen2 sur gen1 avec

import tensorflow as tf
tf.set_random_seed(1234)
gen1 = tf.random_uniform(())
# 4 operations seems to be created after seed has been picked
seed = tf.get_default_graph()._last_id - 4
gen2 = tf.random_uniform((), seed=seed)
with tf.Session() as sess:
  print(gen1.eval())
  print(gen2.eval())
  # 0.96046877
  # 0.96046877

Évidemment, cela ne devrait pas passer en revue le code.

4
P-Gn

Il existe un problème connexe GitHub . Mais dans votre cas, veuillez vous reporter à la documentation de tf.set_random_seed :

Définit la graine aléatoire au niveau du graphique.

Vous voudrez probablement utiliser l'opération same graph et same pour obtenir le same nombre aléatoire dans différent sessions.

import tensorflow as tf

tf.set_random_seed(1234)
generate = tf.random_uniform((10,), 0, 10)
tf.get_default_graph().finalize() # something everybody tends to forget

for i in range(3):
    with tf.Session() as sess:
        b = sess.run(generate)
        print(b)

donne

[9.604688  5.811516  6.4159    9.621765  0.5434954 4.1893444 5.8865128
 7.9785547 8.296125  8.388672 ]
[9.604688  5.811516  6.4159    9.621765  0.5434954 4.1893444 5.8865128
 7.9785547 8.296125  8.388672 ]
[9.604688  5.811516  6.4159    9.621765  0.5434954 4.1893444 5.8865128
 7.9785547 8.296125  8.388672 ]

Dans votre cas, vous avez créé différentes opérations dans le même graphique.

3
Patwie

Vous obtenez des résultats différents sur des exécutions différentes car il y a trois variables generate (opération) définies dans le graphique et pas une. En effet, l'opération de génération dans la boucle for conduit à trois opérations .(Tensor("random_uniform:0"), Tensor("random_uniform_1:0"), Tensor("random_uniform_2:0")). Il suffit de faire print(generate) dans la boucle for. Vous verrez trois opérations différentes comme indiqué ci-dessus.

tf.set_random_seed définit le germe au niveau du graphique. Donc, il sélectionne de manière déterministe la graine pour chaque opération dans le graphique. Ainsi, les trois opérations generate se voient attribuer les trois mêmes germes à chaque exécution. Et c’est pourquoi, pour chaque exécution, vous obtiendriez les mêmes résultats pour les trois variables de manière correspondante. Veuillez consulter this pour plus d’informations sur la définition de valeurs aléatoires.

Donc, si vous voulez avoir les mêmes résultats à chaque fois que vous exécutez une session, vous pouvez le faire:

tf.set_random_seed(1234)
generate = tf.random_uniform((10,), 0, 10)
for i in range(3):
    with tf.Session() as sess:
        b = sess.run(generate)
        print(b)

Mais pourquoi voulez-vous créer des sessions n. Vous devriez idéalement créer une session, puis exécuter la session n fois. La création d'une nouvelle session pour chaque exécution n'est pas nécessaire et chaque fois qu'il tente de placer les variables et les opérations dans le graphique sur le périphérique (GPU ou CPU).

0
Aravindh Kuppusamy