Questa pagina è stata tradotta dall'API Cloud Translation.
Switch to English

Generazione di numeri casuali

Visualizza su TensorFlow.org Esegui in Google Colab Visualizza sorgente su GitHub Scarica il quaderno

TensorFlow fornisce un set di generatori di numeri pseudo-casuali (RNG), nel modulo tf.random . Questo documento descrive come è possibile controllare i generatori di numeri casuali e come questi generatori interagiscono con altri sottosistemi tensorflow.

TensorFlow fornisce due approcci per il controllo del processo di generazione di numeri casuali:

  1. Attraverso l'uso esplicito di oggetti tf.random.Generator . Ciascuno di questi oggetti mantiene uno stato (in tf.Variable ) che verrà modificato dopo ogni generazione di numeri.

  2. Attraverso le funzioni casuali stateless puramente funzionali come tf.random.stateless_uniform . Chiamare queste funzioni con gli stessi argomenti (che includono il seed) e sullo stesso dispositivo produrrà sempre gli stessi risultati.

Impostare

 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 classe tf.random.Generator

La classe tf.random.Generator viene utilizzata nei casi in cui si desidera che ogni chiamata RNG produca risultati diversi. Mantiene uno stato interno (gestito da un oggetto tf.Variable ) che verrà aggiornato ogni volta che vengono generati numeri casuali. Poiché lo stato è gestito da tf.Variable , gode di tutti i servizi forniti da tf.Variable come il checkpoint facile, la dipendenza automatica dal controllo e la sicurezza del thread.

Puoi ottenere un tf.random.Generator creando manualmente un oggetto della classe o chiamando tf.random.get_global_generator() per ottenere il generatore globale predefinito:

 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)

Esistono diversi modi per creare un oggetto generatore. Il più semplice è Generator.from_seed , come mostrato sopra, che crea un generatore da un seme. Un seme è qualsiasi numero intero non negativo. from_seed accetta anche un argomento opzionale alg che è l'algoritmo RNG che verrà utilizzato da questo generatore:

 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)

Vedere la sezione Algoritmi di seguito per ulteriori informazioni al riguardo.

Un altro modo per creare un generatore è con Generator.from_non_deterministic_state . Un generatore creato in questo modo partirà da uno stato non deterministico, a seconda del tempo e del 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)

Esistono altri modi per creare generatori, ad esempio da stati espliciti, che non sono coperti da questa guida.

Quando si utilizza tf.random.get_global_generator per ottenere il generatore globale, è necessario prestare attenzione al posizionamento del dispositivo. Il generatore globale viene creato (da uno stato non deterministico) la prima volta tf.random.get_global_generator viene chiamato tf.random.get_global_generator e posizionato sul dispositivo predefinito in quella chiamata. Quindi, ad esempio, se il primo sito chiamato tf.random.get_global_generator è all'interno di un ambito tf.device("gpu") , il generatore globale verrà inserito nella GPU e, in seguito, utilizzando il generatore globale dalla CPU incorrere in una copia da GPU a CPU.

C'è anche una funzione tf.random.set_global_generator per sostituire il generatore globale con un altro oggetto generatore. Questa funzione dovrebbe essere usata con cautela, perché il vecchio generatore globale potrebbe essere stato catturato da una funzione tf.function (come riferimento debole), e sostituendolo causerà la raccolta dei rifiuti, interrompendo la funzione tf.function . Un modo migliore per ripristinare il generatore globale è utilizzare una delle funzioni di "ripristino" come Generator.reset_from_seed , che non creerà nuovi oggetti generatore.

 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)

Creazione di flussi di numeri casuali indipendenti

In molte applicazioni sono necessari più flussi di numeri casuali indipendenti, indipendenti nel senso che non si sovrapporranno e non avranno alcuna correlazione statisticamente rilevabile. Ciò si ottiene utilizzando Generator.split per creare più generatori che sono garantiti indipendenti l'uno dall'altro (ovvero generazione di flussi indipendenti).

 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 cambierà lo stato del generatore su cui viene chiamato ( g nell'esempio sopra), simile a un metodo RNG come normal . Oltre ad essere indipendenti l'uno dall'altro, i nuovi generatori ( new_gs ) sono anche garantiti come indipendenti da quello vecchio ( g ).

La generazione di nuovi generatori è utile anche quando vuoi assicurarti che il generatore che usi sia sullo stesso dispositivo di altri calcoli, per evitare il sovraccarico della copia tra dispositivi. Per esempio:

 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)

Puoi fare la divisione in modo ricorsivo, chiamando la split su generatori split . Non ci sono limiti (salvo l'overflow di numeri interi) sulla profondità delle ricorsioni.

Interazione con la funzione tf.function

tf.random.Generator obbedisce alle stesse regole di tf.Variable quando usato con la funzione tf.function . Questo include tre aspetti.

Creazione di generatori al di fuori della funzione tf.function

tf.function può usare un generatore creato al di fuori di esso.

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

L'utente deve assicurarsi che l'oggetto generatore sia ancora vivo (non garbage collection) quando viene chiamata la funzione.

Creazione di generatori all'interno di tf.function

La creazione di generatori all'interno di una funzione tf.function può tf.function solo durante la prima esecuzione della funzione.

 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)

Passando generatori come argomenti a tf.function

Se utilizzato come argomento per una funzione tf.function , oggetti generatori diversi con la stessa dimensione di stato (la dimensione dello stato è determinata dall'algoritmo RNG) non causerà il tf.function della funzione tf.function , mentre quelli con dimensioni dello stato differenti lo faranno.

 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

Interazione con strategie di distribuzione

Esistono tre modi in cui il Generator interagisce con le strategie di distribuzione.

Creazione di generatori al di fuori delle strategie di distribuzione

Se un generatore viene creato al di fuori degli ambiti della strategia, l'accesso di tutte le repliche al generatore verrà serializzato e quindi le repliche otterranno numeri casuali diversi.

 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)

Si noti che questo utilizzo può avere problemi di prestazioni perché il dispositivo del generatore è diverso dalle repliche.

Creazione di generatori all'interno di strategie di distribuzione

La creazione di generatori all'interno di ambiti di strategia non è consentita, in quanto vi è ambiguità su come replicare un generatore (ad esempio, dovrebbe essere copiato in modo che ogni replica ottenga gli stessi numeri casuali o "divida" in modo che ogni replica ottenga numeri casuali diversi).

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

Si noti che Strategy.run eseguirà implicitamente la sua funzione argomento in un ambito di strategia:

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

Passando generatori come argomenti a Strategy.run

Se si desidera che ogni replica utilizzi il proprio generatore, è necessario creare n generatori (copiando o suddividendo), dove n è il numero di repliche, quindi passarli come argomenti 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)

GNL senza stato

L'uso di RNG senza stato è semplice. Dal momento che sono solo funzioni pure, non vi sono stati o effetti collaterali.

 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)

Ogni RNG senza stato richiede un argomento seed , che deve essere un Tensore di forma intero [2] . I risultati dell'operazione sono completamente determinati da questo seme.

algoritmi

Generale

Sia la classe tf.random.Generator che le funzioni stateless supportano l'algoritmo Philox (scritto come "philox" o tf.random.Algorithm.PHILOX ) su tutti i dispositivi.

Dispositivi diversi genereranno gli stessi numeri interi, se utilizzano lo stesso algoritmo e iniziano dallo stesso stato. Genereranno anche "quasi gli stessi" numeri in virgola mobile, sebbene possano esserci piccole discrepanze numeriche causate dai diversi modi in cui i dispositivi eseguono il calcolo in virgola mobile (ad es. Ordine di riduzione).

Dispositivi XLA

Sui dispositivi basati su XLA (come TPU e anche CPU / GPU quando XLA è abilitato) è supportato anche l'algoritmo ThreeFry (scritto come "threefry" o tf.random.Algorithm.THREEFRY ). Questo algoritmo è veloce su TPU ma lento su CPU / GPU rispetto a Philox.

Vedi il documento "Numeri casuali paralleli: facile come 1, 2, 3" per maggiori dettagli su questi algoritmi.