Ta strona została przetłumaczona przez Cloud Translation API.
Switch to English

Generowanie liczb losowych

Zobacz na TensorFlow.org Uruchom w Google Colab Wyświetl źródło na GitHub Pobierz notatnik

TensorFlow udostępnia zestaw generatorów liczb pseudolosowych (RNG) w module tf.random . W tym dokumencie opisano, w jaki sposób można sterować generatorami liczb losowych oraz jak te generatory współdziałają z innymi podsystemami tensorflow.

TensorFlow zapewnia dwa podejścia do kontrolowania procesu generowania liczb losowych:

  1. Poprzez jawne użycie obiektów tf.random.Generator . Każdy taki obiekt zachowuje stan (w tf.Variable ), który będzie zmieniany po każdym wygenerowaniu liczby.

  2. Poprzez czysto funkcjonalne bezstanowe funkcje losowe, takie jak tf.random.stateless_uniform . Wywołanie tych funkcji z tymi samymi argumentami (które zawierają ziarno) i na tym samym urządzeniu zawsze da te same wyniki.

Ustawiać

 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()
    ])
 

Klasa tf.random.Generator

Klasa tf.random.Generator jest używana w przypadkach, gdy chcesz, aby każde wywołanie RNG dawało różne wyniki. Utrzymuje stan wewnętrzny (zarządzany przez obiekt tf.Variable ), który będzie aktualizowany za każdym razem, gdy generowane są liczby losowe. Ponieważ stan jest zarządzany przez tf.Variable , korzysta ze wszystkich udogodnień zapewnianych przez tf.Variable takich jak łatwe punkty kontrolne, automatyczna zależność od sterowania i bezpieczeństwo wątków.

Możesz pobrać tf.random.Generator , ręcznie tworząc obiekt klasy lub wywołując tf.random.get_global_generator() aby uzyskać domyślny generator globalny:

 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.6078218  3.162639  -1.0558378]
 [ 1.2078347  0.6414574  0.4019502]], shape=(2, 3), dtype=float32)

Istnieje wiele sposobów tworzenia obiektu generatora. Najłatwiejszym jest Generator.from_seed , jak pokazano powyżej, który tworzy generator z nasion. Ziarno to dowolna nieujemna liczba całkowita. from_seed pobiera również opcjonalny argument alg który jest algorytmem RNG, który będzie używany przez ten generator:

 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)

Zobacz sekcję Algorytmy poniżej, aby uzyskać więcej informacji.

Innym sposobem na utworzenie generatora jest Generator.from_non_deterministic_state . Tak utworzony generator będzie startował ze stanu niedeterministycznego, zależnego np. Od czasu i systemu operacyjnego.

 g = tf.random.Generator.from_non_deterministic_state()
print(g.normal(shape=[2, 3]))
 
tf.Tensor(
[[ 1.1436943  1.729618   1.0391121]
 [-0.8502223 -1.8823647 -1.4051851]], shape=(2, 3), dtype=float32)

Istnieją jeszcze inne sposoby tworzenia generatorów, na przykład z jawnych stanów, które nie są objęte tym przewodnikiem.

Używając tf.random.get_global_generator aby uzyskać globalny generator, musisz uważać na rozmieszczenie urządzeń. Generator globalny jest tworzony (ze stanu niedeterministycznego) przy pierwszym tf.random.get_global_generator i umieszczany na domyślnym urządzeniu podczas tego wywołania. Na przykład, jeśli pierwsza witryna, którą wywołujesz tf.random.get_global_generator znajduje się w zasięgu tf.device("gpu") , globalny generator zostanie umieszczony na GPU, a późniejsze użycie generatora globalnego z procesora ponieść kopiowanie z GPU do CPU.

Istnieje również funkcja tf.random.set_global_generator do zastępowania generatora globalnego innym obiektem generatora. Ta funkcja powinna być używana z rozwagą, ponieważ stary generator globalny mógł zostać przechwycony przez funkcję tf.function (jako słabe odniesienie), a zastąpienie go spowoduje, że zostanie tf.function , co spowoduje uszkodzenie funkcji tf.function . Lepszym sposobem na zresetowanie generatora globalnego jest użycie jednej z funkcji „resetowania”, takiej jak Generator.reset_from_seed , która nie utworzy nowych obiektów generatora.

 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)

Tworzenie niezależnych strumieni liczb losowych

W wielu zastosowaniach potrzeba wielu niezależnych strumieni liczb losowych, niezależnych w tym sensie, że nie będą się nakładać i nie będą miały żadnych statystycznie wykrywalnych korelacji. Osiąga się to za pomocą narzędzia Generator.split do tworzenia wielu generatorów, które są od siebie niezależne (tj. Generują niezależne strumienie).

 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 zmieni stan generatora, na którym jest wywoływany ( g w powyższym przykładzie), podobnie do metody RNG, takiej jak normal . Oprócz tego, że są od siebie niezależne, nowe generatory ( new_gs ) są również niezależne od starego ( g ).

Odradzanie nowych generatorów jest również przydatne, gdy chcesz się upewnić, że używany generator znajduje się na tym samym urządzeniu, co inne obliczenia, aby uniknąć narzutu związanego z kopiowaniem między urządzeniami. Na przykład:

 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(-1.7580209, shape=(), dtype=float32)

Dzielenie można wykonywać rekurencyjnie, wywołując split na podzielonych generatorach. Nie ma ograniczeń (z wyjątkiem przepełnienia liczb całkowitych) głębokości rekursji.

Interakcja z tf.function

tf.random.Generator tym samym regułom, co tf.Variable gdy jest używany z tf.function . Obejmuje to trzy aspekty.

Tworzenie generatorów poza tf.function

tf.function może korzystać z generatora utworzonego poza nim.

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

Użytkownik musi się upewnić, że obiekt generatora nadal działa (nie jest zbierany jako śmieci), gdy funkcja jest wywoływana.

Tworzenie generatorów wewnątrz tf.function

Tworzenie generatorów wewnątrz funkcji tf.function Może tf.function tylko podczas pierwszego uruchomienia funkcji.

 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())
 
WARNING:tensorflow:From /tmpfs/src/tf_docs_env/lib/python3.6/site-packages/tensorflow/python/ops/resource_variable_ops.py:1817: calling BaseResourceVariable.__init__ (from tensorflow.python.ops.resource_variable_ops) with constraint is deprecated and will be removed in a future version.
Instructions for updating:
If using Keras pass *_constraint arguments to layers.
tf.Tensor(0.43842274, shape=(), dtype=float32)
tf.Tensor(1.6272374, shape=(), dtype=float32)

Przekazywanie generatorów jako argumentów do funkcji tf.function

W przypadku użycia jako argument funkcji tf.function , różne obiekty generatora o tym samym rozmiarze stanu (rozmiar stanu jest określany przez algorytm RNG) nie spowodują tf.function funkcji tf.function , podczas gdy te o różnych rozmiarach stanu tak.

 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

Interakcja ze strategiami dystrybucji

Istnieją trzy sposoby współdziałania Generator ze strategiami dystrybucji.

Tworzenie generatorów poza strategiami dystrybucji

Jeśli generator zostanie utworzony poza zakresami strategii, dostęp wszystkich replik do generatora zostanie serializowany, a zatem repliki otrzymają różne liczby losowe.

 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)

Należy pamiętać, że takie użycie może mieć problemy z wydajnością, ponieważ urządzenie generatora różni się od replik.

Tworzenie generatorów w strategiach dystrybucji

Tworzenie generatorów w zakresach strategii jest niedozwolone, ponieważ istnieje niejasność co do sposobu replikacji generatora (np. Należy go skopiować, aby każda replika otrzymywała te same liczby losowe, lub „podzielić”, aby każda replika otrzymała różne liczby losowe).

 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).

Zauważ, że Strategy.run niejawnie uruchomi swoją funkcję argumentu w zakresie strategii:

 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 `run` 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_strategy.py", line 998, 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 282, 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 444, 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 386, in __init__
    trainable=False)
  File "/tmpfs/src/tf_docs_env/lib/python3.6/site-packages/tensorflow/python/ops/stateful_random_ops.py", line 272, 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).

Przekazywanie generatorów jako argumentów do Strategy.run

Jeśli chcesz, aby każda replika korzystała z własnego generatora, musisz utworzyć n generatorów (kopiując lub dzieląc), gdzie n jest liczbą replik, a następnie przekazać je jako argumenty do 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 `run` 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 `run` inside a tf.function to get the best performance.
tf.Tensor(-0.15682742, shape=(), dtype=float32)
tf.Tensor(-0.38042808, shape=(), dtype=float32)

Bezpaństwowe RNG

Korzystanie z bezpaństwowych generatorów liczb losowych jest proste. Ponieważ są to tylko czyste funkcje, nie ma w nich żadnego stanu ani efektu ubocznego.

 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)

Każdy bezstanowy RNG wymaga seed argumentu, która musi być liczbą całkowitą tensora kształtu [2] . Wyniki operacji są w pełni zdeterminowane przez to ziarno.

Algorytmy

Generał

Zarówno klasa tf.random.Generator i funkcje stateless obsługują algorytm Philoxa (zapisany jako "philox" lub tf.random.Algorithm.PHILOX ) na wszystkich urządzeniach.

Różne urządzenia generują te same liczby całkowite, jeśli używają tego samego algorytmu i rozpoczynają się od tego samego stanu. Będą również generować „prawie takie same” liczby zmiennoprzecinkowe, chociaż mogą występować niewielkie rozbieżności liczbowe spowodowane różnymi sposobami wykonywania przez urządzenia obliczeń zmiennoprzecinkowych (np. Porządek redukcji).

Urządzenia XLA

Na urządzeniach z obsługą XLA (takich jak TPU, a także procesor / GPU, gdy włączone jest XLA) obsługiwany jest również algorytm ThreeFry (zapisany jako "threefry" lub tf.random.Algorithm.THREEFRY ). Ten algorytm jest szybki na TPU, ale wolny na CPU / GPU w porównaniu do Philox.

Więcej informacji na temat tych algorytmów można znaleźć w artykule „Parallel Random Numbers: As Easy as 1, 2, 3” .