ترجمت واجهة Cloud Translation API‏ هذه الصفحة.
Switch to English

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

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

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

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

  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.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.9323887   0.3864468   1.5209497 ]
 [ 0.54473144 -0.6031506  -0.47044003]], shape=(2, 3), dtype=float32)

هناك طرق متعددة لإنشاء كائن منشئ. الأسهل هو Generator.from_seed ، كما هو موضح أعلاه ، الذي ينشئ مولدًا من بذرة. البذرة هي أي عدد صحيح غير سالب. from_seed يأخذ أيضا حجة اختياري alg الذي هو خوارزمية RNG التي سيتم استخدامها من قبل هذه المولدات:

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 . سيبدأ المولد الذي تم إنشاؤه بهذه الطريقة من حالة غير حتمية ، اعتمادًا على الوقت ونظام التشغيل.

g = tf.random.Generator.from_non_deterministic_state()
print(g.normal(shape=[2, 3]))
tf.Tensor(
[[-1.3158257   2.4625542   1.3490729 ]
 [ 0.77426016 -2.261468   -0.4887435 ]], 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.43842274, shape=(), dtype=float32)
tf.Tensor(1.6272374, shape=(), dtype=float32)
tf.Tensor(0.43842274, 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.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 في المثال أعلاه) ، على غرار طريقة 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.10396493, shape=(), dtype=float32)

يمكنك القيام بالتقسيم بشكل متكرر ، واستدعاء split على المولدات 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.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())
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 مع استراتيجيات التوزيع.

إنشاء مولدات خارج استراتيجيات التوزيع

إذا تم إنشاء مولد خارج نطاقات الإستراتيجية ، فسيتم إجراء تسلسل لوصول جميع النسخ المتماثلة إلى المولد ، وبالتالي ستحصل النسخ المتماثلة على أرقام عشوائية مختلفة.

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 `experimental_run_v2` 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 `experimental_run_v2` 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 275, 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 441, 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 363, in __init__
    trainable=False)
  File "/tmpfs/src/tf_docs_env/lib/python3.6/site-packages/tensorflow/python/ops/stateful_random_ops.py", line 378, 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 `experimental_run_v2` 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 `experimental_run_v2` inside a tf.function to get the best performance.
tf.Tensor(-0.42417043, shape=(), dtype=float32)
tf.Tensor(0.08118553, shape=(), dtype=float32)

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.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 الحجة، الذي يجب أن يكون عدد صحيح التنسور الشكل [2] . يتم تحديد نتائج المرجع بالكامل بواسطة هذه البذرة.

الخوارزميات

جنرال لواء

tf.random.Generator كل من فئة 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" لمزيد من التفاصيل حول هذه الخوارزميات.