Baysian network error #473
Replies: 7 comments 10 replies
-
Hi @jakubMitura14! Interesting case! I think there's something off with constraint resolution within submodels, but I might be wrong here. The simplest workaround to run the inference for now could be vanilla mean-field—that's of course not ideal for this model, but at least it can give you some results. I am sure other developers will figure this out better. So for the inference, your code is: using MLUtils
using LossFunctions
using Statistics
using RxInfer
using Random
using StableRNGs
using LinearAlgebra # For Matrix(I, n, n) used for diageye
using CSV, DataFrames # Added for data loading
using Plots
dataset_path = "synth_prediction_data_simple.csv"
df = CSV.read(dataset_path, DataFrame)
# Shuffle the DataFrame
shuffled_df = df[shuffle(1:nrow(df)), :]
# Split: 80% train/validation, 20% test
n_total = nrow(shuffled_df)
n_trainval = Int(floor(0.8 * n_total))
trainval_df = shuffled_df[1:n_trainval, :]
test_df = shuffled_df[(n_trainval+1):end, :]
# --- Step 2: Prepare k-Fold Cross-Validation on Train/Validation Set ---
k = 5
folds = kfolds(nrow(trainval_df), k=k)
# Hardcoded cardinalities
cardinalities = Dict{String, Int}(
"Obesity" => 2,
"MI" => 2,
"Cachexia" => 2,
"metabol" => 4,
)
# Function to one-hot encode a single value (0-based index input)
function one_hot_encode(value, cardinality)
# Assumes value is 0-based index
encoded = zeros(Float64, cardinality)
if 0 <= value < cardinality
encoded[value + 1] = 1.0 # Julia arrays are 1-based
else
# Handle unexpected values (e.g., missing, out of range) if necessary
# For now, return a zero vector or throw an error
println("Warning: Value $value is not a valid 0-based index for cardinality $cardinality. Returning zero vector.")
end
return encoded
end
# Prepare data variables (one-hot encoded)
function prepare_data_arrays(df_in, cardinalities)
return (
Cachexia_data = [one_hot_encode(row."Cachexia", cardinalities["Cachexia"]) for row in eachrow(df_in)],
MI_data = [one_hot_encode(row."MI", cardinalities["MI"]) for row in eachrow(df_in)],
Obesity_data = [one_hot_encode(row."Obesity", cardinalities["Obesity"]) for row in eachrow(df_in)],
)
end
# --- Model Definition ---
# Defines the structure of the Bayesian network and how variables relate.
@model function cardio_model(MI_data, Cachexia_data, Obesity_data, Cachexia, Obesity, MI)
Cachexia_data ~ DiscreteTransition(Cachexia, Matrix(I, 2, 2))
Obesity_data ~ DiscreteTransition(Obesity, Matrix(I, 2, 2))
MI_data ~ DiscreteTransition(MI, Matrix(I, 2, 2))
end
# --- Learn Model Definition ---
# Defines how to learn the model parameters (CPTs) from data.
@model function learn_cardio_model(Cachexia_data, MI_data, Obesity_data)
# CPT Definitions
cpt_Cachexia_Obesity_metabol ~ DirichletCollection(ones(4, 2, 2))
cpt_metabol_MI ~ DirichletCollection(ones(2, 4))
# Variable Cachexia (no parents)
Cachexia ~ Categorical([0.5, 0.5])
# Variable Obesity (no parents)
Obesity ~ Categorical([0.5, 0.5])
# Variable metabol (parents: Cachexia, Obesity)
metabol ~ DiscreteTransition(Cachexia, cpt_Cachexia_Obesity_metabol, Obesity)
# Variable MI (parents: metabol)
MI ~ DiscreteTransition(metabol, cpt_metabol_MI)
# Model learning loop
for i in 1:length(MI_data)
MI_data[i] ~ cardio_model(Cachexia_data=Cachexia_data[i],
Obesity_data=Obesity_data[i],
Cachexia=Cachexia,
Obesity=Obesity,
MI=MI)
end
end
# --- Initialization ---
# Specifies initial beliefs about the distributions before observing data.
initialization = @initialization begin
# Initialize CPT distributions (q)
q(cpt_Cachexia_Obesity_metabol) = DirichletCollection(ones(4, 2, 2))
q(cpt_metabol_MI) = DirichletCollection(ones(2, 4))
# Initialize prior means (μ) for all variables
for init in cardio_model
q(Cachexia) = Categorical([0.5, 0.5])
q(Obesity) = Categorical([0.5, 0.5])
q(MI) = Categorical([0.5, 0.5])
end
end
# Store results across folds
fold_results = []
for (fold_idx, (train_idx, val_idx)) in enumerate(folds)
println("Fold $fold_idx:")
train_df = trainval_df[train_idx, :]
train_data = prepare_data_arrays(train_df, cardinalities)
result = infer(
model = learn_cardio_model(),
data = train_data,
constraints = MeanField(),
initialization = initialization,
iterations = 10,
showprogress = true,
free_energy = true,
returnvars = KeepLast(),
options = (limit_stack_depth=500,)
)
# Store results for this fold
fold_result = (
fold = fold_idx,
final_free_energy = result.free_energy[end],
free_energy_trajectory = result.free_energy,
cpt_metabol = mean(result.posteriors[:cpt_Cachexia_Obesity_metabol]),
cpt_mi = mean(result.posteriors[:cpt_metabol_MI]),
marginals = (
Cachexia = mean(result.posteriors[:Cachexia]),
Obesity = mean(result.posteriors[:Obesity]),
metabol = mean(result.posteriors[:metabol]),
MI = mean(result.posteriors[:MI])
)
)
push!(fold_results, fold_result)
println(" Final free energy: ", fold_result.final_free_energy)
end
# Analyze consistency across folds
println("\n=== Cross-Fold Analysis ===")
free_energies = [r.final_free_energy for r in fold_results]
println("Free energies across folds: ", free_energies)
println("Mean free energy: ", mean(free_energies))
println("Std free energy: ", std(free_energies))
# Check if CPTs are similar across folds
println("\nCPT consistency check:")
for i in 2:length(fold_results)
cpt_diff = norm(fold_results[1].cpt_metabol - fold_results[i].cpt_metabol)
println(" Fold 1 vs Fold $i CPT difference (Frobenius norm): ", cpt_diff)
end
println("Inference completed for all folds")
# Now plot the free energy convergence for all folds
p = plot(title="Free Energy Convergence Across Folds",
xlabel="Iteration",
ylabel="Free Energy")
for (i, fold_result) in enumerate(fold_results)
plot!(p, fold_result.free_energy_trajectory,
label="Fold $i",
marker=:circle,
markersize=3)
end
display(p) I think there was something off with your model construction—some variables were simply not used. When it comes to predictions, I think there's something weird going on when passing pred_result = infer(
model = cardio_model(Cachexia=result.posteriors[:Cachexia], Obesity=result.posteriors[:Obesity], MI=result.posteriors[:MI]),
data = (
Cachexia_data = val_data[:Cachexia_data][i],
MI_data = missing,
Obesity_data = val_data[:Obesity_data][i]
),
iterations = 1,
showprogress = true,
options = (limit_stack_depth=500,)
) |
Beta Was this translation helpful? Give feedback.
-
Thanks @albertpod for your input ! Still probably silly question but how to get the accuracy of the model in this way ? Just for scientific correctness this is just greatly reduced model that has some variables and include latent one for the purpose of debugging but I want to predict miocardial infarction in specific subpopulation of patients and want Baysian uncertanity quantification and explainability, and this library seem to be the best to train baysian model on large dataset. As I see maintatners are here If the model would work I would of course invite some of you as a coauthor and technical reviewer of probabilistic part, if it would be intersting for you.
|
Beta Was this translation helpful? Give feedback.
-
Hi @jakubMitura14 , can you try again with the following constraints: constraints = @constraints begin
q(metabol, Cachexia, Obesity, cpt_Cachexia_Obesity_metabol)=q(Cachexia, Obesity, metabol)q(cpt_Cachexia_Obesity_metabol)
q(MI, metabol, cpt_metabol_MI)=q(metabol, MI)q(cpt_metabol_MI)
end The problem is that you applied constraints on submodels where you learn the conditional probability tables in the parent model. Does this solve your problems? |
Beta Was this translation helpful? Give feedback.
-
Thank you @wouterwln ! Updated code
error
|
Beta Was this translation helpful? Give feedback.
-
@albertpod @wouterwln Hello I know that your time is limited what futherdebugging steps You think I could do to resolve issue (get all trained parameters used to infer on validation data and get probability of outcome variable) |
Beta Was this translation helpful? Give feedback.
-
Hi @jakubMitura14, pred_result = infer(
model = cardio_model(),
data = (
Cachexia_data = val_data[:Cachexia_data][i],
MI_data = UnfactorizedData(missing), # <--------- change here
Obesity_data = val_data[:Obesity_data][i],
cpt_Cachexia_Obesity_metabol = mean(result.posteriors[:cpt_Cachexia_Obesity_metabol]),
cpt_metabol_MI = mean(result.posteriors[:cpt_metabol_MI]),
Cachexia = mean(result.posteriors[:Cachexia]),
Obesity = mean(result.posteriors[:Obesity]),
metabol = mean(result.posteriors[:metabol]),
MI = mean(result.posteriors[:MI])
),
iterations = 1,
showprogress = true,
options = (limit_stack_depth=500,)
) I can't check line numbers in your code so I cannot check the entire stack trace, but this is the only place in which I see a message going towards the |
Beta Was this translation helpful? Give feedback.
-
Thanks! @wouterwln Unluckilly it is still giving error adding updated code and error :
give error
|
Beta Was this translation helpful? Give feedback.
Uh oh!
There was an error while loading. Please reload this page.
-
Hello I try to learn simple baysian network on trainingdata and test it then on validation. I had based code largely on """https://examples.rxinfer.com/categories/basic_examples/bayesian_networks/""" Hovewer I keep getting errors . To make it full working example I attach dataframe on which I test the code. One just need to change a "dataset_path"
Code
Error
Thanks for your time
synth_prediction_data_simple.csv
Beta Was this translation helpful? Give feedback.
All reactions