Zufallszahlengenerierung

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

TensorFlow stellt einen Satz von Pseudozufallszahlengeneratoren (RNG), in dem tf.random Modul. Dieses Dokument beschreibt, 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 solches Objekt verwaltet einen Zustand (in tf.Variable ) , die nach jeder Zahlenerzeugung verändert werden.

  2. Durch die rein funktionellen staatenlos Zufallsfunktionen wie tf.random.stateless_uniform . Der Aufruf dieser Funktionen mit denselben Argumenten (einschließlich des Seeds) und auf demselben Gerät führt immer zu denselben Ergebnissen.

Aufstellen

import tensorflow as tf

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

Die tf.random.Generator Klasse

Die tf.random.Generator Klasse wird in Fällen, in denen Sie jeden RNG Anruf unterschiedliche Ergebnisse produzieren wollen. Sie unterhält einen internen Zustand (verwaltet von einem tf.Variable Objekt) , das jedes Mal , Zufallszahlen erzeugt werden , aktualisiert werden. Da der Zustand von verwaltet wird tf.Variable , genießt es alle Einrichtungen , bereitgestellt durch tf.Variable wie leicht Prüfpunkten, automatische Steuerung Abhängigkeit und Thread - Sicherheit.

Sie können einen bekommen tf.random.Generator manuell ein Objekt der Klasse oder Anruf zu schaffen tf.random.get_global_generator() den globalen Standard Generator zu erhalten:

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.43842277 -0.53439844 -0.07710262]
 [ 1.5658046  -0.1012345  -0.2744976 ]], shape=(2, 3), dtype=float32)
tf.Tensor(
[[-0.9668809   1.3911723  -0.5086598 ]
 [ 2.0149372  -0.31007195  0.9234315 ]], shape=(2, 3), dtype=float32)

Es gibt mehrere Möglichkeiten, ein Generatorobjekt zu erstellen. Am einfachsten ist es Generator.from_seed , wie oben gezeigt, die einen Generator aus einem Samen erzeugt. Ein Seed ist eine beliebige nicht negative ganze Zahl. from_seed nimmt auch ein optionales Argument alg , die die RNG - Algorithmus ist , die von diesem Generator verwendet werden soll:

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

Siehe den Algorithmen Abschnitt unten für weitere Informationen über sie.

Eine weitere Möglichkeit , einen Generator zu schaffen , ist mit Generator.from_non_deterministic_state . Ein so erstellter Generator startet aus einem nicht deterministischen Zustand, abhängig von zB Zeit und Betriebssystem.

g = tf.random.Generator.from_non_deterministic_state()
print(g.normal(shape=[2, 3]))
tf.Tensor(
[[-1.3817401  -2.2609987   1.0230105 ]
 [-1.8064909   0.27831787 -0.35673156]], 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.

Bei der Verwendung von tf.random.get_global_generator den globalen Generator zu erhalten, müssen Sie über die Geräteplatzierung vorsichtig sein. Der globale Generator (von einem nicht-deterministischen Zustand) zu dem ersten Zeitpunkt erzeugt wird tf.random.get_global_generator genannt wird , und auf dem Standard - Gerät an diesem Anruf platziert. So zum Beispiel, wenn die erste Website , die Sie anrufen tf.random.get_global_generator innerhalb eines ist tf.device("gpu") Umfang, wird die globale Generator auf der GPU platziert werden und mit dem globalen Generator später von der CPU eine GPU-zu-CPU-Kopie erstellen.

Es gibt auch eine Funktion tf.random.set_global_generator mit einem anderen Generator Objekt des globalen Generator zu ersetzen. Diese Funktion sollte mit Vorsicht jedoch verwendet werden, weil der alte globale Generator von einem erfassten worden sein tf.function (als schwache Referenz), und ersetzt sie bewirkt , dass es Müll gesammelt werden, brechen die tf.function . Ein besserer Weg , die globale Generator zurückgesetzt ist einer der „Reset“ Funktionen nutzen wie Generator.reset_from_seed , die neue Generator Objekte nicht schaffen wird.

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

Erstellen unabhängiger Zufallszahlenstreams

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 durch die Verwendung Generator.split mehr Generatoren zu schaffen , die von ihnen (dh Erzeugen unabhängiger Strömen) unabhängig sein , ist garantiert.

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.43842277, 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 wird den Zustand des Generators ändern , auf die es (genannt wird g in dem obigen Beispiel), ähnlich ein RNG Verfahren wie normal . Neben voneinander unabhängig zu sein, die neuen Generatoren ( new_gs ) sind ebenfalls unabhängig von dem alten (sein garantiert g ).

Das Generieren 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 Overhead des geräteübergreifenden Kopierens 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.03516542, shape=(), dtype=float32)

Sie können rekursiv tun Splitting, ruft split auf gespaltet Generatoren. Der Rekursionstiefe sind keine Grenzen gesetzt (abgesehen von Integer-Überlauf).

Die Interaktion mit tf.function

tf.random.Generator gehorcht den gleichen Regeln wie tf.Variable wenn verwendet mit tf.function . Dazu gehören drei Aspekte.

Erstellen von Generatoren außerhalb tf.function

tf.function kann einen Generator außerhalb davon erstellt wurden .

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

Der Benutzer muss sicherstellen, dass das Generatorobjekt beim Aufrufen der Funktion noch aktiv ist (nicht im Garbage-Collection-Modus).

Erstellen von Generatoren innerhalb tf.function

Schaffung von Generatoren in einem tf.function kann nur während der ersten Ausführung der Funktion passiert.

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.43842277, shape=(), dtype=float32)
tf.Tensor(1.6272374, shape=(), dtype=float32)

Passing Generatoren als Argumente an tf.function

Wenn als Argument für einen gebrauchten tf.function werden verschiedene Generator Objekte Nachvollziehen der Ursache 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)
2

Beachten Sie, dass dieses Nachvollziehen Verhalten stimmt mit der tf.Variable :

num_traces = 0
@tf.function
def foo(v):
  global num_traces
  num_traces += 1
  return v.read_value()
foo(tf.Variable(1))
foo(tf.Variable(2))
print(num_traces)
2

Interaktion mit Vertriebsstrategien

Es gibt zwei Möglichkeiten , in denen Generator mit Vertriebsstrategien in Wechselwirkung tritt.

Erzeugung von Generatoren außerhalb von Vertriebsstrategien

Wenn ein Generator außerhalb der Strategiebereiche erstellt wird, wird der Zugriff aller Repliken auf den Generator serialisiert, und daher erhalten die Repliken 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 `run` 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 bei dieser Verwendung Leistungsprobleme auftreten können, da sich das Gerät des Generators von den Replikaten unterscheidet.

Generatoren innerhalb von Vertriebsstrategien erstellen

Wenn ein Generator innerhalb eines Strategiebereichs erstellt wird, erhält jedes Replikat einen anderen und unabhängigen Strom von Zufallszahlen.

strat = tf.distribute.MirroredStrategy(devices=["cpu:0", "cpu:1"])
with strat.scope():
  g = tf.random.Generator.from_seed(1)
  print(strat.run(lambda: g.normal([])))
  print(strat.run(lambda: g.normal([])))
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 `run` inside a tf.function to get the best performance.
PerReplica:{
  0: tf.Tensor(-0.87930447, shape=(), dtype=float32),
  1: tf.Tensor(0.020661574, shape=(), dtype=float32)
}
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 `run` inside a tf.function to get the best performance.
PerReplica:{
  0: tf.Tensor(-1.5822568, shape=(), dtype=float32),
  1: tf.Tensor(0.77539235, shape=(), dtype=float32)
}

Wenn der Generator (zB erstellt von ausgesät wird Generator.from_seed ) werden die Zufallszahlen durch den Samen bestimmt, auch wenn verschiedene Repliken anders und unkorreliert Zahlen zu bekommen. Man kann sich eine auf einem Replikat generierte Zufallszahl als einen Hash der Replikat-ID und eine "primäre" Zufallszahl vorstellen, die allen Replikaten gemeinsam ist. Daher ist das gesamte System immer noch deterministisch.

tf.random.Generator kann auch im Inneren geschaffen werden Strategy.run :

strat = tf.distribute.MirroredStrategy(devices=["cpu:0", "cpu:1"])
with strat.scope():
  def f():
    g = tf.random.Generator.from_seed(1)
    a = g.normal([])
    b = g.normal([])
    return tf.stack([a, b])
  print(strat.run(f))
  print(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 `run` inside a tf.function to get the best performance.
PerReplica:{
  0: tf.Tensor([-0.87930447 -1.5822568 ], shape=(2,), dtype=float32),
  1: tf.Tensor([0.02066157 0.77539235], shape=(2,), dtype=float32)
}
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 `run` inside a tf.function to get the best performance.
PerReplica:{
  0: tf.Tensor([-0.87930447 -1.5822568 ], shape=(2,), dtype=float32),
  1: tf.Tensor([0.02066157 0.77539235], shape=(2,), dtype=float32)
}

Wir empfehlen nicht mehr vorbei tf.random.Generator als Argumente an Strategy.run , weil Strategy.run im Allgemeinen die Argumente erwartet Tensoren zu sein, nicht Generatoren.

Generatoren sparen

Im Allgemeinen für das Speichern oder die Serialisierung Sie kann ein Griff tf.random.Generator die gleiche Weise können Sie eine handhaben würde tf.Variable oder eine tf.Module (oder ihrer Unterklassen). In TF gibt es zwei Mechanismen für die Serialisierung: Checkpoint und SavedModel .

Kontrollpunkt

Generatoren können frei mit gespeichert und wiederhergestellt werden tf.train.Checkpoint . Der Zufallszahlenstrom vom Wiederherstellungspunkt ist der gleiche wie der vom Speicherpunkt.

filename = "./checkpoint"
g = tf.random.Generator.from_seed(1)
cp = tf.train.Checkpoint(generator=g)
print(g.normal([]))
tf.Tensor(0.43842277, shape=(), dtype=float32)
cp.write(filename)
print("RNG stream from saving point:")
print(g.normal([]))
print(g.normal([]))
RNG stream from saving point:
tf.Tensor(1.6272374, shape=(), dtype=float32)
tf.Tensor(1.6307176, shape=(), dtype=float32)
cp.restore(filename)
print("RNG stream from restoring point:")
print(g.normal([]))
print(g.normal([]))
RNG stream from restoring point:
tf.Tensor(1.6272374, shape=(), dtype=float32)
tf.Tensor(1.6307176, shape=(), dtype=float32)

Sie können auch innerhalb einer Verteilungsstrategie speichern und wiederherstellen:

filename = "./checkpoint"
strat = tf.distribute.MirroredStrategy(devices=["cpu:0", "cpu:1"])
with strat.scope():
  g = tf.random.Generator.from_seed(1)
  cp = tf.train.Checkpoint(my_generator=g)
  print(strat.run(lambda: g.normal([])))
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')
PerReplica:{
  0: tf.Tensor(-0.87930447, shape=(), dtype=float32),
  1: tf.Tensor(0.020661574, shape=(), dtype=float32)
}
with strat.scope():
  cp.write(filename)
  print("RNG stream from saving point:")
  print(strat.run(lambda: g.normal([])))
  print(strat.run(lambda: g.normal([])))
RNG stream from saving point:
PerReplica:{
  0: tf.Tensor(-1.5822568, shape=(), dtype=float32),
  1: tf.Tensor(0.77539235, shape=(), dtype=float32)
}
PerReplica:{
  0: tf.Tensor(-0.5039703, shape=(), dtype=float32),
  1: tf.Tensor(0.1251838, shape=(), dtype=float32)
}
with strat.scope():
  cp.restore(filename)
  print("RNG stream from restoring point:")
  print(strat.run(lambda: g.normal([])))
  print(strat.run(lambda: g.normal([])))
RNG stream from restoring point:
PerReplica:{
  0: tf.Tensor(-1.5822568, shape=(), dtype=float32),
  1: tf.Tensor(0.77539235, shape=(), dtype=float32)
}
PerReplica:{
  0: tf.Tensor(-0.5039703, shape=(), dtype=float32),
  1: tf.Tensor(0.1251838, shape=(), dtype=float32)
}

Sie sollten vor dem Speichern sicherstellen, dass die Repliken in ihrer RNG-Anrufhistorie nicht auseinanderlaufen (zB führt eine Replik einen RNG-Anruf durch, während eine andere zwei RNG-Anrufe tätigt). Andernfalls wird ihre internen RNG Staaten divergieren und tf.train.Checkpoint (die nur die erste Replik des Staates speichert) nicht ordnungsgemäß alle Replikate wiederherstellen.

Sie können einen gespeicherten Prüfpunkt auch in einer anderen Verteilungsstrategie mit einer anderen Anzahl von Replikaten wiederherstellen. Da ein tf.random.Generator Objekt in einer Strategie erstellt wird, kann nur in der gleichen Strategie verwendet werden, zu einer anderen Strategie wiederherzustellen, müssen Sie einen neuen erstellen tf.random.Generator in der Zielstrategie und eine neue tf.train.Checkpoint für ihn, wie in diesem Beispiel gezeigt:

filename = "./checkpoint"
strat1 = tf.distribute.MirroredStrategy(devices=["cpu:0", "cpu:1"])
with strat1.scope():
  g1 = tf.random.Generator.from_seed(1)
  cp1 = tf.train.Checkpoint(my_generator=g1)
  print(strat1.run(lambda: g1.normal([])))
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')
PerReplica:{
  0: tf.Tensor(-0.87930447, shape=(), dtype=float32),
  1: tf.Tensor(0.020661574, shape=(), dtype=float32)
}
with strat1.scope():
  cp1.write(filename)
  print("RNG stream from saving point:")
  print(strat1.run(lambda: g1.normal([])))
  print(strat1.run(lambda: g1.normal([])))
RNG stream from saving point:
PerReplica:{
  0: tf.Tensor(-1.5822568, shape=(), dtype=float32),
  1: tf.Tensor(0.77539235, shape=(), dtype=float32)
}
PerReplica:{
  0: tf.Tensor(-0.5039703, shape=(), dtype=float32),
  1: tf.Tensor(0.1251838, shape=(), dtype=float32)
}
strat2 = tf.distribute.MirroredStrategy(devices=["cpu:0", "cpu:1", "cpu:2"])
with strat2.scope():
  g2 = tf.random.Generator.from_seed(1)
  cp2 = tf.train.Checkpoint(my_generator=g2)
  cp2.restore(filename)
  print("RNG stream from restoring point:")
  print(strat2.run(lambda: g2.normal([])))
  print(strat2.run(lambda: g2.normal([])))
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', '/job:localhost/replica:0/task:0/device:CPU:2')
RNG stream from restoring point:
PerReplica:{
  0: tf.Tensor(-1.5822568, shape=(), dtype=float32),
  1: tf.Tensor(0.77539235, shape=(), dtype=float32),
  2: tf.Tensor(0.6851049, shape=(), dtype=float32)
}
PerReplica:{
  0: tf.Tensor(-0.5039703, shape=(), dtype=float32),
  1: tf.Tensor(0.1251838, shape=(), dtype=float32),
  2: tf.Tensor(-0.58519536, shape=(), dtype=float32)
}

Obwohl g1 und cp1 verschiedene Objekte aus sind g2 und cp2 , werden sie über den gemeinsamen Prüfpunkt - Datei verknüpft filename und Objektnamen my_generator . Überlappende Repliken zwischen Strategien (zB cpu:0 und cpu:1 oben) ihre RNG wie Ströme richtig in den vorherigen Beispielen wiederhergestellt haben. Diese Garantie gilt nicht für den Fall, dass ein Generator in einem Strategiebereich gespeichert und außerhalb eines Strategiebereichs wiederhergestellt wird oder umgekehrt, da ein Gerät außerhalb von Strategien anders behandelt wird als jede Replik in einer Strategie.

Gespeichertes Modell

tf.random.Generator kann auf eine SavedModel gespeichert werden. Der Generator kann innerhalb eines Strategieumfangs erstellt werden. Die Einsparung kann auch im Rahmen einer Strategie erfolgen.

filename = "./saved_model"

class MyModule(tf.Module):

  def __init__(self):
    super(MyModule, self).__init__()
    self.g = tf.random.Generator.from_seed(0)

  @tf.function
  def __call__(self):
    return self.g.normal([])

  @tf.function
  def state(self):
    return self.g.state

strat = tf.distribute.MirroredStrategy(devices=["cpu:0", "cpu:1"])
with strat.scope():
  m = MyModule()
  print(strat.run(m))
  print("state:", m.state())
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')
PerReplica:{
  0: tf.Tensor(-1.4154755, shape=(), dtype=float32),
  1: tf.Tensor(-0.113884404, shape=(), dtype=float32)
}
state: tf.Tensor([256   0   0], shape=(3,), dtype=int64)
with strat.scope():
  tf.saved_model.save(m, filename)
  print("RNG stream from saving point:")
  print(strat.run(m))
  print("state:", m.state())
  print(strat.run(m))
  print("state:", m.state())
INFO:tensorflow:Assets written to: ./saved_model/assets
RNG stream from saving point:
PerReplica:{
  0: tf.Tensor(-0.68758255, shape=(), dtype=float32),
  1: tf.Tensor(0.8084062, shape=(), dtype=float32)
}
state: tf.Tensor([512   0   0], shape=(3,), dtype=int64)
PerReplica:{
  0: tf.Tensor(-0.27342677, shape=(), dtype=float32),
  1: tf.Tensor(-0.53093255, shape=(), dtype=float32)
}
state: tf.Tensor([768   0   0], shape=(3,), dtype=int64)
2021-08-28 01:37:16.866401: W tensorflow/python/util/util.cc:348] Sets are not currently considered sequences, but this may change in the future, so consider avoiding using them.
imported = tf.saved_model.load(filename)
print("RNG stream from loading point:")
print("state:", imported.state())
print(imported())
print("state:", imported.state())
print(imported())
print("state:", imported.state())
RNG stream from loading point:
state: tf.Tensor([256   0   0], shape=(3,), dtype=int64)
tf.Tensor(-1.0359411, shape=(), dtype=float32)
state: tf.Tensor([512   0   0], shape=(3,), dtype=int64)
tf.Tensor(-0.06425078, shape=(), dtype=float32)
state: tf.Tensor([768   0   0], shape=(3,), dtype=int64)

Ein SavedModel Laden enthält tf.random.Generator in eine Vertriebsstrategie wird nicht empfohlen , da die Replikate alle den gleichen Zufallszahlenstrom erzeugen werden (das ist da Replik - ID in SavedModel des Graphen eingefroren ist).

Ein verteiltes Laden tf.random.Generator (ein Generator innerhalb einer Verteilungsstrategie geschaffen) in eine nicht-Strategie - Umgebung, wie im obigen Beispiel, hat auch einen Nachteil. Der RNG-Zustand wird ordnungsgemäß wiederhergestellt, aber die generierten Zufallszahlen unterscheiden sich in ihrer Strategie von der des ursprünglichen Generators (wiederum, weil ein Gerät außerhalb der Strategien anders behandelt wird als jede Replik in einer Strategie).

Zustandslose RNGs

Die Verwendung von zustandslosen RNGs ist einfach. Da es sich nur um reine Funktionen handelt, gibt es keinen Zustand oder Nebeneffekt.

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.30159    -0.95385665]], shape=(2, 3), dtype=float32)
tf.Tensor(
[[ 0.5441101   0.20738031  0.07356433]
 [ 0.04643455 -1.30159    -0.95385665]], shape=(2, 3), dtype=float32)

Jeder stateless RNG erfordert ein seed Argument, das ein ganze Zahl von Tensor Form benötigt wird [2] . Die Ergebnisse der Operation werden vollständig von diesem Samen bestimmt.

Der von zustandslosen RNGs verwendete RNG-Algorithmus ist geräteabhängig, was bedeutet, dass dieselbe Operation, die auf einem anderen Gerät ausgeführt wird, unterschiedliche Ausgaben erzeugen kann.

Algorithmen

Allgemein

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

Unterschiedliche Geräte generieren die gleichen Integer-Zahlen, wenn sie den gleichen Algorithmus verwenden und vom gleichen Zustand ausgehen. Sie erzeugen auch "fast die gleichen" Gleitkommazahlen, obwohl es aufgrund der unterschiedlichen Art und Weise, wie die Geräte die Gleitkommaberechnung ausführen (zB Reduktionsreihenfolge), zu kleinen numerischen Abweichungen kommen kann.

XLA-Geräte

Auf XLA getriebene Geräte (wie TPU, und auch CPU / GPU wenn XLA aktiviert ist ) die ThreeFry Algorithmus (geschrieben als "threefry" oder tf.random.Algorithm.THREEFRY ) wird ebenfalls unterstützt. Dieser Algorithmus ist schnell auf TPU, aber langsam auf CPU/GPU im Vergleich zu Philox.

Siehe Papier ‚Parallel Zufallszahl: So einfach wie 1, 2, 3‘ für weitere Informationen über diese Algorithmen.