Code
import os
if "KERAS_BACKEND" not in os.environ:
# set this to "torch", "tensorflow", or "jax"
"KERAS_BACKEND"] = "jax"
os.environ[
import matplotlib.pyplot as plt
import numpy as np
import bayesflow as bf
import json
import os
if "KERAS_BACKEND" not in os.environ:
# set this to "torch", "tensorflow", or "jax"
"KERAS_BACKEND"] = "jax"
os.environ[
import matplotlib.pyplot as plt
import numpy as np
import bayesflow as bf
import json
Here we will allow for individual differences between participants. The simplest way to start is to estimate the parameter values for each participant independently. Hence we estimate two parameters per participant. It may seem that we have to extend the model. However, since we assume that the participants are independent, we may as well simplify the model to assume data from a single participant only, and apply it to each participant in independently.
We use the category learning data from the “Condensation B” condition from Kruschke (1993) as reported by Lee & Wagenmakers (2013).
with open(os.path.join("data", "KruschkeData.json")) as f:
= json.load(f)
data
data.keys()
dict_keys(['a', 'd1', 'd2', 'n', 'nstim', 'nsubj', 'y'])
We will reuse the prior and simulator from the first example of the GCM. The only difference is that we will remove the nsubj
argument, and in so doing simplifying the simulation so that every data set contains data from a single participant.
def prior():
return dict(
= np.random.exponential(scale=1),
c = np.random.uniform(low=0, high=1)
w
)
def likelihood(c, w, n=data['n'], nstim=data['nstim'], d1=np.array(data['d1']), d2=np.array(data['d2']), a=np.array(data['a'])):
= 0.5
b
= np.exp( - c * (w * d1 + (1-w) * d2))
s
= np.zeros(nstim)
r
for i in range(nstim):
= a == 1
isA = a != 1
isB
= b * np.sum(s[i,isA])
numerator = numerator + (1-b) * np.sum(s[i,isB])
denominator
= numerator / denominator
r[i]
= np.random.binomial(n=n, p=r, size=nstim)
y
return dict(y=y)
= bf.make_simulator([prior, likelihood]) simulator
We will use BasicWorkflow
again. The task is to predict the posterior distribution of the parameters \(c\) and \(w\), given the observed data \(y\). Since \(y\) is of fixed size, we might not need a summary network.
= (bf.Adapter()
adapter "c", lower=0)
.constrain("w", lower=0, upper=1)
.constrain("c", "w"], into="inference_variables")
.concatenate(["y", "inference_conditions")
.rename( )
= bf.BasicWorkflow(
workflow =simulator,
simulator=adapter,
adapter=bf.networks.CouplingFlow(),
inference_network )
=workflow.fit_online(epochs=50, batch_size=256) history
= simulator.sample(1000) test_data
= workflow.plot_default_diagnostics(test_data=test_data, num_samples=500) figs
Now, we can use the approximator to obtain our posterior distributions of \(c\) and \(w\) for all 40 participants in the data set.
= dict(y = np.array(data['y']).transpose())
inference_data
'y'].shape inference_data[
(40, 8)
= workflow.sample(num_samples=2000, conditions=inference_data) posterior_samples
= {par: np.mean(samples, axis=1) for par, samples in posterior_samples.items()} posterior_means
= [2, 30, 32]
highlight = ["blue" if i not in highlight else "red" for i in range(data['nsubj'])]
cols
plt.scatter('c'],
posterior_means['w'],
posterior_means[=cols, zorder=2)
colorfor p in range(data['nsubj']):
if p in highlight:
plt.text('c'][p],
posterior_means['w'][p],
posterior_means[str(p+1), zorder=3, fontsize=12)
for l in range(25):
= [posterior_means['c'][p],
x 'c'][p,l]
posterior_samples[
]= [posterior_means['w'][p],
y 'w'][p,l]
posterior_samples[
]="gray", linewidth=0.2, zorder=0)
plt.plot(x, y, color
0, 4)
plt.xlim("Generalization (c)")
plt.xlabel(0, 1)
plt.ylim("Attention Weight (w)")
plt.ylabel(=plt.title("Parameter estimates for individual participants") f