Se usó la API de Cloud Translation para traducir esta página.
Switch to English

Generación de números aleatorios

Ver en TensorFlow.org Ejecutar en Google Colab Ver código fuente en GitHub Descargar cuaderno

TensorFlow proporciona un conjunto de generadores de números pseudoaleatorios (RNG), en el módulo tf.random . Este documento describe cómo puede controlar los generadores de números aleatorios y cómo estos generadores interactúan con otros subsistemas de tensorflow.

TensorFlow proporciona dos enfoques para controlar el proceso de generación de números aleatorios:

  1. Mediante el uso explícito de los objetos tf.random.Generator . Cada uno de estos objetos mantiene un estado (en tf.Variable ) que cambiará después de cada generación de números.

  2. A través de las funciones aleatorias sin estado puramente funcionales como tf.random.stateless_uniform . Llamar a estas funciones con los mismos argumentos (que incluyen la semilla) y en el mismo dispositivo siempre producirá los mismos resultados.

Preparar

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

La clase tf.random.Generator

La clase tf.random.Generator se usa en los casos en que desea que cada llamada RNG produzca resultados diferentes. Mantiene un estado interno (administrado por un objeto tf.Variable ) que se actualizará cada vez que se generen números aleatorios. Debido a que el estado es administrado por tf.Variable , disfruta de todas las facilidades proporcionadas por tf.Variable , como la fácil verificación, la dependencia de control automático y la seguridad de subprocesos.

Puede obtener un tf.random.Generator creando manualmente un objeto de la clase o llamando a tf.random.get_global_generator() para obtener el generador global predeterminado:

 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)

Hay varias formas de crear un objeto generador. La más fácil es Generator.from_seed , como se muestra arriba, que crea un generador a partir de una semilla. Una semilla es cualquier número entero no negativo. from_seed también toma un argumento opcional alg que es el algoritmo RNG que será utilizado por este generador:

 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)

Consulte la sección Algoritmos a continuación para obtener más información al respecto.

Otra forma de crear un generador es con Generator.from_non_deterministic_state . Un generador creado de esta manera comenzará desde un estado no determinista, según, por ejemplo, el tiempo y el sistema operativo.

 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)

Existen otras formas de crear generadores, como los estados explícitos, que no están cubiertos por esta guía.

Cuando utilice tf.random.get_global_generator para obtener el generador global, debe tener cuidado con la ubicación del dispositivo. El generador global se crea (desde un estado no determinista) la primera vez que se llama a tf.random.get_global_generator y se coloca en el dispositivo predeterminado en esa llamada. Entonces, por ejemplo, si el primer sitio al que llama tf.random.get_global_generator está dentro del tf.device("gpu") , el generador global se colocará en la GPU, y el uso del generador global más adelante de la CPU incurrir en una copia de GPU a CPU.

También hay una función tf.random.set_global_generator para reemplazar el generador global con otro objeto generador. Esta función debe usarse con precaución, ya que el antiguo generador global puede haber sido capturado por una función tf.function (como una referencia débil), y reemplazarlo hará que se recolecte basura, rompiendo la función tf.function Una mejor manera de restablecer el generador global es usar una de las funciones de "restablecimiento" como Generator.reset_from_seed , que no creará nuevos objetos generadores.

 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)

Crear flujos independientes de números aleatorios

En muchas aplicaciones, se necesitan múltiples flujos independientes de números aleatorios, independientes en el sentido de que no se superpondrán y no tendrán correlaciones estadísticamente detectables. Esto se logra mediante el uso de Generator.split para crear múltiples generadores que están garantizados para ser independientes entre sí (es decir, generar flujos independientes).

 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 cambiará el estado del generador en el que se llama ( g en el ejemplo anterior), similar a un método RNG como el normal . Además de ser independientes entre sí, los nuevos generadores ( new_gs ) también tienen la garantía de ser independientes del anterior ( g ).

Generar nuevos generadores también es útil cuando desea asegurarse de que el generador que utiliza esté en el mismo dispositivo que otros cálculos, para evitar la sobrecarga de la copia entre dispositivos. Por ejemplo:

 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)

Puede dividir recursivamente, llamando split en generadores divididos. No hay límites (salvo el desbordamiento de enteros) en la profundidad de las recursiones.

Interacción con la función tf.function

tf.random.Generator obedece las mismas reglas que tf.Variable cuando se usa con tf.function . Esto incluye tres aspectos.

Crear generadores fuera de tf.function

tf.function puede usar un generador creado fuera de él.

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

El usuario debe asegurarse de que el objeto generador aún esté vivo (no recolectado) cuando se llama a la función.

Crear generadores dentro de tf.function

La creación de generadores dentro de una función tf.function solo puede tf.function durante la primera ejecución de la función.

 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)

Pasando generadores como argumentos para tf.function

Cuando se utiliza como argumento para una función tf.function , diferentes objetos generadores con el mismo tamaño de estado (el algoritmo RNG determina el tamaño del estado) no provocarán el tf.function de la función tf.function , mientras que aquellos con diferentes tamaños de estado lo harán.

 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

Interacción con estrategias de distribución.

Hay tres formas en que Generator interactúa con las estrategias de distribución.

Crear generadores fuera de las estrategias de distribución.

Si se crea un generador fuera de los ámbitos estratégicos, el acceso de todas las réplicas al generador se serializará y, por lo tanto, las réplicas obtendrán diferentes números aleatorios.

 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)

Tenga en cuenta que este uso puede tener problemas de rendimiento porque el dispositivo del generador es diferente de las réplicas.

Crear generadores dentro de estrategias de distribución

La creación de generadores dentro de ámbitos estratégicos no está permitida, porque existe una ambigüedad sobre cómo replicar un generador (por ejemplo, si se copia de manera que cada réplica obtenga los mismos números aleatorios, o se 'divida' para que cada réplica obtenga diferentes números aleatorios).

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

Tenga en cuenta que Strategy.run ejecutará su función de argumento en un ámbito de estrategia implícitamente:

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

Pasando generadores como argumentos para Strategy.run

Si desea que cada réplica use su propio generador, necesita hacer n generadores (ya sea copiando o dividiendo), donde n es el número de réplicas, y luego pasarlos como argumentos a 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 apátridas

El uso de RNG sin estado es simple. Como son solo funciones puras, no hay ningún estado o efecto secundario involucrado.

 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)

Cada RNG sin estado requiere un argumento seed , que debe ser un Tensor de forma entero [2] . Los resultados de la operación están completamente determinados por esta semilla.

Algoritmos

General

Tanto la clase tf.random.Generator como las funciones stateless admiten el algoritmo Philox (escrito como "philox" o tf.random.Algorithm.PHILOX ) en todos los dispositivos.

Los diferentes dispositivos generarán los mismos números enteros, si usan el mismo algoritmo y comienzan desde el mismo estado. También generarán "casi el mismo" número de coma flotante, aunque puede haber pequeñas discrepancias numéricas causadas por las diferentes formas en que los dispositivos realizan el cálculo de coma flotante (por ejemplo, orden de reducción).

Dispositivos XLA

En los dispositivos controlados por XLA (como TPU y también CPU / GPU cuando XLA está habilitado), también se admite el algoritmo ThreeFry (escrito como "threefry" o tf.random.Algorithm.THREEFRY ). Este algoritmo es rápido en TPU pero lento en CPU / GPU en comparación con Philox.

Consulte el documento 'Números aleatorios paralelos: tan fácil como 1, 2, 3' para obtener más detalles sobre estos algoritmos.