Gen:如何在高阶生成函数中组合多个生成函数轨迹?

Gen: How to combine multiple generative function traces in a higher-order generative function?

我正在 https://github.com/probcomp/gen-quickstart

查看 "Introduction to Modeling in Gen" 笔记本

第 5 节(调用其他生成函数)要求 "Construct a data set for which it is ambiguous whether the line or sine wave model is best"

我很难理解如何使用组件函数的跟踪(和 returns)来创建我可以使用的有意义的高阶跟踪。

对我来说最直接的 "ambiguous" 模型是 line(xs).+sine(xs)。所以我 Gen.simulateed linesine 来获取痕迹并将它们加在一起,就像这样:

@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 的踪迹,并对踪迹进行逐元素添加以获得新的踪迹。并且不必随机选择一个数字 close 到 :intercept、:phase 等,这样我可以稍后将它包含在我的跟踪中。

顺便说一下,当我这样做时:

traces = [Gen.simulate(combo,(xs,)) for _=1:12];
grid(render_combined, traces)

我明白了

请帮忙谢谢!

您好 - 感谢您对 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_2sine_model_2 的任何地址,具体取决于调用的地址。

请注意,line_model_2sine_model_2 的踪迹都包含 1length(xs) 之间每个整数 i 的地址 (:y, i)。因此,combined_model 的踪迹也将如此:这些是代表最终采样 y 值的地址,无论它们是由两个进程中的哪一个生成的。

构建新数据集

"construct a data set for which it is ambiguous whether the line or sine wave model is best"的问题不需要写一个新的生成函数(用@gen),而是构造一个xs的列表和一个[=25=的列表](在普通的 Julia 中),你认为可能会产生一个 difficult-to-disambiguate 数据集。然后,您可以将 xsys 传递到之前在笔记本中定义的 do_inference 函数,以查看系统对您的数据集得出的结论。请注意,do_inference 函数构造了一个 constraint 选择图,它将每个 (:y, i) 限制为您传入的数据集中的值 ys[i]。这是有效的,因为 (:y, i) 始终是第 i 个数据点的名称,无论 :is_line.

的值如何

更新/操纵痕迹

你写:

I'd expect to be able to introspect sine/line_model's trace from within combo, and do element-wise addition on the traces to get a new trace. And not have to randomly pick a number close to :intercept, :phase, etc. so I can include it in my trace later on.

你当然可以调用 simulate 两次以获得两条轨迹,在像 combo 这样的生成函数之外。但是迹线不能以任意方式操作(例如 "elementwise addition"):作为数据结构,迹线保持某些不变量,比如总是知道它们当前值在生成它们的模型下的确切概率,并且总是保持实际可能的值已从模型生成。

您正在查找的 dictionary-like 数据结构是一个选择图。选择图是可变的,可以构建为在任意地址包含任意值。例如,你可以这样写:

observations = Gen.choicemap()
for (i, y) in enumerate(ys)
  observations[(:y, i)] = y
end

Choicemaps 可以用作生成新轨迹的约束(使用 Gen.generate),作为 Gen 的 low-level Gen.update 方法的参数(允许您在重新计算任何轨迹时更新轨迹相关概率,如果您的更新无效则出错),以及其他几个地方。

希望对您有所帮助:)

感谢 Alex Lew 的澄清,答案比我想象的要简单得多。这是我所做的:

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)

生产:

(或者更正弦的东西,如果推断的话)

最后:

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