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

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

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

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()
    ])
2021-07-23 01:22:18.918395: I tensorflow/stream_executor/platform/default/dso_loader.cc:53] Successfully opened dynamic library libcudart.so.11.0
2021-07-23 01:22:20.114741: I tensorflow/stream_executor/platform/default/dso_loader.cc:53] Successfully opened dynamic library libcuda.so.1
2021-07-23 01:22:20.117589: E tensorflow/stream_executor/cuda/cuda_driver.cc:328] failed call to cuInit: CUDA_ERROR_SYSTEM_DRIVER_MISMATCH: system has unsupported display driver / cuda driver combination
2021-07-23 01:22:20.117639: I tensorflow/stream_executor/cuda/cuda_diagnostics.cc:169] retrieving CUDA diagnostic information for host: kokoro-gcp-ubuntu-prod-952172144
2021-07-23 01:22:20.117651: I tensorflow/stream_executor/cuda/cuda_diagnostics.cc:176] hostname: kokoro-gcp-ubuntu-prod-952172144
2021-07-23 01:22:20.117745: I tensorflow/stream_executor/cuda/cuda_diagnostics.cc:200] libcuda reported version is: 470.57.2
2021-07-23 01:22:20.117782: I tensorflow/stream_executor/cuda/cuda_diagnostics.cc:204] kernel reported version is: 465.27.0
2021-07-23 01:22:20.117793: E tensorflow/stream_executor/cuda/cuda_diagnostics.cc:313] kernel version 465.27.0 does not match DSO version 470.57.2 -- cannot find working devices in this configuration

tf.random.Generator בכיתה

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

אתה יכול לקבל 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.5001068   0.07689949 -0.58556736]
 [ 0.10337339  0.00405895  0.2543702 ]], shape=(2, 3), dtype=float32)
2021-07-23 01:22:20.125886: I tensorflow/core/platform/cpu_feature_guard.cc:142] This TensorFlow binary is optimized with oneAPI Deep Neural Network Library (oneDNN) to use the following CPU instructions in performance-critical operations:  AVX2 AVX512F FMA
To enable them in other operations, rebuild TensorFlow with the appropriate compiler flags.

ישנן מספר דרכים ליצור אובייקט גנרטור. הקלה ביותר היא 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(
[[-0.98018    -0.14114633  0.4464961 ]
 [-0.12185935 -0.60464644 -0.83387   ]], 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 . דרך טובה יותר כדי לאפס את הגנרטור הגלובלית היא להשתמש באחד התפקידים "איפוס" כמו 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.99936944, 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)
2021-07-23 01:22:20.233787: I tensorflow/compiler/mlir/mlir_graph_optimization_pass.cc:176] None of the MLIR Optimization Passes are enabled (registered 2)
2021-07-23 01:22:20.234288: I tensorflow/core/platform/profile_utils/cpu_utils.cc:114] CPU Frequency: 2000175000 Hz

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

יצירת גנרטורים בתוך tf.function

יצירה של גנרטורים בתוך tf.function יכול happend במהלך הריצה הראשונה בלבד של הפונקציה.

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 , חפצי גנרטור שונים יגרמו retracing של 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

שים לב שהתנהגות retracing זו עולה בקנה אחד עם 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.
WARNING:tensorflow:Collective ops is not configured at program startup. Some performance features may not be enabled.
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.
WARNING:tensorflow:Collective ops is not configured at program startup. Some performance features may not be enabled.
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 ), המספרים האקראיים נקבעים על ידי הזרע, למרות העתקים שונים לקבל מספרים שונים מתואמים. אפשר לחשוב על מספר אקראי שנוצר על העתק כ- hash של מזהה העתק ומספר אקראי "ראשוני" המשותף לכל העתקים. לפיכך, המערכת כולה עדיין דטרמיניסטית.

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.
WARNING:tensorflow:Collective ops is not configured at program startup. Some performance features may not be enabled.
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 בדרך כלל מצפה הטיעונים להיות tensors, לא גנרטורים.

שמירת גנרטורים

באופן כללי לשמירה או בהמשכים אתה יכול להתמודד עם tf.random.Generator באותו אופן היית לטפל tf.Variable או tf.Module (או subclasses שלו). בשנת 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.43842274, 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.
WARNING:tensorflow:Collective ops is not configured at program startup. Some performance features may not be enabled.
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.
WARNING:tensorflow:Collective ops is not configured at program startup. Some performance features may not be enabled.
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.
WARNING:tensorflow:Collective ops is not configured at program startup. Some performance features may not be enabled.
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.
WARNING:tensorflow:Collective ops is not configured at program startup. Some performance features may not be enabled.
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-07-23 01:22:20.765754: 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.064250775, shape=(), dtype=float32)
state: tf.Tensor([768   0   0], shape=(3,), dtype=int64)

טעינת SavedModel המכיל tf.random.Generator לתוך אסטרטגיית ההפצה אינה מומלצת משום העתקים לכל יפיק זרם אקראי מספר זהה (וזה בגלל מזהה העתק קפוא הגרף של SavedModel).

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

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.07356432]
 [ 0.04643455 -1.3015898  -0.9538565 ]], shape=(2, 3), dtype=float32)
tf.Tensor(
[[ 0.5441101   0.20738031  0.07356432]
 [ 0.04643455 -1.3015898  -0.9538565 ]], shape=(2, 3), dtype=float32)

כל RNG נתינות דורש seed הטיעון, אשר צריך להיות מותח שלם של צורה [2] . תוצאות האופציה נקבעות במלואן על ידי זרע זה.

אלגוריתם ה- RNG המשמש RNGs חסרי מדינה תלוי בהתקן, כלומר אותה פעולה הפועלת במכשיר אחר עשויה לייצר תפוקות שונות.

אלגוריתמים

כללי

הן 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" לקבלת פרטים נוספים על אלגוריתמים אלה.