Parameter Estimation

In this brief tutorial, we will demonstrate how to perform Bayesian parameter estimation using Turing.jl. For simplicity, we will estimate the utility curvature parameter of an expected utility model from 50 independent identically distributed observations from a single choice set.

Load Packages

The first step is to load the required packages.

using Plots
using Random
using Turing
using UtilityModels
Random.seed!(6541)

Choice Set

Next, we will create the choice set from two trinary gambles. The first gamble is less risky than the second gamble, as defined by lower variance.

gamble1 = Gamble(; 
    p = [.25, .25, .50], 
    v = [44, 40, 5]
)

The second gamble is relatively more risky.

gamble2 = Gamble(; 
    p = [.25, .25, .50], 
    v = [98, 10, 5]
)

In the code block below, we combine the gambles into a vector representing the available options. In addition, we create a model object and generate 50 simulated choices. The gambles and simulated choices are combined into a single data structure so it can be passed to logpdf via Turing and subsequently parsed.

gambles = [gamble1,gamble2]
eu_model = ExpectedUtility(; α = .80, θ = 1)
choices  = rand(eu_model, gambles, 50)
data = (gambles,choices)

Create Turing Model

Below, we create a turing model by prefixing a function with the @model macro. The function passes the data and defines two sampling statements: a prior distribution on the utlity curvature parameter, and a sampling statement for the data.

@model function model(data)
    α ~ truncated(Normal(.8, 1), 0, Inf)
    data ~ ExpectedUtility(; α, θ = 1)
end

Estimate Parameters

Now that the Turing model has been defined, we can pass it, along with the data, to sample to perform Bayesian parameter estimation with an MCMC algorithm called the No-U-turn sampler. The function call below will sample 2,000 times from the posterior distribution (discarding the first 1,000 warmup samples), for 4 parallel chains.

chain = sample(model(data), NUTS(1000, .85), MCMCThreads(), 1000, 4)
┌ Info: Found initial step size
└   ϵ = 0.0125
┌ Info: Found initial step size
└   ϵ = 0.025
┌ Info: Found initial step size
└   ϵ = 0.00625
┌ Info: Found initial step size
└   ϵ = 3.2
Chains MCMC chain (1000×13×4 Array{Float64, 3}):

Iterations        = 1001:1:2000
Number of chains  = 4
Samples per chain = 1000
Wall duration     = 0.91 seconds
Compute duration  = 3.03 seconds
parameters        = α
internals         = lp, n_steps, is_accept, acceptance_rate, log_density, hamiltonian_energy, hamiltonian_energy_error, max_hamiltonian_energy_error, tree_depth, numerical_error, step_size, nom_step_size

Summary Statistics
  parameters      mean       std      mcse    ess_bulk    ess_tail      rhat   ess_per_sec 
      Symbol   Float64   Float64   Float64     Float64     Float64   Float64       Float64 

           α    0.8031    0.0317    0.0009   1299.4792   1501.7839    1.0008      428.8710

Quantiles
  parameters      2.5%     25.0%     50.0%     75.0%     97.5% 
      Symbol   Float64   Float64   Float64   Float64   Float64 

           α    0.7346    0.7841    0.8055    0.8240    0.8591

Plot Posterior Distribution

Inspection of the trace plot does not reveal any anomolies. The density plot shows that the posterior distribution is centered near the data generating value of $\alpha = .80$ and spans roughtly between .75 and .85, suggesting good recovery of the parameter.

plot(chain, grid = false)