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:
- Tentukan
n = len(batch_shape) + len(event_shape)
. (Untuk distribusi skalar,len(event_shape)=0
.) - Jika input tensor
t
memiliki kurang darin
dimensi, pad bentuknya dengan menambahkan dimensi ukuran1
di sebelah kiri sampai memiliki tepatn
dimensi. Panggil dihasilkan tensort'
. - Siaran
n
dimensi paling kanan darit'
terhadap[batch_shape, event_shape]
dari distribusi Anda komputasilog_prob
untuk. Secara lebih rinci: untuk dimensi di manat'
sudah cocok distribusi, melakukan apa-apa, dan untuk dimensi di manat'
memiliki tunggal, replikasi yang tunggal sesuai dengan jumlah kali. Situasi lain adalah kesalahan. (Untuk distribusi skalar, kita hanya menyiarkan terhadapbatch_shape
, karena event_shape =[]
.) - Sekarang kami akhirnya bisa menghitung
log_prob
. Tensor yang dihasilkan akan memiliki bentuk[sample_shape, batch_shape]
, di manasample_shape
didefinisikan sebagai setiap dimensit
ataut'
ke kiri darin
-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.)