Gen: Как объединить несколько следов генеративной функции в генеративную функцию высшего порядка?
Я изучаю тетрадь "Введение в моделирование в Gen" по адресу https://github.com/probcomp/gen-quickstart
Раздел 5 (Вызов других порождающих функций) просит "Построить набор данных, для которого не ясно, является ли линейная или синусоидальная модель наилучшей"
У меня возникла трудная проблема с пониманием того, как я работаю со следами (и возвратами) функций компонента, чтобы создать значимую трассировку высшего порядка, которую я могу использовать.
Для меня самая простая "неоднозначная" модель line(xs).+sine(xs)
, Так что я Gen.simulate
издание line
а также sine
чтобы получить следы и сложить их вместе, вот так:
@gen function combo(xs::Vector{Float64})
my_sin = simulate(sine_model_2,(xs,))
my_lin = simulate(line_model_2,(xs,))
if @trace(bernoulli(0.5), :is_line)
@trace(normal(get_choices(my_lin)[:slope], 0.01), :slope)
@trace(normal(get_choices(my_lin)[:intercept], 0.01), :intercept)
@trace(normal(get_choices(my_lin)[:noise], 0.01), :noise)
else
@trace(normal(get_choices(my_sin)[:phase], 0.01), :phase)
@trace(normal(get_choices(my_sin)[:period], 0.01), :period)
@trace(normal(get_choices(my_sin)[:amplitude], 0.01), :amplitude)
@trace(normal(get_choices(my_sin)[:noise], 0.01), :noise)
end
combo = [get_choices(my_sin)[(:y, i)] + get_choices(my_lin)[(:y, i)] for i=1:length(xs)]
for (i, c) in enumerate(combo)
@trace(normal(c, 0.1), (:y, i))
end
end;
Это явно неправильно, и я знаю, что мне не хватает чего-то фундаментального во всей идее программирования трасс и проб в Gen.
Я ожидаю, что смогу исследовать трассу sine / line_model внутри комбо и делать поэлементное добавление к трассе, чтобы получить новую трассу. И не нужно случайным образом выбирать число, близкое к:intercept,:phase и т. Д., Чтобы я мог включить его в свой след позже.
Кстати, когда я делаю:
traces = [Gen.simulate(combo,(xs,)) for _=1:12];
grid(render_combined, traces)
Пожалуйста, помогите спасибо!
2 ответа
Привет, спасибо за интерес к Gen!:)
Адреса трассы комбинированной модели
Комбинированная модель из учебника выглядит так:
@gen function combined_model(xs::Vector{Float64})
if @trace(bernoulli(0.5), :is_line)
@trace(line_model_2(xs))
else
@trace(sine_model_2(xs))
end
end;
Его следы будут иметь следующие адреса:
:is_line
сохраняя логическое значение, указывающее, был ли сгенерированный набор данных линейным или нет.- Любые адреса от
line_model_2
или жеsine_model_2
в зависимости от того, который был назван.
Обратите внимание, что следы обоих line_model_2
а также sine_model_2
содержать адреса (:y, i)
за каждое целое число i
между 1
а также length(xs)
, Из-за этого, так будет combined_model
следы: это адреса, представляющие окончательную выборку y
значения, независимо от того, какой из двух процессов их генерировал.
Построение нового набора данных
Вопрос "построить набор данных, для которого не ясно, является ли линейная или синусоидальная модель наилучшей", не требует написания новой порождающей функции (с @gen
), а точнее, составляя список xs
и список ys
(в простой Джулии), который, по вашему мнению, мог бы сделать набор данных с неоднозначными затруднениями. Вы можете передать xs
а также ys
в do_inference
функция, определенная ранее в записной книжке, чтобы увидеть, что система делает вывод о вашем наборе данных. Обратите внимание, что do_inference
Функция создает карту выбора ограничений, которая ограничивает каждый (:y, i)
к стоимости ys[i]
из набора данных, который вы передали. Это работает, потому что (:y, i)
всегда имя i-го datapoint, независимо от значения :is_line
,
Обновление / манипулирование следами
Ты пишешь:
Я ожидаю, что смогу исследовать трассу sine / line_model внутри комбо и делать поэлементное добавление к трассе, чтобы получить новую трассу. И не нужно случайным образом выбирать число, близкое к:intercept,:phase и т. Д., Чтобы я мог включить его в свой след позже.
Можно конечно позвонить simulate
дважды, чтобы получить два следа, вне порождающей функции, такой как combo
, Но трассировками нельзя манипулировать произвольными способами (например, "поэлементное сложение"): в качестве структур данных трассировки поддерживают определенные инварианты, например всегда знают точную вероятность их текущих значений в модели, которая их сгенерировала, и всегда содержат значения, которые на самом деле могли иметь был сгенерирован из модели.
Словарная структура данных, которую вы ищете, является картой выбора. Карты выбора являются изменяемыми и могут быть созданы для включения произвольных значений по произвольным адресам. Например, вы можете написать:
observations = Gen.choicemap()
for (i, y) in enumerate(ys)
observations[(:y, i)] = y
end
Карты выбора могут использоваться как ограничения для генерации новых трасс (используя Gen.generate
), как аргументы в пользу низкого уровня Gen Gen.update
метод (с помощью которого можно обновить трассировку при повторном вычислении любых соответствующих вероятностей и с ошибками, если ваши обновления недействительны), и в некоторых других местах.
Надеюсь, это поможет:)
Благодаря разъяснениям Алекса Лью, ответ намного проще, чем я его представлял. Вот что я сделал:
xs = [-5:0.1;5;]
ambiguous = [0.3*x+0.2*sin(x)+normal(0,.5) for x in xs];
ambig_trace = do_inference(combined_model,xs, ambiguous, 100)
render_combined(ambig_trace)
производство:
ht tps:https://stackru.com/images/f5f587d8e6eebd92c6cf6bc707c26b66cc1143e1.png
(или что-то более синусоидальное, если это было выведено)
Тогда наконец:
n_infers = 100
is_sine = 0
for i=1:n_infers
curr_trace = do_inference(combined_model, xs, ambiguous, 100)
if !curr_trace[:is_line] is_sine+=1 end
end
println("posterior probability of sine wave model is $(is_sine/n_infers)")
# => posterior probability of sine wave model is 0.52