فهم أشكال توزيعات TensorFlow

عرض على TensorFlow.org تشغيل في Google Colab عرض المصدر على جيثب تحميل دفتر
import collections

import tensorflow as tf
tf.compat.v2.enable_v2_behavior()

import tensorflow_probability as tfp
tfd = tfp.distributions
tfb = tfp.bijectors

الأساسيات

هناك ثلاثة مفاهيم مهمة مرتبطة بأشكال توزيعات TensorFlow:

  • يصف شكل الحدث على شكل سحب واحد من التوزيع؛ قد يعتمد عبر الأبعاد. التوزيعات العددية، وشكل الحدث [] . ل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 على كل من الثلاثة Poissons.

وهناك طريقة ربما مفيدة للتفكير في ذلك: لأن 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)

احتمالات سجل الحوسبة مباشرة بنفس القدر. لنقم بعمل مثال مع التوزيعات العادية القطرية متعددة المتغيرات. (تعد القيم المتعددة غير ملائمة للبث ، لأن القيود المفروضة على التهم والاحتمالات تعني أن البث غالبًا ما ينتج قيمًا غير مقبولة.) سنستخدم مجموعة من توزيعات ثنائية الأبعاد لها نفس المتوسط ​​ولكن بمقاييس مختلفة (الانحرافات المعيارية):

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

تقنيات التلاعب بالشكل

إعادة تشكيل 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] .

A 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 لتحويل هذا إلى قسمين مختلفة "مجموعات من خمسة في برنولي"، وهو أمر مفيد إذا كنا نريد للنظر في "الصف" لعملة وتقلب الخروج في نمط معين كنتيجة واحدة:

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 لم يتم تنفيذ في الواقع بهذه الطريقة).