此页面由 Cloud Translation API 翻译。
Switch to English

随机数生成

在TensorFlow.org上查看 在Google Colab中运行 在GitHub上查看源代码 下载笔记本

TensorFlow在tf.random模块中提供了一组伪随机数生成器(RNG)。本文档描述了如何控制随机数生成器以及这些生成器如何与其他张量流子系统交互。

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

如果您希望每个RNG调用产生不同的结果,则使用tf.random.Generator类。它维护一个内部状态(由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 。以这种方式创建的生成器将从不确定的状态开始,具体取决于例如时间和OS。

 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使用全局生成器将产生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)

您可以递归splitsplit生成器上调用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)

每个无状态RNG都需要一个seed参数,该参数必须是形状[2]的整数张量。操作的结果完全由该种子决定。

演算法

一般

tf.random.Generator类和stateless函数都支持所有设备上的Philox算法(写为"philox"tf.random.Algorithm.PHILOX )。

如果使用相同的算法并从相同的状态开始,则不同的设备将生成相同的整数。它们也将生成“几乎相同”的浮点数,尽管由于设备执行浮点计算的方式不同(例如归约顺序)可能会导致较小的数值差异。

XLA设备

在XLA驱动的设备(例如TPU,以及启用XLA的CPU / GPU)上,还支持ThreeFry算法(写为"threefry"tf.random.Algorithm.THREEFRY )。与Philox相比,该算法在TPU上速度较快,但在CPU / GPU上速度较慢。

有关这些算法的更多详细信息请参见论文“并行随机数:1、2、3一样容易”