jmschrei / pomegranate

Fast, flexible and easy to use probabilistic modelling in Python.
http://pomegranate.readthedocs.org/en/latest/
MIT License
3.35k stars 589 forks source link

Bayesian Network Predicts Wrong Values #1108

Closed Curvedrain closed 2 months ago

Curvedrain commented 3 months ago

Hello, I've been doing some test code to see how far a node with evidence will impact the marginal probabilities (and predictions) of parent/child nodes. I have the following distribution, with the idea of having the 0th index become increasingly more likely, while the 1st index becomes increasingly less likely.

The predicted probabilities only seem to be correct to a degree. In the example, the evidence is that the last node is of index 1, and thus it should be predicted that all parents must have had index 1 as well, as having an index of 0 causes a 0% chance of achieving an index of 1 in any children. Any ideas why this may occur?

'''Bayesian Network Testing'''

#Imports
import pomegranate.distributions as dist
from pomegranate.bayesian_network import BayesianNetwork
import torch

#Input
length = 23

model = BayesianNetwork()

#The highest node in the network is a 50-50 discrete distribution.
firstdist = dist.Categorical([[0.5, 0.5]])
model.add_distributions([firstdist])

#Then add distributions to fill the remaining length
nextdists = []
for i in range(0, length-1):
    #store the distribution in a list, so that edges can be drawn to that distribution
    nextdists = nextdists + [dist.ConditionalCategorical([[[1.0, 0.0], [0.5, 0.5]]])]
    model.add_distributions([nextdists[i]])

    #the first conditional will have the 50-50 categorical distribution as a parent
    if i == 0:
        model.add_edge(firstdist,nextdists[i])
    #every other conditional will have another conditional as a parent
    else:
        model.add_edge(nextdists[i-1], nextdists[i])

#create an evidence masked tensor where the last node is known to have index 1
evidencelist = [-1] * length
evidencelist[length-1] = 1
X = torch.tensor([evidencelist])
X_masked = torch.masked.MaskedTensor(X, mask=X >= 0)

#print(X_masked)

#predict the indeces of the parent nodes, given the evidence
predictions = model.predict(X_masked)
#predictions = model.predict_proba(X_masked)
print(predictions)

#expecting all 1's
#getting only twenty 1's for the parent nodes, before getting unexpected 0's
#This tweny 1's is true for all length sizes and given evidence (assuming 20+ parents beforehand)
jmschrei commented 2 months ago

What a great test. Sorry for the delay in responding, but this was in the back of my head for a while. I was having trouble figuring out what was happening because I didn't read your final comment, and then something clicked:

https://github.com/jmschrei/pomegranate/blob/master/pomegranate/factor_graph.py#L106

20 is the default number of iterations of LBP, encoded in both the BayesianNetwork and the underlying FactorGraph objects. In your example network, each iteration will cause the information to "climb" one node up. But when I set max_iter to a higher number in the BayesianNetwork object, I didn't see any difference... because I wasn't actually passing that value into the FactorGraph object! So, that's the underlying bug here. I've fixed this for 1.1.0, which I will release soon. Please re-open if this continues to be an issue. I am adding this example to the unit tests, because the number of 1s should be equal to the maximum number of iterations (+1 for the evidence).