Path: blob/master/site/es-419/guide/random_numbers.ipynb
25115 views
Copyright 2019 The TensorFlow Authors.
Generación de números aleatorios
TensorFlow proporciona un conjunto de seudogeneradores de números aleatorios (RNG), en el módulo tf.random
. En este documento se describe cómo es posible controlar los generadores de números aleatorios y cómo esos generadores interactúan con otros subsistemas de TensorFlow.
Nota: No es posible garantizar que los números aleatorios serán consistentes entre todas las versiones de TensorFlow. Vea: Compatibilidad de las versiones
TensorFlow brinda dos opciones para controlar el proceso de generación de números aleatorios:
Mediante el uso explícito de objetos
tf.random.Generator
. Cada uno de tales objetos mantiene un estado (entf.Variable
) que se cambiará después de cada generación de número.A través de funciones aleatorias fuera de su estado puramente funcional, como
tf.random.stateless_uniform
. Si se llama a estas funciones con los mismos argumentos (que incluyen la semilla) y en el mismo dispositivo, siempre producirán los mismos resultados.
Advertencia: Los RNG anteriores de TF 1.x como tf.random.uniform
y tf.random.normal
todavía no quedaron obsoletos, pero sí se desalienta enfáticamente su uso.
Preparación
La clase tf.random.Generator
La clase tf.random.Generator
se usa en aquellos casos en los que se quiere que cada llamada de RNG produzca resultados diferentes. Mantiene un estado interno (gestionado por un objeto tf.Variable
) que se actualizará cada vez que se generen los números aleatorios. Como el estado está gestionado por tf.Variable
, goza de todas las facilidades provistas por tf.Variable
, como la determinación sencilla de puntos de verificación y la seguridad.
Es posible obtener un tf.random.Generator
si crea manualmente un objeto de clase o una llamada tf.random.get_global_generator()
para obtener el generador global predeterminado:
Hay muchas maneras de crear un objeto generador. La más sencilla es con Generator.from_seed
, tal como se muestra arriba, que crea un generador a partir de una semilla. Una semilla es un integrador no generativo. from_seed
también toma un argumento opcional alg
que es el algoritmo de RNG que usará este generador:
Para más información al respecto, vea la sección sobre algoritmos que se encuentra más adelante.
Otra forma de crear un generador es con Generator.from_non_deterministic_state
. Un generador que se cree de este modo partirá de un estado no determinista, dependiendo de, p.ej., el momento y el sistema operativo.
Aún hay otras formas de crear generadores, como por ejemplo, a partir de estados explícitos, pero no se incluyen en esta guía.
Cuando use tf.random.get_global_generator
para obtener el generador global, deberá prestar atención a la ubicación del dispositivo. El generador global se crea (a partir de un estado no determinista) la primera vez que se llama a tf.random.get_global_generator
y se coloca en el dispositivo predeterminado de esa llamada. Entonces, por ejemplo, si el primer sitio que se llama con tf.random.get_global_generator
se encuentra dentro del alcance de tf.device("gpu")
, el generador global se colocará en la GPU, y al usar dicho generador, más adelante desde la CPU, se producirá una copia de GPU a CPU.
También hay una función tf.random.set_global_generator
para reemplazar el generador global por otro objeto generador. Sin embargo, esta función debería usarse con cautela, ya que el generador global anterior puede haber sido capturado por una tf.function
(como una referencia débil) y reemplazarlo haría que fuera recolectado como basura, lo que rompería la tf.function
. Hay una mejor manera de restablecer el generador global y es mediante una de las funciones de "reset" (restablecer) como Generator.reset_from_seed
, que no creará objetos generadores nuevos.
Creación de secuencias (streams) de números aleatorios independientes
Para muchas aplicaciones, uno necesita múltiples streams de números aleatorios independientes. Independientes en el sentido de que no se superpondrán y de que no tendrán ninguna correlación detectable estadísticamente. Todo esto se logra con Generator.split
para crear múltiples generadores que sean, con seguridad, independientes entre sí (es decir, que generen streams independientes).
split
cambiará el estado del generador en el que se lo llame (g
, en el ejemplo anterior). De un modo similar a lo que sucede con un método de RNG como normal
. Además de ser independiente entre sí, los generadores nuevos (new_gs
) también tienen la garantía de ser independientes del anterior (g
).
La generación (spawning) de generadores nuevos también es útil para cuando desee asegurarse de que el generador que utiliza se encuentra en el mismo dispositivo que otros cálculos, con el objetivo de evitar el sobrecoste de copias en dispositivos cruzados. Por ejemplo:
Nota: En teoría, en este caso, se pueden usar constructores como, por ejemplo, from_seed
en vez de split
para obtener un generador nuevo, pero al hacerlo se pierde la seguridad de que el generador nuevo será independiente del generador global. También se corre el riesgo de crear accidentalmente dos generadores con la misma semilla o con semillas que conduzcan a la superposición de streams de números aleatorios.
La división se puede hacer recursivamente, invocando split
en los generadores de división. No hay límites (excepto por el sobreflujo de enteros) para la profundidad de las recursiones.
Interacción con tf.function
tf.random.Generator
obedece a las mismas reglas que tf.Variable
cuando se usa con tf.function
. Esto incluye tres aspectos.
La creación de generadores fuera de tf.function
tf.function
puede usar un generador creado fuera de ella.
El usuario debe asegurarse de que el objeto generador aún esté vivo (no recolectado como basura) cuando se llame a la función.
La creación de generadores dentro de tf.function
La creación de generadores dentro de una tf.function
solamente puede producirse durante la primera ejecución de la función.
Los generadores de pase como argumentos a tf.function
Cuando se usan como un argumento para una tf.function
, los diferentes objetos generadores provocan un nuevo rastreo (retracing) de la tf.function
.
Tenga en cuenta que este comportamiento es consistente con tf.Variable
:
Interacción con estrategias de distribución
Hay dos maneras en las que interacciona Generator
con las estrategias de distribución.
La creación de generadores fuera de las estrategias de distribución
Si un generador se crea fuera de los alcances estratégicos, todos los accesos de las réplicas al generador se serializarán y, entonces, las réplicas obtendrán diferentes números aleatorios.
Tenga en cuenta que pueden haber surgido problemas de desempeño con este uso, ya que el dispositivo del generador es diferente al de las réplicas.
La creación de generadores dentro de las estrategias de distribución
Si un generador se crea dentro del alcance de una estrategia, cada réplica obtendrá una stream de números aleatorios independiente y diferente.
Nota: Actualmente, tf.random.Generator
no ofrece una opción para permitir que diferentes réplicas obtengan streams idénticas (en vez de diferentes); lo cual resulta, técnicamente, no tan complicado. Si tiene un caso de uso para esta función, por favor, hágaselo saber a los desarrolladores de TensorFlow.
Si el generador tiene semilla (p. ej., creado por Generator.from_seed
), los números aleatorios estarán determinados por la semilla, a pesar de que con réplicas diferentes se obtienen distintos números no correlacionados. Uno podría pensar en un número aleatorio generado de una réplica como una función hash del ID de la réplica y un número aleatorio "primario" que es común a todas las réplicas. Por lo tanto, el sistema completo sigue siendo determinista.
tf.random.Generator
también se pueden crear dentro de Strategy.run
:
Ya no recomendamos pasar tf.random.Generator
como argumentos a Strategy.run
, porque, por lo general, Strategy.run
espera que los argumentos sean tensores, no generadores.
Guardado de generadores
Por lo general, para guardar o serializar, un tf.random.Generator
se puede manejar del mismo modo que una tf.Variable
o un tf.Module
(o sus subclases). En TF hay dos mecanismos para la serialización: mediante Puntos de verificación (checkpoint) o con SavedModel.
Punto de verificación (checkpoint)
Los generadores se pueden guardar y restaurar sin problemas con tf.train.Checkpoint
. La stream de números aleatorios a partir del número restaurado será igual al del punto de guardado.
También es posible guardar y restaurar dentro de una estrategia de distribución:
Deberá controlar que las réplicas no se aparten en sus historias de llamadas de RNG (p. ej., una réplica hace una llamada de RNG mientras otra hace dos) antes de guardar. De lo contrario, sus estados RNG internos divergirán y tf.train.Checkpoint
(que solamente guarda el primer estado de réplica) no restaurará las réplicas como corresponde.
También se puede restaurar un punto de verificación (checkpoint) guardado en una estrategia de distribución diferente con un número de réplicas distinto. Dado que un objeto tf.random.Generator
creado en una estrategia solamente se puede usar en la misma estrategia, para restaurarlo en una estrategia diferente, deberá crear un tf.random.Generator
en la estrategia de destino y un tf.train.Checkpoint
nuevo, tal como se muestra en el ejemplo:
A pesar de que g1
y cp1
son objetos diferentes de g2
y cp2
, están vinculados a través de un archivo de punto de verificación filename
y un nombre de objeto my_generator
en común. La superposición de las réplicas entre estrategias (p. ej., las cpu:0
y cpu:1
anteriores) tendrán sus streams de generación de números aleatorios debidamente restaurados, tal como en los ejemplos anteriores. Esta garantía no cubre el caso en que un generador se guarda en un alcance de estrategia y se restablece por fuera del alcance de estrategia o viceversa; debido a que un dispositivo fuera de estrategias se trata de manera diferente al de cualquier réplica dentro de una estrategia.
SavedModel
tf.random.Generator
se puede guardar en un SavedModel. El generador se puede crear dentro del ámbito de una estrategia. El guardado también se puede producir dentro del ámbito de una estrategia.
No se recomienda cargar un SavedModel que contenga tf.random.Generator
dentro de una estrategia de distribución, porque todas las réplicas generarán la misma stream de números aleatorios (esto sucede porque el ID de réplica se congela en el grafo del SavedModel).
También hay una salvedad, la carga de un tf.random.Generator
distribuido (un generador creado dentro de una estrategia de distribución) dentro de un entorno sin estrategias, tal como el del ejemplo anterior. El estado RNG deberá ser apropiadamente restablecido, pero los números aleatorios generados serán distintos del generador original en su propia estrategia (nuevamente, porque un dispositivo fuera de estrategias se trata diferente de cualquier réplica dentro de una estrategia).
RNG sin estado
El uso de los RNG sin estado es simple. Como son solo funciones simples, no hay estado ni efecto colateral involucrado.
Cada RNG sin estado requiere de un argumento seed
, que necesita ser run Tensor entero de forma [2]
. Los resultados de la operación están totalmente determinados por esta semilla.
El algoritmo de RNG usado por las RNG sin estado es dependiente del dispositivo; es decir, la misma operación ejecutada en un dispositivo diferente puede producir distintos resultados.
Algoritmos
General
Tanto la función de clase tf.random.Generator
como stateless
son compatibles con el algoritmos Philox (escrito como "philox"
o tf.random.Algorithm.PHILOX
) en todos los dispositivos.
Los distintos dispositivos generarán los mismos números enteros si se usa el mismo algoritmo y se empieza a partir del mismo estado. También generarán "casi los mismos" números de puntos flotantes, a pesar de que puede haber algunas discrepancias numéricas menores causadas por las diferentes maneras en que los dispositivos llevan a cabo el cálculo de punto flotante (p. ej., el orden de reducción).
Dispositivos con XLA
En los dispositivos con XLA (como las TPU y también las CPU o GPU cuando XLA está activado) el algoritmo ThreeFry (escrito como "threefry"
o como tf.random.Algorithm.THREEFRY
) también se admite. Este algoritmo es rápido en TPU pero lento en CPU o GPU si se lo compara con Philox.
Para más detalles sobre estos algoritmos, consulte la publicación 'Parallel Random Numbers: As Easy as 1, 2, 3' (Números aleatorios paralelos: tan sencillo como contar hasta 3).