דף זה תורגם על ידי Cloud Translation API.
Switch to English

יצירת מספרים אקראיים

צפה ב- TensorFlow.org הפעל בגוגל קולאב צפה במקור ב- GitHub הורד מחברת

TensorFlow מספק קבוצה של מחוללי מספרים פסאודו-אקראיים (RNG), במודול tf.random . מסמך זה מתאר כיצד ניתן לשלוט על מחוללי המספרים האקראיים, וכיצד גנרטורים אלה מתקשרים עם מערכות משנה טנסוריות אחרות.

TensorFlow מספק שתי גישות לשליטה בתהליך יצירת מספרים אקראיים:

  1. באמצעות שימוש מפורש באובייקטים tf.random.Generator . כל אובייקט כזה שומר על מצב ( tf.Variable ) שישתנה לאחר כל יצירת מספרים.

  2. דרך פונקציות אקראיות חסרות מדינה פונקציונליות בלבד כמו 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.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, ושימוש בגנרטור הגלובלי בהמשך מהמעבד לשאת עותק GPU-to-CPU.

יש גם פונקציה tf.random.set_global_generator להחלפת הגנרטור הגלובלי באובייקט גנרטור אחר. יש להשתמש בפונקציה זו תוך מחשבה זהירה, מכיוון שהגנרטור הגלובלי הישן אולי נלכד על ידי tf.function ( tf.function חלשה), והחלפתה תגרום לאיסוף אשפה 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 על גנרטורים מפוצלים. אין גבולות (חסום הצפה שלמה) לעומק הרקורסיות.

אינטראקציה עם 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 יכולה 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 חסרי מדינה

השימוש ב- 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.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 והן פונקציות stateless תומכות באלגוריתם של Philox (כתוב כ- "philox" או tf.random.Algorithm.PHILOX ) בכל המכשירים.

התקנים שונים ייצרו את אותם מספרים שלמים, אם משתמשים באותו אלגוריתם ומתחילים מאותו מצב. הם גם ייצרו "כמעט אותם" מספרי נקודה צפה, אם כי יתכנו פערים מספריים קטנים הנגרמים מהדרכים השונות בהן המכשירים מבצעים את חישוב נקודות הצפה (למשל סדר צמצום).

מכשירי XLA

במכשירים מונעי XLA (כגון TPU, וגם מעבד / GPU כאשר XLA מופעל), תומך גם האלגוריתם ThreeFry (שנכתב כ- "threefry" או tf.random.Algorithm.THREEFRY ). אלגוריתם זה מהיר ב- TPU אך איטי ב- CPU / GPU בהשוואה ל- Philox.

ראה מאמר 'מספרים אקראיים מקבילים: קל כמו 1, 2, 3' לקבלת פרטים נוספים על אלגוריתמים אלה.