הצג באתר TensorFlow.org | הפעל בגוגל קולאב | צפה במקור ב-GitHub | הורד מחברת |
import collections
import tensorflow as tf
tf.compat.v2.enable_v2_behavior()
import tensorflow_probability as tfp
tfd = tfp.distributions
tfb = tfp.bijectors
יסודות
ישנם שלושה מושגים חשובים הקשורים לצורות TensorFlow Distributions:
- צורת אירוע מתארת את הצורה של תיקו בודד מההפצה; זה עשוי להיות תלוי בין מימדים. עבור הפצות סקלר, צורת האירוע היא
[]
. במשך MultivariateNormal 5-ממדים, את צורת האירוע היא[5]
. - הצורה אצווה מתאר עצמאית, לא מופץ באופן זהה שואבת, aka של "אצווה" של הפצות.
- הצורה לדוגמא מתאר עצמאית, מופץ באופן זהה שואבת של אצוות ממשפחת ההפצה.
צורת האירוע ואת הצורה יצווה הם מאפיינים של Distribution
אובייקט, ואילו הצורה המדגמת קשור קריאה ספציפית sample
או log_prob
.
מטרת המחברת היא להמחיש את המושגים הללו באמצעות דוגמאות, אז אם זה לא ברור מיד, אל תדאג!
לקבלת סקירה מושגית אחרת של מושגים אלה, ראו פוסט בבלוג הזה .
הערה על TensorFlow Eager.
מחברת שלמה זה נכתב באמצעות TensorFlow להוט . אף אחד המושגים המוצגים להסתמך על להוט, אם כי עם צורות תצוינה להוטות, הפצת אירוע מוערכות (ולכן ידוע) כאשר Distribution
האובייקט נוצר ב Python, ואילו גרף (מצב שאיננו להוט), אפשר להגדיר הפצות שצורת האירוע והאצווה שלו אינן מוגדרות עד להרצת הגרף.
הפצות סקלריות
כפי שציינו לעיל, Distribution
אובייקט הגדיר צורות אירוע יצווה. נתחיל עם כלי עזר לתיאור הפצות:
def describe_distributions(distributions):
print('\n'.join([str(d) for d in distributions]))
בחלק זה נחקור הפצות סקלר: הפצות עם צורה במקרה של []
. דוגמה טיפוסית היא התפלגות פואסון, שצוינה על ידי rate
:
poisson_distributions = [
tfd.Poisson(rate=1., name='One Poisson Scalar Batch'),
tfd.Poisson(rate=[1., 10., 100.], name='Three Poissons'),
tfd.Poisson(rate=[[1., 10., 100.,], [2., 20., 200.]],
name='Two-by-Three Poissons'),
tfd.Poisson(rate=[1.], name='One Poisson Vector Batch'),
tfd.Poisson(rate=[[1.]], name='One Poisson Expanded Batch')
]
describe_distributions(poisson_distributions)
tfp.distributions.Poisson("One_Poisson_Scalar_Batch", batch_shape=[], event_shape=[], dtype=float32) tfp.distributions.Poisson("Three_Poissons", batch_shape=[3], event_shape=[], dtype=float32) tfp.distributions.Poisson("Two_by_Three_Poissons", batch_shape=[2, 3], event_shape=[], dtype=float32) tfp.distributions.Poisson("One_Poisson_Vector_Batch", batch_shape=[1], event_shape=[], dtype=float32) tfp.distributions.Poisson("One_Poisson_Expanded_Batch", batch_shape=[1, 1], event_shape=[], dtype=float32)
התפלגות פואסון היא הפצה סקלר, ולכן צורת האירוע שלה היא תמיד []
. אם נציין תעריפים נוספים, אלה מופיעים בצורת האצווה. צמד הדוגמאות האחרון מעניין: יש רק קצב בודד, אבל בגלל שהקצב הזה מוטבע במערך numpy עם צורה לא ריקה, הצורה הזו הופכת לצורת האצווה.
ההתפלגות הנורמלית הסטנדרטית היא גם סקלרית. צורת האירוע של זה []
, בדיוק כמו עבור פואסון, אבל נצטרך לשחק עם זה כדי לראות בדוגמא הראשונה שלנו של שידור. נורמלי מוגדר בעזרת loc
ואת scale
פרמטרים:
normal_distributions = [
tfd.Normal(loc=0., scale=1., name='Standard'),
tfd.Normal(loc=[0.], scale=1., name='Standard Vector Batch'),
tfd.Normal(loc=[0., 1., 2., 3.], scale=1., name='Different Locs'),
tfd.Normal(loc=[0., 1., 2., 3.], scale=[[1.], [5.]],
name='Broadcasting Scale')
]
describe_distributions(normal_distributions)
tfp.distributions.Normal("Standard", batch_shape=[], event_shape=[], dtype=float32) tfp.distributions.Normal("Standard_Vector_Batch", batch_shape=[1], event_shape=[], dtype=float32) tfp.distributions.Normal("Different_Locs", batch_shape=[4], event_shape=[], dtype=float32) tfp.distributions.Normal("Broadcasting_Scale", batch_shape=[2, 4], event_shape=[], dtype=float32)
דוגמה המעניינת לעיל היא Broadcasting Scale
וההפצה. loc
פרמטר יש צורה [4]
, ואת scale
פרמטר יש צורה [2, 1]
. באמצעות כללים לשדר numpy , הצורה תצווה היא [2, 4]
. השווה (אבל פחות אלגנטי ולא-מומלץ) דרך להגדיר את "Broadcasting Scale"
הפצה יהיה:
describe_distributions(
[tfd.Normal(loc=[[0., 1., 2., 3], [0., 1., 2., 3.]],
scale=[[1., 1., 1., 1.], [5., 5., 5., 5.]])])
tfp.distributions.Normal("Normal", batch_shape=[2, 4], event_shape=[], dtype=float32)
אנו יכולים לראות מדוע סימון השידור שימושי, למרות שהוא גם מקור לכאבי ראש ובאגים.
דגימת התפלגויות סקלריות
ישנם שני דברים עיקריים שאנחנו יכולים לעשות עם הפצות: אנחנו יכולים sample
מהם נוכל לחשב log_prob
ים. בוא נחקור תחילה דגימה. הכלל הבסיסי הוא שכאשר אנו דוגמים מחלוקה, מותח וכתוצאה מכך יש צורה [sample_shape, batch_shape, event_shape]
, שבו batch_shape
ו event_shape
ניתנים על ידי Distribution
האובייקט, ואת sample_shape
מסופק על ידי קריאה sample
. עבור הפצות סקלר, event_shape = []
, כך המותח חזר מדגם תהיה צורה [sample_shape, batch_shape]
. בואו ננסה את זה:
def describe_sample_tensor_shape(sample_shape, distribution):
print('Sample shape:', sample_shape)
print('Returned sample tensor shape:',
distribution.sample(sample_shape).shape)
def describe_sample_tensor_shapes(distributions, sample_shapes):
started = False
for distribution in distributions:
print(distribution)
for sample_shape in sample_shapes:
describe_sample_tensor_shape(sample_shape, distribution)
print()
sample_shapes = [1, 2, [1, 5], [3, 4, 5]]
describe_sample_tensor_shapes(poisson_distributions, sample_shapes)
tfp.distributions.Poisson("One_Poisson_Scalar_Batch", batch_shape=[], event_shape=[], dtype=float32) Sample shape: 1 Returned sample tensor shape: (1,) Sample shape: 2 Returned sample tensor shape: (2,) Sample shape: [1, 5] Returned sample tensor shape: (1, 5) Sample shape: [3, 4, 5] Returned sample tensor shape: (3, 4, 5) tfp.distributions.Poisson("Three_Poissons", batch_shape=[3], event_shape=[], dtype=float32) Sample shape: 1 Returned sample tensor shape: (1, 3) Sample shape: 2 Returned sample tensor shape: (2, 3) Sample shape: [1, 5] Returned sample tensor shape: (1, 5, 3) Sample shape: [3, 4, 5] Returned sample tensor shape: (3, 4, 5, 3) tfp.distributions.Poisson("Two_by_Three_Poissons", batch_shape=[2, 3], event_shape=[], dtype=float32) Sample shape: 1 Returned sample tensor shape: (1, 2, 3) Sample shape: 2 Returned sample tensor shape: (2, 2, 3) Sample shape: [1, 5] Returned sample tensor shape: (1, 5, 2, 3) Sample shape: [3, 4, 5] Returned sample tensor shape: (3, 4, 5, 2, 3) tfp.distributions.Poisson("One_Poisson_Vector_Batch", batch_shape=[1], event_shape=[], dtype=float32) Sample shape: 1 Returned sample tensor shape: (1, 1) Sample shape: 2 Returned sample tensor shape: (2, 1) Sample shape: [1, 5] Returned sample tensor shape: (1, 5, 1) Sample shape: [3, 4, 5] Returned sample tensor shape: (3, 4, 5, 1) tfp.distributions.Poisson("One_Poisson_Expanded_Batch", batch_shape=[1, 1], event_shape=[], dtype=float32) Sample shape: 1 Returned sample tensor shape: (1, 1, 1) Sample shape: 2 Returned sample tensor shape: (2, 1, 1) Sample shape: [1, 5] Returned sample tensor shape: (1, 5, 1, 1) Sample shape: [3, 4, 5] Returned sample tensor shape: (3, 4, 5, 1, 1)
describe_sample_tensor_shapes(normal_distributions, sample_shapes)
tfp.distributions.Normal("Standard", batch_shape=[], event_shape=[], dtype=float32) Sample shape: 1 Returned sample tensor shape: (1,) Sample shape: 2 Returned sample tensor shape: (2,) Sample shape: [1, 5] Returned sample tensor shape: (1, 5) Sample shape: [3, 4, 5] Returned sample tensor shape: (3, 4, 5) tfp.distributions.Normal("Standard_Vector_Batch", batch_shape=[1], event_shape=[], dtype=float32) Sample shape: 1 Returned sample tensor shape: (1, 1) Sample shape: 2 Returned sample tensor shape: (2, 1) Sample shape: [1, 5] Returned sample tensor shape: (1, 5, 1) Sample shape: [3, 4, 5] Returned sample tensor shape: (3, 4, 5, 1) tfp.distributions.Normal("Different_Locs", batch_shape=[4], event_shape=[], dtype=float32) Sample shape: 1 Returned sample tensor shape: (1, 4) Sample shape: 2 Returned sample tensor shape: (2, 4) Sample shape: [1, 5] Returned sample tensor shape: (1, 5, 4) Sample shape: [3, 4, 5] Returned sample tensor shape: (3, 4, 5, 4) tfp.distributions.Normal("Broadcasting_Scale", batch_shape=[2, 4], event_shape=[], dtype=float32) Sample shape: 1 Returned sample tensor shape: (1, 2, 4) Sample shape: 2 Returned sample tensor shape: (2, 2, 4) Sample shape: [1, 5] Returned sample tensor shape: (1, 5, 2, 4) Sample shape: [3, 4, 5] Returned sample tensor shape: (3, 4, 5, 2, 4)
זה בערך כל מה שיש לומר על sample
: חזר tensors מדגם יש צורה [sample_shape, batch_shape, event_shape]
.
חישוב log_prob
עבור הפצות סקלר
עכשיו בואו נסתכל log_prob
, וזה קצת בעייתי. log_prob
לוקח כקלט מותח (שאינו ריק) המייצג את המיקום (הים) בו כדי לחשב את log_prob
להפצה. בשנים ביותר בתיק פשוט, מותח זו יהיה צורה של הטופס [sample_shape, batch_shape, event_shape]
, שבו batch_shape
ו event_shape
משחק הצורות יצוו במקרה של חלוק. נזכיר שוב כי עבור הפצות סקלר, event_shape = []
, כך מותח קלט יש צורה [sample_shape, batch_shape]
במקרה זה, אנחנו מקבלים בחזרה מותח של הצורה [sample_shape, batch_shape]
:
three_poissons = tfd.Poisson(rate=[1., 10., 100.], name='Three Poissons')
three_poissons
<tfp.distributions.Poisson 'Three_Poissons' batch_shape=[3] event_shape=[] dtype=float32>
three_poissons.log_prob([[1., 10., 100.], [100., 10., 1]]) # sample_shape is [2].
<tf.Tensor: shape=(2, 3), dtype=float32, numpy= array([[ -1. , -2.0785608, -3.2223587], [-364.73938 , -2.0785608, -95.39484 ]], dtype=float32)>
three_poissons.log_prob([[[[1., 10., 100.], [100., 10., 1.]]]]) # sample_shape is [1, 1, 2].
<tf.Tensor: shape=(1, 1, 2, 3), dtype=float32, numpy= array([[[[ -1. , -2.0785608, -3.2223587], [-364.73938 , -2.0785608, -95.39484 ]]]], dtype=float32)>
שים לב איך בדוגמא הראשונה, הקלט והפלט יש צורה [2, 3]
וכן בדוגמא השנייה יש להם צורה [1, 1, 2, 3]
.
זה היה כל מה שהיה לומר לולא השידור. הנה הכללים ברגע שאנחנו לוקחים בחשבון את השידור. אנו מתארים זאת באופן כללי ומציינים הפשטות עבור התפלגויות סקלריות:
- הגדר
n = len(batch_shape) + len(event_shape)
. (בהפצות סקלר,len(event_shape)=0
.) - אם הקלט מותח
t
יש פחות מn
מימדים, כרית צורתו על ידי הוספת מאפיינים של גודל1
בצד שמאל עד שהוא יש בדיוקn
מימדים. התקשר לטנזור וכתוצאהt'
. - לשדר את
n
מימדים הימני ביותר שלt'
נגד[batch_shape, event_shape]
של הפצה שאתה מחשובlog_prob
עבור. ביתר פירוט: לגבי הממדים שבוt'
כבר תואם את חלוק, לא לעשות כלום, ועל הממדים שבוt'
יש סינגלטון, לשכפל כי סינגלטון המספר המתאים של פעמים. כל מצב אחר הוא טעות. (בהפצות סקלר, אנחנו רק לשדר נגדbatch_shape
, מאז event_shape =[]
.) - עכשיו אנחנו סוף סוף יכולים לחשב את
log_prob
. טנזור וכתוצאה יהיה צורה[sample_shape, batch_shape]
, שבוsample_shape
מוגדר להיות כול ממדים שלt
אוt'
שמאלה שלn
ממדי -rightmost:sample_shape = shape(t)[:-n]
.
זה עשוי להיות בלגן אם אינך יודע מה זה אומר, אז בוא נעבוד על כמה דוגמאות:
three_poissons.log_prob([10.])
<tf.Tensor: shape=(3,), dtype=float32, numpy=array([-16.104412 , -2.0785608, -69.05272 ], dtype=float32)>
טנזור [10.]
(עם צורה [1]
) משודר ברחבי batch_shape
של 3, כך אנו מעריכים את כול הסתברות היומן השלושה "חקר בשווי 10.
three_poissons.log_prob([[[1.], [10.]], [[100.], [1000.]]])
<tf.Tensor: shape=(2, 2, 3), dtype=float32, numpy= array([[[-1.0000000e+00, -7.6974149e+00, -9.5394836e+01], [-1.6104412e+01, -2.0785608e+00, -6.9052719e+01]], [[-3.6473938e+02, -1.4348087e+02, -3.2223587e+00], [-5.9131279e+03, -3.6195427e+03, -1.4069575e+03]]], dtype=float32)>
בדוגמא לעיל, מותח הקלט יש צורה [2, 2, 1]
, ואילו אובייקט ההפצות יש צורה אצווה של 3. אז לכול אחת [2, 2]
מידו המדגם, הערך הבודד המסופק מקבל broadcats לכול מבין שלושת הפואסונים.
דרך שימושית ואולי לחשוב על זה: כי three_poissons
יש batch_shape = [2, 3]
, קריאת log_prob
חייב לקחת מותח אשר הממד האחרון הוא או 1 או 3; כל דבר אחר הוא שגיאה. (כללי שידור numpy להתייחס למקרה המיוחד של סקלר כמו להיות לגמרי שווה מותח של צורה [1]
.)
המבחן באים הצלעות שלנו על ידי משחק עם התפלגות פואסון מורכבת יותר עם batch_shape = [2, 3]
:
poisson_2_by_3 = tfd.Poisson(
rate=[[1., 10., 100.,], [2., 20., 200.]],
name='Two-by-Three Poissons')
poisson_2_by_3.log_prob(1.)
<tf.Tensor: shape=(2, 3), dtype=float32, numpy= array([[ -1. , -7.697415 , -95.39484 ], [ -1.3068528, -17.004269 , -194.70169 ]], dtype=float32)>
poisson_2_by_3.log_prob([1.]) # Exactly equivalent to above, demonstrating the scalar special case.
<tf.Tensor: shape=(2, 3), dtype=float32, numpy= array([[ -1. , -7.697415 , -95.39484 ], [ -1.3068528, -17.004269 , -194.70169 ]], dtype=float32)>
poisson_2_by_3.log_prob([[1., 1., 1.], [1., 1., 1.]]) # Another way to write the same thing. No broadcasting.
<tf.Tensor: shape=(2, 3), dtype=float32, numpy= array([[ -1. , -7.697415 , -95.39484 ], [ -1.3068528, -17.004269 , -194.70169 ]], dtype=float32)>
poisson_2_by_3.log_prob([[1., 10., 100.]]) # Input is [1, 3] broadcast to [2, 3].
<tf.Tensor: shape=(2, 3), dtype=float32, numpy= array([[ -1. , -2.0785608, -3.2223587], [ -1.3068528, -5.14709 , -33.90767 ]], dtype=float32)>
poisson_2_by_3.log_prob([[1., 10., 100.], [1., 10., 100.]]) # Equivalent to above. No broadcasting.
<tf.Tensor: shape=(2, 3), dtype=float32, numpy= array([[ -1. , -2.0785608, -3.2223587], [ -1.3068528, -5.14709 , -33.90767 ]], dtype=float32)>
poisson_2_by_3.log_prob([[1., 1., 1.], [2., 2., 2.]]) # No broadcasting.
<tf.Tensor: shape=(2, 3), dtype=float32, numpy= array([[ -1. , -7.697415 , -95.39484 ], [ -1.3068528, -14.701683 , -190.09653 ]], dtype=float32)>
poisson_2_by_3.log_prob([[1.], [2.]]) # Equivalent to above. Input shape [2, 1] broadcast to [2, 3].
<tf.Tensor: shape=(2, 3), dtype=float32, numpy= array([[ -1. , -7.697415 , -95.39484 ], [ -1.3068528, -14.701683 , -190.09653 ]], dtype=float32)>
הדוגמאות לעיל כללו שידור על האצווה, אך צורת הדוגמה הייתה ריקה. נניח שיש לנו אוסף של ערכים, ואנו רוצים לקבל את ההסתברות ביומן של כל ערך בכל נקודה באצווה. נוכל לעשות זאת ידנית:
poisson_2_by_3.log_prob([[[1., 1., 1.], [1., 1., 1.]], [[2., 2., 2.], [2., 2., 2.]]]) # Input shape [2, 2, 3].
<tf.Tensor: shape=(2, 2, 3), dtype=float32, numpy= array([[[ -1. , -7.697415 , -95.39484 ], [ -1.3068528, -17.004269 , -194.70169 ]], [[ -1.6931472, -6.087977 , -91.48282 ], [ -1.3068528, -14.701683 , -190.09653 ]]], dtype=float32)>
או שנוכל לתת לשידור לטפל בממד האצווה האחרון:
poisson_2_by_3.log_prob([[[1.], [1.]], [[2.], [2.]]]) # Input shape [2, 2, 1].
<tf.Tensor: shape=(2, 2, 3), dtype=float32, numpy= array([[[ -1. , -7.697415 , -95.39484 ], [ -1.3068528, -17.004269 , -194.70169 ]], [[ -1.6931472, -6.087977 , -91.48282 ], [ -1.3068528, -14.701683 , -190.09653 ]]], dtype=float32)>
אנחנו יכולים גם (אולי קצת פחות באופן טבעי) לתת לשידור לטפל רק בממד האצווה הראשון:
poisson_2_by_3.log_prob([[[1., 1., 1.]], [[2., 2., 2.]]]) # Input shape [2, 1, 3].
<tf.Tensor: shape=(2, 2, 3), dtype=float32, numpy= array([[[ -1. , -7.697415 , -95.39484 ], [ -1.3068528, -17.004269 , -194.70169 ]], [[ -1.6931472, -6.087977 , -91.48282 ], [ -1.3068528, -14.701683 , -190.09653 ]]], dtype=float32)>
או שאנחנו יכולים לתת לשדר ממדי ידית הן יצווה:
poisson_2_by_3.log_prob([[[1.]], [[2.]]]) # Input shape [2, 1, 1].
<tf.Tensor: shape=(2, 2, 3), dtype=float32, numpy= array([[[ -1. , -7.697415 , -95.39484 ], [ -1.3068528, -17.004269 , -194.70169 ]], [[ -1.6931472, -6.087977 , -91.48282 ], [ -1.3068528, -14.701683 , -190.09653 ]]], dtype=float32)>
האמור לעיל עבד מצוין כשהיו לנו רק שני ערכים שרצינו, אבל נניח שהייתה לנו רשימה ארוכה של ערכים שרצינו להעריך בכל נקודת אצווה. לשם כך, הסימון הבא, המוסיף מימדים נוספים בגודל 1 בצד ימין של הצורה, שימושי ביותר:
poisson_2_by_3.log_prob(tf.constant([1., 2.])[..., tf.newaxis, tf.newaxis])
<tf.Tensor: shape=(2, 2, 3), dtype=float32, numpy= array([[[ -1. , -7.697415 , -95.39484 ], [ -1.3068528, -17.004269 , -194.70169 ]], [[ -1.6931472, -6.087977 , -91.48282 ], [ -1.3068528, -14.701683 , -190.09653 ]]], dtype=float32)>
זהו מופע של סימון פרוסה הפוסע בצעדים , אשר כדאי לדעת.
אחזור three_poissons
עבור שלמות, אותם המבטים למשל כמו:
three_poissons.log_prob([[1.], [10.], [50.], [100.]])
<tf.Tensor: shape=(4, 3), dtype=float32, numpy= array([[ -1. , -7.697415 , -95.39484 ], [ -16.104412 , -2.0785608, -69.05272 ], [-149.47777 , -43.34851 , -18.219261 ], [-364.73938 , -143.48087 , -3.2223587]], dtype=float32)>
three_poissons.log_prob(tf.constant([1., 10., 50., 100.])[..., tf.newaxis]) # Equivalent to above.
<tf.Tensor: shape=(4, 3), dtype=float32, numpy= array([[ -1. , -7.697415 , -95.39484 ], [ -16.104412 , -2.0785608, -69.05272 ], [-149.47777 , -43.34851 , -18.219261 ], [-364.73938 , -143.48087 , -3.2223587]], dtype=float32)>
התפלגויות רב משתנות
כעת אנו פונים להתפלגות רב-משתניות, אשר להן צורת אירוע לא ריקה. בואו נסתכל על התפלגויות רב-נומיות.
multinomial_distributions = [
# Multinomial is a vector-valued distribution: if we have k classes,
# an individual sample from the distribution has k values in it, so the
# event_shape is `[k]`.
tfd.Multinomial(total_count=100., probs=[.5, .4, .1],
name='One Multinomial'),
tfd.Multinomial(total_count=[100., 1000.], probs=[.5, .4, .1],
name='Two Multinomials Same Probs'),
tfd.Multinomial(total_count=100., probs=[[.5, .4, .1], [.1, .2, .7]],
name='Two Multinomials Same Counts'),
tfd.Multinomial(total_count=[100., 1000.],
probs=[[.5, .4, .1], [.1, .2, .7]],
name='Two Multinomials Different Everything')
]
describe_distributions(multinomial_distributions)
tfp.distributions.Multinomial("One_Multinomial", batch_shape=[], event_shape=[3], dtype=float32) tfp.distributions.Multinomial("Two_Multinomials_Same_Probs", batch_shape=[2], event_shape=[3], dtype=float32) tfp.distributions.Multinomial("Two_Multinomials_Same_Counts", batch_shape=[2], event_shape=[3], dtype=float32) tfp.distributions.Multinomial("Two_Multinomials_Different_Everything", batch_shape=[2], event_shape=[3], dtype=float32)
שים לב איך בשלושת הדוגמאות האחרונות, batch_shape הוא תמיד [2]
, אבל אנחנו יכולים להשתמש לשדר שיהיו להן משותף total_count
או משותף probs
(או לא), כי מתחת למכסה המנוע הם משודרות יש צורה זהה.
הדגימה היא פשוטה, בהתחשב במה שאנחנו כבר יודעים:
describe_sample_tensor_shapes(multinomial_distributions, sample_shapes)
tfp.distributions.Multinomial("One_Multinomial", batch_shape=[], event_shape=[3], dtype=float32) Sample shape: 1 Returned sample tensor shape: (1, 3) Sample shape: 2 Returned sample tensor shape: (2, 3) Sample shape: [1, 5] Returned sample tensor shape: (1, 5, 3) Sample shape: [3, 4, 5] Returned sample tensor shape: (3, 4, 5, 3) tfp.distributions.Multinomial("Two_Multinomials_Same_Probs", batch_shape=[2], event_shape=[3], dtype=float32) Sample shape: 1 Returned sample tensor shape: (1, 2, 3) Sample shape: 2 Returned sample tensor shape: (2, 2, 3) Sample shape: [1, 5] Returned sample tensor shape: (1, 5, 2, 3) Sample shape: [3, 4, 5] Returned sample tensor shape: (3, 4, 5, 2, 3) tfp.distributions.Multinomial("Two_Multinomials_Same_Counts", batch_shape=[2], event_shape=[3], dtype=float32) Sample shape: 1 Returned sample tensor shape: (1, 2, 3) Sample shape: 2 Returned sample tensor shape: (2, 2, 3) Sample shape: [1, 5] Returned sample tensor shape: (1, 5, 2, 3) Sample shape: [3, 4, 5] Returned sample tensor shape: (3, 4, 5, 2, 3) tfp.distributions.Multinomial("Two_Multinomials_Different_Everything", batch_shape=[2], event_shape=[3], dtype=float32) Sample shape: 1 Returned sample tensor shape: (1, 2, 3) Sample shape: 2 Returned sample tensor shape: (2, 2, 3) Sample shape: [1, 5] Returned sample tensor shape: (1, 5, 2, 3) Sample shape: [3, 4, 5] Returned sample tensor shape: (3, 4, 5, 2, 3)
חישוב הסתברויות יומן הוא פשוט באותה מידה. בוא נעבוד דוגמה עם התפלגות נורמלית רב משתנים אלכסונית. (ריבוי מילים אינם ידידותיים במיוחד לשידור, מכיוון שהאילוצים על הספירות וההסתברויות פירושם שהשידור יפיק לעתים קרובות ערכים בלתי קבילים.) אנו נשתמש באצווה של 2 הפצות תלת מימדיות עם אותו ממוצע אך קנה מידה שונה (סטיות תקן):
two_multivariate_normals = tfd.MultivariateNormalDiag(loc=[1., 2., 3.], scale_identity_multiplier=[1., 2.])
two_multivariate_normals
<tfp.distributions.MultivariateNormalDiag 'MultivariateNormalDiag' batch_shape=[2] event_shape=[3] dtype=float32>
(שים לב כי למרות השתמשנו הפצות שבו המאזניים היו בכפולות של זהות, זו אינה מגבלה על; שנוכל לעבור scale
במקום scale_identity_multiplier
.)
כעת הבה נעריך את ההסתברות ביומן של כל נקודת אצווה בממוצע שלה ובממוצע מוזז:
two_multivariate_normals.log_prob([[[1., 2., 3.]], [[3., 4., 5.]]]) # Input has shape [2,1,3].
<tf.Tensor: shape=(2, 2), dtype=float32, numpy= array([[-2.7568154, -4.836257 ], [-8.756816 , -6.336257 ]], dtype=float32)>
בדיוק באופן שקול, אנו יכולים להשתמש https://www.tensorflow.org/api_docs/cc/class/tensorflow/ops/strided-slice להכניס צורה נוספת = 1 מימד באמצע קבוע:
two_multivariate_normals.log_prob(
tf.constant([[1., 2., 3.], [3., 4., 5.]])[:, tf.newaxis, :]) # Equivalent to above.
<tf.Tensor: shape=(2, 2), dtype=float32, numpy= array([[-2.7568154, -4.836257 ], [-8.756816 , -6.336257 ]], dtype=float32)>
מצד השני, אם אנחנו לא נכניס את הממד הנוסף, אנחנו עוברים [1., 2., 3.]
עד כדי נצווה הראשונים [3., 4., 5.]
אל השני:
two_multivariate_normals.log_prob(tf.constant([[1., 2., 3.], [3., 4., 5.]]))
<tf.Tensor: shape=(2,), dtype=float32, numpy=array([-2.7568154, -6.336257 ], dtype=float32)>
טכניקות מניפולציה של צורה
ה-Reshape Bijector
Reshape
bijector ניתן להשתמש כדי לעצב מחדש את event_shape של חלוקה. בוא נראה דוגמה:
six_way_multinomial = tfd.Multinomial(total_count=1000., probs=[.3, .25, .2, .15, .08, .02])
six_way_multinomial
<tfp.distributions.Multinomial 'Multinomial' batch_shape=[] event_shape=[6] dtype=float32>
יצרנו הפולינום בעל צורה במקרה של [6]
. שנוי צורה Bijector מאפשרת לנו להתייחס לכך כאל הפצה עם צורה במקרה של [2, 3]
.
Bijector
מייצג פונקציה גזירה, אחד-על-אחד על משנה פתוח של \({\mathbb R}^n\). Bijectors
משמשים בשילוב עם TransformedDistribution
, אשר מודלים חלוקה \(p(y)\) במונחים של חלוקה בסיס \(p(x)\) וכן Bijector
שמייצג \(Y = g(X)\). בוא נראה את זה בפעולה:
transformed_multinomial = tfd.TransformedDistribution(
distribution=six_way_multinomial,
bijector=tfb.Reshape(event_shape_out=[2, 3]))
transformed_multinomial
<tfp.distributions.TransformedDistribution 'reshapeMultinomial' batch_shape=[] event_shape=[2, 3] dtype=float32>
six_way_multinomial.log_prob([500., 100., 100., 150., 100., 50.])
<tf.Tensor: shape=(), dtype=float32, numpy=-178.22021>
transformed_multinomial.log_prob([[500., 100., 100.], [150., 100., 50.]])
<tf.Tensor: shape=(), dtype=float32, numpy=-178.22021>
זהו דבר היחיד Reshape
bijector יכול לעשות: זה לא יכול להפוך ממדי אירוע לממדים יצוו או להיפך.
ההפצה העצמאית
Independent
ההפצה משמשת לטיפול אוסף של עצמאי, לא-בהכרח-זהה (aka יצווה של) הפצות כמו הפצה יחידה. נוסף תמציתי, Independent
מאפשר להמיר ממדים batch_shape
למימדים ב event_shape
. נמחיש בדוגמה:
two_by_five_bernoulli = tfd.Bernoulli(
probs=[[.05, .1, .15, .2, .25], [.3, .35, .4, .45, .5]],
name="Two By Five Bernoulli")
two_by_five_bernoulli
<tfp.distributions.Bernoulli 'Two_By_Five_Bernoulli' batch_shape=[2, 5] event_shape=[] dtype=int32>
אנו יכולים לחשוב על זה כעל מערך שניים על חמישה של מטבעות עם ההסתברויות הקשורות לראשים. הבה נעריך את ההסתברות של קבוצה מסוימת, שרירותית של אחדים-אפסים:
pattern = [[1., 0., 0., 1., 0.], [0., 0., 1., 1., 1.]]
two_by_five_bernoulli.log_prob(pattern)
<tf.Tensor: shape=(2, 5), dtype=float32, numpy= array([[-2.9957323 , -0.10536052, -0.16251892, -1.609438 , -0.2876821 ], [-0.35667497, -0.4307829 , -0.9162907 , -0.7985077 , -0.6931472 ]], dtype=float32)>
אנו יכולים להשתמש Independent
כדי להפוך את זה לשתי "סטים של חמישה ברנולי של" שונים, אשר שימושי אם אנחנו רוצים לשקול "בשורה" של הטלות מטבע שיקרה בעוד דפוס נתון כמו תוצאה אחת:
two_sets_of_five = tfd.Independent(
distribution=two_by_five_bernoulli,
reinterpreted_batch_ndims=1,
name="Two Sets Of Five")
two_sets_of_five
<tfp.distributions.Independent 'Two_Sets_Of_Five' batch_shape=[2] event_shape=[5] dtype=int32>
מבחינה מתמטית, אנו מחשבים את ההסתברות ביומן של כל "סט" של חמישה על ידי סיכום ההסתברויות ביומן של חמשת היפופי המטבע "הבלתי תלויים" בקבוצה, וזה המקום שבו ההתפלגות מקבלת את שמה:
two_sets_of_five.log_prob(pattern)
<tf.Tensor: shape=(2,), dtype=float32, numpy=array([-5.160732 , -3.1954036], dtype=float32)>
אנחנו יכולים ללכת עוד יותר ולהשתמש Independent
כדי ליצור חלוקה שבו אירועים בודדים הם קבוצה של שני מטר על מטר וחצי ברנולי של:
one_set_of_two_by_five = tfd.Independent(
distribution=two_by_five_bernoulli, reinterpreted_batch_ndims=2,
name="One Set Of Two By Five")
one_set_of_two_by_five.log_prob(pattern)
<tf.Tensor: shape=(), dtype=float32, numpy=-8.356134>
שווה זה וציין כי מנקודת המבט של sample
, באמצעות Independent
משנה דבר:
describe_sample_tensor_shapes(
[two_by_five_bernoulli,
two_sets_of_five,
one_set_of_two_by_five],
[[3, 5]])
tfp.distributions.Bernoulli("Two_By_Five_Bernoulli", batch_shape=[2, 5], event_shape=[], dtype=int32) Sample shape: [3, 5] Returned sample tensor shape: (3, 5, 2, 5) tfp.distributions.Independent("Two_Sets_Of_Five", batch_shape=[2], event_shape=[5], dtype=int32) Sample shape: [3, 5] Returned sample tensor shape: (3, 5, 2, 5) tfp.distributions.Independent("One_Set_Of_Two_By_Five", batch_shape=[], event_shape=[2, 5], dtype=int32) Sample shape: [3, 5] Returned sample tensor shape: (3, 5, 2, 5)
בתור תרגיל הפרידה עבור הקורא, מומלץ להתייחס לשיקולים הדומה והשונה בין אצווה וקטור של Normal
הפצות וכן MultivariateNormalDiag
הפצה מנקודת מבט הסתברות דגימה יומן. איך אנחנו יכולים להשתמש Independent
לבנות MultivariateNormalDiag
מתצלומי Normal
הים? (שים לב MultivariateNormalDiag
לא ממש מיושם בדרך זו.)