Построение дискретных таблиц CPD с вероятностью тензорного потока?

Я пытаюсь построить простейший пример байесовской сети с несколькими дискретными случайными величинами и условными вероятностями («Студенческая сеть» из книги Коллера, см. 1)

Хотя и немного громоздко, мне удалось построить эту сеть с помощью pymc3. В частности, создание CPD не так просто в pymc3, см. Фрагмент ниже:

import pymc3 as pm

...

with pm.Model() as basic_model:
    # parameters for categorical are indexed as [0, 1, 2, ...]
    difficulty = pm.Categorical(name='difficulty', p=[0.6, 0.4])

    intelligence = pm.Categorical(name='intelligence', p=[0.7, 0.3])

    grade = pm.Categorical(name='grade',
        p=pm.math.switch(
            theano.tensor.eq(intelligence, 0),
                pm.math.switch(
                    theano.tensor.eq(difficulty, 0),
                        [0.3, 0.4, 0.3],  # I=0, D=0
                        [0.05, 0.25, 0.7]   # I=0, D=1
                    ),
                    pm.math.switch(
                        theano.tensor.eq(difficulty, 0),
                            [0.9, 0.08, 0.02],  # I=1, D=0
                            [0.5, 0.3, 0.2]  # I=1, D=1
                    )
            )
        )

    letter = pm.Categorical(name='letter', p=pm.math.switch(
    ...

Но я понятия не имею, как построить эту сеть с использованием десятковпотоковой вероятности (версии: tfp-nightly==0.7.0.dev20190517, tf-nightly-2.0-preview==2.0.0.dev20190517)

Для безусловных двоичных переменных можно использовать категориальное распределение, например

from tensorflow_probability import distributions as tfd
from tensorflow_probability import edward2 as ed

difficulty = ed.RandomVariable(
                 tfd.Categorical(
                     probs=[0.6, 0.4],
                     name='difficulty'
                 )
             )

Но как построить CPD?

Есть несколько классов / методов в тензорной вероятности потока, которые могут иметь значение (в tensorflow_probability/python/distributions/deterministic.py или устаревшем ConditionalDistribution), но документация довольно скудна (необходимо глубокое понимание tfp).

--- Обновленный вопрос ---

Ответ Криса - хорошая отправная точка. Однако даже для очень простой модели с двумя переменными все еще немного неясно.

Это прекрасно работает:

jdn = tfd.JointDistributionNamed(dict(
    dist_x=tfd.Categorical([0.2, 0.8], validate_args=True),
    dist_y=lambda dist_x: tfd.Bernoulli(probs=tf.gather([0.1, 0.9], indices=dist_x), validate_args=True)
))
print(jdn.sample(10))

но это не удается

jdn = tfd.JointDistributionNamed(dict(
    dist_x=tfd.Categorical([0.2, 0.8], validate_args=True),
    dist_y=lambda dist_x: tfd.Categorical(probs=tf.gather_nd([[0.1, 0.9], [0.5, 0.5]], indices=[dist_x]))
))
print(jdn.sample(10))

(Я пытаюсь явно моделировать категоричность во втором примере только в учебных целях)

- Обновление: решено ---

Очевидно, что в последнем примере было ошибочно использовано tf.gather_nd вместо tf.gather, поскольку мы хотели выбрать только первую или вторую строку на основе вывода dist_x. Этот код работает сейчас:

jdn = tfd.JointDistributionNamed(dict(
    dist_x=tfd.Categorical([0.2, 0.8], validate_args=True),
    dist_y=lambda dist_x: tfd.Categorical(probs=tf.gather([[0.1, 0.9], [0.5, 0.5]], indices=[dist_x]))
))
print(jdn.sample(10))

person John Doe    schedule 23.05.2019    source источник


Ответы (1)


Сложность в этом и, по-видимому, причина того, что это тоньше, чем ожидалось в PyMC, - это - как и почти все в векторизованном программировании - обработка форм.

В TF / TFP лучший (IMO) способ решить эту проблему - использовать один из новых классов TFP JointDistribution{Sequential,Named,Coroutine}. Это позволяет вам естественным образом представлять иерархические модели PGM, а затем выбирать из них, оценивать вероятности регистрации и т. Д.

Я создал блокнот colab с демонстрацией всех трех подходов для всей студенческой сети: https://colab.research.google.com/drive/1D2VZ3OE6tp5pHTsnOAf_7nZZZ74GTeex

Обратите внимание на решающее значение использования tf.gather и tf.gather_nd для управления векторизацией различных двоичных и категориальных переключений.

Посмотрите и дайте мне знать, если у вас возникнут вопросы!

person Chris Suter    schedule 23.05.2019
comment
Большое спасибо Крису за подробный ответ! В общем, суть в том, что распределения можно параметризовать с помощью функций, которые принимают в качестве параметра другое распределение. Уловка состоит в том, чтобы использовать векторизованные операции с tf.gather. Мне все еще немного непонятно, но определенно отличная отправная точка, чтобы копнуть глубже, чтобы понять это, спасибо! - person John Doe; 24.05.2019