درک اشکال توزیع TensorFlow

مشاهده در TensorFlow.org در Google Colab اجرا شود مشاهده منبع در 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] .
  • شکل دسته ای توصیف مستقل، توزیع یکسان نیست تساوی، با نام مستعار یک "دسته" توزیعها.
  • شکل نمونه را توصیف مستقل، توزیع یکسان تساوی از دسته از خانواده توزیع.

شکل رویداد و شکل دسته ای از خواص یک هستند Distribution شی، در حالی که به شکل نمونه با یک تماس خاص به همراه sample و یا log_prob .

هدف این نوت بوک این است که این مفاهیم را از طریق مثال ها نشان دهد، بنابراین اگر این بلافاصله آشکار نشد، نگران نباشید!

برای بررسی اجمالی مفهومی دیگری از این مفاهیم، و این پست وبلاگ .

یادداشتی در مورد TensorFlow Eager.

کل این نوت بوک با استفاده از نوشته شده است TensorFlow مشتاق . هیچ یک از مفاهیم ارائه شده تکیه بر مشتاق، اگر چه با اشتیاق، دسته ای توزیع و رویداد اشکال ارزیابی می شوند (و در نتیجه شناخته می شود) هنگامی که Distribution در نمودار (حالت غیر مشتاق) شی در پایتون ایجاد شده است، در حالی که، ممکن است برای تعریف توزیع که شکل رویداد و دسته آن تا زمانی که نمودار اجرا نشود، مشخص نیست.

توزیع های اسکالر

همانطور که در بالا اشاره شد، یک 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)

از توزیع پواسون توزیع اسکالر است، بنابراین شکل رویداد آن همیشه [] . اگر نرخ‌های بیشتری را مشخص کنیم، به شکل دسته‌ای نشان داده می‌شوند. جفت مثال آخر جالب است: فقط یک نرخ وجود دارد، اما از آنجایی که این نرخ در یک آرایه ناقص با شکل غیر خالی جاسازی شده است، آن شکل به شکل دسته ای تبدیل می شود.

توزیع نرمال استاندارد نیز یک عدد اسکالر است. شکل رویداد این است [] ، درست مثل برای پواسون، اما ما با آن بازی برای دیدن اولین مثال ما از پخش. نرمال با استفاده از مشخص شده است 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] . با استفاده از نامپای قوانین پخش ، شکل دسته ای است [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 : تانسورها نمونه بازگشت شکل دارند [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 است، بنابراین ما ارزیابی احتمال ورود هر سه Poissons 'در ارزش 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 را؛ هر چیز دیگری خطا است (قوانین رادیو و تلویزیون نامپای درمان حالت خاصی از یک اسکالر به عنوان کاملا به یک تانسور شکل معادل [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)>

این یک نمونه از است نماد تکه strided است که ارزش دانستن.

رفتن به 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 توزیع 3 بعدی با مقیاس های میانگین اما متفاوت (انحرافات استاندارد) استفاده خواهیم کرد:

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)>

تکنیک های دستکاری شکل

The 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 توزیع است که برای درمان مجموعه ای از مستقل، نه لزوما یکسان (با نام مستعار یک دسته از) توزیع به عنوان یک توزیع تک استفاده می شود. بیشتر اختصار، 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 به نوبه خود این را به دو «مجموعه از پنج برنولی" متفاوت، مفید است که اگر ما می خواهیم به در نظر گرفتن "ردیف" از سکه flips آیند تا در یک الگوی داده شده به عنوان یک نتیجه واحد:

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 است که در واقع این راه اجرا نمی شود.)