From 9270b603a37f077ceeff68594996bac2b8d1b372 Mon Sep 17 00:00:00 2001 From: Xuan Date: Mon, 16 Jun 2025 17:03:34 -0400 Subject: [PATCH 1/8] Fix incorrect header in SML tutorial. --- docs/src/tutorials/scaling_with_sml.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/src/tutorials/scaling_with_sml.md b/docs/src/tutorials/scaling_with_sml.md index 3b039c4df..a962d9d57 100644 --- a/docs/src/tutorials/scaling_with_sml.md +++ b/docs/src/tutorials/scaling_with_sml.md @@ -334,7 +334,7 @@ plot!(ns, static_with_map_times, label="with map and static outer fn") plot!(ns, fully_static_with_map_times, label="with map and static outer and inner fns") ``` -# Checking the Inference Programs +## Checking the Inference Programs Before wrapping up, let's confirm that all of our models are giving good results: From c0fb7812b0841cda982dd25a292164004862a179 Mon Sep 17 00:00:00 2001 From: Xuan Date: Mon, 16 Jun 2025 19:51:12 -0400 Subject: [PATCH 2/8] Fix heading capitalization. --- docs/src/tutorials/modeling_in_gen.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/src/tutorials/modeling_in_gen.md b/docs/src/tutorials/modeling_in_gen.md index 1a0c7b724..df098708d 100644 --- a/docs/src/tutorials/modeling_in_gen.md +++ b/docs/src/tutorials/modeling_in_gen.md @@ -532,7 +532,7 @@ Write an inference program that generates traces of `sine_model` that explain th ------------------ -## Predicting new data +## Predicting New Data What if we'd want to predict `ys` given `xs`? @@ -786,7 +786,7 @@ Plots.plot(fixed_noise_plot, inferred_noise_plot) ------------------------- -## Calling other generative functions +## Calling Other Generative Functions In addition to making random choices, generative functions can invoke other generative functions. To illustrate this, we will write a probabilistic model @@ -929,7 +929,7 @@ Hint: To estimate the posterior probability that the data was generated by the s ------- -## Modeling with an unbounded number of parameters +## Modeling with an Unbounded Number of Parameters Gen's built-in modeling language can be used to express models that use an unbounded number of parameters. This section walks you through development of From 1289eff70a0e47b51147c46ebd140d47e03ed33d Mon Sep 17 00:00:00 2001 From: Xuan Date: Mon, 16 Jun 2025 20:38:11 -0400 Subject: [PATCH 3/8] Port MCMC inference tutorial. --- docs/pages.jl | 1 + docs/src/assets/example-inference.png | Bin 0 -> 181093 bytes docs/src/index.md | 2 + docs/src/tutorials/mcmc_map.md | 1282 +++++++++++++++++++++++++ docs/src/tutorials/modeling_in_gen.md | 2 +- 5 files changed, 1286 insertions(+), 1 deletion(-) create mode 100644 docs/src/assets/example-inference.png create mode 100644 docs/src/tutorials/mcmc_map.md diff --git a/docs/pages.jl b/docs/pages.jl index 3a0962031..ca74e0d86 100644 --- a/docs/pages.jl +++ b/docs/pages.jl @@ -3,6 +3,7 @@ pages = [ "Tutorials" => [ "Getting Started" => "tutorials/getting_started.md", "Introduction to Modeling in Gen" => "tutorials/modeling_in_gen.md", + "Basics of MCMC and MAP Inference" => "tutorials/mcmc_map.md", "Object Tracking with SMC" => "tutorials/smc.md", "Variational Inference in Gen" => "tutorials/vi.md", "Learning Generative Functions" => "tutorials/learning_gen_fns.md", diff --git a/docs/src/assets/example-inference.png b/docs/src/assets/example-inference.png new file mode 100644 index 0000000000000000000000000000000000000000..e00b3e5801b00c4f9bae6e2794cc67dfd688283e GIT binary patch literal 181093 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 VAI&LXm5w3cqphj0QKe=d^*?M9Hx~c^ literal 0 HcmV?d00001 diff --git a/docs/src/index.md b/docs/src/index.md index 6d5e17494..8c9f6ca52 100644 --- a/docs/src/index.md +++ b/docs/src/index.md @@ -46,7 +46,9 @@ Learn how to use Gen by following these tutorials: Pages = [ "tutorials/getting_started.md", "tutorials/modeling_in_gen.md", + "tutorials/mcmc_map.md", "tutorials/smc.md", + "tutorials/vi.md", "tutorials/scaling_with_sml.md", "tutorials/learning_gen_fns.md" ] diff --git a/docs/src/tutorials/mcmc_map.md b/docs/src/tutorials/mcmc_map.md new file mode 100644 index 000000000..f21cbf4c2 --- /dev/null +++ b/docs/src/tutorials/mcmc_map.md @@ -0,0 +1,1282 @@ +# [Basics of MCMC and MAP Inference](@id mcmc_map_tutorial) + +This tutorial introduces the basics of writing *Markov chain Monte Carlo (MCMC)* +inference programs in Gen. We also briefly cover *maximum a posteriori (MAP)* +inference, as another kind of iterative inference algorithm. + +## Linear Regression with Outliers + +As an example task, we consider a simple extension of the linear regression problem introduced in previous tutorials: *linear regression with outliers*. Suppose we have a dataset of points in the $x,y$ plane that is *mostly* explained by a linear relationship, but which also has several outliers. Our goal will be to automatically identify the outliers, and to find a linear relationship (a slope and intercept, as well as an inherent noise level) that explains rest of the points: + +```@raw html +
+ See https://dspace.mit.edu/bitstream/handle/1721.1/119255/MIT-CSAIL-TR-2018-020.pdf, Figure 2(a) +
+``` + +This is a simple inference problem. But it has two features that make it ideal for introducing concepts in modeling and inference. + +1. First, we want not only to estimate the slope and intercept of the line that best fits the data, but also to classify each point as an inlier or outlier; that is, there are a large number of latent variables of interest, enough to make importance sampling an unreliable method (absent a more involved custom proposal that does the heavy lifting). +2. Second, several of the parameters we're estimating (the slope and intercept) are continuous and amenable to gradient-based search techniques, which will allow us to explore Gen's optimization capabilities. + +Let's get started! + +```@setup mcmc_map_tutorial +import Random, Logging +using Gen, Plots + +# Disable logging, because @animate is verbose otherwise +Logging.disable_logging(Logging.Info) +nothing # hide +``` + +## Writing the Model + +We begin, as usual, by writing a model: a generative function responsible +(conceptually) for simulating a synthetic dataset. + +Our model will take as input a vector of `x` coordinates, and produce as +output corresponding `y` coordinates. + +We will also use this opportunity to introduce some syntactic sugar. +As described in [Introduction to Modeling in Gen](@ref modeling_tutorial), random choices in Gen are given +_addresses_ using the syntax `{addr} ~ distribution(...)`. But this can +be a bit verbose, and often leads to code that looks like the following: + +```julia +x = {:x} ~ normal(0, 1) +slope = {:slope} ~ normal(0, 1) +``` + +In these examples, the variable name is duplicated as the address of the +random choice. Because this is a common pattern, Gen provides syntactic +sugar that makes it nicer to use: + +```julia +# Desugars to "x = {:x} ~ normal(0, 1)" +x ~ normal(0, 1) +# Desugars to "slope = {:slope} ~ normal(0, 1)" +slope ~ normal(0, 1) +``` + +Note that sometimes, it is still necessary to use the `{...}` form, for example +in loops: + +```julia +# INVALID: +for i=1:10 + y ~ normal(0, 1) # The name :y will be used more than once!! + println(y) +end + +# VALID: +for i=1:10 + y = {(:y, i)} ~ normal(0, 1) # OK: the address is different each time. + println(y) +end +``` + +We'll use this new syntax for writing our model of linear regression with +outliers. As we've seen before, the model generates parameters from a prior, +and then simulates data based on those parameters: + +```@example mcmc_map_tutorial +@gen function regression_with_outliers(xs::Vector{<:Real}) + # First, generate some parameters of the model. We make these + # random choices, because later, we will want to infer them + # from data. The distributions we use here express our assumptions + # about the parameters: we think the slope and intercept won't be + # too far from 0; that the noise is relatively small; and that + # the proportion of the dataset that don't fit a linear relationship + # (outliers) could be anything between 0 and 1. + slope ~ normal(0, 2) + intercept ~ normal(0, 2) + noise ~ gamma(1, 1) + prob_outlier ~ uniform(0, 1) + + # Next, we generate the actual y coordinates. + n = length(xs) + ys = Float64[] + + for i = 1:n + # Decide whether this point is an outlier, and set + # mean and standard deviation accordingly + if ({:data => i => :is_outlier} ~ bernoulli(prob_outlier)) + (mu, std) = (0., 10.) + else + (mu, std) = (xs[i] * slope + intercept, noise) + end + # Sample a y value for this point + push!(ys, {:data => i => :y} ~ normal(mu, std)) + end + ys +end +nothing # hide +``` + +## Visualizing the Model + +Let's visualize what our model is doing by drawing some samples from the +prior. We'll use the following helper functions. + +```@raw html +
Helper functions for visualization +``` + +```@example mcmc_map_tutorial +function serialize_trace(trace) + (xs,) = Gen.get_args(trace) + Dict(:slope => trace[:slope], + :intercept => trace[:intercept], + :inlier_std => trace[:noise], + :points => zip(xs, [trace[:data => i => :y] for i in 1:length(xs)]), + :outliers => [trace[:data => i => :is_outlier] for i in 1:length(xs)]) +end + + +function visualize_trace(trace::Trace; title="") + trace = serialize_trace(trace) + + outliers = [pt for (pt, outlier) in zip(trace[:points], trace[:outliers]) if outlier] + inliers = [pt for (pt, outlier) in zip(trace[:points], trace[:outliers]) if !outlier] + Plots.scatter(map(first, inliers), map(last, inliers), markercolor="blue", label=nothing, xlims=[-5, 5], ylims=[-20, 20], title=title) + Plots.scatter!(map(first, outliers), map(last, outliers), markercolor="red", label=nothing) + + inferred_line(x) = trace[:slope] * x + trace[:intercept] + left_x = -5 + left_y = inferred_line(left_x) + right_x = 5 + right_y = inferred_line(right_x) + Plots.plot!([left_x, right_x], [left_y, right_y], color="black", label=nothing) + + # Inlier noise + inlier_std = trace[:inlier_std] + noise_points = [(left_x, left_y + inlier_std), + (right_x, right_y + inlier_std), + (right_x, right_y - inlier_std), + (left_x, left_y - inlier_std)] + Plots.plot!(Shape(map(first, noise_points), map(last, noise_points)), color="black", alpha=0.2, label=nothing) + Plots.plot!(Shape([-5, 5, 5, -5], [10, 10, -10, -10]), color="black", label=nothing, alpha=0.08) +end +nothing # hide +``` + +```@raw html +

+``` + +We'll use these functions to visualize samples from our model. + +```@example mcmc_map_tutorial +# Generate nine traces and visualize them +xs = collect(range(-5, stop=5, length=20)) +traces = [Gen.simulate(regression_with_outliers, (xs,)) for i in 1:9] +Plots.plot([visualize_trace(t) for t in traces]...) +``` + +##### Legend: + +* red points: outliers; +* blue points: inliers (i.e. regular data); +* dark grey shading: noise associated with inliers; and +* light grey shading: noise associated with outliers. + +Note that an outlier can occur anywhere — including close to the line — and +that our model is capable of generating datasets in which the vast majority +of points are outliers. + +## The Limits of Importance Sampling + +To motivate the need for more complex inference algorithms, let's begin by +using the simple importance sampling method from the [Introduction to Modeling](@ref modeling_tutorial) tutorial, and +thinking about where it fails. + +First, let us create a synthetic dataset to do inference _about_. + +```@example mcmc_map_tutorial +function make_synthetic_dataset(n) + Random.seed!(1) + prob_outlier = 0.2 + true_inlier_noise = 0.5 + true_outlier_noise = 5.0 + true_slope = -1 + true_intercept = 2 + xs = collect(range(-5, stop=5, length=n)) + ys = Float64[] + for (i, x) in enumerate(xs) + if rand() < prob_outlier + y = randn() * true_outlier_noise + else + y = true_slope * x + true_intercept + randn() * true_inlier_noise + end + push!(ys, y) + end + (xs, ys) +end + +(xs, ys) = make_synthetic_dataset(20) +Plots.scatter(xs, ys, color="black", xlabel="X", ylabel="Y", + label=nothing, title="Observations - regular data and outliers") +``` + +We will to express our _observations_ as a `ChoiceMap` that constrains the +values of certain random choices to equal their observed values. Here, we +want to constrain the values of the choices with address `:data => i => :y` +(that is, the sampled $y$ coordinates) to equal the observed $y$ values. +Let's write a helper function that takes in a vector of $y$ values and +creates a `ChoiceMap` that we can use to constrain our model: + +```@example mcmc_map_tutorial +function make_constraints(ys::Vector{Float64}) + constraints = Gen.choicemap() + for i=1:length(ys) + constraints[:data => i => :y] = ys[i] + end + constraints +end +nothing # hide +``` + +We can apply it to our dataset's vector of `ys` to make a set of constraints +for doing inference: + +```@example mcmc_map_tutorial +observations = make_constraints(ys) +nothing # hide +``` + +Now, we use the library function `importance_resampling` to draw approximate +posterior samples given those observations: + +```@example mcmc_map_tutorial +function logmeanexp(scores) + logsumexp(scores) - log(length(scores)) +end +nothing # hide +``` + +```@example mcmc_map_tutorial +traces = [first(Gen.importance_resampling(regression_with_outliers, (xs,), observations, 2000)) for i in 1:9] +log_probs = [get_score(t) for t in traces] +println("Average log probability: $(logmeanexp(log_probs))") +Plots.plot([visualize_trace(t) for t in traces]...) +``` + +We see here that importance resampling hasn't completely failed: it generally +finds a reasonable position for the line. But the details are off: there is +little logic to the outlier classification, and the inferred noise around the +line is too wide. The problem is that there are just too many variables to +get right, and so sampling everything in one go is highly unlikely to produce +a perfect hit. + +In the remainder of this tutorial, we'll explore techniques for finding the +right solution _iteratively_, beginning with an initial guess and making many +small changes, until we achieve a reasonable posterior sample. + +## MCMC Part 1: Block Resimulation + +### What is MCMC? + +_Markov Chain Monte Carlo_ ("MCMC") methods are a powerful family of +algorithms for iteratively producing approximate samples from a distribution +(when applied to Bayesian inference problems, the posterior distribution of +unknown (hidden) model variables given data). + +There is a rich theory behind MCMC methods, but we focus on applying MCMC in +Gen and introducing theoretical ideas only when necessary for understanding. +As we will see, Gen provides abstractions that hide and automate much of the +math necessary for implementing MCMC algorithms correctly. + +The general shape of an MCMC algorithm is as follows. We begin by sampling an +intial setting of all unobserved variables; in Gen, we produce an initial +_trace_ consistent with (but not necessarily _probable_ given) our +observations. Then, in a long-running loop, we make small, stochastic changes +to the trace; in order for the algorithm to be asymptotically correct, these +stochastic updates must satisfy certain probabilistic properties. + +One common way of ensuring that the updates do satisfy those properties is to +compute a _Metropolis-Hastings acceptance ratio_. Essentially, after +proposing a change to a trace, we add an "accept or reject" step that +stochastically decides whether to commit the update or to revert it. This is +an over-simplification, but generally speaking, this step ensures we are more +likely to accept changes that make our trace fit the observed data better, +and to reject ones that make our current trace worse. The algorithm also +tries not to go down dead ends: it is more likely to take an exploratory step +into a low-probability region if it knows it can easily get back to where it +came from. + +Gen's `metropolis_hastings` function _automatically_ adds this +"accept/reject" check (including the correct computation of the probability +of acceptance or rejection), so that inference programmers need only +think about what sorts of updates might be useful to propose. Starting in +this section, we'll look at several design patterns for MCMC updates, and how +to apply them in Gen. + +### Block Resimulation + +One of the simplest strategies we can use is called Resimulation MH, and it +works as follows. + +We begin, as in most iterative inference algorithms, by sampling an initial +trace from our model, fixing the observed choices to their observed values. + +```julia +# Gen's `generate` function accepts a model, a tuple of arguments to the model, +# and a `ChoiceMap` representing observations (or constraints to satisfy). It returns +# a complete trace consistent with the observations, and an importance weight. +# In this call, we ignore the weight returned. +(tr, _) = generate(regression_with_outliers, (xs,), observations) +``` + +Then, in each iteration of our program, we propose changes to all our model's +variables in "blocks," by erasing a set of variables from our current trace +and _resimulating_ them from the model. After resimulating each block of +choices, we perform an accept/reject step, deciding whether the proposed +changes are worth making. + +```julia +# Pseudocode +for iter=1:500 + tr = maybe_update_block_1(tr) + tr = maybe_update_block_2(tr) + ... + tr = maybe_update_block_n(tr) +end +``` + +The main design choice in designing a Block Resimulation MH algorithm is how +to block the choices together for resimulation. At one extreme, we could put +each random choice the model makes in its own block. At the other, we could +put all variables into a single block (a strategy sometimes called +"independent" MH, and which bears a strong similarity to importance +resampling, as it involves repeatedly generating completely new traces and +deciding whether to keep them or not). Usually, the right thing to do is +somewhere in between. + +For the regression problem, here is one possible blocking of choices: + +**Block 1: `slope`, `intercept`, and `noise`.** These parameters determine +the linear relationship; resimulating them is like picking a new line. We +know from our importance sampling experiment above that before too long, +we're bound to sample something close to the right line. + +**Blocks 2 through N+1: Each `is_outlier`, in its own block.** One problem we +saw with importance sampling in this problem was that it tried to sample +_every_ outlier classification at once, when in reality the chances of a +single sample that correctly classifies all the points are very low. Here, we +can choose to resimulate each `is_outlier` choice separately, and for each +one, decide whether to use the resimulated value or not. + +**Block N+2: `prob_outlier`.** Finally, we can propose a new `prob_outlier` +value; in general, we can expect to accept the proposal when it is in line +with the current hypothesized proportion of `is_outlier` choices that are +set to `true`. + +Resimulating a block of variables is the simplest form of update that Gen's +`metropolis_hastings` operator (or `mh` for short) supports. When supplied +with a _current trace_ and a _selection_ of trace addresses to resimulate, +`mh` performs the resimulation and the appropriate accept/reject check, then +returns a possibly updated trace, along with a boolean indicating whether the +update was accepted or not. A selection is created using the `select` +method. So a single update of the scheme we proposed above would look like +this: + +Perform a single block resimulation update of a trace. + +```@example mcmc_map_tutorial +function block_resimulation_update(tr) + # Block 1: Update the line's parameters + line_params = select(:noise, :slope, :intercept) + (tr, _) = mh(tr, line_params) + + # Blocks 2-N+1: Update the outlier classifications + (xs,) = get_args(tr) + n = length(xs) + for i=1:n + (tr, _) = mh(tr, select(:data => i => :is_outlier)) + end + + # Block N+2: Update the prob_outlier parameter + (tr, _) = mh(tr, select(:prob_outlier)) + + # Return the updated trace + tr +end +nothing # hide +``` + +All that's left is to (a) obtain an initial trace, and then (b) run that +update in a loop for as long as we'd like: + +```@example mcmc_map_tutorial +function block_resimulation_inference(xs, ys, observations) + observations = make_constraints(ys) + (tr, _) = generate(regression_with_outliers, (xs,), observations) + for iter=1:500 + tr = block_resimulation_update(tr) + end + tr +end +nothing # hide +``` + +Let's test it out: + +```@example mcmc_map_tutorial +scores = Vector{Float64}(undef, 10) +for i=1:10 + @time tr = block_resimulation_inference(xs, ys, observations) + scores[i] = get_score(tr) +end +println("Log probability: ", logmeanexp(scores)) +``` + +We note that this is significantly better than importance sampling, even if +we run importance sampling for about the same amount of (wall-clock) time per +sample: + +```@example mcmc_map_tutorial +scores = Vector{Float64}(undef, 10) +for i=1:10 + @time (tr, _) = importance_resampling(regression_with_outliers, (xs,), observations, 17000) + scores[i] = get_score(tr) +end +println("Log probability: ", logmeanexp(scores)) +``` + +It's one thing to see a log probability increase; it's better to understand +what the inference algorithm is actually doing, and to see _why_ it's doing +better. + +A great tool for debugging and improving MCMC algorithms is visualization. We +can use `Plots.@animate` to produce an +animated visualization: + +```@example mcmc_map_tutorial +t, = generate(regression_with_outliers, (xs,), observations) + +viz = Plots.@animate for i in 1:500 + global t + t = block_resimulation_update(t) + visualize_trace(t; title="Iteration $i/500") +end +gif(viz) +``` + +We can see that although the algorithm keeps changing the inferences of which points are inliers and outliers, +it has a harder time refining the continuous parameters. We address this challenge next. + +## MCMC Part 2: Gaussian Drift MH + +So far, we've seen one form of incremental trace update: + +```julia +(tr, did_accept) = mh(tr, select(:address1, :address2, ...)) +``` + +This update is incremental in that it only proposes changes to part of a +trace (the selected addresses). But when computing _what_ changes to propose, +it ignores the current state completely and resimulates all-new values from +the model. + +That wholesale resimulation of values is often not the best way to search for +improvements. To that end, Gen also offers a more general flavor of MH: + +```julia +(tr, did_accept) = mh(tr, custom_proposal, custom_proposal_args) +``` + +A "custom proposal" is just what it sounds like: whereas before, we were +using the _default resimulation proposal_ to come up with new values for the +selected addresses, we can now pass in a generative function that samples +proposed values however it wants. + +For example, here is a custom proposal that takes in a current trace, and +proposes a new slope and intercept by randomly perturbing the existing +values: + +```@example mcmc_map_tutorial +@gen function line_proposal(current_trace) + slope ~ normal(current_trace[:slope], 0.5) + intercept ~ normal(current_trace[:intercept], 0.5) +end +nothing # hide +``` + +This is often called a "Gaussian drift" proposal, because it essentially amounts to proposing steps of a random walk. (What makes it different from a random walk is that we will still use an MH accept/reject step to make sure we don't wander into areas of very low probability.) + +To use the proposal, we write: + +```julia +(tr, did_accept) = mh(tr, line_proposal, ()) +``` + +Two things to note: + +1. We no longer need to pass a selection of addresses. Instead, Gen assumes + that whichever addresses are sampled by the proposal (in this case, + `:slope` and `:intercept`) are being proposed to. + +2. The argument list to the proposal is an empty tuple, `()`. The + `line_proposal` generative function does expect an argument, the previous + trace, but this is supplied automatically to all MH custom proposals + (a proposal generative function for use with `mh` must take as its first argument the + current trace of the model). + +Let's swap it into our update: + +```@example mcmc_map_tutorial +function gaussian_drift_update(tr) + # Gaussian drift on line params + (tr, _) = mh(tr, line_proposal, ()) + + # Block resimulation: Update the outlier classifications + (xs,) = get_args(tr) + n = length(xs) + for i=1:n + (tr, _) = mh(tr, select(:data => i => :is_outlier)) + end + + # Block resimulation: Update the prob_outlier parameter + (tr, w) = mh(tr, select(:prob_outlier)) + (tr, w) = mh(tr, select(:noise)) + tr +end +nothing # hide +``` + +If we compare the Gaussian Drift proposal visually with our old algorithm, we +can see the new behavior: + +```@example mcmc_map_tutorial +tr1, = generate(regression_with_outliers, (xs,), observations) +tr2 = tr1 + +viz = Plots.@animate for i in 1:300 + global tr1, tr2 + tr1 = gaussian_drift_update(tr1) + tr2 = block_resimulation_update(tr2) + Plots.plot(visualize_trace(tr1; title="Drift Kernel (Iter $i)"), + visualize_trace(tr2; title="Resim Kernel (Iter $i)")) +end +gif(viz) +``` + +------------------------------------------------------------------- + +### Exercise: Analyzing the algorithms + +Run the cell above several times. Compare the two +algorithms with respect to the following: + +- How fast do they find a relatively good line? + +- Does one of them tend to get stuck more than the other? Under what + conditions? Why? + +------------------------------------------------------------------- + +A more quantitative comparison demonstrates that our change has +improved our inference quality: + +```@example mcmc_map_tutorial +function gaussian_drift_inference(xs, observations) + (tr, _) = generate(regression_with_outliers, (xs,), observations) + for iter=1:500 + tr = gaussian_drift_update(tr) + end + tr +end + +scores = Vector{Float64}(undef, 10) +for i=1:10 + @time tr = gaussian_drift_inference(xs, observations) + scores[i] = get_score(tr) +end +println("Log probability: ", logmeanexp(scores)) +``` + +## MCMC Part 3: Heuristic Guidance + +In this section, we'll look at another strategy for improving MCMC inference: +using arbitrary heuristics to make smarter proposals. In particular, we'll +use a method called "Random Sample Consensus" (or RANSAC) to quickly find +promising settings of the slope and intercept parameters. + +RANSAC works as follows: +1. We repeatedly choose a small random subset of the points, say, of size 3. +2. We do least-squares linear regression to find a line of best fit for those + points. +3. We count how many points (from the entire set) are near the line we found. +4. After a suitable number of iterations (say, 10), we return the line that + had the highest score. + +Here's our implementation of the algorithm in Julia: + +```@example mcmc_map_tutorial +import StatsBase + +struct RANSACParams + """the number of random subsets to try""" + iters::Int + + """the number of points to use to construct a hypothesis""" + subset_size::Int + + """the error threshold below which a datum is considered an inlier""" + eps::Float64 + + function RANSACParams(iters, subset_size, eps) + if iters < 1 + error("iters < 1") + end + new(iters, subset_size, eps) + end +end + + +function ransac(xs::Vector{Float64}, ys::Vector{Float64}, params::RANSACParams) + best_num_inliers::Int = -1 + best_slope::Float64 = NaN + best_intercept::Float64 = NaN + for i=1:params.iters + # select a random subset of points + rand_ind = StatsBase.sample(1:length(xs), params.subset_size, replace=false) + subset_xs = xs[rand_ind] + subset_ys = ys[rand_ind] + + # estimate slope and intercept using least squares + A = hcat(subset_xs, ones(length(subset_xs))) + slope, intercept = A \ subset_ys # use backslash operator for least sq soln + + ypred = intercept .+ slope * xs + + # count the number of inliers for this (slope, intercept) hypothesis + inliers = abs.(ys - ypred) .< params.eps + num_inliers = sum(inliers) + + if num_inliers > best_num_inliers + best_slope, best_intercept = slope, intercept + best_num_inliers = num_inliers + end + end + + # return the hypothesis that resulted in the most inliers + (best_slope, best_intercept) +end +nothing # hide +``` + +We can now wrap it in a Gen proposal that calls out to RANSAC, then samples a +slope and intercept near the one it proposed. + +```@example mcmc_map_tutorial +@gen function ransac_proposal(prev_trace, xs, ys) + (slope_guess, intercept_guess) = ransac(xs, ys, RANSACParams(10, 3, 1.)) + slope ~ normal(slope_guess, 0.1) + intercept ~ normal(intercept_guess, 1.0) +end +nothing # hide +``` + +(Notice that although `ransac` makes random choices, they are not addressed +(and they happen outside of a Gen generative function), so Gen cannot reason +about them. This is OK (see [1]). Writing proposals that have +traced internal randomness (i.e., that make traced random choices that are +not directly used in the proposal) can lead to better inference, but requires +the use of a more complex version of Gen's `mh` operator, which is beyond the +scope of this tutorial.) + +[1] [Using probabilistic programs as +proposals](https://arxiv.org/abs/1801.03612), Marco F. Cusumano-Towner, Vikash K. Mansinghka, 2018. + +One iteration of our update algorithm will now look like this: + +```@example mcmc_map_tutorial +function ransac_update(tr) + # Use RANSAC to (potentially) jump to a better line + # from wherever we are + (tr, _) = mh(tr, ransac_proposal, (xs, ys)) + + # Spend a while refining the parameters, using Gaussian drift + # to tune the slope and intercept, and resimulation for the noise + # and outliers. + for j=1:20 + (tr, _) = mh(tr, select(:prob_outlier)) + (tr, _) = mh(tr, select(:noise)) + (tr, _) = mh(tr, line_proposal, ()) + # Reclassify outliers + for i=1:length(get_args(tr)[1]) + (tr, _) = mh(tr, select(:data => i => :is_outlier)) + end + end + tr +end +``` + +We can now run our main loop for just 5 iterations, and achieve pretty good +results. (Of course, since we do 20 inner loop iterations in `ransac_update`, +this is really closer to 100 iterations.) The running time is significantly +less than before, without a real dip in quality: + +```@example mcmc_map_tutorial +function ransac_inference(xs, ys, observations) + (slope, intercept) = ransac(xs, ys, RANSACParams(10, 3, 1.)) + slope_intercept_init = choicemap() + slope_intercept_init[:slope] = slope + slope_intercept_init[:intercept] = intercept + (tr, _) = generate(regression_with_outliers, (xs,), merge(observations, slope_intercept_init)) + for iter=1:5 + tr = ransac_update(tr) + end + tr +end + +scores = Vector{Float64}(undef, 10) +for i=1:10 + @time tr = ransac_inference(xs, ys, observations) + scores[i] = get_score(tr) +end +println("Log probability: ", logmeanexp(scores)) +``` + +Let's visualize the algorithm: + +```@example mcmc_map_tutorial +(slope, intercept) = ransac(xs, ys, RANSACParams(10, 3, 1.)) +slope_intercept_init = choicemap() +slope_intercept_init[:slope] = slope +slope_intercept_init[:intercept] = intercept +(tr, _) = generate(regression_with_outliers, (xs,), merge(observations, slope_intercept_init)) + +viz = Plots.@animate for i in 1:100 + global tr + + if i % 20 == 0 + (tr, _) = mh(tr, ransac_proposal, (xs, ys)) + end + + # Spend a while refining the parameters, using Gaussian drift + # to tune the slope and intercept, and resimulation for the noise + # and outliers. + (tr, _) = mh(tr, select(:prob_outlier)) + (tr, _) = mh(tr, select(:noise)) + (tr, _) = mh(tr, line_proposal, ()) + + # Reclassify outliers + for i=1:length(get_args(tr)[1]) + (tr, _) = mh(tr, select(:data => i => :is_outlier)) + end + + visualize_trace(tr; title="Iteration $i") +end +gif(viz) +``` + +------------------------------------------------------------------- +### Exercise +#### Improving the heuristic +Currently, the RANSAC heuristic does not use the current trace's information +at all. Try changing it to use the current state as follows: +Instead of a constant `eps` parameter that controls whether a point is +considered an inlier, make this decision based on the currently hypothesized +noise level. Specifically, set `eps` to be equal to the `noise` parameter of the trace. + +Examine whether this improves inference (no need to respond in words here). + +```@example mcmc_map_tutorial +# Modify the function below (which currently is just a copy of `ransac_proposal`) +# as described above so that implements a RANSAC proposal with inlier +# status decided by the noise parameter of the previous trace +# (do not modify the return value, which is unneccessary for a proposal, +# but used for testing) + +@gen function ransac_proposal_noise_based(prev_trace, xs, ys) + params = RANSACParams(10, 3, 1.) + (slope_guess, intercept_guess) = ransac(xs, ys, params) + slope ~ normal(slope_guess, 0.1) + intercept ~ normal(intercept_guess, 1.0) + return params, slope, intercept # (return values just for testing) +end +nothing # hide +``` + +```@raw html +
Solution +``` + +```julia +@gen function ransac_proposal_noise_based(prev_trace, xs, ys) + eps = prev_trace[:noise] + params = RANSACParams(10, 3, eps) + (slope_guess, intercept_guess) = ransac(xs, ys, params) + slope ~ normal(slope_guess, 0.1) + intercept ~ normal(intercept_guess, 1.0) + return params, slope, intercept # (return values just for testing) +end +``` + +```@raw html +
+``` + +------------------------------------------------------------------- + +The code below runs the RANSAC inference as above, but using `ransac_proposal_noise_based`. + +```@example mcmc_map_tutorial +function ransac_update_noise_based(tr) + # Use RANSAC to (potentially) jump to a better line + (tr, _) = mh(tr, ransac_proposal_noise_based, (xs, ys)) + # Refining the parameters + for j=1:20 + (tr, _) = mh(tr, select(:prob_outlier)) + (tr, _) = mh(tr, select(:noise)) + (tr, _) = mh(tr, line_proposal, ()) + # Reclassify outliers + for i=1:length(get_args(tr)[1]) + (tr, _) = mh(tr, select(:data => i => :is_outlier)) + end + end + tr +end +function ransac_inference_noise_based(xs, ys, observations) + # Use an initial epsilon value of 1. + (slope, intercept) = ransac(xs, ys, RANSACParams(10, 3, 1.)) + slope_intercept_init = choicemap() + slope_intercept_init[:slope] = slope + slope_intercept_init[:intercept] = intercept + (tr, _) = generate(regression_with_outliers, (xs,), merge(observations, slope_intercept_init)) + for iter=1:5 + tr = ransac_update_noise_based(tr) + end + tr +end + +scores = Vector{Float64}(undef, 10) +for i=1:10 + @time tr = ransac_inference_noise_based(xs, ys, observations) + scores[i] = get_score(tr) +end +println("Log probability: ", logmeanexp(scores)) +``` + +------------------------------------------------------------------- + +### Exercise +Implement a heuristic-based proposal that selects the points that are +currently classified as *inliers*, finds the line of best fit for this +subset of points, and adds some noise. + +_Hint_: you can get the result for linear regression using least squares approximation by +solving a linear system using Julia's [backslash operator, `\`](https://docs.julialang.org/en/v1/base/math/#Base.:\\-Tuple{Any,Any}) (as is done in the `ransac` +function, above). + +We provide some starter code. You can test your solution by modifying the plotting code above. + +```@example mcmc_map_tutorial +@gen function inlier_heuristic_proposal(prev_trace, xs, ys) + # Put your code below, ensure that you compute values for + # inlier_slope, inlier_intercept and delete the two placeholders + # below. + + inlier_slope = 10000. # + inlier_intercept = 10000. # + + + # Make a noisy proposal. + slope ~ normal(inlier_slope, 0.5) + intercept ~ normal(inlier_intercept, 0.5) + # We return values here for testing; normally, proposals don't have to return values. + return inlier_slope, inlier_intercept +end + +function inlier_heuristic_update(tr) + # Use inlier heuristics to (potentially) jump to a better line + # from wherever we are. + (tr, _) = mh(tr, inlier_heuristic_proposal, (xs, ys)) + # Spend a while refining the parameters, using Gaussian drift + # to tune the slope and intercept, and resimulation for the noise + # and outliers. + for j=1:20 + (tr, _) = mh(tr, select(:prob_outlier)) + (tr, _) = mh(tr, select(:noise)) + (tr, _) = mh(tr, line_proposal, ()) + # Reclassify outliers + for i=1:length(get_args(tr)[1]) + (tr, _) = mh(tr, select(:data => i => :is_outlier)) + end + end + tr +end +nothing # hide +``` + +```@example mcmc_map_tutorial +tr, = Gen.generate(regression_with_outliers, (xs,), observations) +viz = @animate for i in 1:50 + global tr + tr = inlier_heuristic_update(tr) + visualize_trace(tr; title="Iteration $i") +end +gif(viz) +``` + +```@raw html +
Solution +``` + +```julia +@gen function inlier_heuristic_proposal(prev_trace, xs, ys) + # get_indeces for inliers. + inlier_indeces = filter( + i -> !prev_trace[:data => i => :is_outlier], + 1:length(xs) + ) + xs_inlier = xs[inlier_indeces] + ys_inlier = ys[inlier_indeces] + # estimate slope and intercept using least squares. + A = hcat(xs_inlier, ones(length(xs_inlier))) + inlier_slope, inlier_intercept = A \ ys_inlier + + # Make a noisy proposal. + slope ~ normal(inlier_slope, 0.5) + intercept ~ normal(inlier_intercept, 0.5) + # We return values here for testing; normally, proposals don't have to return values. + return inlier_slope, inlier_intercept +end; + +function inlier_heuristic_update(tr) + # Use inlier heuristics to (potentially) jump to a better line + # from wherever we are. + (tr, _) = mh(tr, inlier_heuristic_proposal, (xs, ys)) + # Spend a while refining the parameters, using Gaussian drift + # to tune the slope and intercept, and resimulation for the noise + # and outliers. + for j=1:20 + (tr, _) = mh(tr, select(:prob_outlier)) + (tr, _) = mh(tr, select(:noise)) + (tr, _) = mh(tr, line_proposal, ()) + # Reclassify outliers + for i=1:length(get_args(tr)[1]) + (tr, _) = mh(tr, select(:data => i => :is_outlier)) + end + end + tr +end +``` + +```@raw html +
+``` + +------------------------------------------------------------------- + +### Exercise: Initialization + +In our inference program above, when generating an initial trace on which to +iterate, we initialize the slope and intercept to values proposed by RANSAC. +If we don't do this, the performance decreases sharply, despite the fact that +we still propose new slope/intercept pairs from RANSAC once the loop starts. +Why is this? + +------------------------------------------------------------------- + +## MAP Optimization + +Everything we've done so far has been within the MCMC framework. But +sometimes you're not interested in getting posterior samples—sometimes you +just want a single likely explanation for your data. Gen also provides tools +for _maximum a posteriori_ estimation ("MAP estimation"), the problem of +finding a trace that maximizes the posterior probability under the model +given observations. + +For example, let's say we wanted to take a trace and assign each point's +`is_outlier` score to the most likely possibility. We can do this by +iterating over both possible traces, scoring them, and choosing the one with +the higher score. We can do this using Gen's +[`update`](https://www.gen.dev/docs/stable/ref/gfi/#Update-1) function, +which allows us to manually update a trace to satisfy some constraints: + +```@example mcmc_map_tutorial +function is_outlier_map_update(tr) + (xs,) = get_args(tr) + for i=1:length(xs) + constraints = choicemap(:prob_outlier => 0.1) + constraints[:data => i => :is_outlier] = false + (trace1,) = update(tr, (xs,), (NoChange(),), constraints) + constraints[:data => i => :is_outlier] = true + (trace2,) = update(tr, (xs,), (NoChange(),), constraints) + tr = (get_score(trace1) > get_score(trace2)) ? trace1 : trace2 + end + tr +end +nothing # hide +``` + +For continuous parameters, we can use Gen's `map_optimize` function, which uses _automatic differentiation_ to shift the selected parameters in the direction that causes the probability of the trace to increase most sharply: + +```julia +tr = map_optimize(tr, select(:slope, :intercept), max_step_size=1., min_step_size=1e-5) +``` + +Putting these updates together, we can write an inference program that uses our RANSAC algorithm from above to get an initial trace, then tunes it using optimization: + +```@example mcmc_map_tutorial +using StatsBase: mean + +(slope, intercept) = ransac(xs, ys, RANSACParams(10, 3, 1.)) +slope_intercept_init = choicemap() +slope_intercept_init[:slope] = slope +slope_intercept_init[:intercept] = intercept +(tr,) = generate(regression_with_outliers, (xs,), merge(observations, slope_intercept_init)) + + +ransac_score, final_score = 0, 0 +viz = Plots.@animate for i in 1:35 + global tr, ransac_score + if i < 6 + tr = ransac_update(tr) + else + tr = map_optimize(tr, select(:slope, :intercept), max_step_size=1., min_step_size=1e-5) + tr = map_optimize(tr, select(:noise), max_step_size=1e-2, min_step_size=1e-5) + tr = is_outlier_map_update(tr) + optimal_prob_outlier = mean([tr[:data => i => :is_outlier] for i in 1:length(xs)]) + optimal_prob_outlier = min(0.5, max(0.05, optimal_prob_outlier)) + tr, = update(tr, (xs,), (NoChange(),), choicemap(:prob_outlier => optimal_prob_outlier)) + end + + if i == 5 + ransac_score = get_score(tr) + end + + visualize_trace(tr; title="Iteration $i $(i < 6 ? "(RANSAC init)" : "(MAP optimization)")") +end +final_score = get_score(tr) + +println("Score after ransac: $(ransac_score). Final score: $(final_score).") +gif(viz) +``` + +Below, we evaluate the algorithm and we see that it gets our best scores yet, which is what it's meant to do: + +```@example mcmc_map_tutorial +function map_inference(xs, ys, observations) + (slope, intercept) = ransac(xs, ys, RANSACParams(10, 3, 1.)) + slope_intercept_init = choicemap() + slope_intercept_init[:slope] = slope + slope_intercept_init[:intercept] = intercept + (tr, _) = generate(regression_with_outliers, (xs,), merge(observations, slope_intercept_init)) + for iter=1:5 + tr = ransac_update(tr) + end + + for iter = 1:20 + # Take a single gradient step on the line parameters. + tr = map_optimize(tr, select(:slope, :intercept), max_step_size=1., min_step_size=1e-5) + tr = map_optimize(tr, select(:noise), max_step_size=1e-2, min_step_size=1e-5) + + # Choose the most likely classification of outliers. + tr = is_outlier_map_update(tr) + + # Update the prob outlier + choices = get_choices(tr) + optimal_prob_outlier = count(i -> choices[:data => i => :is_outlier], 1:length(xs)) / length(xs) + optimal_prob_outlier = min(0.5, max(0.05, optimal_prob_outlier)) + (tr, _) = update(tr, (xs,), (NoChange(),), choicemap(:prob_outlier => optimal_prob_outlier)) + end + tr +end + +scores = Vector{Float64}(undef, 10) +for i=1:10 + @time tr = map_inference(xs,ys,observations) + scores[i] = get_score(tr) +end +println(logmeanexp(scores)) +``` + +This doesn't necessarily mean that it's "better," though. It finds the most +probable explanation of the data, which is a different problem from the one +we tackled with MCMC inference. There, the goal was to sample from the +posterior, which allows us to better characterize our uncertainty. Using +MCMC, there might be a borderline point that is sometimes classified as an +outlier and sometimes not, reflecting our uncertainty; with MAP optimization, +we will always be shown the most probable answer. + +--------- + +Below we generate a dataset for which there are two distinct possible explanations +(the grey lines) under our model `regression_with_outliers`. + +```@example mcmc_map_tutorial +function make_bimodal_dataset(n) + Random.seed!(4) + prob_outlier = 0.2 + true_inlier_noise = 0.5 + true_outlier_noise = 5.0 + true_slope1 = 1 + true_intercept1 = 0 + true_slope2 = -2/3 + true_intercept2 = 0 + xs = collect(range(-5, stop=5, length=n)) + ys = Float64[] + for (i, x) in enumerate(xs) + if rand() < prob_outlier + y = randn() * true_outlier_noise + else + if rand((true,false)) + y = true_slope1 * x + true_intercept1 + randn() * true_inlier_noise + else + y = true_slope2 * x + true_intercept2 + randn() * true_inlier_noise + end + end + push!(ys, y) + end + xs,ys,true_slope1,true_slope2,true_intercept1,true_intercept2 +end + +(xs, ys_bimodal, m1,m2,b1,b2) = make_bimodal_dataset(20) +observations_bimodal = make_constraints(ys_bimodal) + +Plots.scatter(xs, ys_bimodal, color="black", xlabel="X", ylabel="Y", label=nothing, title="Bimodal data") +Plots.plot!(xs,m1.*xs.+b1, color="blue", label=nothing) +Plots.plot!(xs,m2.*xs.+b2, color="green", label=nothing) +``` + +### Exercise + +For this dataset, the code below will run (i) Metropolis hastings with a +Gaussian Drift proposal and (ii) MAP optimization, using implementations from +above. Make sure you understand what it is doing. Do both algorithms explore +both modes (i.e. both possible explanations)? Play with running the +algorithms multiple times. + +If one or both algorithms doesn't then explain in a few sentences why you +think this is. + +```@example mcmc_map_tutorial +(slope, intercept) = ransac(xs, ys_bimodal, RANSACParams(10, 3, 1.)) +slope_intercept_init = choicemap() +slope_intercept_init[:slope] = slope +slope_intercept_init[:intercept] = intercept +(tr,) = generate( + regression_with_outliers, (xs,), + merge(observations_bimodal, slope_intercept_init)) + +tr_drift = tr +tr_map = tr + +viz = Plots.@animate for i in 1:305 + global tr_map, tr_drift + if i < 6 + tr_drift = ransac_update(tr) + tr_map = tr_drift + else + # Take a single gradient step on the line parameters. + tr_map = map_optimize(tr_map, select(:slope, :intercept), max_step_size=1., min_step_size=1e-5) + tr_map = map_optimize(tr_map, select(:noise), max_step_size=1e-2, min_step_size=1e-5) + # Choose the most likely classification of outliers. + tr_map = is_outlier_map_update(tr_map) + # Update the prob outlier + optimal_prob_outlier = mean([tr_map[:data => i => :is_outlier] for i in 1:length(xs)]) + optimal_prob_outlier = min(0.5, max(0.05, optimal_prob_outlier)) + tr_map, = update(tr_map, (xs,), (NoChange(),), choicemap(:prob_outlier => optimal_prob_outlier)) + + # Gaussian drift update: + tr_drift = gaussian_drift_update(tr_drift) + end + + Plots.plot(visualize_trace(tr_drift; title="Drift (Iter $i)"), visualize_trace(tr_map; title="MAP (Iter $i)")) +end + +drift_final_score = get_score(tr_drift) +map_final_score = get_score(tr_map) +println("i. MH Gaussian drift score $(drift_final_score)") +println("ii. MAP final score: $(final_score).") + +gif(viz) +``` + +The above was good for an overall qualitative examination, but let's also +examine a little more quantitatively how often the two proposals explore +the two modes, by running multiple times and keeping track of how often the +slope is positive/negative for each, for a few different initializations. + +**Warning: the following cell may take a few minutes to run.** + +```@example mcmc_map_tutorial +total_runs = 25 + +for (index, value) in enumerate([(1, 0), (-1, 0), ransac(xs, ys_bimodal, RANSACParams(10, 3, 1.))]) + n_pos_drift = n_neg_drift = n_pos_map = n_neg_map = 0 + + for i=1:total_runs + pos_drift = neg_drift = pos_map = neg_map = false + + #### RANSAC for initializing + (slope, intercept) = value # ransac(xs, ys_bimodal, RANSACParams(10, 3, 1.)) + slope_intercept_init = choicemap() + slope_intercept_init[:slope] = slope + slope_intercept_init[:intercept] = intercept + (tr,) = generate( + regression_with_outliers, (xs,), + merge(observations_bimodal, slope_intercept_init)) + for iter=1:5 + tr = ransac_update(tr) + end + ransac_score = get_score(tr) + tr_drift = tr # version of the trace for the Gaussian drift algorithm + tr_map = tr # version of the trace for the MAP optimization + + #### Refine the parameters according to each of the algorithms + for iter = 1:300 + # MAP optimiztion: + # Take a single gradient step on the line parameters. + tr_map = map_optimize(tr_map, select(:slope, :intercept), max_step_size=1., min_step_size=1e-5) + tr_map = map_optimize(tr_map, select(:noise), max_step_size=1e-2, min_step_size=1e-5) + # Choose the most likely classification of outliers. + tr_map = is_outlier_map_update(tr_map) + # Update the prob outlier + optimal_prob_outlier = count(i -> tr_map[:data => i => :is_outlier], 1:length(xs)) / length(xs) + optimal_prob_outlier = min(0.5, max(0.05, optimal_prob_outlier)) + (tr_map, _) = update(tr_map, (xs,), (NoChange(),), choicemap(:prob_outlier => optimal_prob_outlier)) + + # Gaussian drift update: + tr_drift = gaussian_drift_update(tr_drift) + + if tr_drift[:slope] > 0 + pos_drift = true + elseif tr_drift[:slope] < 0 + neg_drift = true + end + if tr_map[:slope] > 0 + pos_map = true + elseif tr_map[:slope] < 0 + neg_map = true + end + end + + if pos_drift + n_pos_drift += 1 + end + if neg_drift + n_neg_drift += 1 + end + if pos_map + n_pos_map += 1 + end + if neg_map + n_neg_map += 1 + end + end + (slope, intercept) = value + println("\n\nWITH INITIAL SLOPE $(slope) AND INTERCEPT $(intercept)") + println("TOTAL RUNS EACH: $(total_runs)") + println("\n times neg. slope times pos. slope") + println("\ndrift: $(n_neg_drift) $(n_pos_drift)") + println("\nMAP: $(n_neg_map) $(n_pos_map)") +end +``` + +Although this experiment is imperfect, we can broadly see that the drift kernel often explores both modes within a single run, whereas this is rarer for the MAP kernel (in 25 runs, the MAP kernel visits on average 1.08 of the 2 modes, whereas the drift kernel visits 1.6). \ No newline at end of file diff --git a/docs/src/tutorials/modeling_in_gen.md b/docs/src/tutorials/modeling_in_gen.md index df098708d..c2b17d597 100644 --- a/docs/src/tutorials/modeling_in_gen.md +++ b/docs/src/tutorials/modeling_in_gen.md @@ -1,4 +1,4 @@ -# [Introduction to Modeling in Gen](@id introduction_to_modeling_in_gen) +# [Introduction to Modeling in Gen](@id modeling_tutorial) Welcome! In this tutorial, you'll get your feet wet with Gen, a multi-paradigm platform for probabilistic modeling and inference. By From fd2230c44a2294cd40df66b102cb439a1b1cb625 Mon Sep 17 00:00:00 2001 From: Xuan Date: Wed, 18 Jun 2025 21:34:24 -0400 Subject: [PATCH 4/8] Fix @ref links. --- docs/src/tutorials/mcmc_map.md | 102 ++++++++++++++------------ docs/src/tutorials/modeling_in_gen.md | 4 +- 2 files changed, 58 insertions(+), 48 deletions(-) diff --git a/docs/src/tutorials/mcmc_map.md b/docs/src/tutorials/mcmc_map.md index f21cbf4c2..c4821dee0 100644 --- a/docs/src/tutorials/mcmc_map.md +++ b/docs/src/tutorials/mcmc_map.md @@ -39,9 +39,10 @@ Our model will take as input a vector of `x` coordinates, and produce as output corresponding `y` coordinates. We will also use this opportunity to introduce some syntactic sugar. -As described in [Introduction to Modeling in Gen](@ref modeling_tutorial), random choices in Gen are given -_addresses_ using the syntax `{addr} ~ distribution(...)`. But this can -be a bit verbose, and often leads to code that looks like the following: +As described in [Introduction to Modeling in Gen](@ref modeling_tutorial), +random choices in Gen are given _addresses_ using the syntax +`{addr} ~ distribution(...)`. But this can be a bit verbose, and often leads to +code that looks like the following: ```julia x = {:x} ~ normal(0, 1) @@ -219,12 +220,12 @@ Plots.scatter(xs, ys, color="black", xlabel="X", ylabel="Y", label=nothing, title="Observations - regular data and outliers") ``` -We will to express our _observations_ as a `ChoiceMap` that constrains the +We will to express our _observations_ as a [`ChoiceMap`](@ref) that constrains the values of certain random choices to equal their observed values. Here, we want to constrain the values of the choices with address `:data => i => :y` (that is, the sampled $y$ coordinates) to equal the observed $y$ values. Let's write a helper function that takes in a vector of $y$ values and -creates a `ChoiceMap` that we can use to constrain our model: +creates a [`ChoiceMap`](@ref) that we can use to constrain our model: ```@example mcmc_map_tutorial function make_constraints(ys::Vector{Float64}) @@ -282,10 +283,11 @@ algorithms for iteratively producing approximate samples from a distribution (when applied to Bayesian inference problems, the posterior distribution of unknown (hidden) model variables given data). -There is a rich theory behind MCMC methods, but we focus on applying MCMC in -Gen and introducing theoretical ideas only when necessary for understanding. -As we will see, Gen provides abstractions that hide and automate much of the -math necessary for implementing MCMC algorithms correctly. +There is a rich theory behind MCMC methods (see [this paper](https://doi.org/10.1023/A:1020281327116) +for an introduction), but we focus on applying MCMC in Gen, introducing +theoretical ideas only when necessary for understanding. As we will see, Gen +provides abstractions that hide and automate much of the math necessary for +implementing MCMC algorithms correctly. The general shape of an MCMC algorithm is as follows. We begin by sampling an intial setting of all unobserved variables; in Gen, we produce an initial @@ -305,7 +307,7 @@ tries not to go down dead ends: it is more likely to take an exploratory step into a low-probability region if it knows it can easily get back to where it came from. -Gen's `metropolis_hastings` function _automatically_ adds this +Gen's [`metropolis_hastings`](@ref) function _automatically_ adds this "accept/reject" check (including the correct computation of the probability of acceptance or rejection), so that inference programmers need only think about what sorts of updates might be useful to propose. Starting in @@ -318,7 +320,8 @@ One of the simplest strategies we can use is called Resimulation MH, and it works as follows. We begin, as in most iterative inference algorithms, by sampling an initial -trace from our model, fixing the observed choices to their observed values. +trace from our model using the [`generate`](@ref) API function, fixing the +observed choices to their observed values. ```julia # Gen's `generate` function accepts a model, a tuple of arguments to the model, @@ -373,17 +376,16 @@ with the current hypothesized proportion of `is_outlier` choices that are set to `true`. Resimulating a block of variables is the simplest form of update that Gen's -`metropolis_hastings` operator (or `mh` for short) supports. When supplied -with a _current trace_ and a _selection_ of trace addresses to resimulate, -`mh` performs the resimulation and the appropriate accept/reject check, then -returns a possibly updated trace, along with a boolean indicating whether the -update was accepted or not. A selection is created using the `select` -method. So a single update of the scheme we proposed above would look like -this: - -Perform a single block resimulation update of a trace. +[`metropolis_hastings`](@ref) operator (or [`mh`](@ref) for short) supports. +When supplied with a _current trace_ and a _selection_ of trace addresses to +resimulate, [`mh`](@ref) performs the resimulation and the appropriate +accept/reject check, then returns a possibly updated trace, along with a Boolean +indicating whether the update was accepted or not. A selection is created using +the [`select`](@ref) method. So a single update of the scheme we proposed above +would look like this: ```@example mcmc_map_tutorial +# Perform a single block resimulation update of a trace. function block_resimulation_update(tr) # Block 1: Update the line's parameters line_params = select(:noise, :slope, :intercept) @@ -463,8 +465,9 @@ end gif(viz) ``` -We can see that although the algorithm keeps changing the inferences of which points are inliers and outliers, -it has a harder time refining the continuous parameters. We address this challenge next. +We can see that although the algorithm keeps changing the inferences of which +points are inliers and outliers, it has a harder time refining the continuous +parameters. We address this challenge next. ## MCMC Part 2: Gaussian Drift MH @@ -503,7 +506,10 @@ end nothing # hide ``` -This is often called a "Gaussian drift" proposal, because it essentially amounts to proposing steps of a random walk. (What makes it different from a random walk is that we will still use an MH accept/reject step to make sure we don't wander into areas of very low probability.) +This is often called a "Gaussian drift" proposal, because it essentially amounts +to proposing steps of a random walk. (What makes it different from a random walk +is that we will still use an MH accept/reject step to make sure we don't wander +into areas of very low probability.) To use the proposal, we write: @@ -520,8 +526,8 @@ Two things to note: 2. The argument list to the proposal is an empty tuple, `()`. The `line_proposal` generative function does expect an argument, the previous trace, but this is supplied automatically to all MH custom proposals - (a proposal generative function for use with `mh` must take as its first argument the - current trace of the model). + (a proposal generative function for use with [`mh`](@ref) must take as its + first argument the current trace of the model). Let's swap it into our update: @@ -634,7 +640,6 @@ struct RANSACParams end end - function ransac(xs::Vector{Float64}, ys::Vector{Float64}, params::RANSACParams) best_num_inliers::Int = -1 best_slope::Float64 = NaN @@ -681,14 +686,13 @@ nothing # hide (Notice that although `ransac` makes random choices, they are not addressed (and they happen outside of a Gen generative function), so Gen cannot reason -about them. This is OK (see [1]). Writing proposals that have -traced internal randomness (i.e., that make traced random choices that are -not directly used in the proposal) can lead to better inference, but requires -the use of a more complex version of Gen's `mh` operator, which is beyond the -scope of this tutorial.) +about them. This is OK (see [1]). Writing proposals that have traced internal +randomness (i.e., that make traced random choices that are not directly used +in the proposal) can lead to better inference, but requires the use of a more +complex version of Gen's [`mh`](@ref) operator, which is beyond the scope of +this tutorial.) -[1] [Using probabilistic programs as -proposals](https://arxiv.org/abs/1801.03612), Marco F. Cusumano-Towner, Vikash K. Mansinghka, 2018. +[1] [Using probabilistic programs as proposals](https://arxiv.org/abs/1801.03612), Marco F. Cusumano-Towner, Vikash K. Mansinghka, 2018. One iteration of our update algorithm will now look like this: @@ -777,12 +781,12 @@ gif(viz) ### Exercise #### Improving the heuristic Currently, the RANSAC heuristic does not use the current trace's information -at all. Try changing it to use the current state as follows: -Instead of a constant `eps` parameter that controls whether a point is -considered an inlier, make this decision based on the currently hypothesized -noise level. Specifically, set `eps` to be equal to the `noise` parameter of the trace. +at all. Try changing it to use the current state as follows: Instead of a +constant `eps` parameter that controls whether a point is considered an inlier, +make this decision based on the currently hypothesized noise level. +Specifically, set `eps` to be equal to the `noise` parameter of the trace. -Examine whether this improves inference (no need to respond in words here). +Examine whether this improves inference. ```@example mcmc_map_tutorial # Modify the function below (which currently is just a copy of `ransac_proposal`) @@ -869,8 +873,7 @@ currently classified as *inliers*, finds the line of best fit for this subset of points, and adds some noise. _Hint_: you can get the result for linear regression using least squares approximation by -solving a linear system using Julia's [backslash operator, `\`](https://docs.julialang.org/en/v1/base/math/#Base.:\\-Tuple{Any,Any}) (as is done in the `ransac` -function, above). +solving a linear system using Julia's [backslash operator, `\`](https://docs.julialang.org/en/v1/base/math/#Base.:\\-Tuple{Any,Any}) (as is done in the `ransac` function, above). We provide some starter code. You can test your solution by modifying the plotting code above. @@ -994,8 +997,7 @@ given observations. For example, let's say we wanted to take a trace and assign each point's `is_outlier` score to the most likely possibility. We can do this by iterating over both possible traces, scoring them, and choosing the one with -the higher score. We can do this using Gen's -[`update`](https://www.gen.dev/docs/stable/ref/gfi/#Update-1) function, +the higher score. We can do this using Gen's [`update`](@ref) function, which allows us to manually update a trace to satisfy some constraints: ```@example mcmc_map_tutorial @@ -1014,13 +1016,17 @@ end nothing # hide ``` -For continuous parameters, we can use Gen's `map_optimize` function, which uses _automatic differentiation_ to shift the selected parameters in the direction that causes the probability of the trace to increase most sharply: +For continuous parameters, we can use Gen's [`map_optimize`](@ref) function, +which uses _automatic differentiation_ to shift the selected parameters in the +direction that causes the probability of the trace to increase most sharply: ```julia tr = map_optimize(tr, select(:slope, :intercept), max_step_size=1., min_step_size=1e-5) ``` -Putting these updates together, we can write an inference program that uses our RANSAC algorithm from above to get an initial trace, then tunes it using optimization: +Putting these updates together, we can write an inference program that uses our +RANSAC algorithm from above to get an initial trace, then tunes it using +optimization: ```@example mcmc_map_tutorial using StatsBase: mean @@ -1058,7 +1064,8 @@ println("Score after ransac: $(ransac_score). Final score: $(final_score).") gif(viz) ``` -Below, we evaluate the algorithm and we see that it gets our best scores yet, which is what it's meant to do: +Below, we evaluate the algorithm and we see that it gets our best scores yet, +which is what it's meant to do: ```@example mcmc_map_tutorial function map_inference(xs, ys, observations) @@ -1279,4 +1286,7 @@ for (index, value) in enumerate([(1, 0), (-1, 0), ransac(xs, ys_bimodal, RANSACP end ``` -Although this experiment is imperfect, we can broadly see that the drift kernel often explores both modes within a single run, whereas this is rarer for the MAP kernel (in 25 runs, the MAP kernel visits on average 1.08 of the 2 modes, whereas the drift kernel visits 1.6). \ No newline at end of file +Although this experiment is imperfect, we can broadly see that the drift kernel +often explores both modes within a single run, whereas this is rarer for the MAP +kernel (in 25 runs, the MAP kernel visits on average 1.08 of the 2 modes, +whereas the drift kernel visits 1.6). \ No newline at end of file diff --git a/docs/src/tutorials/modeling_in_gen.md b/docs/src/tutorials/modeling_in_gen.md index c2b17d597..0893e4da8 100644 --- a/docs/src/tutorials/modeling_in_gen.md +++ b/docs/src/tutorials/modeling_in_gen.md @@ -444,7 +444,7 @@ observed data. That is, the inference program will try to find a trace that well explains the dataset we created above. We can inspect that trace to find estimates of the slope and intercept of a line that fits the data. -Functions like `importance_resampling` expect us to provide a _model_ and +Functions like [`importance_resampling`](@ref) expect us to provide a _model_ and also an _choice map_ representing our data set and relating it to the model. A choice map maps random choice addresses from the model to values from our data set. Here, we want to tie model addresses like `(:y, 4)` to data set @@ -476,7 +476,7 @@ trace = do_inference(line_model, xs, ys, 100) render_trace(trace) ``` -We see that `importance_resampling` found a reasonable slope and intercept to explain the data. We can also visualize many samples in a grid: +We see that [`importance_resampling`](@ref) found a reasonable slope and intercept to explain the data. We can also visualize many samples in a grid: ```@example modeling_tutorial traces = [do_inference(line_model, xs, ys, 100) for _=1:10]; From 7f79ccfa1b42b6cf7a6026fd675dc3802d6629ad Mon Sep 17 00:00:00 2001 From: Xuan Date: Sun, 6 Jul 2025 17:09:32 -0400 Subject: [PATCH 5/8] Add enumerative inference tutorial. --- docs/Project.toml | 1 + docs/pages.jl | 1 + docs/src/index.md | 1 + docs/src/tutorials/enumerative.md | 892 ++++++++++++++++++++++++++++++ 4 files changed, 895 insertions(+) create mode 100644 docs/src/tutorials/enumerative.md diff --git a/docs/Project.toml b/docs/Project.toml index 6dfe81a83..4b5a5c8e3 100644 --- a/docs/Project.toml +++ b/docs/Project.toml @@ -2,6 +2,7 @@ Documenter = "e30172f5-a6a5-5a46-863b-614d45cd2de4" Gen = "ea4f424c-a589-11e8-07c0-fd5c91b9da4a" Plots = "91a5bcdd-55d7-5caf-9e0b-520d859cae80" +StatsBase = "2913bbd2-ae8a-5f71-8c99-4fb6c76f3a91" [compat] Documenter = "1" diff --git a/docs/pages.jl b/docs/pages.jl index ca74e0d86..32bd58078 100644 --- a/docs/pages.jl +++ b/docs/pages.jl @@ -4,6 +4,7 @@ pages = [ "Getting Started" => "tutorials/getting_started.md", "Introduction to Modeling in Gen" => "tutorials/modeling_in_gen.md", "Basics of MCMC and MAP Inference" => "tutorials/mcmc_map.md", + "Debugging Models with Enumeration" => "tutorials/enumerative.md", "Object Tracking with SMC" => "tutorials/smc.md", "Variational Inference in Gen" => "tutorials/vi.md", "Learning Generative Functions" => "tutorials/learning_gen_fns.md", diff --git a/docs/src/index.md b/docs/src/index.md index 8c9f6ca52..c07304950 100644 --- a/docs/src/index.md +++ b/docs/src/index.md @@ -47,6 +47,7 @@ Pages = [ "tutorials/getting_started.md", "tutorials/modeling_in_gen.md", "tutorials/mcmc_map.md", + "tutorials/enumerative.md", "tutorials/smc.md", "tutorials/vi.md", "tutorials/scaling_with_sml.md", diff --git a/docs/src/tutorials/enumerative.md b/docs/src/tutorials/enumerative.md new file mode 100644 index 000000000..613cc90ec --- /dev/null +++ b/docs/src/tutorials/enumerative.md @@ -0,0 +1,892 @@ +# [Debugging Models with Enumerative Inference](@id enumerative_tutorial) + +When working with probabilistic models, we often rely on Monte Carlo methods like importance sampling (as introduced in [Introduction to Modeling](@ref modeling_tutorial)) and MCMC (as introduced in [Basics of MCMC](@ref mcmc_map_tutorial)) to approximate posterior distributions. But how can we tell if these approximations are actually working correctly? Sometimes poor inference results stem from bugs in our inference algorithms, while other times they reveal fundamental issues with our model specification. + +This tutorial introduces *enumerative inference* as a debugging tool. Unlike the sampling-based methods we've seen in previous tutorials, which draw samples that are approximately distributed according to the posterior distribution over latent values, enumerative inference systematically evaluates the posterior probability of every value in the latent space (or a discretized version of this space). + +When the latent space isn't too large (e.g. not too many dimensions), this approach can compute a "gold standard" posterior approximation that other methods can be compared against, helping us distinguish between inference failures and model misspecification. Enumerative inference is often slower than a well-tuned Monte Carlo algorithm (since it may enumerate over regions with very low probability), but having a gold-standard posterior allows us to check that faster and more efficient algorithms are working correctly. + +```@setup enumerative_tutorial +using Gen, Plots, StatsBase, Printf +import Random +Random.seed!(0) +``` + +## Enumeration for Discrete Models + +Let's start with a simple example where enumeration can be used to perform *exact inference*, computing the exact posterior probability for every possible combination of discrete latent variables. We'll build a robust Bayesian linear regression model, but unlike the continuous model from the MCMC tutorial, we'll use discrete priors for all latent variables. + +```@example enumerative_tutorial +@gen function discrete_regression(xs::Vector{<:Real}) + # Discrete priors for slope and intercept + slope ~ uniform_discrete(-2, 2) # Slopes: -2, -1, 0, 1, 2 + intercept ~ uniform_discrete(-2, 2) # Intercepts: -2, -1, 0, 1, 2 + + # Sample outlier classification and y value for each x value + n = length(xs) + ys = Float64[] + for i = 1:n + # Prior on outlier probability + is_outlier = {:data => i => :is_outlier} ~ bernoulli(0.1) + + if is_outlier + # Outliers have large noise + y = {:data => i => :y} ~ normal(0., 5.) + else + # Inliers follow the linear relationship, with low noise + y_mean = slope * xs[i] + intercept + y = {:data => i => :y} ~ normal(y_mean, 1.) + end + push!(ys, y) + end + + return ys +end +nothing # hide +``` + +Let's generate some synthetic data with a true slope of 1 and intercept of 0: + +```@example enumerative_tutorial +# Generate synthetic data +true_slope = 1 +true_intercept = 0 +xs = [-2., -1., 0., 1., 2.] +ys = true_slope .* xs .+ true_intercept .+ 1.0 * randn(5) + +# Make one point an outlier +ys[3] = 4.0 + +# Visualize the data +point_colors = [:blue, :blue, :red, :blue, :blue] +scatter(xs, ys, label="Observations", markersize=6, xlabel="x", ylabel="y", + color=point_colors) +plot!(xs, true_slope .* xs .+ true_intercept, + label="True line", linestyle=:dash, linewidth=2, color=:black) +``` + +Now we can use enumerative inference to compute the exact posterior. We'll enumerate over all possible combinations of slope, intercept, and outlier classifications: + +```@example enumerative_tutorial +# Create observations choicemap +observations = choicemap() +for (i, y) in enumerate(ys) + observations[:data => i => :y] = y +end + +# Set up the enumeration grid +# We enumerate over discrete slope, intercept, and outlier classifications +grid_specs = Tuple[ + (:slope, -2:2), # 5 possible slopes + (:intercept, -2:2), # 3 possible intercepts +] +for i in 1:length(xs) + push!(grid_specs, (:data => i => :is_outlier, [false, true])) +end + +# Create the enumeration grid +grid = choice_vol_grid(grid_specs...) +nothing # hide +``` + +Here, we used [`choice_vol_grid`](@ref) to enumerate over all possible combinations of slope, intercept, and outlier classifications. The resulting `grid` object is a multi-dimensional iterator, where each element consists of a [`ChoiceMap`](@ref) that specifies the values of all latent variables, and the log-volume of latent space covered by that element of the grid. Since all latent variables are discrete, the volume of latent space covered by each element is equal to 1 (and hence the log-volume is 0). We can inspect the first element of this grid using the `first` function: + +```@example enumerative_tutorial +choices, log_vol = first(grid) +println("Log volume: ", log_vol) +println("Choices: ") +show(stdout, "text/plain", choices) +``` + +Having constructed the enumeration grid, we now pass this to the [`enumerative_inference`](@ref) function, along with the generative model (`discrete_regression`), model arguments (in this case, `xs`), and the `observations`: + +```@example enumerative_tutorial +# Run enumerative inference +traces, log_norm_weights, lml_est = + enumerative_inference(discrete_regression, (xs,), observations, grid) + +println("Grid size: ", size(grid)) +println("Log marginal likelihood: ", lml_est) +``` + +The [`enumerative_inference`](@ref) function returns an array of `traces` and an array of normalized log posterior probabilities (`log_norm_weights`) with the same shape as the input `grid`. It also returns an estimate of the log marginal likelihood (`lml_est`) of the observations. The estimate is *exact* in this case, since we enumerated over all possible combinations of latent variables. + +Each trace corresponds to a full combination of the latent variables that were enumerated over. As such, the `log_norm_weights` array represents the *joint* posterior distribution over all latent variables. By summing over all traces which have the same value for a specific latent variable (or equivalently, by summing over a dimension of the `log_norm_weights` array), we can compute the *marginal* posterior distribution for that variable. We'll do this below for the `slope` and `intercept` variables: + +```@example enumerative_tutorial +# Compute 2D marginal posterior over slope and intercept +sum_dims = Tuple(3:ndims(log_norm_weights)) # Sum over all other variables +posterior_grid = sum(exp.(log_norm_weights), dims=sum_dims) +posterior_grid = dropdims(posterior_grid; dims=sum_dims) +``` + +Let's visualize the marginal posteriors over these variables, as well the joint posterior for both variables together. Below is some code to plot a 2D posterior heatmap with 1D marginals as histograms. + +```@raw html +
Code to plot 2D grid of posterior values +``` + +```@example enumerative_tutorial +using Plots + +function plot_posterior_grid( + x_range, y_range, xy_probs; + is_discrete = true, x_true = missing, y_true = missing, + xlabel = "", ylabel = "" +) + # Create the main heatmap + p_main = heatmap(x_range, y_range, xy_probs, colorbar=false, widen=false, + color=:grays, xlabel=xlabel, ylabel=ylabel) + if is_discrete + # Add true parameters + scatter!(p_main, [x_true], [y_true], legend=true, + markersize=36, markershape=:rect, color=:white, + markerstrokecolor=:red, label="True Parameters") + # Annotate each cell with its posterior probability + for idx in CartesianIndices(xy_probs) + i, j = Tuple(idx) + prob_str = @sprintf("%.3f", xy_probs[j, i]) + prob_color = xy_probs[j, i] > 0.2 ? :black : :white + annotate!(x_range[i], y_range[j], + text(prob_str, color = prob_color, pointsize=12)) + end + else + # Add true parameters + if !ismissing(x_true) && !ismissing(y_true) + scatter!(p_main, [x_true], [y_true], legend=true, + markersize=6, color=:red, markershape=:cross, + label="True Parameters") + end + if !ismissing(x_true) + vline!([x_true], linestyle=:dash, linewidth=1, color=:red, + label="", alpha=0.5) + end + if !ismissing(y_true) + hline!([y_true], linestyle=:dash, linewidth=1, color=:red, + label="", alpha=0.5) + end + end + + # Create 1D marginal histograms + x_probs = vec(sum(xy_probs, dims=1)) + y_probs = vec(sum(xy_probs, dims=2)) + p_top = bar(x_range, x_probs, orientation=:v, ylims=(0, maximum(x_probs)), + bar_width=diff(x_range)[1], linewidth=0, color=:black, + showaxis=true, ticks=false, legend=false, widen=false) + p_right = bar(y_range, y_probs, orientation=:h, xlims=(0, maximum(y_probs)), + bar_width=diff(y_range)[1], linewidth=0, color=:black, + showaxis=true, ticks=false, legend=false, widen=false) + if !is_discrete + xlims!(p_top, xlims(p_main)) + ylims!(p_right, ylims(p_main)) + if !ismissing(x_true) + vline!(p_top, [x_true], linestyle=:dash, + linewidth=1, color=:red, legend=false) + end + if !ismissing(y_true) + hline!(p_right, [y_true], linestyle=:dash, + linewidth=1, color=:red, legend=false) + end + end + + # Create empty plot for top-right corner + p_empty = plot(legend=false, grid=false, showaxis=false, ticks=false) + + # Combine plots using layout + plot(p_top, p_empty, p_main, p_right, + layout=@layout([a{0.9w,0.1h} b{0.1w,0.1h}; c{0.9w,0.9h} d{0.1w,0.9h}]), + size=(750, 750)) +end +nothing # hide +``` + +```@raw html +
+``` + +```@example enumerative_tutorial +# Extract parameter ranges +slope_range = [trs[1][:slope] for trs in eachslice(traces, dims=1)] +intercept_range = [trs[1][:intercept] for trs in eachslice(traces, dims=2)] + +# Plot 2D posterior heatmap with 1D marginals as histograms +plot_posterior_grid(intercept_range, slope_range, posterior_grid, + x_true = true_intercept, y_true = true_slope, + xlabel = "Intercept", ylabel = "Slope", + is_discrete = true) +``` + +As can be seen, the posterior concentrates around the true values of the slope and intercept, though there is some uncertainty about both. + +We can also examine which points are most likely to be outliers: + +```@example enumerative_tutorial +# Compute posterior probability of each point being an outlier +outlier_probs = zeros(length(xs)) +for (j, trace) in enumerate(traces) + for i in 1:length(xs) + if trace[:data => i => :is_outlier] + outlier_probs[i] += exp(log_norm_weights[j]) + end + end +end + +bar(1:length(xs), outlier_probs, + xlabel="x", ylabel="P(outlier | data)", + color=:black, ylim=(0, 1), legend=false) +``` + +Notice that enumerative inference correctly identifies that point 3 (which we made an outlier) has a high probability of being an outlier, while maintaining uncertainty about the exact classifications. + +## Enumeration for Continuous Models + +Many generative models of interest have continuous latent variables. While we can't enumerate over continuous spaces exactly, we can create a discrete approximation of a continuous target distribution by defining a grid. Let's extend our model to use continuous priors: + +```@example enumerative_tutorial +@gen function continuous_regression(xs::Vector{<:Real}) + # Continuous slope and intercept priors + slope ~ normal(0, 1) + intercept ~ normal(0, 2) + + # Sample outlier classification and y value for each x value + n = length(xs) + ys = Float64[] + for i = 1:n + # Prior on outlier probability + is_outlier = {:data => i => :is_outlier} ~ bernoulli(0.1) + + if is_outlier + # Outliers have large noise + y = {:data => i => :y} ~ normal(0., 5.) + else + # Inliers follow the linear relationship, with low noise + y_mean = slope * xs[i] + intercept + y = {:data => i => :y} ~ normal(y_mean, 1.) + end + push!(ys, y) + end + + return ys +end +nothing # hide +``` + +We now construct a grid over the latent space using [`choice_vol_grid`](@ref). For continuous variables, we need to provide a range of grid points (including start and end points), and specify that the variable is `:continuous`: + +```@example enumerative_tutorial +grid = choice_vol_grid( + (:slope, -3:0.25:3, :continuous), # 24 grid intervals + (:intercept, -4:0.5:4, :continuous), # 16 grid intervals + # Still enumerate exactly over outlier classifications + (:data => 1 => :is_outlier, [false, true]), + (:data => 2 => :is_outlier, [false, true]), + (:data => 3 => :is_outlier, [false, true]), + (:data => 4 => :is_outlier, [false, true]), + (:data => 5 => :is_outlier, [false, true]); + anchor = :midpoint # Anchor evaluation point at midpoint of each interval +) + +println("Grid size for continuous model: ", size(grid)) +println("Number of grid elements: ", length(grid)) +``` + +When some variables are specified as `:continuous`, the [`choice_vol_grid`](@ref) function automatically computes the log-volume of each grid cell. Inspecting the first element of the grid, we see that the log-volume is equal to `log(0.25 * 0.5) ≈ -2.0794`, since that grid cell is covers a volume of 0.25 * 0.5 = 0.125 of the slope-intercept latent space. We also see that the `slope` and `intercept` variables lie at the midpoint of this grid cell, since the `anchor` keyword argument was set to `:midpoint`: + +```@example enumerative_tutorial +choices, log_vol = first(grid) +println("Log volume: ", log_vol) +println("Choices: ") +choices +``` + +Now let's generate some synthetic data to do inference on. We'll use ground-truth continuous parameters that don't lie exactly on the grid, in order to show that enumerative inference can still produce a reasonable approximation when the posterior is sufficiently smooth. + +```@example enumerative_tutorial +# Generate synthetic data +true_slope = -1.21 +true_intercept = 2.56 +xs = [-2., -1., 0., 1., 2.] +ys = true_slope .* xs .+ true_intercept .+ 1.0 * randn(5) + +# Make one point an outlier +ys[2] = 0. + +# Create observations choicemap +observations = choicemap() +for (i, y) in enumerate(ys) + observations[:data => i => :y] = y +end + +# Visualize the data +point_colors = [:blue, :red, :blue, :blue, :blue] +scatter(xs, ys, label="Observations", markersize=6, xlabel="x", ylabel="y", + color=point_colors) +plot!(xs, true_slope .* xs .+ true_intercept, + label="True line", linestyle=:dash, linewidth=2, color=:black) +``` + +As in the discrete case, we can use [`enumerative_inference`](@ref) to perform inference on the continuous model: + +```@example enumerative_tutorial +# Run inference on the continuous model +traces, log_norm_weights, lml_est = + enumerative_inference(continuous_regression, (xs,), observations, grid) + +println("Log marginal likelihood: ", lml_est) +``` + +Again, we can visualize the joint posterior over the `slope` and `intercept` variables with the help of some plotting code. + +```@example enumerative_tutorial +# Compute marginal posterior over slope and intercept +sum_dims = Tuple(3:ndims(log_norm_weights)) # Sum over all other variables +posterior_grid = sum(exp.(log_norm_weights), dims=sum_dims) +posterior_grid = dropdims(posterior_grid; dims=sum_dims) + +# Extract parameter ranges +slope_range = [trs[1][:slope] for trs in eachslice(traces, dims=1)] +intercept_range = [trs[1][:intercept] for trs in eachslice(traces, dims=2)] + +# Plot 2D posterior heatmap with 1D marginals as histograms +p = plot_posterior_grid(intercept_range, slope_range, posterior_grid, + x_true = true_intercept, y_true = true_slope, + xlabel = "Intercept", ylabel = "Slope", is_discrete = false) +``` + +We can see that the true parameters lie in a cell with reasonably high posterior probability, though there is a fair amount of uncertainty due to bimodal nature of the posterior distribution. This manifests in the posterior over outlier classifications as well: + +```@example enumerative_tutorial +# Compute posterior probability of each point being an outlier +outlier_probs = zeros(length(xs)) +for i = 1:length(xs) + for (j, trace) in enumerate(traces) + if trace[:data => i => :is_outlier] + outlier_probs[i] += exp(log_norm_weights[j]) + end + end +end + +# Plot posterior probability of each point being an outlier +bar(1:length(xs), outlier_probs, + xlabel="x", ylabel="P(outlier | data)", + color=:black, ylim=(0, 1), legend=false) +``` + +The points at both `x=1` and `x=2` are inferred to be possible outliers, corresponding to each possible mode of the full posterior distribution. By extracting the slice of the `log_norm_weights` array that corresponds to `x=2` being an outlier (i.e., when `data => 2 => :is_outlier` is `true`), we can visualize the posterior distribution over the `slope` and `intercept` variables conditional on `x=2` being an outlier. As shown below, this conditional posterior is no longer bimodal, and concentrates more closely around the true parameters. + +```@example enumerative_tutorial +# Extract slice of weights corresponding to x=2 being an outlier +cond_log_norm_weights = log_norm_weights[:,:,:,end:end,:,:,:] + +# Compute marginal posterior over slope & intercept given that x=2 is an outlier +sum_dims = Tuple(3:ndims(cond_log_norm_weights)) # Sum over all other variables +posterior_grid = sum(exp.(cond_log_norm_weights), dims=sum_dims) +posterior_grid = dropdims(posterior_grid; dims=sum_dims) + +# Extract parameter ranges +slope_range = [trs[1][:slope] for trs in eachslice(traces, dims=1)] +intercept_range = [trs[1][:intercept] for trs in eachslice(traces, dims=2)] + +# Plot 2D posterior heatmap with 1D marginals as histograms +plot_posterior_grid(intercept_range, slope_range, posterior_grid, + x_true = true_intercept, y_true = true_slope, + xlabel = "Intercept", ylabel = "Slope", is_discrete = false) +``` + +Instead of extracting a slice of the full weight array, we could also have used [`choice_vol_grid`](@ref) to construct an enumeration grid with `data => 2 => :is_outlier` constrained to `true`, and then called [`enumerative_inference`](@ref) with this conditional grid. This ability to compute conditional posteriors is another useful aspect of enumerative inference: Even when the latent space becomes too high-dimensional for enumeration over the full joint posterior, we can still inspect the conditional posteriors over some variables conditioned on the values of other variables, and check whether they make sense. + +## Diagnosing Model Misspecification + +As we have seen above, enumerative inference allows us to approximate a posterior distribution with a high degree of fidelity (at the expense of additional computation). This allows us to distinguish between two ways that inference in a Bayesian model can go wrong: + +- **Inference Failure:** The inference algorithm fails to approximate the true posterior distribution well (e.g. due to a bad importance sampling proposal, a poorly-designed MCMC kernel, or insufficient computation). + +- **Model Misspecification:** The Bayesian model itself is misspecified, such that the true posterior distribution does not correspond with our intuitions about what the posterior should look like. + +Both of these issues can occur at the same time: an algorithm might fail to converge to the true posterior, and the model might be misspecified. Regardless, since enumerative inference can approximate the true posterior distribution arbitrarily well (by making the grid arbitrarily large and fine), we can use it to check whether some other algorithm converges to the true posterior, and also whether the true posterior itself concords with our intuitions. + +As a demonstration, let us write a version of the continuous regression model with narrow slope and intercept priors, and a high probability of outliers: + +```@example enumerative_tutorial +@gen function misspecified_regression(xs::Vector{<:Real}) + # Narrow slope and intercept priors + slope ~ normal(0, sqrt(0.5)) + intercept ~ normal(0, sqrt(0.5)) + + # Sample outlier classification and y value for each x value + n = length(xs) + ys = Float64[] + for i = 1:n + # High (25% chance) prior probability of being an outlier + is_outlier = {:data => i => :is_outlier} ~ bernoulli(0.25) + + if is_outlier + # Outliers have large noise + y = {:data => i => :y} ~ normal(0., 5.) + else + # Inliers follow the linear relationship, with low noise + y_mean = slope * xs[i] + intercept + y = {:data => i => :y} ~ normal(y_mean, 1.) + end + push!(ys, y) + end + + return ys +end +nothing # hide +``` + +To create a case where the model is misspecified, we generate data with a steep slope and a large intercept, but no outliers: + +```@example enumerative_tutorial +# Generate synthetic data +true_slope = 2.8 +true_intercept = -2.4 +xs = [-2., -1., 0., 1., 2.] +ys = true_slope .* xs .+ true_intercept .+ 1.0 * randn(5) + +# Create observations choicemap +observations = choicemap() +for (i, y) in enumerate(ys) + observations[:data => i => :y] = y +end + +# Visualize the data +point_colors = [:blue, :blue, :blue, :blue, :blue] +scatter(xs, ys, label="Observations", markersize=6, xlabel="x", ylabel="y", + color=point_colors) +plot!(xs, true_slope .* xs .+ true_intercept, + label="True line", linestyle=:dash, linewidth=2, color=:black) +``` + +Now let us try using [`importance_resampling`](@ref) to approximate the posterior distribution under the misspecified model: + +```@example enumerative_tutorial +# Try importance resampling with 2000 inner samples and 100 outer samples +println("Running importance sampling...") +traces = [importance_resampling(misspecified_regression, (xs,), observations, 2000)[1] for i in 1:100] + +# Compute the mean slope and intercept +mean_slope = sum(trace[:slope] for trace in traces) / length(traces) +mean_intercept = sum(trace[:intercept] for trace in traces) / length(traces) + +println("Mean slope: ", mean_slope) +println("Mean intercept: ", mean_intercept) +``` + +Instead of recovering anything close to the true parameters, importance sampling infers a much smaller mean for the slope and intercept. We can also visualize the joint posterior over the slope and intercept by plotting a 2D histogram from the samples: + +```@raw html +
Code to plot posterior samples +``` + +```@example enumerative_tutorial +function plot_posterior_samples( + x_range, y_range, x_values, y_values; + x_true = missing, y_true = missing, + xlabel = "", ylabel = "" +) + # Create the main heatmap + p_main = histogram2d(x_values, y_values, bins=(x_range, y_range), + show_empty_bins=true, normalize=:probability, + color=:grays, colorbar=false, legend=false, + xlabel=xlabel, ylabel=ylabel) + xlims!(p_main, minimum(x_range), maximum(x_range)) + ylims!(p_main, minimum(y_range), maximum(y_range)) + # Add true parameters + if !ismissing(x_true) && !ismissing(y_true) + scatter!(p_main, [true_intercept], [true_slope], + markersize=6, color=:red, markershape=:cross, + label="True Parameters", legend=true) + end + if !ismissing(x_true) + vline!([x_true], linestyle=:dash, linewidth=1, color=:red, + label="", alpha=0.5) + end + if !ismissing(y_true) + hline!([y_true], linestyle=:dash, linewidth=1, color=:red, + label="", alpha=0.5) + end + + # Create 1D marginal histograms + p_top = histogram(x_values, bins=x_range, orientation=:v, legend=false, + normalize=:probability, linewidth=0, color=:black, + showaxis=true, ticks=false) + x_probs_max = maximum(p_top.series_list[2].plotattributes[:y]) + ylims!(p_top, 0, x_probs_max) + xlims!(p_top, minimum(x_range), maximum(x_range)) + p_right = histogram(y_values, bins=y_range, orientation=:h, legend=false, + normalize=:probability, linewidth=0, color=:black, + showaxis=true, ticks=false) + y_probs_max = maximum(p_right.series_list[2].plotattributes[:y]) + xlims!(p_right, 0, y_probs_max) + ylims!(p_right, minimum(y_range), maximum(y_range)) + # Add true parameters + if !ismissing(x_true) + vline!(p_top, [x_true], linestyle=:dash, + linewidth=1, color=:red, legend=false) + end + if !ismissing(y_true) + hline!(p_right, [y_true], linestyle=:dash, + linewidth=1, color=:red, legend=false) + end + + # Create empty plot for top-right corner + p_empty = plot(legend=false, grid=false, showaxis=false, ticks=false) + + # Combine plots using layout + plot(p_top, p_empty, p_main, p_right, + layout=@layout([a{0.9w,0.1h} b{0.1w,0.1h}; c{0.9w,0.9h} d{0.1w,0.9h}]), + size=(750, 750)) +end +nothing # hide +``` + +```@raw html +
+``` + +```@example enumerative_tutorial +# Plot a 2D histogram for the slope and intercept variables +slopes = [trace[:slope] for trace in traces] +intercepts = [trace[:intercept] for trace in traces] +plot_posterior_samples(-4:0.25:4, -4:0.25:4, intercepts, slopes, + x_true=true_intercept, y_true=true_slope, + xlabel="Intercept", ylabel="Slope") +``` + +The distribution of samples produced by importance sampling lies far from the true slope and intercept, and concentrates around values that do not intuitively make sense given the data. The distribution over outlier classifications sheds some light on the problem: + +```@example enumerative_tutorial +# Estimate posterior probability of each point being an outlier +outlier_probs = zeros(length(xs)) +for i = 1:length(xs) + for (j, trace) in enumerate(traces) + if trace[:data => i => :is_outlier] + outlier_probs[i] += 1/length(traces) + end + end +end + +# Plot posterior probability of each point being an outlier +bar(1:length(xs), outlier_probs, + xlabel="x", ylabel="P(outlier | data)", + color=:black, ylim=(0, 1), legend=false) +``` + +Importance sampling infers that many of the points are likely to be outliers. That is, instead of inferring a steep slope and a negative intercept, importance sampling prefers to explain the data as a flatter line with *many* outliers. + +These inferences are indicative of model misspecification. Still, we can't be confident that this isn't just an inference failure. After all, we used importance sampling with the prior as our proposal distribution. Since the prior over slopes and intercepts is very narrow, it is very likely that *none* of the 2000 inner samples used by [`importance_resampling`](@ref) came close to the true slope and intercept. So it is possible that the issues above arise because importance sampling fails to produce a good approximation of the true posterior. + +Before using enumerative inference to resolve this ambiguity, let us try using an MCMC inference algorithm, which might avoid the inference failures of importance sampling by exploring a broader region of the latent space. Similar to the [tutorial on MCMC](@ref mcmc_map_tutorial), we'll use an MCMC kernel that performs Gaussian drift on the continuous parameters, followed by block resimulation on the outlier classifications: + +```@example enumerative_tutorial +@gen function line_proposal(trace) + slope ~ normal(trace[:slope], 0.5) + intercept ~ normal(trace[:intercept], 0.5) +end + +function mcmc_kernel(trace) + # Gaussian drift on line parameters + (trace, _) = mh(trace, line_proposal, ()) + + # Block resimulation: Update the outlier classifications + (xs,) = get_args(trace) + n = length(xs) + for i=1:n + (trace, _) = mh(trace, select(:data => i => :is_outlier)) + end + return trace +end + +function mcmc_sampler(kernel, trace, n_iters::Int, n_burnin::Int = 0) + traces = Vector{typeof(trace)}() + for i in 1:(n_iters + n_burnin) + trace = kernel(trace) + if i > n_burnin + push!(traces, trace) + end + end + return traces +end +nothing # hide +``` + +In addition, we will intiialize MCMC at the true slope and intercept. This way, we can rule out the possibility that MCMC never explores the region of latent space near the true parameters. + +```@example enumerative_tutorial +# Generate initial trace at true slope and intercept +constraints = choicemap( + :slope => true_slope, + :intercept => true_intercept +) +constraints = merge(constraints, observations) +(trace, _) = Gen.generate(misspecified_regression, (xs,), constraints) + +# Run MCMC for 10,000 iterations with a burn-in of 500 +traces = mcmc_sampler(mcmc_kernel, trace, 10000, 500) + +# Compute the mean slope and intercept +mean_slope = sum(trace[:slope] for trace in traces) / length(traces) +mean_intercept = sum(trace[:intercept] for trace in traces) / length(traces) + +println("Mean slope: ", mean_slope) +println("Mean intercept: ", mean_intercept) +``` + +Like importance sampling, MCMC infers a much smaller slope and intercept than the true parameters. Let us visualize the joint posterior. + +```@example enumerative_tutorial +# Plot posterior samples +slopes = [trace[:slope] for trace in traces] +intercepts = [trace[:intercept] for trace in traces] +plot_posterior_samples(-4:0.25:4, -4:0.25:4, intercepts, slopes, + x_true=true_intercept, y_true=true_slope, + xlabel="Intercept", ylabel="Slope") +``` + +Let us also plot the inferred outlier probabilities: + +```@example enumerative_tutorial +# Estimate posterior probability of each point being an outlier +outlier_probs = zeros(length(xs)) +for i = 1:length(xs) + for (j, trace) in enumerate(traces) + if trace[:data => i => :is_outlier] + outlier_probs[i] += 1/length(traces) + end + end +end + +# Plot posterior probability of each point being an outlier +bar(1:length(xs), outlier_probs, + xlabel="x", ylabel="P(outlier | data)", + color=:black, ylim=(0, 1), legend=false) +``` + +Both MCMC and importance sampling produce similar inferences, inferring a flat slope with many outliers rather than a steep slope with few outliers. This is despite the fact that MCMC was initialized at the true parameters, strongly indicating that model misspecification is at play here. + +In general, however, we don't have access to the true parameters, nor do we always know if MCMC will converge to the posterior given a finite sample budget. To decisively diagnose model misspecification, we now use enumerative inference with a sufficiently fine grid, ensuring systemic coverage over the latent space. + +```@example enumerative_tutorial +# Construct enumeration grid +grid = choice_vol_grid( + (:slope, -4:0.25:4, :continuous), # 32 grid intervals + (:intercept, -4:0.25:4, :continuous), # 32 grid intervals + # Enumerate exactly over outlier classifications + (:data => 1 => :is_outlier, [false, true]), + (:data => 2 => :is_outlier, [false, true]), + (:data => 3 => :is_outlier, [false, true]), + (:data => 4 => :is_outlier, [false, true]), + (:data => 5 => :is_outlier, [false, true]); + anchor = :midpoint # Anchor evaluation point at midpoint of each interval +) + +# Run enumerative inference +traces, log_norm_weights, lml_est = + enumerative_inference(misspecified_regression, (xs,), observations, grid) + +# Compute marginal posterior over slope and intercept +sum_dims = Tuple(3:ndims(log_norm_weights)) # Sum over all other variables +posterior_grid = sum(exp.(log_norm_weights), dims=sum_dims) +posterior_grid = dropdims(posterior_grid; dims=sum_dims) + +# Extract parameter ranges +slope_range = [trs[1][:slope] for trs in eachslice(traces, dims=1)] +intercept_range = [trs[1][:intercept] for trs in eachslice(traces, dims=2)] + +# Plot 2D posterior heatmap with 1D marginals as histograms +plot_posterior_grid(intercept_range, slope_range, posterior_grid, + x_true = true_intercept, y_true = true_slope, + xlabel = "Intercept", ylabel = "Slope", is_discrete = false) +``` + +While enumerative inference produces a posterior approximation that is smoother than both importance sampling and MCMC, it still assigns a very low posterior density to the true slope and intercept. Inspecting the outlier classifications, we see that many points are inferred as likely outliers: + +```@example enumerative_tutorial +# Compute posterior probability of each point being an outlier +outlier_probs = zeros(length(xs)) +for (j, trace) in enumerate(traces) + for i in 1:length(xs) + if trace[:data => i => :is_outlier] + outlier_probs[i] += exp(log_norm_weights[j]) + end + end +end + +bar(1:length(xs), outlier_probs, + xlabel="x", ylabel="P(outlier | data)", + color=:black, ylim=(0, 1), legend=false) +``` + +This confirms that *model misspecification is the underlying issue*: The generative model we wrote doesn't capture our intuitions about what posterior inference from the data should give us. + +## Addressing Model Misspecification + +Now that we know our model is misspecified, how do we fix it? In the specific example we considered, the priors over the slope and intercept are too narrow, whereas the outlier probability is too high. A straightforward fix would thus be to widen the slope and intercept priors, while lowering the outlier probability. + +However, this change might not generalize to other sets of observations. If some data really is well-characterized by a shallow slope with many outliers, we would like to infer this as well. A more robust solution then, is to introduce *hyper-priors*: Priors on the parameters of the slope and intercept priors and the outlier probability. Adding hyper-priors results in a hierarchical Bayesian model: + +```@example enumerative_tutorial +@gen function h_bayes_regression(xs::Vector{<:Real}) + # Hyper-prior on slope and intercept prior variances + slope_var ~ inv_gamma(1, 1) + intercept_var ~ inv_gamma(1, 1) + # Slope and intercept priors + slope ~ normal(0, sqrt(slope_var)) + intercept ~ normal(0, sqrt(intercept_var)) + # Prior on outlier probability + prob_outlier ~ beta(1, 1) + + # Sample outlier classification and y value for each x value + n = length(xs) + ys = Float64[] + for i = 1:n + # Sample outlier classification + is_outlier = {:data => i => :is_outlier} ~ bernoulli(prob_outlier) + + if is_outlier + # Outliers have large noise + y = {:data => i => :y} ~ normal(0., 5.) + else + # Inliers follow the linear relationship, with low noise + y_mean = slope * xs[i] + intercept + y = {:data => i => :y} ~ normal(y_mean, 1.) + end + push!(ys, y) + end + + return ys +end +nothing # hide +``` + +Let's run enumerative inference on this expanded model, using a coarser grid to compensate for the increased dimensionality of the latent space: + +```@example enumerative_tutorial +# Construct enumeration grid +grid = choice_vol_grid( + (:slope, -4:1:4, :continuous), # 8 grid intervals + (:intercept, -4:1:4, :continuous), # 8 grid intervals + (:slope_var, 0:1:5, :continuous), # 5 grid intervals + (:intercept_var, 0:1:5, :continuous), # 5 grid intervals + (:prob_outlier, 0.0:0.2:1.0, :continuous), # 5 grid intervals + # Enumerate exactly over outlier classifications + (:data => 1 => :is_outlier, [false, true]), + (:data => 2 => :is_outlier, [false, true]), + (:data => 3 => :is_outlier, [false, true]), + (:data => 4 => :is_outlier, [false, true]), + (:data => 5 => :is_outlier, [false, true]); + anchor = :midpoint # Anchor evaluation point at midpoint of each interval +) + +# Run enumerative inference (this may take a while) +traces, log_norm_weights, lml_est = + enumerative_inference(h_bayes_regression, (xs,), observations, grid) + +# Compute marginal posterior over slope and intercept +sum_dims = Tuple(3:ndims(log_norm_weights)) # Sum over all other variables +posterior_grid = sum(exp.(log_norm_weights), dims=sum_dims) +posterior_grid = dropdims(posterior_grid; dims=sum_dims) + +# Extract parameter ranges +slope_range = [trs[1][:slope] for trs in eachslice(traces, dims=1)] +intercept_range = [trs[1][:intercept] for trs in eachslice(traces, dims=2)] + +# Plot 2D posterior heatmap with 1D marginals as histograms +plot_posterior_grid(intercept_range, slope_range, posterior_grid, + x_true = true_intercept, y_true = true_slope, + xlabel = "Intercept", ylabel = "Slope", is_discrete = false) +``` + +We see that the mode of the posterior distribution is now close to the true parameters (though there is also a secondary mode corresponding to the interpretation that the data has a shallow slope with outliers). To get a sense of why inference is now reasonable under our new model, let us visualize the conditional posteriors over `slope_var`, `intercept_var` and `prob_outlier` when `slope` and `intercept` are fixed at their true values. + +```@example enumerative_tutorial +# Construct enumeration grid conditional on true slope and intercept +cond_grid = choice_vol_grid( + (:slope_var, 0.0:0.5:5.0, :continuous), # 10 grid intervals + (:intercept_var, 0.0:0.5:5.0, :continuous), # 10 grid intervals + (:prob_outlier, 0.0:0.1:1.0, :continuous), # 10 grid intervals + # Enumerate exactly over outlier classifications + (:data => 1 => :is_outlier, [false, true]), + (:data => 2 => :is_outlier, [false, true]), + (:data => 3 => :is_outlier, [false, true]), + (:data => 4 => :is_outlier, [false, true]), + (:data => 5 => :is_outlier, [false, true]); + anchor = :midpoint # Anchor evaluation point at the right of each interval +) + +# Run enumerative inference over conditional posterior +constraints = choicemap(:slope => true_slope, :intercept => true_intercept) +constraints = merge(constraints, observations) +traces, log_norm_weights, lml_est = + enumerative_inference(h_bayes_regression, (xs,), constraints, cond_grid) + +# Compute marginal posterior over slope_var and intercept_var +sum_dims = Tuple(3:ndims(log_norm_weights)) # Sum over all other variables +posterior_grid = sum(exp.(log_norm_weights), dims=sum_dims) +posterior_grid = dropdims(posterior_grid; dims=sum_dims) + +# Extract parameter ranges +slope_var_range = [trs[1][:slope_var] for trs in eachslice(traces, dims=1)] +intercept_var_range = [trs[1][:intercept_var] for trs in eachslice(traces, dims=2)] + +# Plot 2D posterior heatmap with 1D marginals as histograms +plot_posterior_grid(intercept_var_range, slope_var_range, posterior_grid, + xlabel = "Intercept Variance", ylabel = "Slope Variance", + is_discrete = false) +``` + +```@example enumerative_tutorial +# Compute marginal posterior over prob_outlier +sum_dims = (1, 2, 4:ndims(log_norm_weights)...) # Sum over all other variables +prob_outlier_grid = sum(exp.(log_norm_weights), dims=sum_dims) +prob_outlier_grid = dropdims(prob_outlier_grid; dims=sum_dims) +prob_outlier_range = [trs[1][:prob_outlier] for trs in eachslice(traces, dims=3)] + +# Plot marginal posterior distribution over prob_outlier +bar(prob_outlier_range, prob_outlier_grid, + legend=false, bar_width=diff(prob_outlier_range)[1], + linewidth=0, color=:black, widen=false, xlims=(0, 1), + xlabel = "Outlier Probability (prob_outlier)", + ylabel = "Conditional Posterior Probability") +``` + +Conditional on the observed data and the true parameters (`slope = 2.8` and `intercept = -2.4`), the distribution over `slope_var` and `intercept_var` skews towards large values, while the distribution over `prob_outlier` skews towards low values. This avoids the failure mode that arose when the slope and interceptt priors were forced to be narrow. Instead, `slope_var`, `intercept_var` and `prob_outlier` can adjust upwards or downwards to adapt to the observed data. + +Having gained confidence that our new model is well-specified by performing enumerative inference at a coarse-grained level, we can now use MCMC to approximate the posterior more efficiently, and with a higher degree of spatial resolution. + +```@example enumerative_tutorial +function h_bayes_mcmc_kernel(trace) + # Gaussian drift on line parameters + (trace, _) = mh(trace, line_proposal, ()) + + # Block resimulation: Update the outlier classifications + (xs,) = get_args(trace) + n = length(xs) + for i=1:n + (trace, _) = mh(trace, select(:data => i => :is_outlier)) + end + + # Block resimulation: Update the prior parameters + (trace, _) = mh(trace, select(:slope_var)) + (trace, _) = mh(trace, select(:intercept_var)) + (trace, _) = mh(trace, select(:prob_outlier)) + return trace +end + +# Generate initial trace from prior +trace, _ = Gen.generate(h_bayes_regression, (xs,), observations) + +# Run MCMC for 20,000 iterations with a burn-in of 500 +traces = mcmc_sampler(h_bayes_mcmc_kernel, trace, 20000, 500) + +# Plot posterior samples +slopes = [trace[:slope] for trace in traces] +intercepts = [trace[:intercept] for trace in traces] +plot_posterior_samples(-4:0.25:4, -4:0.25:4, intercepts, slopes, + x_true=true_intercept, y_true=true_slope, + xlabel="Intercept", ylabel="Slope") +``` + +MCMC produces samples that concentrate around the true parameters, while still exhibiting some of the bimodality we saw when using coarse-grained enumerative inference. From 8d7dd5faf53f81ef7946be8ead49e07a9f0ef86f Mon Sep 17 00:00:00 2001 From: Xuan Date: Sun, 6 Jul 2025 17:09:50 -0400 Subject: [PATCH 6/8] Fix typo. --- docs/src/tutorials/vi.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/src/tutorials/vi.md b/docs/src/tutorials/vi.md index 985377b8a..7dc49814a 100644 --- a/docs/src/tutorials/vi.md +++ b/docs/src/tutorials/vi.md @@ -36,7 +36,7 @@ end nothing # hide ``` -Since `x_mu` and `x_log_std`are not fixed to particular values, this generative function defines a *family* of distributions, not just one. Note that we intentionally chose to parameterize the distribution by the log standard deviation `x_log_std`, so that every parameter has full support over the real line, and we can perform unconstrained optimization of the parameters. +Since `x_mu` and `x_log_std` are not fixed to particular values, this generative function defines a *family* of distributions, not just one. Note that we intentionally chose to parameterize the distribution by the log standard deviation `x_log_std`, so that every parameter has full support over the real line, and we can perform unconstrained optimization of the parameters. To perform variational inference, we need to initialize the variational parameters to their starting values: From 83307b6703fc79ad0bd2e090fe9642c314691b54 Mon Sep 17 00:00:00 2001 From: Xuan Date: Sun, 6 Jul 2025 22:44:33 -0400 Subject: [PATCH 7/8] Update CI and doc build to use Julia 1.10. --- .github/workflows/ContinuousIntegration.yml | 2 +- .github/workflows/Documentation.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ContinuousIntegration.yml b/.github/workflows/ContinuousIntegration.yml index 399440f70..f33e76bf2 100644 --- a/.github/workflows/ContinuousIntegration.yml +++ b/.github/workflows/ContinuousIntegration.yml @@ -25,7 +25,7 @@ jobs: strategy: fail-fast: false matrix: - julia-version: ['1', '1.6', 'nightly'] + julia-version: ['1', '1.10', '1.6', 'nightly'] julia-arch: [x64] os: [ubuntu-latest] steps: diff --git a/.github/workflows/Documentation.yml b/.github/workflows/Documentation.yml index ef66d34a5..fa490c892 100644 --- a/.github/workflows/Documentation.yml +++ b/.github/workflows/Documentation.yml @@ -19,7 +19,7 @@ jobs: - uses: actions/checkout@v2 - uses: julia-actions/setup-julia@v1 with: - version: '1.6' + version: '1.10' - name: Install dependencies run: julia --project=docs/ -e 'using Pkg; Pkg.develop(PackageSpec(path=pwd())); Pkg.instantiate()' - name: Build and deploy documentation From 12e35ab9ba5c4107693b9c1f048b42fe959e5f85 Mon Sep 17 00:00:00 2001 From: Xuan Date: Mon, 7 Jul 2025 00:02:37 -0400 Subject: [PATCH 8/8] Fix another typo. --- docs/src/tutorials/enumerative.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/src/tutorials/enumerative.md b/docs/src/tutorials/enumerative.md index 613cc90ec..79dc6b103 100644 --- a/docs/src/tutorials/enumerative.md +++ b/docs/src/tutorials/enumerative.md @@ -852,7 +852,7 @@ bar(prob_outlier_range, prob_outlier_grid, ylabel = "Conditional Posterior Probability") ``` -Conditional on the observed data and the true parameters (`slope = 2.8` and `intercept = -2.4`), the distribution over `slope_var` and `intercept_var` skews towards large values, while the distribution over `prob_outlier` skews towards low values. This avoids the failure mode that arose when the slope and interceptt priors were forced to be narrow. Instead, `slope_var`, `intercept_var` and `prob_outlier` can adjust upwards or downwards to adapt to the observed data. +Conditional on the observed data and the true parameters (`slope = 2.8` and `intercept = -2.4`), the distribution over `slope_var` and `intercept_var` skews towards large values, while the distribution over `prob_outlier` skews towards low values. This avoids the failure mode that arose when the slope and intercept priors were forced to be narrow. Instead, `slope_var`, `intercept_var` and `prob_outlier` can adjust upwards or downwards to adapt to the observed data. Having gained confidence that our new model is well-specified by performing enumerative inference at a coarse-grained level, we can now use MCMC to approximate the posterior more efficiently, and with a higher degree of spatial resolution.