Esta página foi traduzida pela API Cloud Translation.
Switch to English

Geração de número aleatório

Ver no TensorFlow.org Executar no Google Colab Ver fonte no GitHub Baixar caderno

O TensorFlow fornece um conjunto de geradores de números pseudoaleatórios (RNG), no módulo tf.random . Este documento descreve como você pode controlar os geradores de números aleatórios e como esses geradores interagem com outros subsistemas de tensorflow.

O TensorFlow oferece duas abordagens para controlar o processo de geração de números aleatórios:

  1. Por meio do uso explícito de objetos tf.random.Generator . Cada um desses objetos mantém um estado (em tf.Variable ) que será alterado após cada geração de número.

  2. Por meio de funções aleatórias sem estado puramente funcionais, como tf.random.stateless_uniform . Chamar essas funções com os mesmos argumentos (que incluem a semente) e no mesmo dispositivo sempre produzirá os mesmos resultados.

Configuração

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

A classe tf.random.Generator

A classe tf.random.Generator é usada nos casos em que você deseja que cada chamada RNG produza resultados diferentes. Ele mantém um estado interno (gerenciado por um objeto tf.Variable ) que será atualizado toda vez que números aleatórios forem gerados. Como o estado é gerenciado por tf.Variable , ele desfruta de todas as facilidades fornecidas por tf.Variable , como fácil verificação, dependência de controle automático e segurança de thread.

Você pode obter um tf.random.Generator criando manualmente um objeto da classe ou chamar tf.random.get_global_generator() para obter o gerador global padrão:

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.9323887   0.3864468   1.5209497 ]
 [ 0.54473144 -0.6031506  -0.47044003]], shape=(2, 3), dtype=float32)

Existem várias maneiras de criar um objeto gerador. O mais fácil é Generator.from_seed , como mostrado acima, que cria um gerador a partir de uma semente. Uma semente é qualquer número inteiro não negativo. from_seed também leva um argumento opcional alg que é o algoritmo RNG que será usado por este gerador:

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 a seção Algoritmos abaixo para obter mais informações sobre isso.

Outra forma de criar um gerador é com Generator.from_non_deterministic_state . Um gerador criado desta forma iniciará de um estado não determinístico, dependendo, por exemplo, do tempo e do sistema operacional.

g = tf.random.Generator.from_non_deterministic_state()
print(g.normal(shape=[2, 3]))
tf.Tensor(
[[-1.3158257   2.4625542   1.3490729 ]
 [ 0.77426016 -2.261468   -0.4887435 ]], shape=(2, 3), dtype=float32)

Existem ainda outras maneiras de criar geradores, como a partir de estados explícitos, que não são cobertos por este guia.

Ao usar tf.random.get_global_generator para obter o gerador global, você precisa ter cuidado com o posicionamento do dispositivo. O gerador global é criado (a partir de um estado não determinístico) na primeira vez que tf.random.get_global_generator é chamado e colocado no dispositivo padrão nessa chamada. Então, por exemplo, se o primeiro site que você chamar tf.random.get_global_generator estiver dentro de um tf.device("gpu") , o gerador global será colocado na GPU, e usando o gerador global mais tarde a partir da CPU incorrer em uma cópia de GPU para CPU.

Também existe uma função tf.random.set_global_generator para substituir o gerador global por outro objeto gerador. Esta função deve ser usada com cautela, porque o gerador global antigo pode ter sido capturado por um tf.function (como uma referência fraca), e substituí-lo fará com que seja coletado como lixo, quebrando o tf.function . A melhor maneira de redefinir o gerador global é usar uma das funções de "redefinição", como Generator.reset_from_seed , que não criará novos objetos do gerador.

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)

Criação de fluxos independentes de números aleatórios

Em muitas aplicações, são necessários vários fluxos de números aleatórios independentes, independentes no sentido de que eles não se sobreporão e não terão nenhuma correlação detectável estatisticamente. Isso é obtido usando Generator.split para criar vários geradores que são garantidamente independentes uns dos outros (ou seja, gerando fluxos independentes).

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 mudará o estado do gerador no qual é chamado ( g no exemplo acima), semelhante a um método RNG, como normal . Além de serem independentes uns dos outros, os novos geradores ( new_gs ) também têm garantia de serem independentes do antigo ( g ).

Gerar novos geradores também é útil quando você deseja ter certeza de que o gerador usado está no mesmo dispositivo que outros cálculos, para evitar a sobrecarga da cópia entre dispositivos. Por exemplo:

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

Você pode fazer a divisão recursivamente, chamando a split em geradores split . Não há limites (exceto estouro de inteiro) na profundidade das recursões.

Interação com tf.function

tf.random.Generator obedece às mesmas regras que tf.Variable quando usado com tf.function . Isso inclui três aspectos.

Criação de geradores fora de tf.function

tf.function pode usar um gerador criado fora dele.

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

O usuário precisa ter certeza de que o objeto gerador ainda está ativo (não coletado como lixo) quando a função é chamada.

Criação de geradores dentro de tf.function

A criação de geradores dentro de uma função tf.function só pode acontecer durante a primeira execução da função.

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())
tf.Tensor(0.43842274, shape=(), dtype=float32)
tf.Tensor(1.6272374, shape=(), dtype=float32)

Passando geradores como argumentos para tf.function

Quando usados ​​como um argumento para uma função tf.function , objetos geradores diferentes com o mesmo tamanho de estado (o tamanho do estado é determinado pelo algoritmo RNG) não causam o tf.function da função tf.function , enquanto aqueles com tamanhos de estado diferentes o 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

Interação com estratégias de distribuição

Existem três maneiras pelas quais o Generator interage com as estratégias de distribuição.

Criação de geradores fora das estratégias de distribuição

Se um gerador for criado fora dos escopos da estratégia, o acesso de todas as réplicas ao gerador será serializado e, portanto, as réplicas receberão diferentes números aleatórios.

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 `experimental_run_v2` inside a tf.function to get the best performance.
tf.Tensor(0.43842274, shape=(), dtype=float32)
tf.Tensor(1.6272374, shape=(), dtype=float32)

Observe que esse uso pode ter problemas de desempenho porque o dispositivo do gerador é diferente das réplicas.

Criação de geradores dentro de estratégias de distribuição

Criar geradores dentro de escopos de estratégia não é permitido, porque há ambigüidade sobre como replicar um gerador (por exemplo, deve ser copiado para que cada réplica receba os mesmos números aleatórios, ou 'dividido' para que cada réplica receba diferentes números aleatórios).

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

Observe que Strategy.run executará sua função de argumento em um escopo de estratégia implicitamente:

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 `experimental_run_v2` 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_run.py", line 323, 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 275, 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 441, 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 363, in __init__
    trainable=False)
  File "/tmpfs/src/tf_docs_env/lib/python3.6/site-packages/tensorflow/python/ops/stateful_random_ops.py", line 378, 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 geradores como argumentos para Strategy.run

Se quiser que cada réplica use seu próprio gerador, você precisa fazer n geradores (copiando ou dividindo), onde n é o número de réplicas e, em seguida, passá-los como argumentos para 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 `experimental_run_v2` 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 `experimental_run_v2` inside a tf.function to get the best performance.
tf.Tensor(-0.42417043, shape=(), dtype=float32)
tf.Tensor(0.08118553, shape=(), dtype=float32)

RNGs sem estado

O uso de RNGs sem estado é simples. Como são apenas funções puras, não há estado ou efeito colateral envolvido.

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)

Todo RNG sem estado requer um argumento seed , que precisa ser um Tensor inteiro de forma [2] . Os resultados da operação são totalmente determinados por esta semente.

Algoritmos

Geral

Tanto a classe tf.random.Generator quanto as funções stateless suportam o algoritmo Philox (escrito como "philox" ou tf.random.Algorithm.PHILOX ) em todos os dispositivos.

Dispositivos diferentes gerarão os mesmos números inteiros, se usarem o mesmo algoritmo e partirem do mesmo estado. Eles também irão gerar "quase os mesmos" números de ponto flutuante, embora possa haver pequenas discrepâncias numéricas causadas pelas diferentes maneiras como os dispositivos realizam o cálculo de ponto flutuante (por exemplo, ordem de redução).

Dispositivos XLA

Em dispositivos baseados em XLA (como TPU e também CPU / GPU quando o XLA está habilitado), o algoritmo ThreeFry (escrito como "threefry" ou tf.random.Algorithm.THREEFRY ) também é compatível. Este algoritmo é rápido na TPU, mas lento na CPU / GPU em comparação com o Philox.

Consulte o artigo 'Números aleatórios paralelos: tão fácil quanto 1, 2, 3' para obter mais detalhes sobre esses algoritmos.