![]() | ![]() | ![]() | ![]() |
TensorFlow מספק קבוצה של מחוללי מספרים פסאודו-אקראיים (RNG), במודול tf.random
. מסמך זה מתאר כיצד ניתן לשלוט על מחוללי המספרים האקראיים וכיצד גנרטורים אלה מתקשרים עם מערכות משנה זורמות טנסור אחרות.
TensorFlow מספק שתי גישות לשליטה בתהליך יצירת מספרים אקראיים:
באמצעות שימוש מפורש באובייקטים
tf.random.Generator
. כל אובייקט כזה שומר על מצב (tf.Variable
) שישתנה לאחר כל יצירת מספרים.באמצעות פונקציות אקראיות חסרות מדינה פונקציונליות בלבד כמו
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.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.43842277 -0.53439844 -0.07710262] [ 1.5658046 -0.1012345 -0.2744976 ]], shape=(2, 3), dtype=float32) tf.Tensor( [[ 1.3604434 -0.3518851 3.5727046 ] [ 0.09712801 -1.2254813 1.346795 ]], 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.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( [[ 1.4167604 -2.1248727 -0.39414507] [-1.1975709 0.26199496 -0.31982416]], 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 ל- 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.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.59089047, 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
יכולה 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
, אובייקטים שונים של גנרטורים בעלי גודל המצב זהה (גודל המצב נקבע על ידי אלגוריתם 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 `run` inside a tf.function to get the best performance. tf.Tensor(0.43842277, 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 `run` 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 572, in wrapper return func(*args, **kwargs) File "<ipython-input-1-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 453, 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 375, in __init__ trainable=False) File "/tmpfs/src/tf_docs_env/lib/python3.6/site-packages/tensorflow/python/ops/stateful_random_ops.py", line 390, 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 `run` 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 `run` inside a tf.function to get the best performance. tf.Tensor(0.4355794, shape=(), dtype=float32) tf.Tensor(0.78352904, 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.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]
. תוצאות האופציה נקבעות במלואן על ידי זרע זה.
אלגוריתמים
כללי
הן מחלקת 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' לקבלת פרטים נוספים על אלגוריתמים אלה.