Посмотреть на TensorFlow.org | Запускаем в Google Colab | Посмотреть исходный код на GitHub | Скачать блокнот |
Введение
TensorFlow Вероятность (TFP) предлагает ряд JointDistribution
абстракций , которые делают вероятностный вывод проще, позволяя пользователю легко выразить вероятностную графическую модель в почти математической форме; абстракция генерирует методы для выборки из модели и оценки логарифмической вероятности выборок из модели. В этом уроке мы рассмотрим «autobatched» варианты, которые были разработаны после оригинальных JointDistribution
абстракций. По сравнению с исходными абстракциями без автозаполнения, версии с автозаполнением проще в использовании и более эргономичны, что позволяет выражать многие модели с меньшим количеством шаблонов. В этой коллаборации мы исследуем простую модель в (возможно, утомительных) деталях, проясняя проблемы, которые решает автоматическая пакетная обработка, и (надеюсь) по ходу обучения читателя больше о концепциях формы TFP.
До введения autobatching, там было несколько различных вариантов JointDistribution
, соответствующий различные синтаксических стили для выражения вероятностных моделей: JointDistributionSequential
, JointDistributionNamed
и JointDistributionCoroutine
. Auobatching существует как примесь, так что мы теперь имеем AutoBatched
варианты всех из них. В этом уроке мы рассмотрим различие между JointDistributionSequential
и JointDistributionSequentialAutoBatched
; однако все, что мы здесь делаем, применимо и к другим вариантам, практически без изменений.
Зависимости и предварительные условия
Импорт и настройки
import functools
import numpy as np
import tensorflow.compat.v2 as tf
tf.enable_v2_behavior()
import tensorflow_probability as tfp
tfd = tfp.distributions
Предпосылка: проблема байесовской регрессии
Мы рассмотрим очень простой сценарий байесовской регрессии:
\[ \begin{align*} m & \sim \text{Normal}(0, 1) \\ b & \sim \text{Normal}(0, 1) \\ Y & \sim \text{Normal}(mX + b, 1) \end{align*} \]
В этой модели, m
и b
взяты из стандартных нормалей, и наблюдения Y
взяты из нормального распределения которого среднее зависит от случайных величин m
и b
, а некоторые (неслучайный, известный) ковариаты X
. (Для простоты в этом примере мы предполагаем, что масштаб всех случайных величин известен.)
Для выполнения логического вывода в этой модели, мы должны были бы знать как регрессор X
и наблюдение Y
, но для целей данного руководства, мы должны будем только X
, поэтому мы определим простой фиктивный X
:
X = np.arange(7)
X
array([0, 1, 2, 3, 4, 5, 6])
Desiderata
В вероятностном выводе мы часто хотим выполнить две основные операции:
-
sample
: Чертеж образцов от модели. -
log_prob
: Вычисление вероятности журнала образца от модели.
Основной вклад СФП в JointDistribution
абстракций (а также многие другие подходы к вероятностному программированию), чтобы позволить пользователям создавать модели раз и иметь доступ к обоим sample
и log_prob
вычислений.
Учитывая , что мы имеем 7 точек в наборе данных ( X.shape = (7,)
), мы можем констатировать пожелания к отличной JointDistribution
:
-
sample()
должен составить списокTensors
, имеющих форму[(), (), (7,)
], что соответствует скалярному наклону, скалярному смещению и вектор наблюдений, соответственно. -
log_prob(sample())
должен произвести скаляр: вероятность журнала конкретного наклона, смещения и наблюдений. -
sample([5, 3])
должен составить списокTensors
, имеющих форму[(5, 3), (5, 3), (5, 3, 7)]
, представляющий собой(5, 3)
- партию образцов из модель. -
log_prob(sample([5, 3]))
должен производитьTensor
с формой (5, 3).
Теперь мы рассмотрим последовательность JointDistribution
моделей, посмотреть , как достичь вышеуказанных желательных, и мы надеемся узнать немного больше о TFP формы по пути.
Спойлер предупреждение: Подход , который удовлетворяет выше Desiderata без добавления шаблонного является autobatching .
Первая попытка; JointDistributionSequential
jds = tfd.JointDistributionSequential([
tfd.Normal(loc=0., scale=1.), # m
tfd.Normal(loc=0., scale=1.), # b
lambda b, m: tfd.Normal(loc=m*X + b, scale=1.) # Y
])
Это более или менее прямой перевод модели в код. Склон m
и смещение b
просты. Y
определяется с помощью lambda
-функции: общая картина является то , что lambda
- -функции \(k\) аргументов в JointDistributionSequential
(JDS) использует предыдущие \(k\) распределений в модели. Обратите внимание на «обратный» порядок.
Мы будем называть sample_distributions
, которая возвращает как образец и основные «суб-распределения» , которые были использованы для создания образца. (Мы могли произвести только образец, вызвав sample
, позже в учебнике будет удобно иметь распределение , а также.) Образец мы производим в порядке:
dists, sample = jds.sample_distributions()
sample
[<tf.Tensor: shape=(), dtype=float32, numpy=-1.668757>, <tf.Tensor: shape=(), dtype=float32, numpy=0.6585061>, <tf.Tensor: shape=(7,), dtype=float32, numpy= array([ 0.18573815, -1.79962 , -1.8106272 , -3.5971394 , -6.6625295 , -7.308844 , -9.832693 ], dtype=float32)>]
Но log_prob
дает результат с нежелательной формой:
jds.log_prob(sample)
<tf.Tensor: shape=(7,), dtype=float32, numpy= array([-4.4777603, -4.6775575, -4.7430477, -4.647725 , -4.5746684, -4.4368567, -4.480562 ], dtype=float32)>
И множественная выборка не работает:
try:
jds.sample([5, 3])
except tf.errors.InvalidArgumentError as e:
print(e)
Incompatible shapes: [5,3] vs. [7] [Op:Mul]
Попробуем разобраться, что не так.
Краткий обзор: пакет и форма события
В TFP, обычное (не JointDistribution
) распределение вероятностей имеет форму событий и пакетную форму, и понимание разницы имеет решающее значение для эффективного использования TFP:
- Форма события описывает форму единственного розыгрыша из раздачи; розыгрыш может зависеть от размеров. Для скалярных распределений форма события - []. Для 5-мерного MultivariateNormal форма события [5].
- Форма пакета описывает независимые, не идентично распределенные розыгрыши, также известные как «пакет» распределений. Представление пакета дистрибутивов в одном объекте Python - один из ключевых способов повышения эффективности TFP при масштабировании.
Для наших целей, критический факт , чтобы иметь в виду, что если мы называем log_prob
на одном образце из распределения, то результат всегда будет иметь форму, спички (т.е., имеет в качестве крайних правых размеров) пакетное форму.
Для обсуждения более углубленном форм см в «Понимание TensorFlow Распределения Shapes» учебник .
Почему не log_prob(sample())
Производит скаляр?
Давайте использовать наши знания о пакетном и события формы , чтобы исследовать то , что происходит с log_prob(sample())
. Вот снова наш образец:
sample
[<tf.Tensor: shape=(), dtype=float32, numpy=-1.668757>, <tf.Tensor: shape=(), dtype=float32, numpy=0.6585061>, <tf.Tensor: shape=(7,), dtype=float32, numpy= array([ 0.18573815, -1.79962 , -1.8106272 , -3.5971394 , -6.6625295 , -7.308844 , -9.832693 ], dtype=float32)>]
А вот и наши раздачи:
dists
[<tfp.distributions.Normal 'Normal' batch_shape=[] event_shape=[] dtype=float32>, <tfp.distributions.Normal 'Normal' batch_shape=[] event_shape=[] dtype=float32>, <tfp.distributions.Normal 'JointDistributionSequential_sample_distributions_Normal' batch_shape=[7] event_shape=[] dtype=float32>]
Логарифмическая вероятность вычисляется путем суммирования логарифмических вероятностей подраспределений в (согласованных) элементах частей:
log_prob_parts = [dist.log_prob(s) for (dist, s) in zip(dists, sample)]
log_prob_parts
[<tf.Tensor: shape=(), dtype=float32, numpy=-2.3113134>, <tf.Tensor: shape=(), dtype=float32, numpy=-1.1357536>, <tf.Tensor: shape=(7,), dtype=float32, numpy= array([-1.0306933, -1.2304904, -1.2959809, -1.200658 , -1.1276014, -0.9897899, -1.0334952], dtype=float32)>]
np.sum(log_prob_parts) - jds.log_prob(sample)
<tf.Tensor: shape=(7,), dtype=float32, numpy=array([0., 0., 0., 0., 0., 0., 0.], dtype=float32)>
Таким образом, один уровень объяснения в том , что вычисление вероятности журнала возвращает 7-тензорный , потому что третья подкомпонента log_prob_parts
является 7-Тензором. Но почему?
Ну, мы видим , что последний элемент dists
, который соответствует нашему распределению по Y
в mathematial композиции, имеет batch_shape
из [7]
. Другими словами, наше распределение по Y
представляет собой пакетный из 7 независимых нормалей (с различными способами и, в данном случае, тот же масштаб).
Теперь мы понимаем , что это не так: в JDS, распределение по Y
имеет batch_shape=[7]
, образец из JDS представляет скаляры для m
и b
и «партии» 7 независимых нормалей. и log_prob
вычисляет 7 отдельных лог-вероятности, каждая из которых представляет собой вероятность того журнала рисования m
и b
и единого наблюдения Y[i]
в какой - то X[i]
.
Крепление log_prob(sample())
с Independent
Напомним , что dists[2]
имеет event_shape=[]
и batch_shape=[7]
:
dists[2]
<tfp.distributions.Normal 'JointDistributionSequential_sample_distributions_Normal' batch_shape=[7] event_shape=[] dtype=float32>
Используя TFP в Independent
метадистрибутив, преобразующий размеры партии до размеров событий, мы можем преобразовать это в распределение с event_shape=[7]
и batch_shape=[]
(мы будем переименовать его y_dist_i
, потому что это распределение на Y
, с _i
стоя в нашей Independent
упаковке):
y_dist_i = tfd.Independent(dists[2], reinterpreted_batch_ndims=1)
y_dist_i
<tfp.distributions.Independent 'IndependentJointDistributionSequential_sample_distributions_Normal' batch_shape=[] event_shape=[7] dtype=float32>
Теперь log_prob
из 7-вектора является скаляром:
y_dist_i.log_prob(sample[2])
<tf.Tensor: shape=(), dtype=float32, numpy=-7.9087086>
Под чехлами, Independent
сумм по партии:
y_dist_i.log_prob(sample[2]) - tf.reduce_sum(dists[2].log_prob(sample[2]))
<tf.Tensor: shape=(), dtype=float32, numpy=0.0>
И в самом деле, мы можем использовать это , чтобы построить новый jds_i
( i
снова означает Independent
) , где log_prob
возвращает скаляр:
jds_i = tfd.JointDistributionSequential([
tfd.Normal(loc=0., scale=1.), # m
tfd.Normal(loc=0., scale=1.), # b
lambda b, m: tfd.Independent( # Y
tfd.Normal(loc=m*X + b, scale=1.),
reinterpreted_batch_ndims=1)
])
jds_i.log_prob(sample)
<tf.Tensor: shape=(), dtype=float32, numpy=-11.355776>
Пара заметок:
-
jds_i.log_prob(s)
не то же самое , какtf.reduce_sum(jds.log_prob(s))
. Первый дает "правильную" логарифмическую вероятность совместного распределения. Последние суммы по 7-тензором, каждый элемент которого равен сумме бревенчатого вероятностиm
,b
, и один элемент журнала вероятностиY
, так что overcountsm
иb
. (log_prob(m) + log_prob(b) + log_prob(Y)
, возвращает результат , а не бросать исключение , потому что СФП следует TF и вещание правила Numpy в;. Добавление скаляра к вектору формирует вектор размера результата) - В данном конкретном случае, мы могли бы решить эту проблему и добиться того же результата , используя
MultivariateNormalDiag
вместоIndependent(Normal(...))
.MultivariateNormalDiag
является вектор-распределением (то есть, у него уже есть вектор событий-форма). IndeeedMultivariateNormalDiag
может быть (но не) реализован в виде композицииIndependent
иNormal
. Это стоит помнить , что данный векторV
, образцы отn1 = Normal(loc=V)
, иn2 = MultivariateNormalDiag(loc=V)
неразличимы; разность beween этих распределений является то , чтоn1.log_prob(n1.sample())
является вектор иn2.log_prob(n2.sample())
является скаляром.
Несколько образцов?
Рисование нескольких образцов по-прежнему не работает:
try:
jds_i.sample([5, 3])
except tf.errors.InvalidArgumentError as e:
print(e)
Incompatible shapes: [5,3] vs. [7] [Op:Mul]
Давайте подумаем, почему. Когда мы называем jds_i.sample([5, 3])
, мы сначала нарисовать образцы для m
и b
, каждый из которых имеет форму (5, 3)
. Далее, мы будем пытаться построить Normal
распределение с помощью:
tfd.Normal(loc=m*X + b, scale=1.)
Но если m
имеет форму (5, 3)
и X
имеет форму 7
, мы не можем умножить их вместе, и на самом деле это ошибка мы попадя:
m = tfd.Normal(0., 1.).sample([5, 3])
try:
m * X
except tf.errors.InvalidArgumentError as e:
print(e)
Incompatible shapes: [5,3] vs. [7] [Op:Mul]
Чтобы решить эту проблему, давайте подумаем о том, какие свойства распределение по Y
имеет иметь. Если мы назвали jds_i.sample([5, 3])
, то мы знаем m
и b
оба имеют форму (5, 3)
. Какую форму следует призыв к sample
на Y
распределения продукции? Очевидный ответ (5, 3, 7)
: для каждой партии точки, мы хотим , чтобы образец того же размера , как X
. Мы можем добиться этого, используя возможности трансляции TensorFlow, добавляя дополнительные измерения:
m[..., tf.newaxis].shape
TensorShape([5, 3, 1])
(m[..., tf.newaxis] * X).shape
TensorShape([5, 3, 7])
Добавление оси и к m
и b
, мы можем определить новый JDS , которая поддерживает несколько образцов:
jds_ia = tfd.JointDistributionSequential([
tfd.Normal(loc=0., scale=1.), # m
tfd.Normal(loc=0., scale=1.), # b
lambda b, m: tfd.Independent( # Y
tfd.Normal(loc=m[..., tf.newaxis]*X + b[..., tf.newaxis], scale=1.),
reinterpreted_batch_ndims=1)
])
shaped_sample = jds_ia.sample([5, 3])
shaped_sample
[<tf.Tensor: shape=(5, 3), dtype=float32, numpy= array([[-1.1133379 , 0.16390413, -0.24177533], [-1.1312429 , -0.6224666 , -1.8182136 ], [-0.31343174, -0.32932565, 0.5164407 ], [-0.0119963 , -0.9079621 , 2.3655841 ], [-0.26293617, 0.8229698 , 0.31098196]], dtype=float32)>, <tf.Tensor: shape=(5, 3), dtype=float32, numpy= array([[-0.02876974, 1.0872147 , 1.0138507 ], [ 0.27367726, -1.331534 , -0.09084719], [ 1.3349475 , -0.68765205, 1.680652 ], [ 0.75436825, 1.3050154 , -0.9415123 ], [-1.2502679 , -0.25730947, 0.74611956]], dtype=float32)>, <tf.Tensor: shape=(5, 3, 7), dtype=float32, numpy= array([[[-1.8258233e+00, -3.0641669e-01, -2.7595463e+00, -1.6952467e+00, -4.8197951e+00, -5.2986512e+00, -6.6931367e+00], [ 3.6438566e-01, 1.0067395e+00, 1.4542470e+00, 8.1155670e-01, 1.8868095e+00, 2.3877139e+00, 1.0195159e+00], [-8.3624744e-01, 1.2518480e+00, 1.0943471e+00, 1.3052304e+00, -4.5756745e-01, -1.0668410e-01, -7.0669651e-02]], [[-3.1788960e-01, 9.2615485e-03, -3.0963073e+00, -2.2846246e+00, -3.2269263e+00, -6.0213070e+00, -7.4806519e+00], [-3.9149747e+00, -3.5155020e+00, -1.5669601e+00, -5.0759468e+00, -4.5065498e+00, -5.6719379e+00, -4.8012795e+00], [ 1.3053948e-01, -8.0493152e-01, -4.7845001e+00, -4.9721808e+00, -7.1365709e+00, -9.6198196e+00, -9.7951422e+00]], [[ 2.0621397e+00, 3.4639853e-01, 7.0252883e-01, -1.4311566e+00, 3.3790007e+00, 1.1619035e+00, -8.9105040e-01], [-7.8956139e-01, -8.5023916e-01, -9.7148323e-01, -2.6229355e+00, -2.7150445e+00, -2.4633870e+00, -2.1841538e+00], [ 7.7627432e-01, 2.2401071e+00, 3.7601702e+00, 2.4245868e+00, 4.0690269e+00, 4.0605016e+00, 5.1753912e+00]], [[ 1.4275590e+00, 3.3346462e+00, 1.5374103e+00, -2.2849756e-01, 9.1219616e-01, -3.1220305e-01, -3.2643962e-01], [-3.1910419e-02, -3.8848895e-01, 9.9946201e-02, -2.3619974e+00, -1.8507402e+00, -3.6830821e+00, -5.4907336e+00], [-7.1941972e-02, 2.1602919e+00, 4.9575748e+00, 4.2317696e+00, 9.3528280e+00, 1.0526063e+01, 1.5262107e+01]], [[-2.3257759e+00, -2.5343289e+00, -3.5342445e+00, -4.0423255e+00, -3.2361765e+00, -3.3434000e+00, -2.6849220e+00], [ 1.5006512e-02, -1.9866472e-01, 7.6781356e-01, 1.6228745e+00, 1.4191239e+00, 2.6655579e+00, 4.4663467e+00], [ 2.6599693e+00, 1.2663836e+00, 1.7162113e+00, 1.4839669e+00, 2.0559487e+00, 2.5976877e+00, 2.5977583e+00]]], dtype=float32)>]
jds_ia.log_prob(shaped_sample)
<tf.Tensor: shape=(5, 3), dtype=float32, numpy= array([[-12.483114 , -10.139662 , -11.514159 ], [-11.656767 , -17.201958 , -12.132455 ], [-17.838818 , -9.474525 , -11.24898 ], [-13.95219 , -12.490049 , -17.123957 ], [-14.487818 , -11.3755455, -10.576363 ]], dtype=float32)>
В качестве дополнительной проверки мы проверим, что вероятность записи в журнал для одной точки пакета соответствует тому, что было у нас раньше:
(jds_ia.log_prob(shaped_sample)[3, 1] -
jds_i.log_prob([shaped_sample[0][3, 1],
shaped_sample[1][3, 1],
shaped_sample[2][3, 1, :]]))
<tf.Tensor: shape=(), dtype=float32, numpy=0.0>
Автобатчинг для победы
Отлично! Теперь у нас есть версия JointDistribution , который обрабатывает все наши Desiderata: log_prob
возвращает скаляр благодаря использованию в tfd.Independent
, а также несколько образцов работают теперь, когда мы исправили вещания путем добавления дополнительных осей.
Что, если бы я сказал вам, что есть более простой и лучший способ? Существует, и это называется JointDistributionSequentialAutoBatched
(JDSAB):
jds_ab = tfd.JointDistributionSequentialAutoBatched([
tfd.Normal(loc=0., scale=1.), # m
tfd.Normal(loc=0., scale=1.), # b
lambda b, m: tfd.Normal(loc=m*X + b, scale=1.) # Y
])
jds_ab.log_prob(jds.sample())
<tf.Tensor: shape=(), dtype=float32, numpy=-12.954952>
shaped_sample = jds_ab.sample([5, 3])
jds_ab.log_prob(shaped_sample)
<tf.Tensor: shape=(5, 3), dtype=float32, numpy= array([[-12.191533 , -10.43885 , -16.371655 ], [-13.292994 , -11.97949 , -16.788685 ], [-15.987699 , -13.435732 , -10.6029 ], [-10.184758 , -11.969714 , -14.275676 ], [-12.740775 , -11.5654125, -12.990162 ]], dtype=float32)>
jds_ab.log_prob(shaped_sample) - jds_ia.log_prob(shaped_sample)
<tf.Tensor: shape=(5, 3), dtype=float32, numpy= array([[0., 0., 0.], [0., 0., 0.], [0., 0., 0.], [0., 0., 0.], [0., 0., 0.]], dtype=float32)>
Как это работает? В то время как вы могли бы попытаться прочитать код для глубокого понимания, мы дадим обзор кратко , которая достаточна для большинства случаев применения:
- Напомним , что наша первая проблема состояла в том , что наше распределение для
Y
былоbatch_shape=[7]
иevent_shape=[]
, и мы использовалиIndependent
для преобразования пакетного измерения к измерению событий. JDSAB игнорирует пакетные формы распределений компонентов; вместо этого он обрабатывает пакетный форму в качестве общей собственности модели, которая , как предполагается, будет[]
(если не указано иное, установивbatch_ndims > 0
). Эффект эквивалентен использованию tfd.Independent , чтобы преобразовать все размеры партии распределений компонентов в размерах событий, как мы делали вручную выше. - Наша вторая проблема заключалась в необходимости массировать формы
m
иb
так , чтобы они могли транслировать соответственно сX
при создании нескольких образцов. С JDSAB, вы пишете модель для создания единого образца, и мы «поднять» всю модель для создания нескольких образцов с использованием TensorFlow в vectorized_map . (Эта функция , аналогичная Джакс VMAP .)
Изучение вопроса пакетной формы более подробно, мы можем сравнить партию форму нашего оригинальных «плохой» совместного распределение jds
, наши Пакетных фиксированные распределения jds_i
и jds_ia
, и наш autobatched jds_ab
:
jds.batch_shape
[TensorShape([]), TensorShape([]), TensorShape([7])]
jds_i.batch_shape
[TensorShape([]), TensorShape([]), TensorShape([])]
jds_ia.batch_shape
[TensorShape([]), TensorShape([]), TensorShape([])]
jds_ab.batch_shape
TensorShape([])
Мы видим , что первоначальный jds
имеет subdistributions с различными пакетными формами. jds_i
и jds_ia
исправить это путем создания subdistributions с тем же (пустой) пакетной форме. jds_ab
имеет только один (пустой) пакетную форму.
Стоит отметить , что JointDistributionSequentialAutoBatched
предлагает некоторую дополнительную общность бесплатно. Предположим , что мы делаем ковариат X
(и, косвенно, наблюдения Y
) двумерная:
X = np.arange(14).reshape((2, 7))
X
array([[ 0, 1, 2, 3, 4, 5, 6], [ 7, 8, 9, 10, 11, 12, 13]])
Наша JointDistributionSequentialAutoBatched
работает без изменений (нам необходимо пересмотреть модель , потому что форма X
кэшируются jds_ab.log_prob
):
jds_ab = tfd.JointDistributionSequentialAutoBatched([
tfd.Normal(loc=0., scale=1.), # m
tfd.Normal(loc=0., scale=1.), # b
lambda b, m: tfd.Normal(loc=m*X + b, scale=1.) # Y
])
shaped_sample = jds_ab.sample([5, 3])
shaped_sample
[<tf.Tensor: shape=(5, 3), dtype=float32, numpy= array([[ 0.1813647 , -0.85994506, 0.27593774], [-0.73323774, 1.1153806 , 0.8841938 ], [ 0.5127983 , -0.29271227, 0.63733214], [ 0.2362284 , -0.919168 , 1.6648189 ], [ 0.26317367, 0.73077047, 2.5395133 ]], dtype=float32)>, <tf.Tensor: shape=(5, 3), dtype=float32, numpy= array([[ 0.09636458, 2.0138032 , -0.5054413 ], [ 0.63941646, -1.0785882 , -0.6442188 ], [ 1.2310615 , -0.3293852 , 0.77637213], [ 1.2115169 , -0.98906034, -0.07816773], [-1.1318136 , 0.510014 , 1.036522 ]], dtype=float32)>, <tf.Tensor: shape=(5, 3, 2, 7), dtype=float32, numpy= array([[[[-1.9685398e+00, -1.6832136e+00, -6.9127172e-01, 8.5992378e-01, -5.3123581e-01, 3.1584005e+00, 2.9044402e+00], [-2.5645006e-01, 3.1554163e-01, 3.1186538e+00, 1.4272424e+00, 1.2843871e+00, 1.2266440e+00, 1.2798605e+00]], [[ 1.5973477e+00, -5.3631151e-01, 6.8143606e-03, -1.4910895e+00, -2.1568544e+00, -2.0513713e+00, -3.1663666e+00], [-4.9448099e+00, -2.8385928e+00, -6.9027486e+00, -5.6543546e+00, -7.2378774e+00, -8.1577444e+00, -9.3582869e+00]], [[-2.1233239e+00, 5.8853775e-02, 1.2024102e+00, 1.6622503e+00, -1.9197327e-01, 1.8647723e+00, 6.4322817e-01], [ 3.7549341e-01, 1.5853541e+00, 2.4594500e+00, 2.1952972e+00, 1.7517658e+00, 2.9666045e+00, 2.5468128e+00]]], [[[ 8.9906776e-01, 6.7375046e-01, 7.3354661e-01, -9.9894643e-01, -3.4606690e+00, -3.4810467e+00, -4.4315586e+00], [-3.0670738e+00, -6.3628020e+00, -6.2538433e+00, -6.8091092e+00, -7.7134805e+00, -8.6319380e+00, -8.6904278e+00]], [[-2.2462025e+00, -3.3060855e-01, 1.8974400e-01, 3.1422038e+00, 4.1483402e+00, 3.5642972e+00, 4.8709240e+00], [ 4.7880130e+00, 5.8790064e+00, 9.6695948e+00, 7.8112822e+00, 1.2022618e+01, 1.2411858e+01, 1.4323385e+01]], [[-1.0189297e+00, -7.8115642e-01, 1.6466728e+00, 8.2378983e-01, 3.0765080e+00, 3.0170646e+00, 5.1899948e+00], [ 6.5285158e+00, 7.8038850e+00, 6.4155884e+00, 9.0899811e+00, 1.0040427e+01, 9.1404457e+00, 1.0411951e+01]]], [[[ 4.5557004e-01, 1.4905317e+00, 1.4904103e+00, 2.9777462e+00, 2.8620450e+00, 3.4745665e+00, 3.8295493e+00], [ 3.9977460e+00, 5.7173767e+00, 7.8421035e+00, 6.3180594e+00, 6.0838981e+00, 8.2257290e+00, 9.6548376e+00]], [[-7.0750320e-01, -3.5972297e-01, 4.3136525e-01, -2.3301599e+00, -5.0374687e-01, -2.8338656e+00, -3.4453444e+00], [-3.1258626e+00, -3.4687450e+00, -1.2045374e+00, -4.0196013e+00, -5.8831010e+00, -4.2965469e+00, -4.1388311e+00]], [[ 2.1969774e+00, 2.4614549e+00, 2.2314475e+00, 1.8392437e+00, 2.8367062e+00, 4.8600502e+00, 4.2273531e+00], [ 6.1879644e+00, 5.1792760e+00, 6.1141996e+00, 5.6517797e+00, 8.9979610e+00, 7.5938139e+00, 9.7918644e+00]]], [[[ 1.5249090e+00, 1.1388919e+00, 8.6903995e-01, 3.0762129e+00, 1.5128503e+00, 3.5204377e+00, 2.4760864e+00], [ 3.4166217e+00, 3.5930209e+00, 3.1694956e+00, 4.5797420e+00, 4.5271711e+00, 2.8774328e+00, 4.7288942e+00]], [[-2.3095846e+00, -2.0595703e+00, -3.0093951e+00, -3.8594103e+00, -4.9681158e+00, -6.4256043e+00, -5.5345035e+00], [-6.4306297e+00, -7.0924540e+00, -8.4075985e+00, -1.0417805e+01, -1.1727266e+01, -1.1196255e+01, -1.1333830e+01]], [[-7.0419472e-01, 1.4568675e+00, 3.7946482e+00, 4.8489718e+00, 6.6498446e+00, 9.0224218e+00, 1.1153137e+01], [ 1.0060651e+01, 1.1998097e+01, 1.5326431e+01, 1.7957514e+01, 1.8323889e+01, 2.0160881e+01, 2.1269085e+01]]], [[[-2.2360647e-01, -1.3632748e+00, -7.2704530e-01, 2.3558271e-01, -1.0381399e+00, 1.9387857e+00, -3.3694571e-01], [ 1.6015106e-01, 1.5284677e+00, -4.8567140e-01, -1.7770648e-01, 2.1919653e+00, 1.3015286e+00, 1.3877077e+00]], [[ 1.3688663e+00, 2.6602898e+00, 6.6657305e-01, 4.6554832e+00, 5.7781887e+00, 4.9115267e+00, 4.8446012e+00], [ 5.1983776e+00, 6.2297459e+00, 6.3848300e+00, 8.4291229e+00, 7.1309576e+00, 1.0395646e+01, 8.5736713e+00]], [[ 1.2675294e+00, 5.2844582e+00, 5.1331611e+00, 8.9993315e+00, 1.0794343e+01, 1.4039831e+01, 1.5731170e+01], [ 1.9084715e+01, 2.2191265e+01, 2.3481146e+01, 2.5803375e+01, 2.8632090e+01, 3.0234968e+01, 3.1886738e+01]]]], dtype=float32)>]
jds_ab.log_prob(shaped_sample)
<tf.Tensor: shape=(5, 3), dtype=float32, numpy= array([[-28.90071 , -23.052422, -19.851362], [-19.775568, -25.894997, -20.302256], [-21.10754 , -23.667885, -20.973007], [-19.249458, -20.87892 , -20.573763], [-22.351208, -25.457762, -24.648403]], dtype=float32)>
С другой стороны, наши аккуратно обработаны JointDistributionSequential
больше не работает:
jds_ia = tfd.JointDistributionSequential([
tfd.Normal(loc=0., scale=1.), # m
tfd.Normal(loc=0., scale=1.), # b
lambda b, m: tfd.Independent( # Y
tfd.Normal(loc=m[..., tf.newaxis]*X + b[..., tf.newaxis], scale=1.),
reinterpreted_batch_ndims=1)
])
try:
jds_ia.sample([5, 3])
except tf.errors.InvalidArgumentError as e:
print(e)
Incompatible shapes: [5,3,1] vs. [2,7] [Op:Mul]
Чтобы исправить это, мы должны были бы добавить второй tf.newaxis
и к m
и b
соответствуют форме, и увеличить reinterpreted_batch_ndims
до 2 в вызове Independent
. В этом случае позволить автоматическому дозирующему оборудованию решать вопросы формы короче, проще и эргономичнее.
Еще раз отмечу , что в то время как этот ноутбук разведанных JointDistributionSequentialAutoBatched
, другие варианты JointDistribution
имеют одинаковую AutoBatched
. (Для пользователей JointDistributionCoroutine
, JointDistributionCoroutineAutoBatched
имеет дополнительное преимущество , что вам больше не нужно указывать Root
узлы, если вы никогда не использовали JointDistributionCoroutine
. Вы можете спокойно игнорировать это заявление)
Заключительные мысли
В этом ноутбуке, мы ввели JointDistributionSequentialAutoBatched
и работал на простом примере подробно. Надеюсь, вы узнали что-то о формах TFP и об автодозировании!