Hi all,
Thank you for your replies.
- I’ve attempted using the
transform = ‘affine’ and it worked somewhat when testing ECDF with a small posterior to validation sample ratio, but becomes divergent when increased to a 20:1 ratio.
- Reducing the depth of
transform = ‘spline’ did not solve the issue.
- Neither did using
"method": "rk45".
As for the training duration, I find that flow matching for some reason required fewer simulations to stabilise compared to coupling flows, which is the reverse of what you said. I’ve since put replicating the outputs on BFv2 on hold first as I complete the pipeline with BFv1.
Currently, I am facing an issue with my inferred ter (non-decision) parameter being greater than my observed minimum reaction time. I’ve attempted to address this by making the prior a batchable context, and feeding the minimum reaction time as an upper bound when drawing parameter ter. I feed the min RT as a parameter in the summary and direct conditions. Specifically
def prior_minrt_context():
return np.random.uniform(0.1, 0.8)
prior_context = bf.simulation.ContextGenerator(
batchable_context_fun = prior_minrt_context)
def flex_rt(rt_limit):
ub = rt_limit - 0.44
return scipy.stats.truncnorm.rvs(-0.36/0.08, ub/0.08, loc = 0.44, scale = 0.08)
For the priors themselves, I did
PARAM_NAMES = ["Pre drift",
"Boundary separation",
"Non-decision time",
"Post drift bias",
"Confidence boundary bias",
"Confidence curve bias",
"Efficiency"]
def batch_prior(prior_batchable_context, batch_size):
params = np.zeros((batch_size, len(PARAM_NAMES)), dtype=np.float32)
ub = np.asarray(prior_batchable_context, dtype=np.float32).reshape(batch_size)
for b in range(batch_size):
# EXAMPLE priors: replace with your original ones if they differ.
v = np.random.gamma(2, 0.5)
a = np.random.gamma(11.69, 0.12)
# non-decision time (ter) bounded by dataset minRT
ter = flex_rt(rt_limit = ub[b])
v_bias = np.random.normal(0, 2)
a_bias = np.random.normal(-1, 2)
m_bias = scipy.stats.truncnorm.rvs(-0.99/0.5, 4/0.5, loc = 1, scale = 0.5)
v_ratio = scipy.stats.truncnorm.rvs(-0.93/0.35, 1.99/0.35, loc = 0.96, scale = 0.35)
params[b] = [v, a, ter, v_bias, a_bias, m_bias, v_ratio]
return params
prior = bf.simulation.Prior(batch_prior_fun = batch_prior, context_generator = prior_context, param_names = PARAM_NAMES)
As for the configurator, I added minRT into the summary and direct conditions
from tensorflow.keras.utils import to_categorical
# A configurator extracts the results of the model to a format that the neural network would like
def configurator(forward_dict):
# Prepare placeholder dict
out_dict = {}
# Extract simulated response times
data = forward_dict["sim_data"]
# (B, N, 1)
context = np.array(forward_dict["sim_batchable_context"])[..., None].astype(np.float32)
resp = ((data[:, :, 1] + 1.0) * 0.5).astype(np.float32)
stim = ((data[:, :, 3] + 1.0) * 0.5).astype(np.float32)
rt_resp = data[:, :, 0:1].astype(np.float32)
cat_resp = to_categorical(resp, num_classes=2)
cat_acc = to_categorical(data[:, :, 2], num_classes=2)
cat_stim = to_categorical(stim, num_classes=2)
ord_conf = data[:, :, 4:5].astype(np.float32)
post_rt = data[:, :, 5:6].astype(np.float32)
vec_num_obs = forward_dict["sim_non_batchable_context"] * np.ones((data.shape[0], 1))
vec_num_sqrt = np.sqrt(vec_num_obs).astype(np.float32)
vec_min_rt = np.array(forward_dict["prior_batchable_context"]).reshape(-1, 1)
vec_min_rt = vec_min_rt.astype(np.float32)
B = context.shape[0]
N = context.shape[1]
min_rt_array = np.array(forward_dict["prior_batchable_context"]).reshape(B, 1, 1)
min_rt_array = np.repeat(min_rt_array, repeats = N, axis = 1).astype(np.float32)
out_dict["summary_conditions"] = np.concatenate([rt_resp, cat_resp, cat_acc, cat_stim, ord_conf, post_rt, context, min_rt_array], axis=-1)
out_dict["direct_conditions"] = np.hstack((vec_num_sqrt, vec_min_rt))
out_dict["parameters"] = forward_dict["prior_draws"].astype(np.float32)
return out_dict
As for my summary and inference networks, they are
summary_net = bf.networks.SetTransformer(input_dim = 11, summary_dim = 32, name = "meta_ddm_summary")
inference_net = bf.networks.InvertibleNetwork(
num_params = len(prior.param_names),
coupling_settings = {"dense_args": dict(kernel_regularizer=None), "dropout": False},
num_coupling_layers = 10,
coupling_design = "spline",
name = "meta_ddm_inference")
As for the inference
ddm_data = pd.read_csv('/home/jovyan/Documents/Processed Data/new/params/metad.csv')
ddm_data = ddm_data.drop(columns = ['AssessmentDay', 'BlockNumber', 'TrialNumber'])
# -1/0 translates to (1, 0) and 1 translates to (0, 1)
ddm_data['stim1'] = ddm_data.apply(lambda x: (x.stim - 1) * -1, axis = 1)
ddm_data['resp1'] = ddm_data.apply(lambda x: (x.resp - 1) * -1, axis = 1)
ddm_data['accu1'] = ddm_data.apply(lambda x: (x.accu - 1) * -1, axis = 1)
ddm_data['context'] = 0
ddm_data['minrt'] = ddm_data.groupby('PlayerID')['rt1'].transform('min')
# rt_resp, cat_resp, cat_acc, cat_stim, ord_conf, post_rt, context, minrt
ddm_data = ddm_data[['PlayerID', 'rt1', 'resp1', 'resp', 'accu1', 'accu', 'stim1', 'stim', 'conf', 'rt2', 'context', 'minrt']]
def extract(df, IDs):
PlayerID = []
v = []
a = []
ter = []
v_bias = []
a_bias = []
m_bias = []
v_ratio = []
for ID in IDs:
test_df = df[df['PlayerID'] == ID]
test_df = test_df.drop(columns = ['PlayerID'])
test_array = test_df.to_numpy(dtype = 'float32')
test_array = np.expand_dims(test_array, 0)
sqrt_obs = np.sqrt(test_array.shape[1])
min_rt = np.min(test_array[:, :, 0]) # This is a bit redundant as the dataframe itself already has a column for min_rt per participant
direct_array = np.array([sqrt_obs, min_rt], dtype = 'float32')
direct_array = np.expand_dims(direct_array, 0)
test_dict = {'summary_conditions': test_array, 'direct_conditions': direct_array}
test_params = amortizer.sample(test_dict, n_samples = 1000)
# PlayerID
PlayerID.append(ID)
# v
v.append(np.mean(test_params[:, 0]))
# a
a.append(np.mean(test_params[:, 1]))
# ter
ter.append(np.mean(test_params[:, 2]))
# v_bias
v_bias.append(np.mean(test_params[:, 3]))
# a_bias
a_bias.append(np.mean(test_params[:, 4]))
# m_bias
m_bias.append(np.mean(test_params[:, 5]))
# v_ratio
v_ratio.append(np.mean(test_params[:, 6]))
dict_out = {'PlayerID': PlayerID, 'v': v, 'a': a, 'ter': ter, 'v_bias': v_bias, 'a_bias': a_bias, 'm_bias': m_bias, 'v_ratio': v_ratio}
df_out = pd.DataFrame(dict_out)
return df_out
However, my outputted ter still exceeds the minimum reaction time/rt per participant. and by quite a bit as well. I’ve also checked my validation sims output just in case, and the simulator seems to work as intended - the ter parameters generated were always below the minRT of prior_batchable_contexts set.
test = model(batch_size = 1000)
validation_sims = configurator(test)
all(test['prior_batchable_context'] - validation_sims['parameters'][:, 2]) > 0
> True
I was wondering if there may be better solutions for this?