مشاهده در TensorFlow.org | در Google Colab اجرا شود | مشاهده منبع در GitHub | دانلود دفترچه یادداشت |
TensorFlow مجموعه ای از مولدهای اعداد شبه تصادفی (RNG) را در ماژول tf.random
می دهد. این سند توضیح می دهد که چگونه می توانید مولدهای اعداد تصادفی را کنترل کنید، و چگونه این مولدها با سایر سیستم های فرعی تنسورفلو تعامل دارند.
TensorFlow دو رویکرد برای کنترل فرآیند تولید اعداد تصادفی ارائه میکند:
از طریق استفاده صریح از اشیاء
tf.random.Generator
. هر شیء از این قبیل حالتی را حفظ می کند (درtf.Variable
) که پس از تولید هر عدد تغییر می کند.از طریق توابع تصادفی بدون حالت کاملاً کاربردی مانند
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" را ببینید.