Memahami Bentuk Distribusi TensorFlow

Lihat di TensorFlow.org Jalankan di Google Colab Lihat sumber di GitHub Unduh buku catatan
import collections

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

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

Dasar-dasar

Ada tiga konsep penting yang terkait dengan bentuk Distribusi TensorFlow:

  • Bentuk acara menggambarkan bentuk imbang tunggal dari distribusi; mungkin tergantung lintas dimensi. Untuk distribusi skalar, bentuk acara ini [] . Untuk MultivariateNormal 5 dimensi, bentuk acara ini [5] .
  • Bentuk Batch menggambarkan independen, tidak identik didistribusikan menarik, alias "batch" distribusi.
  • Bentuk sampel menggambarkan independen, identik didistribusikan menarik dari batch dari keluarga distribusi.

Acara bentuk dan bentuk batch sifat dari Distribution objek, sedangkan bentuk sampel dikaitkan dengan panggilan khusus untuk sample atau log_prob .

Tujuan buku catatan ini adalah untuk mengilustrasikan konsep-konsep ini melalui contoh, jadi jika hal ini tidak segera terlihat, jangan khawatir!

Untuk gambaran konseptual lain konsep-konsep ini, lihat posting blog ini .

Catatan tentang TensorFlow Eager.

Seluruh notebook ini ditulis dengan menggunakan TensorFlow Bersemangat . Tak satu pun dari konsep yang disajikan mengandalkan Bersemangat, meskipun dengan Eager, bets distribusi dan acara bentuk dievaluasi (dan karena itu dikenal) ketika Distribution objek dibuat dengan Python, sedangkan pada grafik (mode non-Eager), adalah mungkin untuk menentukan distribusi yang acara dan bentuk batchnya tidak ditentukan sampai grafik dijalankan.

Distribusi Skalar

Seperti yang kami sebutkan di atas, Distribution objek telah mendefinisikan event dan bets bentuk. Kami akan mulai dengan utilitas untuk menjelaskan distribusi:

def describe_distributions(distributions):
  print('\n'.join([str(d) for d in distributions]))

Pada bagian ini kita akan menjelajahi distribusi skalar: distribusi dengan bentuk acara [] . Sebuah contoh khas adalah distribusi Poisson, ditentukan oleh 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)

Distribusi Poisson adalah distribusi skalar, sehingga bentuknya acara yang selalu [] . Jika kami menentukan lebih banyak tarif, ini muncul dalam bentuk batch. Pasangan terakhir dari contoh menarik: hanya ada satu tingkat, tetapi karena tingkat itu tertanam dalam array numpy dengan bentuk yang tidak kosong, bentuk itu menjadi bentuk batch.

Distribusi Normal standar juga merupakan skalar. Itu bentuk acara adalah [] , seperti untuk Poisson, tapi kami akan bermain dengan itu untuk melihat contoh pertama kami penyiaran. Normal ditentukan dengan menggunakan loc dan scale parameter:

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)

Contoh yang menarik di atas adalah Broadcasting Scale distribusi. The loc parameter memiliki bentuk [4] , dan scale parameter memiliki bentuk [2, 1] . Menggunakan Numpy penyiaran aturan , bentuk batch [2, 4] . Cara setara (tapi kurang elegan dan tidak direkomendasikan) untuk menentukan "Broadcasting Scale" distribusi akan menjadi:

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)

Kita dapat melihat mengapa notasi penyiaran berguna, meskipun itu juga merupakan sumber sakit kepala dan bug.

Distribusi Skalar Pengambilan Sampel

Ada dua hal utama yang dapat kita lakukan dengan distribusi: kita bisa sample dari mereka dan kita dapat menghitung log_prob s. Mari kita jelajahi sampling terlebih dahulu. Aturan dasar adalah bahwa ketika kita sampel dari distribusi, Tensor yang dihasilkan memiliki bentuk [sample_shape, batch_shape, event_shape] , di mana batch_shape dan event_shape disediakan oleh Distribution objek, dan sample_shape disediakan oleh panggilan untuk sample . Untuk distribusi skalar, event_shape = [] , sehingga Tensor kembali dari sampel akan memiliki bentuk [sample_shape, batch_shape] . Mari kita coba:

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)

Itu tentang semua yang ada untuk mengatakan tentang sample : tensor sampel kembali memiliki bentuk [sample_shape, batch_shape, event_shape] .

Komputasi log_prob Untuk Scalar Distribusi

Sekarang mari kita lihat log_prob , yang agak rumit. log_prob mengambil sebagai masukan (non-kosong) tensor mewakili lokasi (s) di mana untuk menghitung log_prob untuk distribusi. Dalam kasus yang paling sederhana, tensor ini akan memiliki bentuk dari bentuk [sample_shape, batch_shape, event_shape] , di mana batch_shape dan event_shape pertandingan batch dan acara bentuk distribusi. Ingat sekali lagi bahwa untuk distribusi skalar, event_shape = [] , sehingga tensor masukan memiliki bentuk [sample_shape, batch_shape] Dalam hal ini, kami mendapatkan kembali tensor bentuk [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)>

Perhatikan bagaimana dalam contoh pertama, input dan output memiliki bentuk [2, 3] dan dalam contoh kedua mereka memiliki bentuk [1, 1, 2, 3] .

Hanya itu yang bisa dikatakan, jika bukan karena penyiaran. Berikut adalah aturan setelah kami mempertimbangkan penyiaran. Kami menjelaskannya secara umum dan mencatat penyederhanaan untuk distribusi skalar:

  1. Tentukan n = len(batch_shape) + len(event_shape) . (Untuk distribusi skalar, len(event_shape)=0 .)
  2. Jika input tensor t memiliki kurang dari n dimensi, pad bentuknya dengan menambahkan dimensi ukuran 1 di sebelah kiri sampai memiliki tepat n dimensi. Panggil dihasilkan tensor t' .
  3. Siaran n dimensi paling kanan dari t' terhadap [batch_shape, event_shape] dari distribusi Anda komputasi log_prob untuk. Secara lebih rinci: untuk dimensi di mana t' sudah cocok distribusi, melakukan apa-apa, dan untuk dimensi di mana t' memiliki tunggal, replikasi yang tunggal sesuai dengan jumlah kali. Situasi lain adalah kesalahan. (Untuk distribusi skalar, kita hanya menyiarkan terhadap batch_shape , karena event_shape = [] .)
  4. Sekarang kami akhirnya bisa menghitung log_prob . Tensor yang dihasilkan akan memiliki bentuk [sample_shape, batch_shape] , di mana sample_shape didefinisikan sebagai setiap dimensi t atau t' ke kiri dari n -rightmost dimensi: sample_shape = shape(t)[:-n] .

Ini mungkin berantakan jika Anda tidak tahu apa artinya, jadi mari kita kerjakan beberapa contoh:

three_poissons.log_prob([10.])
<tf.Tensor: shape=(3,), dtype=float32, numpy=array([-16.104412 ,  -2.0785608, -69.05272  ], dtype=float32)>

Tensor [10.] (dengan bentuk [1] ) disiarkan di seluruh batch_shape dari 3, jadi kita mengevaluasi probabilitas log ketiga Poissons' pada nilai 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)>

Dalam contoh di atas, tensor masukan memiliki bentuk [2, 2, 1] , sedangkan distribusi objek memiliki bentuk batch 3. Jadi untuk masing-masing [2, 2] dimensi sampel, nilai tunggal yang disediakan mendapat broadcats untuk masing-masing dari tiga Poisson.

Cara yang mungkin berguna untuk memikirkan itu: karena three_poissons memiliki batch_shape = [2, 3] , panggilan untuk log_prob harus mengambil Tensor yang dimensi terakhir adalah 1 atau 3; hal lain adalah kesalahan. (Aturan penyiaran numpy mengobati kasus khusus dari skalar sebagai benar-benar setara dengan Tensor bentuk [1] .)

Mari kita uji daging kita dengan bermain dengan distribusi Poisson lebih kompleks dengan 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)>

Contoh di atas melibatkan penyiaran melalui batch, tetapi bentuk sampelnya kosong. Misalkan kita memiliki kumpulan nilai, dan kita ingin mendapatkan probabilitas log dari setiap nilai pada setiap titik dalam batch. Kita bisa melakukannya secara manual:

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

Atau kita bisa membiarkan penyiaran menangani dimensi batch terakhir:

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

Kami juga dapat (mungkin agak kurang alami) membiarkan penyiaran hanya menangani dimensi batch pertama:

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

Atau kita bisa membiarkan penyiaran pegangan kedua bets dimensi:

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

Di atas bekerja dengan baik ketika kami hanya memiliki dua nilai yang kami inginkan, tetapi misalkan kami memiliki daftar panjang nilai yang ingin kami evaluasi di setiap titik batch. Untuk itu, notasi berikut, yang menambahkan dimensi ekstra ukuran 1 ke sisi kanan bentuk, sangat berguna:

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

Ini adalah sebuah contoh dari notasi slice strided , yang bernilai mengetahui.

Akan kembali ke three_poissons untuk kelengkapan, misalnya sama terlihat seperti:

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

Distribusi multivariat

Kami sekarang beralih ke distribusi multivarian, yang memiliki bentuk acara yang tidak kosong. Mari kita lihat distribusi multinomial.

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)

Perhatikan bagaimana dalam tiga contoh terakhir, batch_shape selalu [2] , namun kita dapat menggunakan penyiaran baik memiliki bersama total_count atau bersama probs (atau tidak), karena di bawah tenda mereka disiarkan memiliki bentuk yang sama.

Pengambilan sampel sangat mudah, mengingat apa yang sudah kita ketahui:

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)

Menghitung probabilitas log sama mudahnya. Mari kita bekerja contoh dengan distribusi Normal Multivariat diagonal. (Multinomial tidak terlalu ramah siaran, karena kendala pada jumlah dan probabilitas berarti penyiaran akan sering menghasilkan nilai yang tidak dapat diterima.) Kami akan menggunakan kumpulan 2 distribusi 3 dimensi dengan rata-rata yang sama tetapi skala yang berbeda (deviasi standar):

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>

(Perhatikan bahwa meskipun kami menggunakan distribusi mana timbangan yang kelipatan identitas, ini bukan pembatasan, kita bisa lulus scale bukannya scale_identity_multiplier .)

Sekarang mari kita evaluasi probabilitas log dari setiap titik batch pada meannya dan pada mean yang digeser:

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

Tepat ekuivalen, kita dapat menggunakan https://www.tensorflow.org/api_docs/cc/class/tensorflow/ops/strided-slice untuk menyisipkan bentuk ekstra = 1 dimensi di tengah konstan:

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

Di sisi lain, jika kita tidak melakukan insert dimensi ekstra, kami melewati [1., 2., 3.] ke titik angkatan pertama dan [3., 4., 5.] untuk yang kedua:

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

Teknik Manipulasi Bentuk

Bijektor Bentuk Ulang

The Reshape bijector dapat digunakan untuk membentuk kembali event_shape dari distribusi. Mari kita lihat contohnya:

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>

Kami menciptakan multinomial dengan bentuk acara [6] . The Pembentuk Bijector memungkinkan kita untuk memperlakukan ini sebagai distribusi dengan bentuk acara [2, 3] .

Sebuah Bijector merupakan terdiferensiasi, satu-ke-satu fungsi pada bagian terbuka dari \({\mathbb R}^n\). Bijectors digunakan dalam hubungannya dengan TransformedDistribution , yang model distribusi \(p(y)\) dalam hal distribusi dasar \(p(x)\) dan Bijector yang mewakili \(Y = g(X)\). Mari kita lihat aksinya:

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>

Ini adalah satu-satunya hal yang Reshape bijector bisa melakukannya: tidak dapat mengubah acara dimensi ke dimensi batch atau sebaliknya.

Distribusi Independen

The Independent distribusi digunakan untuk mengobati koleksi independen, tidak-tentu-identik (alias batch) distribusi sebagai distribusi tunggal. Lebih ringkas, Independent memungkinkan untuk mengkonversi dimensi dalam batch_shape untuk dimensi dalam event_shape . Kami akan mengilustrasikan dengan contoh:

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>

Kita dapat menganggap ini sebagai susunan koin dua kali lima dengan probabilitas kepala yang terkait. Mari kita evaluasi probabilitas himpunan satu-dan-nol tertentu yang arbitrer:

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

Kita dapat menggunakan Independent untuk mengubah ini menjadi dua "set lima Bernoulli" yang berbeda, yang berguna jika kita ingin mempertimbangkan "baris" koin membalik datang dalam pola yang diberikan sebagai hasil tunggal:

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>

Secara matematis, kami menghitung probabilitas log dari setiap "kumpulan" lima dengan menjumlahkan probabilitas log dari lima lemparan koin "independen" dalam himpunan, di situlah distribusi mendapatkan namanya:

two_sets_of_five.log_prob(pattern)
<tf.Tensor: shape=(2,), dtype=float32, numpy=array([-5.160732 , -3.1954036], dtype=float32)>

Kita bisa pergi lebih jauh dan menggunakan Independent untuk membuat distribusi di mana peristiwa individu adalah seperangkat dua-by-lima Bernoulli:

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>

Itu perlu dicatat bahwa dari perspektif sample , menggunakan Independent tidak ada perubahan:

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)

Sebagai latihan perpisahan bagi pembaca, kami sarankan mengingat perbedaan dan persamaan antara batch vektor Normal distribusi dan MultivariateNormalDiag distribusi dari sampling dan log perspektif probabilitas. Bagaimana kita bisa menggunakan Independent untuk membangun sebuah MultivariateNormalDiag dari batch Normal s? (Perhatikan bahwa MultivariateNormalDiag tidak benar-benar dilaksanakan dengan cara ini.)