You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
@@ -6,11 +6,11 @@ Below, we demonstrate how to estimate parameters of the LCA model using the [Neu
6
6
7
7
## Example
8
8
9
-
We'll estimate parameters of the LCA model, which is particularly challenging due to its complex dynamics, where parameters like leak rate (λ) and lateral inhibition (β) can be difficult to recover. This example draws from a more in-depth case that highlights many of the steps one ought to consider when utilizing amortized inference for cognitive modeling; see [Principled Amortized Bayesian Workflow for Cognitive Modeling](https://bayesflow.org/stable-legacy/_examples/Amortized_Bayesian_Workflow_for_Cognitive_Modeling.html).
9
+
We'll estimate parameters of the LCA model, which is particularly challenging due to its complex dynamics, where parameters like leak rate (λ) and lateral inhibition (β) can be difficult to recover. This example draws from a more in-depth case that highlights many of the steps one ought to consider when utilizing amortized inference for cognitive modeling; see [Principled Amortized Bayesian Workflow for Cognitive Modeling](https://bayesflow.org/stable-legacy/_examples/LCA_Model_Posterior_Estimation.html).
Unlike traditional Bayesian approaches, simulationbased inference methods require us to define a prior sampling function to generate training data. We will use this function to sample a range of parameters for training:
27
+
Unlike traditional Bayesian inference methods, simulation-based inference approaches require us to define a prior sampling function specifically to generate synthetic training data. While traditional methods like MCMC also sample from the prior, those samples are used directly during inference rather than to create a separate training dataset. In SBI, we use the prior to sample a wide range of parameters and simulate corresponding data, which we then use to train a model (e.g., a neural network) to approximate the posterior. We will use the following function to sample a range of parameters for training:
39
28
40
-
```@example
29
+
```julia
41
30
# Function to sample parameters from priors
42
31
functionsample(K::Integer)
43
-
ν1 = rand(Gamma(2, 1/1.2), K) # Drift rate 1
44
-
ν2 = rand(Gamma(2, 1/1.2), K) # Drift rate 2
45
-
α = rand(Gamma(10, 1/6), K) # Threshold
46
-
β = rand(Beta(1, 5), K) # Lateral inhibition
47
-
λ = rand(Beta(1, 5), K) # Leak rate
48
-
τ = rand(Gamma(1.5, 1/5.0), K) # Non-decision time
32
+
ν1 =rand(Gamma(2, 1/1.2f0), K) # Drift rate 1
33
+
ν2 =rand(Gamma(2, 1/1.2f0), K) # Drift rate 2
34
+
α =rand(Gamma(10, 1/6f0), K) # Threshold
35
+
β =rand(Beta(1, 5f0), K) # Lateral inhibition
36
+
λ =rand(Beta(1, 5f0), K) # Leak rate
37
+
τ =rand(Gamma(1.5, 1/5.0f0), K) # Non-decision time
49
38
50
39
# Stack parameters into a matrix (d×K)
51
40
θ =vcat(ν1', ν2', α', β', λ', τ')
@@ -58,7 +47,7 @@ end
58
47
59
48
Neural estimators learn the mapping from data to parameters through simulation. Here we define a function to simulate LCA model data. To do so we will use the [LCA](https://itsdfish.github.io/SequentialSamplingModels.jl/dev/lca/).
60
49
61
-
```@example
50
+
```julia
62
51
# Function to simulate data from the LCA model
63
52
functionsimulate(θ, n_trials_per_param)
64
53
# Simulate data for each parameter vector
@@ -74,7 +63,7 @@ function simulate(θ, n_trials_per_param)
74
63
choices, rts =rand(model, n_trials_per_param)
75
64
76
65
# Return as a transpose matrix where each column is a trial
77
-
return Float32.([choices rts]')
66
+
return [choices rts]'
78
67
end
79
68
80
69
return simulated_data
@@ -83,11 +72,24 @@ end
83
72
84
73
## Define Neural Network Architecture
85
74
86
-
For LCA parameter recovery, we use a DeepSet architecture which respects the permutation invariance of trial data. For more details on the method see NeuralEstimators.jl documentation. To construct the network architecture we will use the Flux.jl package.
75
+
For LCA parameter recovery, we use a DeepSet architecture which respects the permutation invariance of trial data. For more details on the method [see NeuralEstimators.jl documentation](https://msainsburydale.github.io/NeuralEstimators.jl/dev/API/architectures/#NeuralEstimators.DeepSet). To construct the network architecture we will use the Flux.jl package.
87
76
88
-
```@example
77
+
```julia
89
78
# Create neural network architecture for parameter recovery
90
-
function create_neural_estimator()
79
+
functioncreate_neural_estimator(;
80
+
ν_bounds = (0.1, 4.0),
81
+
α_bounds = (0.5, 3.5),
82
+
β_bounds = (0.0, 0.5),
83
+
λ_bounds = (0.0, 0.5),
84
+
τ_bounds = (0.1, 0.5)
85
+
)
86
+
# Unpack defined parameter Bounds
87
+
ν_min, ν_max = ν_bounds # Drift rates
88
+
α_min, α_max = α_bounds # Threshold
89
+
β_min, β_max = β_bounds # Lateral inhibition
90
+
λ_min, λ_max = λ_bounds # Leak rate
91
+
τ_min, τ_max = τ_bounds # Non-decision time
92
+
91
93
# Input dimension: 2 (choice and RT for each trial)
92
94
n =2
93
95
# Output dimension: 6 parameters
@@ -132,25 +134,25 @@ end
132
134
133
135
## Training the Neural Estimator
134
136
135
-
Neural estimators, like all deep learning methods, require a training phase where they learn the mapping from data to parameters. Here we will train the estimator, simulating data as we go where the sampler provides new parameter vectors from the prior, and a simulator can be provided to continuously simulate new data conditional on the parameters. For more details on the training see the API for arguments [here](https://msainsburydale.github.io/NeuralEstimators.jl/dev/API/core/#Training).
137
+
Neural estimators, like all deep learning methods, require a training phase during which they learn the mapping from data to parameters. Here, we train the estimator by simulating data on the fly: the sampler provides new parameter vectors from the prior, and the simulator generates corresponding data conditional on those parameters. Since we use online training and the network never sees the same simulated dataset twice, overfitting is less likely. For more details on training, see the API for arguments [here](https://msainsburydale.github.io/NeuralEstimators.jl/dev/API/core/#Training).
136
138
137
-
```@example
139
+
```julia
138
140
# Create the neural estimator
139
141
estimator =create_neural_estimator()
140
142
141
143
# Train network
142
144
trained_estimator =train(
143
145
estimator,
144
-
sample, # Parameter sampler function
145
-
simulate, # Data simulator function
146
-
m = 100, # Number of trials per parameter vector
147
-
K = 10000, # Number of training parameter vectors
148
-
K_val = 2000, # Number of validation parameter vectors
149
-
loss = Flux.mae, # Mean absolute error loss
150
-
epochs = 50, # Number of training epochs
151
-
epochs_per_Z_refresh = 1, # Refresh data every epoch
152
-
epochs_per_θ_refresh = 5, # Refresh parameters every 5 epochs
153
-
batchsize = 64, # Batch size for training
146
+
sample, # Parameter sampler function
147
+
simulate, # Data simulator function
148
+
m =100, # Number of trials per parameter vector
149
+
K =10000, # Number of training parameter vectors
150
+
K_val =2000, # Number of validation parameter vectors
151
+
loss = Flux.mae, # Mean absolute error loss
152
+
epochs =60, # Number of training epochs
153
+
epochs_per_Z_refresh =1, # Refresh data every epoch
154
+
epochs_per_θ_refresh =5, # Refresh parameters every 5 epochs
155
+
batchsize =16, # Batch size for training
154
156
verbose =true
155
157
)
156
158
```
@@ -159,7 +161,7 @@ trained_estimator = train(
159
161
160
162
We can assess the performance of our trained estimator on held-out test data:
A key advantage of neural estimation is the ability to quickly conduct inference after training. For example, we can visualize the recovery of parameters:
188
+
A key advantage of neural estimation is the ability to quickly conduct inference after training. For example, we can visualize the recovery of parameters. While NeuralEstimators provides built-in visualization capabilities through the [AlgebraOfGraphics.jl](https://github.com/MakieOrg/AlgebraOfGraphics.jl), we will demonstrate custom plotting below:
187
189
188
-
```@example
190
+
```julia
189
191
# Extract data from assessment
190
192
df = assessment.df
191
193
@@ -219,7 +221,7 @@ display(p_combined)
219
221
220
222
Once trained, the estimator can instantly recover parameters from new data via a forward pass:
221
223
222
-
```@example
224
+
```julia
223
225
# Generate "observed" data
224
226
ν = [2.5, 2.0]
225
227
α =1.5
@@ -249,6 +251,206 @@ Neural estimators are particularly effective for models with computationally int
249
251
250
252
Additional details can be found in the [NeuralEstimators.jl documentation](https://github.com/msainsburydale/NeuralEstimators).
251
253
254
+
# Complete Code
255
+
```@raw html
256
+
<details>
257
+
<summary><b>Show Details</b></summary>
258
+
```
259
+
```julia
260
+
using NeuralEstimators
261
+
using SequentialSamplingModels
262
+
using Flux
263
+
using Distributions
264
+
using Random
265
+
using Plots
266
+
267
+
Random.seed!(123)
268
+
269
+
# Function to sample parameters from priors
270
+
functionsample(K::Integer)
271
+
ν1 =rand(Gamma(2, 1/1.2f0), K) # Drift rate 1
272
+
ν2 =rand(Gamma(2, 1/1.2f0), K) # Drift rate 2
273
+
α =rand(Gamma(10, 1/6f0), K) # Threshold
274
+
β =rand(Beta(1, 5f0), K) # Lateral inhibition
275
+
λ =rand(Beta(1, 5f0), K) # Leak rate
276
+
τ =rand(Gamma(1.5, 1/5.0f0), K) # Non-decision time
277
+
278
+
# Stack parameters into a matrix (d×K)
279
+
θ =vcat(ν1', ν2', α', β', λ', τ')
280
+
281
+
return θ
282
+
end
283
+
284
+
# Function to simulate data from the LCA model
285
+
functionsimulate(θ, n_trials_per_param)
286
+
# Simulate data for each parameter vector
287
+
simulated_data =map(eachcol(θ)) do param
288
+
# Extract parameters for this model
289
+
ν1, ν2, α, β, λ, τ = param
290
+
ν = [ν1, ν2] # Two-choice LCA
291
+
292
+
# Create LCA model with SSM
293
+
model =LCA(; ν, α, β, λ, τ)
294
+
295
+
# Generate choices and reaction times
296
+
choices, rts =rand(model, n_trials_per_param)
297
+
298
+
# Return as a transpose matrix where each column is a trial
299
+
return [choices rts]'
300
+
301
+
end
302
+
303
+
return simulated_data
304
+
end
305
+
306
+
# Create neural network architecture for parameter recovery
307
+
functioncreate_neural_estimator(;
308
+
ν_bounds = (0.1, 4.0),
309
+
α_bounds = (0.5, 3.5),
310
+
β_bounds = (0.0, 0.5),
311
+
λ_bounds = (0.0, 0.5),
312
+
τ_bounds = (0.1, 0.5)
313
+
)
314
+
# Unpack defined parameter Bounds
315
+
ν_min, ν_max = ν_bounds # Drift rates
316
+
α_min, α_max = α_bounds # Threshold
317
+
β_min, β_max = β_bounds # Lateral inhibition
318
+
λ_min, λ_max = λ_bounds # Leak rate
319
+
τ_min, τ_max = τ_bounds # Non-decision time
320
+
321
+
# Input dimension: 2 (choice and RT for each trial)
322
+
n =2
323
+
# Output dimension: 6 parameters
324
+
d =6# ν[1], ν[2], α, β, λ, τ
325
+
# Width of hidden layers
326
+
w =128
327
+
328
+
# Inner network - processes each trial independently
Miletić, S., Turner, B. M., Forstmann, B. U., & van Maanen, L. (2017). Parameter recovery for the leaky competing accumulator model. Journal of Mathematical Psychology, 76, 25-50.
0 commit comments