Эта страница была переведа с помощью Cloud Translation API.
Switch to English

Генерация случайных чисел

Посмотреть на TensorFlow.org Запустить в Google Colab Посмотреть источник на GitHub Скачать блокнот

TensorFlow предоставляет набор генераторов псевдослучайных чисел (RNG) в модуле tf.random . Этот документ описывает, как вы можете управлять генераторами случайных чисел и как эти генераторы взаимодействуют с другими подсистемами тензорного потока.

TensorFlow предоставляет два подхода для управления процессом генерации случайных чисел:

  1. Благодаря явному использованию объектов tf.random.Generator . Каждый такой объект поддерживает состояние (в tf.Variable ), которое будет меняться после каждой генерации номера.

  2. Через чисто функциональные случайные функции без состояния, такие как tf.random.stateless_uniform . Вызов этих функций с одинаковыми аргументами (включая начальное число) и на одном и том же устройстве всегда даст одинаковые результаты.

Настроить

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

Класс tf.random.Generator

Класс tf.random.Generator используется в тех случаях, когда вы хотите, чтобы каждый вызов RNG приводил к разным результатам. Он поддерживает внутреннее состояние (управляемое объектом tf.Variable ), которое будет обновляться каждый раз, когда генерируются случайные числа. Поскольку состояние управляется tf.Variable , оно использует все возможности, предоставляемые tf.Variable такие как простое определение контрольных точек, автоматическое управление зависимостями и безопасность потоков.

Вы можете получить tf.random.Generator , вручную создав объект класса или 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.6078218  3.162639  -1.0558378]
 [ 1.2078347  0.6414574  0.4019502]], shape=(2, 3), dtype=float32)

Есть несколько способов создать объект генератора. Самым простым является Generator.from_seed , как показано выше, который создает генератор из начального числа. Семя - это любое неотрицательное целое число. from_seed также принимает необязательный аргумент alg который является алгоритмом RNG, который будет использоваться этим генератором:

 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)

См. Раздел Алгоритмы ниже для получения дополнительной информации об этом.

Другой способ создать генератор - использовать Generator.from_non_deterministic_state . Генератор, созданный таким образом, будет запускаться из недетерминированного состояния, в зависимости, например, от времени и ОС.

 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)

Есть и другие способы создания генераторов, например, из явных состояний, которые не рассматриваются в этом руководстве.

При использовании tf.random.get_global_generator для получения глобального генератора вы должны быть осторожны с размещением устройства. Глобальный генератор создается (из недетерминированного состояния) при первом tf.random.get_global_generator и помещается на устройство по умолчанию при этом вызове. Так, например, если первый сайт, который вы называете tf.random.get_global_generator находится в области tf.device("gpu") , глобальный генератор будет помещен в графический процессор, а использование глобального генератора позже из ЦП понести копию GPU-CPU.

Существует также функция tf.random.set_global_generator для замены глобального генератора другим объектом-генератором. Эту функцию следует использовать с осторожностью, потому что старый глобальный генератор мог быть захвачен tf.function (в качестве слабой ссылки), и замена ее приведет к его сборке мусора, что tf.function . Лучший способ сбросить глобальный генератор - использовать одну из функций «сброса», например Generator.reset_from_seed , которая не будет создавать новые объекты генератора.

 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)

Создание независимых потоков случайных чисел

Во многих приложениях требуется несколько независимых потоков случайных чисел, независимых в том смысле, что они не будут перекрываться и не будут иметь статистически обнаруживаемых корреляций. Это достигается с помощью Generator.split для создания нескольких генераторов, которые гарантированно не зависят друг от друга (т.е. генерируют независимые потоки).

 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 изменит состояние генератора, на котором он вызывается ( g в вышеприведенном примере), аналогично методу RNG, например normal . В дополнение к независимости от друг друга, новые генераторы ( new_gs ) также гарантированно не зависят от старого ( g ).

Создание новых генераторов также полезно, когда вы хотите убедиться, что используемый вами генератор находится на том же устройстве, что и другие вычисления, чтобы избежать накладных расходов при копировании между устройствами. Например:

 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)

Вы можете сделать разделение рекурсивно, вызывая split на разделенных генераторах. Нет никаких ограничений (исключая переполнение целых чисел) на глубину рекурсий.

Взаимодействие с tf.function

tf.random.Generator подчиняется тем же правилам, что и tf.Variable когда используется с tf.function . Это включает в себя три аспекта.

Создание генераторов вне функции tf.function

tf.function может использовать генератор, созданный вне ее.

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

Пользователь должен убедиться, что объект генератора еще жив (не собран) при вызове функции.

Создание генераторов внутри функции tf.function

Создание генераторов внутри tf.function может произойти только во время первого запуска функции.

 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)

Передача генераторов в качестве аргументов функции tf.function

При использовании в качестве аргумента функции tf.function различные объекты-генераторы с одинаковым размером состояния (размер состояния определяется алгоритмом RNG) не будут вызывать откат функции tf.function , тогда как 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

Взаимодействие со стратегиями распределения

Существует три способа взаимодействия Generator со стратегиями распределения.

Создание генераторов вне стратегий распространения

Если генератор создается вне области действия стратегии, доступ всех реплик к генератору будет сериализован, и, следовательно, реплики получат разные случайные числа.

 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)

Обратите внимание, что это использование может иметь проблемы с производительностью, потому что устройство генератора отличается от реплик.

Создание генераторов внутри стратегий распространения

Создание генераторов внутри областей стратегии не допускается, поскольку существует неоднозначность в отношении того, как реплицировать генератор (например, следует ли его копировать, чтобы каждая реплика получала одинаковые случайные числа, или «разбивать», чтобы каждая реплика получала разные случайные числа).

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

Обратите внимание, что Strategy.run будет неявно запускать свою функцию аргумента в области действия стратегии:

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

Передача генераторов в качестве аргументов в Strategy.run

Если вы хотите, чтобы каждая реплика использовала свой собственный генератор, вам нужно создать n генераторов (путем копирования или разделения), где n - количество реплик, а затем передать их в качестве аргументов в 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)

RNG без гражданства

Использование RNG без гражданства просто. Поскольку они являются просто чистыми функциями, в них не вовлечено состояние или побочные эффекты.

 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)

Каждому ГСЧ без состояния требуется seed аргумент, который должен быть целочисленным тензором формы [2] . Результаты оп полностью определены этим семенем.

Алгоритмы

Общее

И класс tf.random.Generator и функции stateless поддерживают алгоритм Филокса (записанный как "philox" или tf.random.Algorithm.PHILOX ) на всех устройствах.

Разные устройства генерируют одинаковые целые числа, если используют один и тот же алгоритм и исходят из одного и того же состояния. Они также будут генерировать «почти одинаковые» числа с плавающей запятой, хотя могут быть небольшие численные расхождения, вызванные различными способами, которыми устройства выполняют вычисления с плавающей запятой (например, порядок уменьшения).

Устройства XLA

На устройствах под управлением XLA (таких как TPU, а также CPU / GPU, когда включен tf.random.Algorithm.THREEFRY ) также поддерживается алгоритм ThreeFry (записанный как "threefry" или tf.random.Algorithm.THREEFRY ). Этот алгоритм быстрый на TPU, но медленный на CPU / GPU по сравнению с Philox.

См. Статью «Параллельные случайные числа: так же просто, как 1, 2, 3» для более подробной информации об этих алгоритмах.