How to assign a belief/value to a node in a bayesian network, using predict_proba() from pomegranate

797 Views Asked by At

I'm trying to create my own bayesian network programme to model a very simple court ruling scenario using pomegranate, very similar to the monty hall problem which is well documented as an example of bayesian networks with pomegranate. I have made the node testifies (whether the witness testifies that the defendant is guilty or not) dependent on the nodes reliable (if the witness is reliable or not) and guilty (whether the defendant has committed the crime or not). However when I try to assign a certain value or belief to the nodes, in order to see how the conditional probabilities of the other nodes change, using beliefs = predict_proba() it seems that no values are being assigned to these nodes as the array beliefs simply returns the full probability distribution of each variable as if every variable in the system is still unknown. Is there anything missing from my code which could fix this?

from pomegranate import *
import math
import pomegranate as pg
reliable = DiscreteDistribution({'T':3.0/4.0, 'F':1.0/4.0})
guilty = DiscreteDistribution({'T':1.0/2.0, 'F':1.0/2.0})
testifies = ConditionalProbabilityTable([['T','T','T',1.0],
                                         ['T','T','F',0.0],
                                         ['T','F','T',0.0],
                                         ['T','F','F',1.0],
                                         ['F','T','T',0.5],
                                         ['F','T','F',0.5],
                                         ['F','F','T',0.5],
                                         ['F','F','F',0.5]],[reliable,guilty])


s1 = State(reliable,name="Reliable")
s2 = State(guilty,name="Guilty")
s3 = State(testifies,name="Testifies")

network = BayesianNetwork("Court Ruling")
network.add_states(s1,s2,s3)

network.add_edge(s1,s3)
network.add_edge(s2,s3)

network.bake()


beliefs = network.predict_proba({'reliable':'T','guilty':'F'})
beliefs

This returns

array([ {
"class" : "Distribution",
"dtype" : "str",
"name" : "DiscreteDistribution",
"parameters" : [
    {
        "T" : 0.7499999999999998,
        "F" : 0.2500000000000002
    }
],
"frozen" : false
},
   {
"class" : "Distribution",
"dtype" : "str",
"name" : "DiscreteDistribution",
"parameters" : [
    {
        "T" : 0.5,
        "F" : 0.5
    }
],
"frozen" : false
},
   {
"class" : "Distribution",
"dtype" : "str",
"name" : "DiscreteDistribution",
"parameters" : [
    {
        "T" : 0.5,
        "F" : 0.5
    }
],
"frozen" : false
}], dtype=object)

When it should just return

array(['T','F',
   {
"class" : "Distribution",
"dtype" : "str",
"name" : "DiscreteDistribution",
"parameters" : [
    {
        "T" : 0.5,
        "F" : 0.5
    }
],
"frozen" : false
}], dtype=object)
1

There are 1 best solutions below

0
On

Good question, this is indeed surprising behavior. A little poking and I discovered there are two things going on here.

  1. You need to send a list of dict. It's completely ignoring what you send and just giving the marginal for the whole network. This nonsense yields the same answer:
network.predict_proba({'bunting':'red','frog':'green'})
  1. Adding a list reveals it is case-sensitive, as this:
network.predict_proba([{'reliable':'T','guilty':'F'}])

yields this:

---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
...
ValueError: Model does not contain a state named 'reliable'

Answer

That leads us to one of two ways to get what you want. Your way with Caps:

network.predict_proba([{'Reliable':'T','Guilty':'F'}])

or a list of lists, where we have to include None for nodes to infer:

network.predict_proba([['T','F',None]])

both of which yield:

[array(['T', 'F',
        {
     "class" : "Distribution",
     "dtype" : "str",
     "name" : "DiscreteDistribution",
     "parameters" : [
         {
             "F" : 1.0,
             "T" : 0.0
         }
     ],
     "frozen" : false
 }], dtype=object)]

That's not the [.5, .5] you expected, but it is the correct answer from your CPT.

Extra

You will get [.5, .5] if you flip the inputs:

network.predict_proba([{'Reliable':'F','Guilty':'T'}])

[array(['F', 'T',
        {
     "class" : "Distribution",
     "dtype" : "str",
     "name" : "DiscreteDistribution",
     "parameters" : [
         {
             "F" : 0.5,
             "T" : 0.5
         }
     ],
     "frozen" : false
 }], dtype=object)]

And if you can do inverse reasoning, if you know how to read the node order:

network.predict_proba([{'Testifies':'T'}])
[array([{
     "class" : "Distribution",
     "dtype" : "str",
     "name" : "DiscreteDistribution",
     "parameters" : [
         {
             "T" : 0.7499999999999999,
             "F" : 0.25000000000000017
         }
     ],
     "frozen" : false
 },
        {
     "class" : "Distribution",
     "dtype" : "str",
     "name" : "DiscreteDistribution",
     "parameters" : [
         {
             "T" : 0.8749999999999999,
             "F" : 0.12500000000000022
         }
     ],
     "frozen" : false
 },
        'T'], dtype=object)]