Google I / O kembali hadir pada 18-20 Mei! Pesan tempat dan buat jadwal Anda Daftar sekarang

Pembuatan nomor acak

Lihat di TensorFlow.org Jalankan di Google Colab Lihat sumber di GitHub Unduh buku catatan

TensorFlow menyediakan satu set generator nomor pseudo-random (RNG), dalam modul tf.random . Dokumen ini menjelaskan bagaimana Anda dapat mengontrol generator nomor acak, dan bagaimana generator ini berinteraksi dengan sub-sistem tensorflow lainnya.

TensorFlow menyediakan dua pendekatan untuk mengontrol proses pembuatan nomor acak:

  1. Melalui penggunaan eksplisit objek tf.random.Generator . Setiap objek tersebut mempertahankan status (dalam tf.Variable ) yang akan diubah setelah setiap pembangkitan nomor.

  2. Melalui fungsi acak tanpa kewarganegaraan yang berfungsi tf.random.stateless_uniform seperti tf.random.stateless_uniform . Memanggil fungsi-fungsi ini dengan argumen yang sama (termasuk seed) dan pada perangkat yang sama akan selalu memberikan hasil yang sama.

Mendirikan

import tensorflow as tf

# Creates 2 virtual devices cpu:0 and cpu:1 for using distribution strategy
physical_devices = tf.config.list_physical_devices("CPU")
tf.config.experimental.set_virtual_device_configuration(
    physical_devices[0], [
        tf.config.experimental.VirtualDeviceConfiguration(),
        tf.config.experimental.VirtualDeviceConfiguration()
    ])

Kelas tf.random.Generator

Kelas tf.random.Generator digunakan jika Anda ingin setiap panggilan RNG menghasilkan hasil yang berbeda. Ini mempertahankan keadaan internal (dikelola oleh objek tf.Variable ) yang akan diperbarui setiap kali nomor acak dihasilkan. Karena status dikelola oleh tf.Variable , ia menikmati semua fasilitas yang disediakan oleh tf.Variable seperti checkpointing yang mudah, ketergantungan kontrol otomatis, dan keamanan thread.

Anda bisa mendapatkan tf.random.Generator dengan membuat objek kelas secara manual atau panggil tf.random.get_global_generator() untuk mendapatkan generator global default:

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.43842277 -0.53439844 -0.07710262]
 [ 1.5658046  -0.1012345  -0.2744976 ]], shape=(2, 3), dtype=float32)
tf.Tensor(
[[ 1.3604434  -0.3518851   3.5727046 ]
 [ 0.09712801 -1.2254813   1.346795  ]], shape=(2, 3), dtype=float32)

Ada beberapa cara untuk membuat objek generator. Yang paling mudah adalah Generator.from_seed , seperti yang ditunjukkan di atas, yang membuat generator dari seed. Benih adalah bilangan bulat non-negatif. from_seed juga mengambil argumen opsional alg yang merupakan algoritma RNG yang akan digunakan oleh generator ini:

g1 = tf.random.Generator.from_seed(1, alg='philox')
print(g1.normal(shape=[2, 3]))
tf.Tensor(
[[ 0.43842277 -0.53439844 -0.07710262]
 [ 1.5658046  -0.1012345  -0.2744976 ]], shape=(2, 3), dtype=float32)

Lihat bagian Algoritma di bawah ini untuk informasi lebih lanjut tentangnya.

Cara lain untuk membuat generator adalah dengan Generator.from_non_deterministic_state . Generator yang dibuat dengan cara ini akan mulai dari keadaan non-deterministik, tergantung pada misalnya waktu dan OS.

g = tf.random.Generator.from_non_deterministic_state()
print(g.normal(shape=[2, 3]))
tf.Tensor(
[[ 1.4167604  -2.1248727  -0.39414507]
 [-1.1975709   0.26199496 -0.31982416]], shape=(2, 3), dtype=float32)

Masih ada cara lain untuk membuat generator, seperti dari status eksplisit, yang tidak tercakup dalam panduan ini.

Saat menggunakan tf.random.get_global_generator untuk mendapatkan generator global, Anda harus berhati-hati tentang penempatan perangkat. Generator global dibuat (dari keadaan non-deterministik) saat pertama kali tf.random.get_global_generator dipanggil, dan ditempatkan pada perangkat default saat panggilan itu. Jadi, misalnya, jika situs pertama yang Anda panggil tf.random.get_global_generator berada dalam tf.device("gpu") , generator global akan ditempatkan di GPU, dan menggunakan generator global nanti dari CPU akan menimbulkan salinan GPU-ke-CPU.

Ada juga fungsi tf.random.set_global_generator untuk mengganti generator global dengan objek generator lain. Fungsi ini harus digunakan dengan hati-hati, karena generator global lama mungkin telah ditangkap oleh fungsi tf.function (sebagai referensi yang lemah), dan menggantinya akan menyebabkannya menjadi sampah yang dikumpulkan, merusak fungsi tf.function . Cara yang lebih baik untuk mengatur ulang generator global adalah dengan menggunakan salah satu fungsi "reset" seperti Generator.reset_from_seed , yang tidak akan membuat objek generator baru.

g = tf.random.Generator.from_seed(1)
print(g.normal([]))
print(g.normal([]))
g.reset_from_seed(1)
print(g.normal([]))
tf.Tensor(0.43842277, shape=(), dtype=float32)
tf.Tensor(1.6272374, shape=(), dtype=float32)
tf.Tensor(0.43842277, shape=(), dtype=float32)

Membuat aliran nomor acak independen

Dalam banyak aplikasi, seseorang memerlukan beberapa aliran angka acak independen, independen dalam arti bahwa aliran tersebut tidak akan tumpang tindih dan tidak akan memiliki korelasi yang dapat dideteksi secara statistik. Ini dicapai dengan menggunakan Generator.split untuk membuat beberapa generator yang dijamin tidak tergantung satu sama lain (yaitu menghasilkan aliran independen).

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.43842277, 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 akan mengubah status generator yang dipanggil ( g dalam contoh di atas), mirip dengan metode RNG seperti normal . Selain independen satu sama lain, generator baru ( new_gs ) juga dijamin independen dari yang lama ( g ).

Memunculkan generator baru juga berguna saat Anda ingin memastikan generator yang Anda gunakan berada di perangkat yang sama dengan komputasi lain, untuk menghindari kelebihan salinan lintas perangkat. Sebagai contoh:

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

Anda dapat melakukan pemisahan secara rekursif, memanggil split pada generator yang split . Tidak ada batasan (kecuali bilangan bulat overflow) pada kedalaman rekursi.

Interaksi dengan tf.function

tf.random.Generator mematuhi aturan yang sama seperti tf.Variable saat digunakan dengan tf.function . Ini mencakup tiga aspek.

Membuat generator di luar tf.function

tf.function dapat menggunakan generator yang dibuat di luarnya.

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

Pengguna perlu memastikan bahwa objek generator masih hidup (bukan sampah yang dikumpulkan) saat fungsi tersebut dipanggil.

Membuat generator di dalam tf.function

Pembuatan generator di dalam fungsi tf.function dapat terjadi selama menjalankan fungsi pertama kali.

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

Meneruskan generator sebagai argumen ke tf.function

Saat digunakan sebagai argumen untuk fungsi tf.function , objek generator yang berbeda dengan ukuran status yang sama (ukuran status ditentukan oleh algoritme RNG) tidak akan menyebabkan penelusuran ulang tf.function , sedangkan objek dengan ukuran status berbeda akan.

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

Interaksi dengan strategi distribusi

Ada tiga cara Generator berinteraksi dengan strategi distribusi.

Membuat generator di luar strategi distribusi

Jika generator dibuat di luar cakupan strategi, semua akses replika ke generator akan diserialkan, dan karenanya replika akan mendapatkan nomor acak yang berbeda.

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

Perhatikan bahwa penggunaan ini mungkin memiliki masalah kinerja karena perangkat generator berbeda dari replika.

Membuat generator di dalam strategi distribusi

Dilarang membuat generator di dalam lingkup strategi, karena ada ketidakjelasan tentang cara mereplikasi generator (misalnya, apakah generator harus disalin sehingga setiap replika mendapatkan nomor acak yang sama, atau 'dipisah' sehingga setiap replika mendapatkan nomor acak yang berbeda).

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

Perhatikan bahwa Strategy.run akan menjalankan fungsi argumennya dalam lingkup strategi secara implisit:

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_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 572, in wrapper
    return func(*args, **kwargs)
  File "<ipython-input-1-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 453, 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 375, in __init__
    trainable=False)
  File "/tmpfs/src/tf_docs_env/lib/python3.6/site-packages/tensorflow/python/ops/stateful_random_ops.py", line 390, 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).

Meneruskan generator sebagai argumen ke Strategy.run

Jika Anda ingin setiap replika menggunakan generatornya sendiri, Anda perlu membuat n generator (baik dengan menyalin atau memisahkan), di mana n adalah jumlah replika, lalu meneruskannya sebagai argumen ke 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.4355794, shape=(), dtype=float32)
tf.Tensor(0.78352904, shape=(), dtype=float32)

RNG tanpa negara

Penggunaan RNG tanpa kewarganegaraan itu sederhana. Karena mereka hanyalah fungsi murni, tidak ada keadaan atau efek samping yang terlibat.

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.30159    -0.95385665]], shape=(2, 3), dtype=float32)
tf.Tensor(
[[ 0.5441101   0.20738031  0.07356433]
 [ 0.04643455 -1.30159    -0.95385665]], shape=(2, 3), dtype=float32)

Setiap RNG stateless membutuhkan argumen seed , yang harus berupa Tensor integer bentuk [2] . Hasil operasi sepenuhnya ditentukan oleh benih ini.

Algoritma

Umum

Baik kelas tf.random.Generator dan fungsi stateless mendukung algoritme Philox (ditulis sebagai "philox" atau tf.random.Algorithm.PHILOX ) di semua perangkat.

Perangkat yang berbeda akan menghasilkan bilangan bulat yang sama, jika menggunakan algoritma yang sama dan memulai dari keadaan yang sama. Mereka juga akan menghasilkan angka float-point yang "hampir sama", meskipun mungkin ada sedikit perbedaan numerik yang disebabkan oleh berbagai cara perangkat melakukan komputasi titik-mengambang (misalnya urutan reduksi).

Perangkat XLA

Pada perangkat yang digerakkan XLA (seperti TPU, dan juga CPU / GPU saat XLA diaktifkan), algoritme ThreeFry (ditulis sebagai "threefry" atau tf.random.Algorithm.THREEFRY ) juga didukung. Algoritme ini cepat pada TPU tetapi lambat pada CPU / GPU dibandingkan dengan Philox.

Lihat makalah 'Nomor Acak Paralel: Semudah 1, 2, 3' untuk detail lebih lanjut tentang algoritma ini.