توليد عدد عشوائي

عرض على TensorFlow.org تشغيل في Google Colab عرض المصدر على جيثب تحميل دفتر

يوفر TensorFlow مجموعة من مولدات الأرقام العشوائية الزائفة (RNG) ، في وحدة tf.random . يصف هذا المستند كيف يمكنك التحكم في مولدات الأرقام العشوائية ، وكيف تتفاعل هذه المولدات مع أنظمة tensorflow الفرعية الأخرى.

يوفر TensorFlow طريقتين للتحكم في عملية توليد الأرقام العشوائية:

  1. من خلال الاستخدام الصريح للكائنات tf.random.Generator . يحتفظ كل كائن بحالة (في tf.Variable ) والتي سيتم تغييرها بعد كل إنشاء رقم.

  2. من خلال وظائف عشوائية عديمة الحالة وظيفية بحتة مثل tf.random.stateless_uniform . سيؤدي استدعاء هذه الوظائف باستخدام نفس الوسيطات (التي تشمل البداية) وعلى نفس الجهاز دائمًا إلى الحصول على نفس النتائج.

يثبت

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-to-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.Variable عند استخدامه مع tf.function . وهذا يشمل ثلاثة جوانب.

إنشاء مولدات خارج 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 بشكل صحيح كما في الأمثلة السابقة. لا يغطي هذا الضمان الحالة عند حفظ أحد المولد في نطاق إستراتيجية واستعادته خارج أي نطاق إستراتيجية أو العكس ، لأن الجهاز خارج الإستراتيجيات يتم التعامل معه على أنه مختلف عن أي نسخة متماثلة في الإستراتيجية.

نموذج

يمكن حفظ 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 في إستراتيجية التوزيع لأن النسخ المتماثلة ستنشئ جميعها نفس دفق الأرقام العشوائية (وهذا بسبب تجميد معرف النسخة المتماثلة في الرسم البياني لـ SavedModel).

تحميل tf.random.Generator موزع (مولد تم إنشاؤه ضمن إستراتيجية توزيع) في بيئة غير إستراتيجية ، مثل المثال أعلاه ، له أيضًا تحذير. ستتم استعادة حالة RNG بشكل صحيح ، لكن الأرقام العشوائية التي تم إنشاؤها ستكون مختلفة عن المُنشئ الأصلي في استراتيجيتها (مرة أخرى لأنه يتم التعامل مع جهاز خارج الاستراتيجيات على أنه مختلف عن أي نسخة متماثلة في إستراتيجية).

RNGs عديمة الحالة

استخدام RNGs عديمة الحالة أمر بسيط. نظرًا لأنها مجرد وظائف نقية ، فلا توجد حالة أو آثار جانبية.

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)

يتطلب كل 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 ولكنها بطيئة على وحدة المعالجة المركزية / وحدة معالجة الرسومات مقارنةً بـ Philox.

راجع مقالة "أرقام عشوائية متوازية: سهلة مثل 1 ، 2 ، 3" لمزيد من التفاصيل حول هذه الخوارزميات.