הבנת צורות הפצה של TensorFlow

הצג באתר 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] .

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

  1. הגדר n = len(batch_shape) + len(event_shape) . (בהפצות סקלר, len(event_shape)=0 .)
  2. אם הקלט מותח t יש פחות מ n מימדים, כרית צורתו על ידי הוספת מאפיינים של גודל 1 בצד שמאל עד שהוא יש בדיוק n מימדים. התקשר לטנזור וכתוצאה t' .
  3. לשדר את n מימדים הימני ביותר של t' נגד [batch_shape, event_shape] של הפצה שאתה מחשוב log_prob עבור. ביתר פירוט: לגבי הממדים שבו t' כבר תואם את חלוק, לא לעשות כלום, ועל הממדים שבו t' יש סינגלטון, לשכפל כי סינגלטון המספר המתאים של פעמים. כל מצב אחר הוא טעות. (בהפצות סקלר, אנחנו רק לשדר נגד batch_shape , מאז event_shape = [] .)
  4. עכשיו אנחנו סוף סוף יכולים לחשב את 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 לא ממש מיושם בדרך זו.)