تولید تعداد تصادفی

مشاهده در TensorFlow.org در Google Colab اجرا شود مشاهده منبع در GitHub دانلود دفترچه یادداشت

TensorFlow مجموعه ای از مولدهای اعداد شبه تصادفی (RNG) را در ماژول tf.random می دهد. این سند توضیح می دهد که چگونه می توانید مولدهای اعداد تصادفی را کنترل کنید، و چگونه این مولدها با سایر سیستم های فرعی تنسورفلو تعامل دارند.

TensorFlow دو رویکرد برای کنترل فرآیند تولید اعداد تصادفی ارائه می‌کند:

  1. از طریق استفاده صریح از اشیاء tf.random.Generator . هر شیء از این قبیل حالتی را حفظ می کند (در tf.Variable ) که پس از تولید هر عدد تغییر می کند.

  2. از طریق توابع تصادفی بدون حالت کاملاً کاربردی مانند tf.random.stateless_uniform . فراخوانی این توابع با آرگومان‌های یکسان (که شامل seed می‌شود) و روی یک دستگاه، همیشه نتایج یکسانی را ایجاد می‌کند.

برپایی

import tensorflow as tf

# Creates some virtual devices (cpu:0, cpu:1, etc.) 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(),
        tf.config.experimental.VirtualDeviceConfiguration()
    ])

کلاس tf.random.Generator

کلاس tf.random.Generator در مواردی استفاده می شود که می خواهید هر تماس RNG نتایج متفاوتی ایجاد کند. یک حالت داخلی (که توسط یک شی 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.43842277 -0.53439844 -0.07710262]
 [ 1.5658046  -0.1012345  -0.2744976 ]], shape=(2, 3), dtype=float32)
tf.Tensor(
[[-0.5496427   0.24263908 -1.1436267 ]
 [ 1.861458   -0.6756685  -0.9900103 ]], shape=(2, 3), dtype=float32)

راه های متعددی برای ایجاد یک شی مولد وجود دارد. ساده ترین Generator.from_seed است، همانطور که در بالا نشان داده شده است، که یک مولد از یک دانه ایجاد می کند. دانه هر عدد صحیح غیر منفی است. from_seed همچنین یک الگوریتم آرگومان اختیاری می گیرد که الگوریتم alg است که توسط این مولد استفاده می شود:

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)

برای اطلاعات بیشتر در مورد آن به بخش الگوریتم ها در زیر مراجعه کنید.

راه دیگر برای ایجاد یک ژنراتور با Generator.from_non_deterministic_state است. یک ژنراتور ایجاد شده به این روش، بسته به زمان و سیستم عامل، از حالت غیر قطعی شروع می شود.

g = tf.random.Generator.from_non_deterministic_state()
print(g.normal(shape=[2, 3]))
tf.Tensor(
[[-0.9078738   0.11009752  1.037219  ]
 [ 0.661036    0.4169741   1.4539026 ]], 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.43842277, shape=(), dtype=float32)
tf.Tensor(1.6272374, shape=(), dtype=float32)
tf.Tensor(0.43842277, 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.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 وضعیت ژنراتوری را که در آن فراخوانی می شود تغییر می دهد ( 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(0.4142675, shape=(), dtype=float32)

می توانید تقسیم را به صورت بازگشتی انجام دهید، و در ژنراتورهای تقسیم شده، split را فراخوانی کنید. هیچ محدودیتی (منع سرریز اعداد صحیح) در عمق بازگشت وجود ندارد.

تعامل با tf.function

tf.random.Generator هنگام استفاده با tf.function از قوانین tf.Variable پیروی می کند. این شامل سه جنبه است.

ایجاد ژنراتورهای خارج از tf.function

tf.function می تواند از یک ژنراتور ایجاد شده در خارج از آن استفاده کند.

g = tf.random.Generator.from_seed(1)
@tf.function
def foo():
  return g.normal([])
print(foo())
tf.Tensor(0.43842277, 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.43842277, shape=(), dtype=float32)
tf.Tensor(1.6272374, shape=(), dtype=float32)

انتقال ژنراتورها به عنوان آرگومان به tf.function

هنگامی که به عنوان یک آرگومان برای یک 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)
2

توجه داشته باشید که این رفتار ردیابی مجدد با tf.Variable سازگار است:

num_traces = 0
@tf.function
def foo(v):
  global num_traces
  num_traces += 1
  return v.read_value()
foo(tf.Variable(1))
foo(tf.Variable(2))
print(num_traces)
2

تعامل با استراتژی های توزیع

دو روش وجود دارد که در آن 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():
  g = tf.random.Generator.from_seed(1)
  print(strat.run(lambda: g.normal([])))
  print(strat.run(lambda: g.normal([])))
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.
PerReplica:{
  0: tf.Tensor(-0.87930447, shape=(), dtype=float32),
  1: tf.Tensor(0.020661574, shape=(), dtype=float32)
}
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.
PerReplica:{
  0: tf.Tensor(-1.5822568, shape=(), dtype=float32),
  1: tf.Tensor(0.77539235, shape=(), dtype=float32)
}

اگر مولد دانه‌بندی شده باشد (مثلاً توسط Generator.from_seed ایجاد شده است)، اعداد تصادفی توسط دانه تعیین می‌شوند، حتی اگر کپی‌های مختلف اعداد متفاوت و نامرتبط دریافت کنند. می توان یک عدد تصادفی تولید شده بر روی یک ماکت را به عنوان هش شناسه ماکت و یک عدد تصادفی "اولیه" در نظر گرفت که برای همه ماکت ها مشترک است. از این رو، کل سیستم هنوز قطعی است.

tf.random.Generator همچنین می تواند در داخل Strategy.run ایجاد شود:

strat = tf.distribute.MirroredStrategy(devices=["cpu:0", "cpu:1"])
with strat.scope():
  def f():
    g = tf.random.Generator.from_seed(1)
    a = g.normal([])
    b = g.normal([])
    return tf.stack([a, b])
  print(strat.run(f))
  print(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.
PerReplica:{
  0: tf.Tensor([-0.87930447 -1.5822568 ], shape=(2,), dtype=float32),
  1: tf.Tensor([0.02066157 0.77539235], shape=(2,), dtype=float32)
}
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.
PerReplica:{
  0: tf.Tensor([-0.87930447 -1.5822568 ], shape=(2,), dtype=float32),
  1: tf.Tensor([0.02066157 0.77539235], shape=(2,), dtype=float32)
}

ما دیگر ارسال tf.random.Generator را به عنوان آرگومان به Strategy.run توصیه نمی کنیم، زیرا Strategy.run عموماً انتظار دارد که آرگومان ها تانسور باشند، نه مولد.

صرفه جویی در ژنراتورها

به طور کلی برای ذخیره یا سریال سازی، می توانید یک tf.random.Generator را به همان روشی که با tf.Variable یا tf.Module (یا زیر کلاس های آن) رفتار می کنید، مدیریت کنید. در TF دو مکانیسم برای سریال سازی وجود دارد: Checkpoint و SavedModel .

ایست بازرسی

ژنراتورها را می توان آزادانه با استفاده از tf.train.Checkpoint ذخیره و بازیابی کرد. جریان اعداد تصادفی از نقطه بازیابی مانند جریان از نقطه ذخیره خواهد بود.

filename = "./checkpoint"
g = tf.random.Generator.from_seed(1)
cp = tf.train.Checkpoint(generator=g)
print(g.normal([]))
tf.Tensor(0.43842277, shape=(), dtype=float32)
cp.write(filename)
print("RNG stream from saving point:")
print(g.normal([]))
print(g.normal([]))
RNG stream from saving point:
tf.Tensor(1.6272374, shape=(), dtype=float32)
tf.Tensor(1.6307176, shape=(), dtype=float32)
cp.restore(filename)
print("RNG stream from restoring point:")
print(g.normal([]))
print(g.normal([]))
RNG stream from restoring point:
tf.Tensor(1.6272374, shape=(), dtype=float32)
tf.Tensor(1.6307176, shape=(), dtype=float32)

همچنین می توانید در یک استراتژی توزیع ذخیره و بازیابی کنید:

filename = "./checkpoint"
strat = tf.distribute.MirroredStrategy(devices=["cpu:0", "cpu:1"])
with strat.scope():
  g = tf.random.Generator.from_seed(1)
  cp = tf.train.Checkpoint(my_generator=g)
  print(strat.run(lambda: g.normal([])))
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')
PerReplica:{
  0: tf.Tensor(-0.87930447, shape=(), dtype=float32),
  1: tf.Tensor(0.020661574, shape=(), dtype=float32)
}
with strat.scope():
  cp.write(filename)
  print("RNG stream from saving point:")
  print(strat.run(lambda: g.normal([])))
  print(strat.run(lambda: g.normal([])))
RNG stream from saving point:
PerReplica:{
  0: tf.Tensor(-1.5822568, shape=(), dtype=float32),
  1: tf.Tensor(0.77539235, shape=(), dtype=float32)
}
PerReplica:{
  0: tf.Tensor(-0.5039703, shape=(), dtype=float32),
  1: tf.Tensor(0.1251838, shape=(), dtype=float32)
}
with strat.scope():
  cp.restore(filename)
  print("RNG stream from restoring point:")
  print(strat.run(lambda: g.normal([])))
  print(strat.run(lambda: g.normal([])))
RNG stream from restoring point:
PerReplica:{
  0: tf.Tensor(-1.5822568, shape=(), dtype=float32),
  1: tf.Tensor(0.77539235, shape=(), dtype=float32)
}
PerReplica:{
  0: tf.Tensor(-0.5039703, shape=(), dtype=float32),
  1: tf.Tensor(0.1251838, shape=(), dtype=float32)
}

قبل از ذخیره، باید مطمئن شوید که کپی‌ها در تاریخچه تماس RNG خود از هم جدا نمی‌شوند (مثلاً یک ماکت یک تماس RNG برقرار می‌کند در حالی که دیگری دو تماس RNG برقرار می‌کند). در غیر این صورت، حالت‌های RNG داخلی آن‌ها از هم جدا می‌شود و tf.train.Checkpoint (که فقط حالت نسخه اول را ذخیره می‌کند) همه کپی‌ها را به درستی بازیابی نمی‌کند.

شما همچنین می توانید یک چک پوینت ذخیره شده را به یک استراتژی توزیع متفاوت با تعداد متفاوتی از کپی بازیابی کنید. از آنجا که یک شی tf.random.Generator ایجاد شده در یک استراتژی فقط می تواند در همان استراتژی استفاده شود، برای بازگرداندن به یک استراتژی متفاوت، باید یک tf.random.Generator جدید در استراتژی هدف و یک tf.train.Checkpoint برای آن، همانطور که در این مثال نشان داده شده است:

filename = "./checkpoint"
strat1 = tf.distribute.MirroredStrategy(devices=["cpu:0", "cpu:1"])
with strat1.scope():
  g1 = tf.random.Generator.from_seed(1)
  cp1 = tf.train.Checkpoint(my_generator=g1)
  print(strat1.run(lambda: g1.normal([])))
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')
PerReplica:{
  0: tf.Tensor(-0.87930447, shape=(), dtype=float32),
  1: tf.Tensor(0.020661574, shape=(), dtype=float32)
}
with strat1.scope():
  cp1.write(filename)
  print("RNG stream from saving point:")
  print(strat1.run(lambda: g1.normal([])))
  print(strat1.run(lambda: g1.normal([])))
RNG stream from saving point:
PerReplica:{
  0: tf.Tensor(-1.5822568, shape=(), dtype=float32),
  1: tf.Tensor(0.77539235, shape=(), dtype=float32)
}
PerReplica:{
  0: tf.Tensor(-0.5039703, shape=(), dtype=float32),
  1: tf.Tensor(0.1251838, shape=(), dtype=float32)
}
strat2 = tf.distribute.MirroredStrategy(devices=["cpu:0", "cpu:1", "cpu:2"])
with strat2.scope():
  g2 = tf.random.Generator.from_seed(1)
  cp2 = tf.train.Checkpoint(my_generator=g2)
  cp2.restore(filename)
  print("RNG stream from restoring point:")
  print(strat2.run(lambda: g2.normal([])))
  print(strat2.run(lambda: g2.normal([])))
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', '/job:localhost/replica:0/task:0/device:CPU:2')
RNG stream from restoring point:
PerReplica:{
  0: tf.Tensor(-1.5822568, shape=(), dtype=float32),
  1: tf.Tensor(0.77539235, shape=(), dtype=float32),
  2: tf.Tensor(0.6851049, shape=(), dtype=float32)
}
PerReplica:{
  0: tf.Tensor(-0.5039703, shape=(), dtype=float32),
  1: tf.Tensor(0.1251838, shape=(), dtype=float32),
  2: tf.Tensor(-0.58519536, shape=(), dtype=float32)
}

اگرچه g1 و cp1 اشیاء متفاوتی از g2 و cp2 هستند، اما از طریق filename رایج چک پوینت و نام شی my_generator به هم مرتبط می شوند. تکرارهای همپوشانی بین استراتژی ها (مانند cpu:0 و cpu:1 در بالا) جریان های RNG آنها را به درستی مانند نمونه های قبلی بازیابی می کند. این ضمانت مواردی را که یک ژنراتور در محدوده استراتژی ذخیره شده و خارج از محدوده استراتژی بازیابی می‌شود را پوشش نمی‌دهد، زیرا دستگاهی خارج از استراتژی‌ها با هر کپی در یک استراتژی متفاوت تلقی می‌شود.

SavedModel

tf.random.Generator را می توان در SavedModel ذخیره کرد. مولد را می توان در محدوده استراتژی ایجاد کرد. پس انداز همچنین می تواند در محدوده استراتژی اتفاق بیفتد.

filename = "./saved_model"

class MyModule(tf.Module):

  def __init__(self):
    super(MyModule, self).__init__()
    self.g = tf.random.Generator.from_seed(0)

  @tf.function
  def __call__(self):
    return self.g.normal([])

  @tf.function
  def state(self):
    return self.g.state

strat = tf.distribute.MirroredStrategy(devices=["cpu:0", "cpu:1"])
with strat.scope():
  m = MyModule()
  print(strat.run(m))
  print("state:", m.state())
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')
PerReplica:{
  0: tf.Tensor(-1.4154755, shape=(), dtype=float32),
  1: tf.Tensor(-0.113884404, shape=(), dtype=float32)
}
state: tf.Tensor([256   0   0], shape=(3,), dtype=int64)
with strat.scope():
  tf.saved_model.save(m, filename)
  print("RNG stream from saving point:")
  print(strat.run(m))
  print("state:", m.state())
  print(strat.run(m))
  print("state:", m.state())
INFO:tensorflow:Assets written to: ./saved_model/assets
RNG stream from saving point:
PerReplica:{
  0: tf.Tensor(-0.68758255, shape=(), dtype=float32),
  1: tf.Tensor(0.8084062, shape=(), dtype=float32)
}
state: tf.Tensor([512   0   0], shape=(3,), dtype=int64)
PerReplica:{
  0: tf.Tensor(-0.27342677, shape=(), dtype=float32),
  1: tf.Tensor(-0.53093255, shape=(), dtype=float32)
}
state: tf.Tensor([768   0   0], shape=(3,), dtype=int64)
2021-09-22 20:45:46.222281: W tensorflow/python/util/util.cc:348] Sets are not currently considered sequences, but this may change in the future, so consider avoiding using them.
imported = tf.saved_model.load(filename)
print("RNG stream from loading point:")
print("state:", imported.state())
print(imported())
print("state:", imported.state())
print(imported())
print("state:", imported.state())
RNG stream from loading point:
state: tf.Tensor([256   0   0], shape=(3,), dtype=int64)
tf.Tensor(-1.0359411, shape=(), dtype=float32)
state: tf.Tensor([512   0   0], shape=(3,), dtype=int64)
tf.Tensor(-0.06425078, shape=(), dtype=float32)
state: tf.Tensor([768   0   0], shape=(3,), dtype=int64)

بارگیری SavedModel حاوی tf.random.Generator در استراتژی توزیع توصیه نمی‌شود زیرا همگی نسخه‌های تکراری جریان اعداد تصادفی یکسانی را ایجاد می‌کنند (که به این دلیل است که شناسه replica در نمودار SavedModel ثابت است).

بارگذاری یک tf.random.Generator توزیع شده (مولد ایجاد شده در یک استراتژی توزیع) در یک محیط غیر استراتژی، مانند مثال بالا، یک هشدار نیز دارد. حالت RNG به درستی بازیابی می‌شود، اما اعداد تصادفی تولید شده با مولد اصلی در استراتژی آن متفاوت خواهند بود (دوباره به این دلیل که دستگاهی خارج از استراتژی‌ها متفاوت از هر کپی در استراتژی تلقی می‌شود).

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

هر RNG بدون حالت نیاز به آرگومان seed دارد که باید یک تانسور عدد صحیح شکل [2] باشد. نتایج عملیات به طور کامل توسط این دانه تعیین می شود.

الگوریتم RNG مورد استفاده توسط RNG های بدون حالت وابسته به دستگاه است، به این معنی که عملیات مشابهی که روی دستگاه دیگری اجرا می شود ممکن است خروجی های متفاوتی تولید کند.

الگوریتم ها

عمومی

هم کلاس tf.random.Generator و هم توابع stateless حالت از الگوریتم Philox (نوشته شده به عنوان "philox" یا tf.random.Algorithm.PHILOX ) در همه دستگاه ها پشتیبانی می کنند.

اگر از الگوریتم یکسانی استفاده شود و از یک حالت شروع شود، دستگاه‌های مختلف اعداد صحیح یکسانی را تولید می‌کنند. آنها همچنین اعداد ممیز شناور «تقریباً یکسان» را تولید خواهند کرد، اگرچه ممکن است اختلافات عددی کوچکی به دلیل روش‌های متفاوتی که دستگاه‌ها محاسبه ممیز شناور را انجام می‌دهند (مثلاً ترتیب کاهش) وجود داشته باشد.

دستگاه های XLA

در دستگاه‌های مبتنی بر XLA (مانند TPU و همچنین CPU/GPU هنگامی که XLA فعال است) الگوریتم ThreeFry (نوشته شده به عنوان "threefry" یا tf.random.Algorithm.THREEFRY ) نیز پشتیبانی می‌شود. این الگوریتم در TPU سریع است اما در CPU/GPU در مقایسه با Philox کند است.

برای جزئیات بیشتر در مورد این الگوریتم‌ها، مقاله "اعداد تصادفی موازی: به آسانی 1، 2، 3" را ببینید.