Python bindings of Easy Factor Graph.
Easy Factor Graph is a general purpose library handling undirected graphical models.
Markov Random Fields as well as Conditional Random Fields fall under this cathegory.
Refer to this documentation for all the details.
Isn't great that you can have the access to the whole power of Easy Factor Graph inside python?? Leave a star in the original repo please ;).
USAGE
FACTORS CONSTRUCTION
from efg import Variable
A = Variable(3, "A")
B = Variable(3, "B")
Such variables can be referred by the factors correlating them. In order to build a simple correlating factor this is what you would do:
from efg import Factor, Group
factor_AB = Factor.makeSimplyCorrelated(Group.make([A, B]))
And this is what you would do to generate an exponential simple correlating factor:
from efg import FactorExponential
factor_AB_exponential = FactorExponential(factor_AB, 1.5)
You can also define custom factors, specifying the shape function that maps the values in their domain with their images. For example:
factor_BC = Factor.makeSimplyCorrelated(Group.make([B, Variable(3, 'C')]))
factor_BC.set([0,1], 2)
factor_BC.set([2,0], 1.3)
MODELS CONSTRUCTION
Factor graphs can be built incrementally, passing one by one the factors that compose them. Without loss of generality suppose to start from an empty random field:
from efg import RandomField
model = RandomField()
then, you can build some factors and enrich the model with them:
A = Variable(4, "varA")
B = Variable(4, "varB")
C = Variable(4, "varC")
factor_AB = Factor.makeSimplyCorrelated(Group.make([A, B]))
model.addConstFactor(factor_AB)
model.copyConstFactor(Factor.makeSimplyCorrelated(Group.make([A, C])))
The previously added factor are kept constant in the model. In order to enrich the model with a tunable factor you can call a different method:
factor_exp_BC = FactorExponential(Factor.makeSimplyCorrelated(Group.make([B, C])), 1.0)
model.addTunableFactor(factor_exp_BC)
D = Variable(4, "varD")
factor_exp_CD = FactorExponential(Factor.makeSimplyCorrelated(Group.make([C, D])), 1.5)
model.addTunableFactor(factor_exp_CD)
You can also add a tunable factor, that must share its weigth with an already inserted factor of the model:
model.addTunableFactorSharingWeight(FactorExponential(Factor.makeSimplyCorrelated(Group.make([C, D])),
1.5
),
[B, C]
)
You can also import the entire graph defined in an xml file (check these samples for the expected format):
from efg import xml as efg_xml
model = RandomField()
efg_xml.from_file(model, 'some/path/model.xml')
Similarly, you can also import the structure defined in a json
import json
from efg import json as efg_json
model_json = json...
model = RandomField()
modelJson = efg_json.from_string(model, json.dumps(model_json))
QUERY THE MODEL
A generated model can be queried in many ways. However, any query that you can do, is conditioned to the latest set of evidences.
Setting the evidences can be easily done by calling:
model.setEvidenceByName("variable_1", 0)
model.setEvidenceByName("variable_2", 2)
You can get the conditioned marginal distribution of a variable by calling:
conditioned_marginals = model.getMarginalDistributionByName("var_A")
Or you might be interested in the maximum a posteriori estimation of the entire evidence set:
MAP_hidden_set = model.getHiddenSetMAP()
As already mentioned, results are subjected to the latest evidences set (which can be also empty). Of course, you can update the evidences and get the updated marginals:
model.removeAllEvidences()
model.setEvidenceByName("evid_1", 1)
conditioned_marginals = model.getMarginalDistributionByName("var_A")
GIBBS SAMPLING
from efg import SamplesGenerationContext
ctxt = SamplesGenerationContext()
ctxt.setDeltaIterations(1000)
ctxt.setSeed(0)
ctxt.setTransient(500)
samples =
model.makeSamples(info,
4
)