Посмотреть на 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:
- Форма события описывает форму одного розыгрыша от распределения; это может зависеть от размеров. Для скалярных распределений, форма события
[]
. Для 5-мерного MultivariateNormal, форма события[5]
. - Пакетная форма описывает независимой, не одинаково распределены розыгрышами, иначе «партия» распределения.
- Образец формы описывает независимые одинаково распределенные розыгрыши партий из семейства распределения.
Форма событий и партия форма являются свойствами Distribution
объекта, в то время как образец форма связана с определенным вызовом sample
или log_prob
.
Цель этой записной книжки - проиллюстрировать эти концепции на примерах, поэтому, если это не сразу очевидно, не волнуйтесь!
Другой концептуальный обзор этих понятий см этот блог .
Заметка о TensorFlow Eager.
Весь этот ноутбук написан с использованием TensorFlow Нетерпеливый . Ни одна из концепций не представлены полагаются на Нетерпеливый, хотя и с Нетерпеливые, распределение партии и события формы оцениваются (и , следовательно , они известны) , когда Distribution
объект создается в Python, тогда как в графике (режиме без Eager), то можно определить распределение чьи формы событий и пакетов не определены до тех пор, пока график не будет запущен.
Скалярные распределения
Как мы уже отмечали выше, Distribution
объекта определенного события и командные формы. Начнем с утилиты для описания дистрибутивов:
def describe_distributions(distributions):
print('\n'.join([str(d) for d in distributions]))
В этом разделе мы рассмотрим скалярные распределения: распределение с формой событий []
. Типичным примером является распределение Пуассона, задается rate
:
poisson_distributions = [
tfd.Poisson(rate=1., name='One Poisson Scalar Batch'),
tfd.Poisson(rate=[1., 10., 100.], name='Three Poissons'),
tfd.Poisson(rate=[[1., 10., 100.,], [2., 20., 200.]],
name='Two-by-Three Poissons'),
tfd.Poisson(rate=[1.], name='One Poisson Vector Batch'),
tfd.Poisson(rate=[[1.]], name='One Poisson Expanded Batch')
]
describe_distributions(poisson_distributions)
tfp.distributions.Poisson("One_Poisson_Scalar_Batch", batch_shape=[], event_shape=[], dtype=float32) tfp.distributions.Poisson("Three_Poissons", batch_shape=[3], event_shape=[], dtype=float32) tfp.distributions.Poisson("Two_by_Three_Poissons", batch_shape=[2, 3], event_shape=[], dtype=float32) tfp.distributions.Poisson("One_Poisson_Vector_Batch", batch_shape=[1], event_shape=[], dtype=float32) tfp.distributions.Poisson("One_Poisson_Expanded_Batch", batch_shape=[1, 1], event_shape=[], dtype=float32)
Распределение Пуассона является скаляром распределения, поэтому его форма события всегда []
. Если мы укажем больше ставок, они появятся в форме партии. Последняя пара примеров интересна: есть только одна ставка, но поскольку эта скорость встроена в массив numpy с непустой формой, эта форма становится формой пакета.
Стандартное нормальное распределение также является скаляром. Это форма события []
, так же , как для Пуассона, но мы будем играть с ним , чтобы увидеть наш первый пример вещания. Нормальный задается с помощью loc
и scale
параметров:
normal_distributions = [
tfd.Normal(loc=0., scale=1., name='Standard'),
tfd.Normal(loc=[0.], scale=1., name='Standard Vector Batch'),
tfd.Normal(loc=[0., 1., 2., 3.], scale=1., name='Different Locs'),
tfd.Normal(loc=[0., 1., 2., 3.], scale=[[1.], [5.]],
name='Broadcasting Scale')
]
describe_distributions(normal_distributions)
tfp.distributions.Normal("Standard", batch_shape=[], event_shape=[], dtype=float32) tfp.distributions.Normal("Standard_Vector_Batch", batch_shape=[1], event_shape=[], dtype=float32) tfp.distributions.Normal("Different_Locs", batch_shape=[4], event_shape=[], dtype=float32) tfp.distributions.Normal("Broadcasting_Scale", batch_shape=[2, 4], event_shape=[], dtype=float32)
Интересный пример выше является Broadcasting Scale
распределения. loc
параметр имеет форму [4]
, а scale
параметр имеет форму [2, 1]
. Используя Numpy правила радиовещательных , партия форма [2, 4]
. Эквивалент (но менее элегантно и не рекомендуемый) способ определения "Broadcasting Scale"
распределение будет:
describe_distributions(
[tfd.Normal(loc=[[0., 1., 2., 3], [0., 1., 2., 3.]],
scale=[[1., 1., 1., 1.], [5., 5., 5., 5.]])])
tfp.distributions.Normal("Normal", batch_shape=[2, 4], event_shape=[], dtype=float32)
Мы можем понять, почему нотация вещания полезна, хотя она также является источником головной боли и ошибок.
Выборка скалярных распределений
Есть две основные вещи , которые мы можем сделать с распределениями: мы можем sample
от них , и мы можем вычислить log_prob
с. Давайте сначала рассмотрим выборку. Основное правило заключается в том , что , когда мы выборку из распределения, полученный тензор имеет форму [sample_shape, batch_shape, event_shape]
, где batch_shape
и event_shape
обеспечиваются Distribution
объекта и sample_shape
обеспечивается вызовом sample
. Для скалярных распределений, event_shape = []
, поэтому Тензорная вернулся из образца будет иметь форму [sample_shape, batch_shape]
. Давай попробуем:
def describe_sample_tensor_shape(sample_shape, distribution):
print('Sample shape:', sample_shape)
print('Returned sample tensor shape:',
distribution.sample(sample_shape).shape)
def describe_sample_tensor_shapes(distributions, sample_shapes):
started = False
for distribution in distributions:
print(distribution)
for sample_shape in sample_shapes:
describe_sample_tensor_shape(sample_shape, distribution)
print()
sample_shapes = [1, 2, [1, 5], [3, 4, 5]]
describe_sample_tensor_shapes(poisson_distributions, sample_shapes)
tfp.distributions.Poisson("One_Poisson_Scalar_Batch", batch_shape=[], event_shape=[], dtype=float32) Sample shape: 1 Returned sample tensor shape: (1,) Sample shape: 2 Returned sample tensor shape: (2,) Sample shape: [1, 5] Returned sample tensor shape: (1, 5) Sample shape: [3, 4, 5] Returned sample tensor shape: (3, 4, 5) tfp.distributions.Poisson("Three_Poissons", batch_shape=[3], event_shape=[], dtype=float32) Sample shape: 1 Returned sample tensor shape: (1, 3) Sample shape: 2 Returned sample tensor shape: (2, 3) Sample shape: [1, 5] Returned sample tensor shape: (1, 5, 3) Sample shape: [3, 4, 5] Returned sample tensor shape: (3, 4, 5, 3) tfp.distributions.Poisson("Two_by_Three_Poissons", batch_shape=[2, 3], event_shape=[], dtype=float32) Sample shape: 1 Returned sample tensor shape: (1, 2, 3) Sample shape: 2 Returned sample tensor shape: (2, 2, 3) Sample shape: [1, 5] Returned sample tensor shape: (1, 5, 2, 3) Sample shape: [3, 4, 5] Returned sample tensor shape: (3, 4, 5, 2, 3) tfp.distributions.Poisson("One_Poisson_Vector_Batch", batch_shape=[1], event_shape=[], dtype=float32) Sample shape: 1 Returned sample tensor shape: (1, 1) Sample shape: 2 Returned sample tensor shape: (2, 1) Sample shape: [1, 5] Returned sample tensor shape: (1, 5, 1) Sample shape: [3, 4, 5] Returned sample tensor shape: (3, 4, 5, 1) tfp.distributions.Poisson("One_Poisson_Expanded_Batch", batch_shape=[1, 1], event_shape=[], dtype=float32) Sample shape: 1 Returned sample tensor shape: (1, 1, 1) Sample shape: 2 Returned sample tensor shape: (2, 1, 1) Sample shape: [1, 5] Returned sample tensor shape: (1, 5, 1, 1) Sample shape: [3, 4, 5] Returned sample tensor shape: (3, 4, 5, 1, 1)
describe_sample_tensor_shapes(normal_distributions, sample_shapes)
tfp.distributions.Normal("Standard", batch_shape=[], event_shape=[], dtype=float32) Sample shape: 1 Returned sample tensor shape: (1,) Sample shape: 2 Returned sample tensor shape: (2,) Sample shape: [1, 5] Returned sample tensor shape: (1, 5) Sample shape: [3, 4, 5] Returned sample tensor shape: (3, 4, 5) tfp.distributions.Normal("Standard_Vector_Batch", batch_shape=[1], event_shape=[], dtype=float32) Sample shape: 1 Returned sample tensor shape: (1, 1) Sample shape: 2 Returned sample tensor shape: (2, 1) Sample shape: [1, 5] Returned sample tensor shape: (1, 5, 1) Sample shape: [3, 4, 5] Returned sample tensor shape: (3, 4, 5, 1) tfp.distributions.Normal("Different_Locs", batch_shape=[4], event_shape=[], dtype=float32) Sample shape: 1 Returned sample tensor shape: (1, 4) Sample shape: 2 Returned sample tensor shape: (2, 4) Sample shape: [1, 5] Returned sample tensor shape: (1, 5, 4) Sample shape: [3, 4, 5] Returned sample tensor shape: (3, 4, 5, 4) tfp.distributions.Normal("Broadcasting_Scale", batch_shape=[2, 4], event_shape=[], dtype=float32) Sample shape: 1 Returned sample tensor shape: (1, 2, 4) Sample shape: 2 Returned sample tensor shape: (2, 2, 4) Sample shape: [1, 5] Returned sample tensor shape: (1, 5, 2, 4) Sample shape: [3, 4, 5] Returned sample tensor shape: (3, 4, 5, 2, 4)
Вот и все , что можно сказать об sample
: возвращаемые образцы тензоры имеют форму [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]
.
Это было бы все, что можно было бы сказать, если бы не радиовещание. Вот правила, если принять во внимание трансляцию. Опишем его в полной общности и отметим упрощения для скалярных распределений:
- Определение
n = len(batch_shape) + len(event_shape)
. (Для скалярных распределений,len(event_shape)=0
.) - Если входной тензор
t
имеет меньше , чемn
размеры, колодки свою форму, добавляя размеры размер1
слева , пока она не имеет ровноn
размеры. Вызвать результирующий тензорt'
. - Broadcast в
n
размеры правых изt'
против[batch_shape, event_shape]
распределения вы вычисленияlog_prob
для. Более подробно: для измерений , гдеt'
уже спичек распределения, ничего не делать, а для измерений , гдеt'
имеет синглтон, тиражировать , что синглтон соответствующего числа раз. Любая другая ситуация - ошибка. (Для скалярных распределений, мы только транслировать противbatch_shape
, так как event_shape =[]
) . - Теперь мы , наконец , удалось вычислить
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; все остальное - ошибка. (Правила Numpy вещания рассматривать частный случай скаляра как абсолютно эквивалентен тензор формы [1]
.)
Давайте проверим наши отбивные, играя с более сложным распределением Пуассона с batch_shape = [2, 3]
:
poisson_2_by_3 = tfd.Poisson(
rate=[[1., 10., 100.,], [2., 20., 200.]],
name='Two-by-Three Poissons')
poisson_2_by_3.log_prob(1.)
<tf.Tensor: shape=(2, 3), dtype=float32, numpy= array([[ -1. , -7.697415 , -95.39484 ], [ -1.3068528, -17.004269 , -194.70169 ]], dtype=float32)>
poisson_2_by_3.log_prob([1.]) # Exactly equivalent to above, demonstrating the scalar special case.
<tf.Tensor: shape=(2, 3), dtype=float32, numpy= array([[ -1. , -7.697415 , -95.39484 ], [ -1.3068528, -17.004269 , -194.70169 ]], dtype=float32)>
poisson_2_by_3.log_prob([[1., 1., 1.], [1., 1., 1.]]) # Another way to write the same thing. No broadcasting.
<tf.Tensor: shape=(2, 3), dtype=float32, numpy= array([[ -1. , -7.697415 , -95.39484 ], [ -1.3068528, -17.004269 , -194.70169 ]], dtype=float32)>
poisson_2_by_3.log_prob([[1., 10., 100.]]) # Input is [1, 3] broadcast to [2, 3].
<tf.Tensor: shape=(2, 3), dtype=float32, numpy= array([[ -1. , -2.0785608, -3.2223587], [ -1.3068528, -5.14709 , -33.90767 ]], dtype=float32)>
poisson_2_by_3.log_prob([[1., 10., 100.], [1., 10., 100.]]) # Equivalent to above. No broadcasting.
<tf.Tensor: shape=(2, 3), dtype=float32, numpy= array([[ -1. , -2.0785608, -3.2223587], [ -1.3068528, -5.14709 , -33.90767 ]], dtype=float32)>
poisson_2_by_3.log_prob([[1., 1., 1.], [2., 2., 2.]]) # No broadcasting.
<tf.Tensor: shape=(2, 3), dtype=float32, numpy= array([[ -1. , -7.697415 , -95.39484 ], [ -1.3068528, -14.701683 , -190.09653 ]], dtype=float32)>
poisson_2_by_3.log_prob([[1.], [2.]]) # Equivalent to above. Input shape [2, 1] broadcast to [2, 3].
<tf.Tensor: shape=(2, 3), dtype=float32, numpy= array([[ -1. , -7.697415 , -95.39484 ], [ -1.3068528, -14.701683 , -190.09653 ]], dtype=float32)>
В приведенных выше примерах использовалась широковещательная передача по партии, но форма образца была пустой. Предположим, у нас есть набор значений, и мы хотим получить логарифмическую вероятность каждого значения в каждой точке пакета. Мы могли сделать это вручную:
poisson_2_by_3.log_prob([[[1., 1., 1.], [1., 1., 1.]], [[2., 2., 2.], [2., 2., 2.]]]) # Input shape [2, 2, 3].
<tf.Tensor: shape=(2, 2, 3), dtype=float32, numpy= array([[[ -1. , -7.697415 , -95.39484 ], [ -1.3068528, -17.004269 , -194.70169 ]], [[ -1.6931472, -6.087977 , -91.48282 ], [ -1.3068528, -14.701683 , -190.09653 ]]], dtype=float32)>
Или мы могли бы позволить широковещательной передаче обрабатывать последнее измерение пакета:
poisson_2_by_3.log_prob([[[1.], [1.]], [[2.], [2.]]]) # Input shape [2, 2, 1].
<tf.Tensor: shape=(2, 2, 3), dtype=float32, numpy= array([[[ -1. , -7.697415 , -95.39484 ], [ -1.3068528, -17.004269 , -194.70169 ]], [[ -1.6931472, -6.087977 , -91.48282 ], [ -1.3068528, -14.701683 , -190.09653 ]]], dtype=float32)>
Мы также можем (возможно, несколько менее естественно) позволить широковещательной передаче обрабатывать только первое измерение пакета:
poisson_2_by_3.log_prob([[[1., 1., 1.]], [[2., 2., 2.]]]) # Input shape [2, 1, 3].
<tf.Tensor: shape=(2, 2, 3), dtype=float32, numpy= array([[[ -1. , -7.697415 , -95.39484 ], [ -1.3068528, -17.004269 , -194.70169 ]], [[ -1.6931472, -6.087977 , -91.48282 ], [ -1.3068528, -14.701683 , -190.09653 ]]], dtype=float32)>
Или мы могли позволить передавать ручки и пакетные размеры:
poisson_2_by_3.log_prob([[[1.]], [[2.]]]) # Input shape [2, 1, 1].
<tf.Tensor: shape=(2, 2, 3), dtype=float32, numpy= array([[[ -1. , -7.697415 , -95.39484 ], [ -1.3068528, -17.004269 , -194.70169 ]], [[ -1.6931472, -6.087977 , -91.48282 ], [ -1.3068528, -14.701683 , -190.09653 ]]], dtype=float32)>
Вышеупомянутое работало нормально, когда у нас было только два значения, которые мы хотели, но предположим, что у нас есть длинный список значений, которые мы хотели бы оценивать в каждой точке пакета. Для этого чрезвычайно полезны следующие обозначения, которые добавляют дополнительные размеры размера 1 к правой стороне фигуры:
poisson_2_by_3.log_prob(tf.constant([1., 2.])[..., tf.newaxis, tf.newaxis])
<tf.Tensor: shape=(2, 2, 3), dtype=float32, numpy= array([[[ -1. , -7.697415 , -95.39484 ], [ -1.3068528, -17.004269 , -194.70169 ]], [[ -1.6931472, -6.087977 , -91.48282 ], [ -1.3068528, -14.701683 , -190.09653 ]]], dtype=float32)>
Это пример 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 трехмерных распределений с одинаковым средним, но разными масштабами (стандартными отклонениями):
two_multivariate_normals = tfd.MultivariateNormalDiag(loc=[1., 2., 3.], scale_identity_multiplier=[1., 2.])
two_multivariate_normals
<tfp.distributions.MultivariateNormalDiag 'MultivariateNormalDiag' batch_shape=[2] event_shape=[3] dtype=float32>
(Обратите внимание , что , хотя мы использовали распределения , где масштабы были кратны идентичности, это не является ограничением на, мы могли бы пройти scale
вместо scale_identity_multiplier
.)
Теперь давайте оценим логарифмическую вероятность каждой точки партии по ее среднему и смещенному среднему значению:
two_multivariate_normals.log_prob([[[1., 2., 3.]], [[3., 4., 5.]]]) # Input has shape [2,1,3].
<tf.Tensor: shape=(2, 2), dtype=float32, numpy= array([[-2.7568154, -4.836257 ], [-8.756816 , -6.336257 ]], dtype=float32)>
Точно то же самое, мы можем использовать https://www.tensorflow.org/api_docs/cc/class/tensorflow/ops/strided-slice вставить дополнительную форму = 1 измерение в середине константы:
two_multivariate_normals.log_prob(
tf.constant([[1., 2., 3.], [3., 4., 5.]])[:, tf.newaxis, :]) # Equivalent to above.
<tf.Tensor: shape=(2, 2), dtype=float32, numpy= array([[-2.7568154, -4.836257 ], [-8.756816 , -6.336257 ]], dtype=float32)>
С другой стороны, если мы не делаем вставки дополнительного измерения, мы переходим [1., 2., 3.]
к первой партии точки и [3., 4., 5.]
в секунду:
two_multivariate_normals.log_prob(tf.constant([[1., 2., 3.], [3., 4., 5.]]))
<tf.Tensor: shape=(2,), dtype=float32, numpy=array([-2.7568154, -6.336257 ], dtype=float32)>
Методы изменения формы
Изменяющий бижектор
Reshape
bijector может быть использован для изменения формы 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]
. Reshape 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
, чтобы превратить это в двух различных «наборов пять Бернулли», что полезно , если мы хотим рассмотреть «ряд» монеты переворачивается придумывают в данной схеме, один исход:
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
s? (Обратите внимание , что MultivariateNormalDiag
не на самом деле реализован таким образом.)