|
Infer.NET user guide
How to add a new factor and message operatorsIn this section, we explain how to add a new factor to Infer.Net. For illustration, we create a new factor called 'Sum' that sums the elements of an array of doubles. To place a custom factor in your model once it is created, use Variable<T>.Factor() as described on this page. Step 1: Make a factor class1. Create a static class with your name of choice. In this example, we name the class as "MyFactor". If you plan on making a number of factors, they can all go in this one class.
2. Create a static method that represents the factor. Here, we implement a method called "Sum".
3. This method should take ordinary values, not Variables. It implements the case where the factor's input parameters are known - in our case we can think of the factor getting an array of values and summing them.
4. In our Sum example, if we receive an array of values, the sum is completely determined, i.e. the operation is deterministic. However, other factors are nondeterministic, i.e. they could produce a stochastic output even if the input is known. This includes all factors based on distributions such as Gaussian, Gamma, etc. For example, the Gaussian factor (Factor.Gaussian) is parameterised by mean and precision. Even if we fix mean and precision, we still have to sample from a Gaussian distribution to produce the output. In such cases the factor method must indicate the fact that it is sampling by attaching the [Stochastic] attribute. Below is an example where we have slightly modified the Sum factor to add Gaussian noise.
5. (Optional) Customize parameter naming. Every factor has named parameters. In a graphical model these parameters can be thought of as edges. Each edge is named based on the name of the factor method and its parameter names. In our example, the edges therefore are called "Sum" and "array" by default. When defining your factor, you can override the defaults by attaching the ParameterNames attribute to your factor method:
Step 2: Make an operator classThe operator class implements inference when factor arguments are stochastic. To implement the operator class, we need to first derive the messages that the factor will send to the variable nodes it's attached to. For our example of 'Sum' factor, we are interested in implementing expectation propagation when the incoming messages are 'Gaussian'. The figure below shows the factor graph and the messages that need to be sent from the 'Sum' factor. The notation on the right of this table implements these messages:
1. Somewhere in the assembly, there must be an annotation that tells Infer.NET to look for message functions here. This annotation looks as follows:
2. Create a public static class with any name. Conventionally, this is the name of the factor followed by 'Op'. In our example, this class will be named SumOp.
3. Add a 'FactorMethod' attribute to tie the operator class to the factor that it is implementing. The syntax is:
4. Add declarations for operator methods. Each method is named [recipient][suffix], for example if the recipient is "Sum" and the suffix is "AverageConditional", the function is named "SumAverageConditional". The recipient name is the name of the edge that connects the variable to the factor. This is automatically determined from the factor method and can be overridden as described above. The suffix is determined by the choice of the message passing algorithm: 'AverageConditional' for EP and 'AverageLogarithm' for VMP.
5. Implement the operator methods. For each inference algorithm we would like to implement, we need to work out the mathematics of message passing. For our Gaussian example, the messages to be passed are described in the figure above. We compute these messages using the GetMeanAndVariance method of the Gaussian class (this is faster than separate calls to GetMean and GetVariance). For SumAverageConditional, we return a Gaussian object. For ArrayAverageConditional, we must return a distribution over an array. To avoid creating this object ourselves, we add a special parameter called "result". Infer.NET will automatically fill in this parameter with a pre-allocated message of the correct size. All we need to do is fill in the contents of "result" and return it. This code is not quite correct as it does not handle the special case where one element of the array is uniform. For simplicity of exposition, we omit checking for this.
6. Add parameter annotations. Parameter annotations help Infer.NET create a good schedule for message updates. One common annotation is [SkipIfUniform]. This is attached if a uniform distribution for that parameter would force the output to be uniform. For the Sum factor, if any input is uniform, then the output is uniform, so we add [SkipIfUniform] to all operator method parameters corresponding to incoming messages. This instructs the scheduler to 'skip' the call to this method if the inputs are uniform. 7. Add overloads for observed variables. When a variable is observed, its incoming message will be a concrete value rather than a distribution. For this factor, we have two variables: "array" and "sum". If "array" is observed, Infer.NET will call our original factor method. When "sum" is observed, Infer.NET will look for an operator method accepting " public static GaussianArray ArrayAverageConditional<GaussianArray>([SkipIfUniform] GaussianArray array, double sum, GaussianArray result) where GaussianArray : IList<Gaussian>
|



