Diese Seite wurde von der Cloud Translation API übersetzt.
Switch to English

Zufallszahlengenerierung

Ansicht auf TensorFlow.org In Google Colab ausführen Quelle auf GitHub anzeigen Notizbuch herunterladen

TensorFlow bietet eine Reihe von Pseudozufallszahlengeneratoren (RNG) im Modul tf.random . In diesem Dokument wird beschrieben, wie Sie die Zufallszahlengeneratoren steuern können und wie diese Generatoren mit anderen Tensorflow-Subsystemen interagieren.

TensorFlow bietet zwei Ansätze zur Steuerung des Zufallszahlengenerierungsprozesses:

  1. Durch die explizite Verwendung von tf.random.Generator Objekten. Jedes dieser Objekte behält einen Zustand (in tf.Variable ) bei, der nach jeder Zahlengenerierung geändert wird.

  2. Durch die rein funktionalen zustandslosen Zufallsfunktionen wie tf.random.stateless_uniform . Das Aufrufen dieser Funktionen mit denselben Argumenten (einschließlich des Startwerts) und auf demselben Gerät führt immer zu denselben Ergebnissen.

Installieren

import tensorflow as tf

# Creates 2 virtual devices cpu:0 and cpu:1 for using distribution strategy
physical_devices = tf.config.experimental.list_physical_devices("CPU")
tf.config.experimental.set_virtual_device_configuration(
    physical_devices[0], [
        tf.config.experimental.VirtualDeviceConfiguration(),
        tf.config.experimental.VirtualDeviceConfiguration()
    ])

Die tf.random.Generator Klasse

Die tf.random.Generator Klasse wird in Fällen verwendet, in denen jeder RNG-Aufruf unterschiedliche Ergebnisse liefern soll. Es behält einen internen Status bei (der von einem tf.Variable Objekt verwaltet wird), der jedes Mal aktualisiert wird, wenn Zufallszahlen generiert werden. Da der Status von tf.Variable verwaltet wird, verfügt er über alle von tf.Variable bereitgestellten tf.Variable wie einfaches Checkpointing, automatische Steuerungsabhängigkeit und Thread-Sicherheit.

Sie können einen tf.random.Generator indem Sie manuell ein Objekt der Klasse tf.random.get_global_generator() oder tf.random.get_global_generator() aufrufen, um den globalen Standardgenerator tf.random.get_global_generator() :

g1 = tf.random.Generator.from_seed(1)
print(g1.normal(shape=[2, 3]))
g2 = tf.random.get_global_generator()
print(g2.normal(shape=[2, 3]))
tf.Tensor(
[[ 0.43842274 -0.53439844 -0.07710262]
 [ 1.5658046  -0.1012345  -0.2744976 ]], shape=(2, 3), dtype=float32)
tf.Tensor(
[[-0.9323887   0.3864468   1.5209497 ]
 [ 0.54473144 -0.6031506  -0.47044003]], shape=(2, 3), dtype=float32)

Es gibt mehrere Möglichkeiten, ein Generatorobjekt zu erstellen. Am einfachsten ist Generator.from_seed , wie oben gezeigt, das einen Generator aus einem Seed erstellt. Ein Startwert ist eine nicht negative Ganzzahl. from_seed auch ein optionales Argument alg dem es sich um den RNG-Algorithmus handelt, der von diesem Generator verwendet wird:

g1 = tf.random.Generator.from_seed(1, alg='philox')
print(g1.normal(shape=[2, 3]))
tf.Tensor(
[[ 0.43842274 -0.53439844 -0.07710262]
 [ 1.5658046  -0.1012345  -0.2744976 ]], shape=(2, 3), dtype=float32)

Weitere Informationen hierzu finden Sie im Abschnitt Algorithmen unten.

Eine andere Möglichkeit, einen Generator zu erstellen, ist Generator.from_non_deterministic_state . Ein auf diese Weise erstellter Generator startet von einem nicht deterministischen Zustand, abhängig von z. B. Zeit und Betriebssystem.

g = tf.random.Generator.from_non_deterministic_state()
print(g.normal(shape=[2, 3]))
tf.Tensor(
[[-1.3158257   2.4625542   1.3490729 ]
 [ 0.77426016 -2.261468   -0.4887435 ]], shape=(2, 3), dtype=float32)

Es gibt noch andere Möglichkeiten, Generatoren zu erstellen, z. B. aus expliziten Zuständen, die in diesem Handbuch nicht behandelt werden.

Wenn Sie tf.random.get_global_generator , um den globalen Generator tf.random.get_global_generator , müssen Sie bei der Platzierung der Geräte vorsichtig sein. Der globale Generator wird beim ersten tf.random.get_global_generator (aus einem nicht deterministischen Zustand) erstellt und bei diesem Aufruf auf dem Standardgerät platziert. Wenn sich beispielsweise die erste Site, die Sie tf.random.get_global_generator aufrufen, innerhalb eines tf.device("gpu") , wird der globale Generator auf der GPU platziert und der globale Generator später von der CPU verwendet eine GPU-zu-CPU-Kopie erstellen.

Es gibt auch eine Funktion tf.random.set_global_generator zum Ersetzen des globalen Generators durch ein anderes Generatorobjekt. Diese Funktion sollte mit Vorsicht verwendet werden, da der alte globale Generator möglicherweise von einer tf.function (als schwache Referenz) erfasst wurde. tf.function Sie ihn ersetzen, wird er durch Müll gesammelt und die tf.function . Eine bessere Möglichkeit, den globalen Generator zurückzusetzen, besteht darin, eine der "Reset" -Funktionen wie Generator.reset_from_seed , mit der keine neuen Generatorobjekte erstellt werden.

g = tf.random.Generator.from_seed(1)
print(g.normal([]))
print(g.normal([]))
g.reset_from_seed(1)
print(g.normal([]))
tf.Tensor(0.43842274, shape=(), dtype=float32)
tf.Tensor(1.6272374, shape=(), dtype=float32)
tf.Tensor(0.43842274, shape=(), dtype=float32)

Erstellen unabhängiger Zufallszahlenströme

In vielen Anwendungen benötigt man mehrere unabhängige Zufallszahlenströme, unabhängig in dem Sinne, dass sie sich nicht überlappen und keine statistisch nachweisbaren Korrelationen aufweisen. Dies wird erreicht, indem Generator.split , um mehrere Generator.split zu erstellen, die garantiert unabhängig voneinander sind (dh unabhängige Streams generieren).

g = tf.random.Generator.from_seed(1)
print(g.normal([]))
new_gs = g.split(3)
for new_g in new_gs:
  print(new_g.normal([]))
print(g.normal([]))
tf.Tensor(0.43842274, shape=(), dtype=float32)
tf.Tensor(2.536413, shape=(), dtype=float32)
tf.Tensor(0.33186463, shape=(), dtype=float32)
tf.Tensor(-0.07144657, shape=(), dtype=float32)
tf.Tensor(-0.79253083, shape=(), dtype=float32)

split ändert den Zustand des Generators, auf dem es aufgerufen wird ( g im obigen Beispiel), ähnlich einer RNG-Methode wie normal . Die neuen Generatoren ( new_gs ) sind nicht nur unabhängig voneinander, new_gs auch garantiert unabhängig von den alten ( g ).

Das Laichen neuer Generatoren ist auch nützlich, wenn Sie sicherstellen möchten, dass sich der von Ihnen verwendete Generator auf demselben Gerät wie andere Berechnungen befindet, um den Aufwand für geräteübergreifende Kopien zu vermeiden. Zum Beispiel:

with tf.device("cpu"):  # change "cpu" to the device you want
  g = tf.random.get_global_generator().split(1)[0]  
  print(g.normal([]))  # use of g won't cause cross-device copy, unlike the global generator
tf.Tensor(0.10396493, shape=(), dtype=float32)

Sie können die Aufteilung rekursiv durchführen und bei split Generatoren die Aufteilung aufrufen. Der Tiefe der Rekursionen sind keine Grenzen gesetzt (außer bei einem Ganzzahlüberlauf).

Interaktion mit tf.function

tf.random.Generator befolgt die gleichen Regeln wie tf.Variable wenn es mit tf.function . Dies umfasst drei Aspekte.

Generatoren außerhalb der Funktion tf.function

tf.function kann einen außerhalb davon erstellten Generator verwenden.

g = tf.random.Generator.from_seed(1)
@tf.function
def foo():
  return g.normal([])
print(foo())
tf.Tensor(0.43842274, shape=(), dtype=float32)

Der Benutzer muss sicherstellen, dass das Generatorobjekt noch aktiv ist (nicht durch Müll gesammelt), wenn die Funktion aufgerufen wird.

Generatoren innerhalb der Funktion tf.function

Die Erstellung von Generatoren innerhalb einer tf.function kann nur während des ersten Durchlaufs der Funktion erfolgen.

g = None
@tf.function
def foo():
  global g
  if g is None:
    g = tf.random.Generator.from_seed(1)
  return g.normal([])
print(foo())
print(foo())
tf.Tensor(0.43842274, shape=(), dtype=float32)
tf.Tensor(1.6272374, shape=(), dtype=float32)

Generatoren als Argumente an tf.function

Bei Verwendung als Argument für eine tf.function unterschiedliche Generatorobjekte mit derselben Statusgröße (die tf.function wird durch den RNG-Algorithmus bestimmt) keine Rückverfolgung der tf.function , während solche mit unterschiedlichen Statusgrößen dies tf.function .

num_traces = 0
@tf.function
def foo(g):
  global num_traces
  num_traces += 1
  return g.normal([])
foo(tf.random.Generator.from_seed(1))
foo(tf.random.Generator.from_seed(2))
print(num_traces)
1

Interaktion mit Vertriebsstrategien

Es gibt drei Möglichkeiten, wie Generator mit Verteilungsstrategien interagiert.

Generatoren außerhalb von Verteilungsstrategien erstellen

Wenn ein Generator außerhalb des Strategiebereichs erstellt wird, wird der Zugriff aller Replikate auf den Generator serialisiert, und daher erhalten die Replikate unterschiedliche Zufallszahlen.

g = tf.random.Generator.from_seed(1)
strat = tf.distribute.MirroredStrategy(devices=["cpu:0", "cpu:1"])
with strat.scope():
  def f():
    print(g.normal([]))
  results = strat.run(f)
WARNING:tensorflow:There are non-GPU devices in `tf.distribute.Strategy`, not using nccl allreduce.
INFO:tensorflow:Using MirroredStrategy with devices ('/job:localhost/replica:0/task:0/device:CPU:0', '/job:localhost/replica:0/task:0/device:CPU:1')
WARNING:tensorflow:Using MirroredStrategy eagerly has significant overhead currently. We will be working on improving this in the future, but for now please wrap `call_for_each_replica` or `experimental_run` or `experimental_run_v2` inside a tf.function to get the best performance.
tf.Tensor(0.43842274, shape=(), dtype=float32)
tf.Tensor(1.6272374, shape=(), dtype=float32)

Beachten Sie, dass diese Verwendung möglicherweise Leistungsprobleme aufweist, da sich das Gerät des Generators von den Replikaten unterscheidet.

Generatoren innerhalb von Verteilungsstrategien erstellen

Das Erstellen von Generatoren innerhalb von Strategiebereichen ist nicht zulässig, da Unklarheiten darüber bestehen, wie ein Generator repliziert werden soll (z. B. sollte er so kopiert werden, dass jede Replik die gleichen Zufallszahlen erhält, oder "aufgeteilt", damit jede Replik unterschiedliche Zufallszahlen erhält).

strat = tf.distribute.MirroredStrategy(devices=["cpu:0", "cpu:1"])
with strat.scope():
  try:
    tf.random.Generator.from_seed(1)
  except ValueError as e:
    print("ValueError:", e)
WARNING:tensorflow:There are non-GPU devices in `tf.distribute.Strategy`, not using nccl allreduce.
INFO:tensorflow:Using MirroredStrategy with devices ('/job:localhost/replica:0/task:0/device:CPU:0', '/job:localhost/replica:0/task:0/device:CPU:1')
ValueError: Creating a generator within a strategy scope is disallowed, because there is ambiguity on how to replicate a generator (e.g. should it be copied so that each replica gets the same random numbers, or 'split' so that each replica gets different random numbers).

Beachten Sie, dass Strategy.run seine Argumentfunktion implizit in einem Strategiebereich ausführt:

strat = tf.distribute.MirroredStrategy(devices=["cpu:0", "cpu:1"])
def f():
  tf.random.Generator.from_seed(1)
try:
  strat.run(f)
except ValueError as e:
  print("ValueError:", e)
WARNING:tensorflow:There are non-GPU devices in `tf.distribute.Strategy`, not using nccl allreduce.
INFO:tensorflow:Using MirroredStrategy with devices ('/job:localhost/replica:0/task:0/device:CPU:0', '/job:localhost/replica:0/task:0/device:CPU:1')
WARNING:tensorflow:Using MirroredStrategy eagerly has significant overhead currently. We will be working on improving this in the future, but for now please wrap `call_for_each_replica` or `experimental_run` or `experimental_run_v2` inside a tf.function to get the best performance.
INFO:tensorflow:Error reported to Coordinator: Creating a generator within a strategy scope is disallowed, because there is ambiguity on how to replicate a generator (e.g. should it be copied so that each replica gets the same random numbers, or 'split' so that each replica gets different random numbers).
Traceback (most recent call last):
  File "/tmpfs/src/tf_docs_env/lib/python3.6/site-packages/tensorflow/python/training/coordinator.py", line 297, in stop_on_exception
    yield
  File "/tmpfs/src/tf_docs_env/lib/python3.6/site-packages/tensorflow/python/distribute/mirrored_run.py", line 323, in run
    self.main_result = self.main_fn(*self.main_args, **self.main_kwargs)
  File "/tmpfs/src/tf_docs_env/lib/python3.6/site-packages/tensorflow/python/autograph/impl/api.py", line 275, in wrapper
    return func(*args, **kwargs)
  File "<ipython-input-14-2cd7806456bd>", line 3, in f
    tf.random.Generator.from_seed(1)
  File "/tmpfs/src/tf_docs_env/lib/python3.6/site-packages/tensorflow/python/ops/stateful_random_ops.py", line 441, in from_seed
    return cls(state=state, alg=alg)
  File "/tmpfs/src/tf_docs_env/lib/python3.6/site-packages/tensorflow/python/ops/stateful_random_ops.py", line 363, in __init__
    trainable=False)
  File "/tmpfs/src/tf_docs_env/lib/python3.6/site-packages/tensorflow/python/ops/stateful_random_ops.py", line 378, in _create_variable
    "Creating a generator within a strategy scope is disallowed, because "
ValueError: Creating a generator within a strategy scope is disallowed, because there is ambiguity on how to replicate a generator (e.g. should it be copied so that each replica gets the same random numbers, or 'split' so that each replica gets different random numbers).
ValueError: Creating a generator within a strategy scope is disallowed, because there is ambiguity on how to replicate a generator (e.g. should it be copied so that each replica gets the same random numbers, or 'split' so that each replica gets different random numbers).

Generatoren als Argumente an Strategy.run

Wenn Sie möchten, dass jedes Replikat einen eigenen Generator verwendet, müssen Sie n Generatoren erstellen (entweder durch Kopieren oder Teilen), wobei n die Anzahl der Replikate ist, und diese dann als Argumente an Strategy.run .

strat = tf.distribute.MirroredStrategy(devices=["cpu:0", "cpu:1"])
gs = tf.random.get_global_generator().split(2)
# to_args is a workaround for the absence of APIs to create arguments for 
# run. It will be replaced when such APIs are available.
def to_args(gs):  
  with strat.scope():
    def f():
      return [gs[tf.distribute.get_replica_context().replica_id_in_sync_group]]
    return strat.run(f)
args = to_args(gs)
def f(g):
  print(g.normal([]))
results = strat.run(f, args=args)
WARNING:tensorflow:There are non-GPU devices in `tf.distribute.Strategy`, not using nccl allreduce.
INFO:tensorflow:Using MirroredStrategy with devices ('/job:localhost/replica:0/task:0/device:CPU:0', '/job:localhost/replica:0/task:0/device:CPU:1')
WARNING:tensorflow:Using MirroredStrategy eagerly has significant overhead currently. We will be working on improving this in the future, but for now please wrap `call_for_each_replica` or `experimental_run` or `experimental_run_v2` inside a tf.function to get the best performance.
WARNING:tensorflow:Using MirroredStrategy eagerly has significant overhead currently. We will be working on improving this in the future, but for now please wrap `call_for_each_replica` or `experimental_run` or `experimental_run_v2` inside a tf.function to get the best performance.
tf.Tensor(-0.42417043, shape=(), dtype=float32)
tf.Tensor(0.08118553, shape=(), dtype=float32)

Staatenlose RNGs

Die Verwendung zustandsloser RNGs ist einfach. Da es sich nur um reine Funktionen handelt, sind keine Zustände oder Nebenwirkungen beteiligt.

print(tf.random.stateless_normal(shape=[2, 3], seed=[1, 2]))
print(tf.random.stateless_normal(shape=[2, 3], seed=[1, 2]))
tf.Tensor(
[[ 0.5441101   0.20738031  0.07356433]
 [ 0.04643455 -1.3015898  -0.95385665]], shape=(2, 3), dtype=float32)
tf.Tensor(
[[ 0.5441101   0.20738031  0.07356433]
 [ 0.04643455 -1.3015898  -0.95385665]], shape=(2, 3), dtype=float32)

Jedes zustandslose RNG erfordert ein seed , das ein ganzzahliger Tensor der Form sein muss [2] . Die Ergebnisse der Operation werden vollständig durch diesen Samen bestimmt.

Algorithmen

Allgemeines

Sowohl die tf.random.Generator Klasse als auch die stateless Funktionen unterstützen den Philox-Algorithmus (geschrieben als "philox" oder tf.random.Algorithm.PHILOX ) auf allen Geräten.

Verschiedene Geräte generieren dieselben Ganzzahlen, wenn sie denselben Algorithmus verwenden und von demselben Status ausgehen. Sie erzeugen auch "fast die gleichen" Gleitkommazahlen, obwohl es möglicherweise kleine numerische Abweichungen gibt, die durch die unterschiedlichen Arten der Ausführung der Gleitkommaberechnung durch die Geräte verursacht werden (z. B. Reduzierungsreihenfolge).

XLA-Geräte

Auf XLA-gesteuerten Geräten (wie TPU und auch CPU / GPU, wenn XLA aktiviert ist) wird auch der ThreeFry-Algorithmus (geschrieben als "threefry" oder tf.random.Algorithm.THREEFRY ) unterstützt. Dieser Algorithmus ist auf TPU schnell, auf CPU / GPU jedoch langsamer als auf Philox.

Weitere Informationen zu diesen Algorithmen finden Sie im Artikel 'Parallele Zufallszahlen: So einfach wie 1, 2, 3' .