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

乱数生成

TensorFlow.orgで見る Google Colabで実行 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.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は、このジェネレータで使用される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.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を使用して、互いに独立している(つまり、独立したストリームを生成する)ことが保証されている複数の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 )の状態を変更します。これは、 normalなどのRNGメソッドと同様です。互いに独立であることに加えて、新たな発電機( 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)

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())
 
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が配布戦略と相互作用する方法は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 `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引数が必要です。これは形状の整数Tensorである必要があります[2] 。 opの結果は、このシードによって完全に決定されます。

アルゴリズム

一般的な

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としてtf.random.Algorithm.THREEFRY )もサポートされています。このアルゴリズムは、Philoxと比較して、TPUでは高速ですが、CPU / GPUでは低速です。

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