このページは Cloud Translation API によって翻訳されました。
Switch to English

乱数の生成

TensorFlow.orgで表示 GoogleColabで実行 GitHubでソースを表示ノートブックをダウンロード

TensorFlowは、 tf.randomモジュールで一連の疑似乱数ジェネレーター(RNG)をtf.randomます。このドキュメントでは、乱数ジェネレーターを制御する方法と、これらのジェネレーターが他のテンソルフローサブシステムと相互作用する方法について説明します。

TensorFlowは、乱数生成プロセスを制御するための2つのアプローチを提供します。

  1. tf.random.Generatorオブジェクトを明示的に使用する。このような各オブジェクトは、数値が生成されるたびに変更される状態( tf.Variable )を維持します。

  2. tf.random.stateless_uniformような純粋に機能的なステートレスランダム関数を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クラス

tf.random.Generatorクラスは、各RNG呼び出しで異なる結果を生成する場合に使用されます。乱数が生成されるたびに更新される内部状態( tf.Variableオブジェクトによって管理される)をtf.Variableます。状態は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.9323887   0.3864468   1.5209497 ]
 [ 0.54473144 -0.6031506  -0.47044003]], shape=(2, 3), dtype=float32)

ジェネレータオブジェクトを作成する方法は複数あります。最も簡単であるGenerator.from_seed 、上記のように、それが種子から発生を作成します。シードは、負でない整数です。 from_seedは、このジェネレーターによって使用されるRNGアルゴリズムであるオプションの引数algも取ります。

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を使用することGenerator.from_non_deterministic_state 。この方法で作成されたジェネレーターは、時間やOSなどに応じて、非決定論的な状態から開始されます。

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)

明示的な状態など、ジェネレーターを作成する方法は他にもありますが、このガイドでは取り上げていません。

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などの「リセット」関数の1つを使用することです。これは、新しいジェネレーターオブジェクトを作成しません。

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を使用して、互いに独立していることが保証されている(つまり、独立したストリームを生成する)複数の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は、 normalなどのRNGメソッドと同様に、呼び出されたジェネレーター(上記の例ではg )の状態を変更しnormal 。互いに独立していることに加えて、新しいジェネレーター( new_gs )も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(0.10396493, shape=(), dtype=float32)

splitれたジェネレーターでsplitを呼び出して、再帰的に分割を行うことができます。再帰の深さに制限はありません(整数オーバーフローを除く)。

tf.function相互作用

tf.random.Generator同じルールに従うtf.Variableで使用tf.function 。これには3つの側面が含まれます。

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())
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が配布戦略と相互作用する方法は3つあります。

流通戦略外の発電機の作成

ジェネレーターが戦略スコープ外で作成された場合、ジェネレーターへのすべてのレプリカのアクセスがシリアル化されるため、レプリカは異なる乱数を取得します。

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)

ジェネレーターのデバイスはレプリカとは異なるため、この使用法ではパフォーマンスの問題が発生する可能性があることに注意してください。

流通戦略内での発電機の作成

ジェネレーターを複製する方法にあいまいさがあるため、戦略スコープ内にジェネレーターを作成することは許可されていません(たとえば、各レプリカが同じ乱数を取得するようにコピーするか、各レプリカが異なる乱数を取得するように「分割」する必要があります)。

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

ジェネレーターを引数として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 `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)

ステートレス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引数が必要です。 seed引数は形状の整数テンソルである必要があります[2] 。作戦の結果はこの種によって完全に決定されます。

アルゴリズム

一般

tf.random.Generatorクラスとstateless関数はどちらも、すべてのデバイスでPhiloxアルゴリズム( "philox"またはtf.random.Algorithm.PHILOX )をtf.random.Algorithm.PHILOXしています。

同じアルゴリズムを使用し、同じ状態から開始する場合、異なるデバイスは同じ整数を生成します。また、デバイスが浮動小数点計算を実行するさまざまな方法(たとえば、削減次数)によって引き起こされる小さな数値の不一致がある場合もありますが、「ほぼ同じ」浮動小数点数を生成します。

XLAデバイス

XLA駆動型デバイス(TPU、XLAが有効な場合はCPU / GPUなど)では、ThreeFryアルゴリズム( "threefry"またはtf.random.Algorithm.THREEFRY )もサポートされます。このアルゴリズムは、Philoxと比較して、TPUでは高速ですが、CPU / GPUでは低速です。

これらのアルゴリズムの詳細については論文「Parallel Random Numbers:As Easy as 1、2、3」を参照してください。