{ "cells": [ { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "# Factor Graphs" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "\n", "### Preliminaries\n", "\n", "- Goal \n", " - Introduction to Forney-style factor graphs and message passing-based inference \n", "- Materials \n", " - Mandatory\n", " - These lecture notes \n", " - Loeliger (2007), [The factor graph approach to model based signal processing](https://github.com/bertdv/BMLIP/blob/master/lessons/notebooks/files/Loeliger-2007-The-factor-graph-approach-to-model-based-signal-processing.pdf), pp. 1295-1302 (until section V)\n", " - Optional\n", " - Frederico Wadehn (2015), [Probabilistic graphical models: Factor graphs and more](https://www.youtube.com/watch?v=Fv2YbVg9Frc&t=31) video lecture (**recommended**)\n", " - References\n", " - Forney (2001), [Codes on graphs: normal realizations](https://github.com/bertdv/BMLIP/blob/master/lessons/notebooks/files/Forney-2001-Codes-on-graphs-normal-realizations.pdf)\n", " " ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "### Why Factor Graphs?\n", "\n", "- A probabilistic inference task gets its computational load mainly through the need for marginalization (i.e., computing integrals). E.g., for a model $p(x_1,x_2,x_3,x_4,x_5)$, the inference task $p(x_2|x_3)$ is given by \n", "$$\n", "p(x_2|x_3) = \\frac{p(x_2,x_3)}{p(x_3)} = \\frac{\\int \\cdots \\int p(x_1,x_2,x_3,x_4,x_5) \\, \\mathrm{d}x_1 \\mathrm{d}x_4 \\mathrm{d}x_5}{\\int \\cdots \\int p(x_1,x_2,x_3,x_4,x_5) \\, \\mathrm{d}x_1 \\mathrm{d}x_2 \\mathrm{d}x_4 \\mathrm{d}x_5}$$" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "fragment" } }, "source": [ "- Since these computations (integrals or sums) suffer from the \"curse of dimensionality\", we often need to solve a simpler problem in order to get an answer. " ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "fragment" } }, "source": [ "- Factor graphs provide a computationally efficient approach to solving inference problems **if the probabilistic model can be factorized**. " ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "fragment" } }, "source": [ "- Factorization helps. For instance, if $p(x_1,x_2,x_3,x_4,x_5) = p(x_1)p(x_2,x_3)p(x_4)p(x_5|x_4)$, then\n", "\n", "$$\n", "p(x_2|x_3) = \\frac{\\int \\cdots \\int p(x_1)p(x_2,x_3)p(x_4)p(x_5|x_4) \\, \\mathrm{d}x_1 \\mathrm{d}x_4 \\mathrm{d}x_5}{\\int \\cdots \\int p(x_1)p(x_2,x_3)p(x_4)p(x_5|x_4) \\, \\mathrm{d}x_1 \\mathrm{d}x_2 \\mathrm{d}x_4 \\mathrm{d}x_5} \n", " = \\frac{p(x_2,x_3)}{\\int p(x_2,x_3) \\mathrm{d}x_2}\n", "$$\n", "\n", "which is computationally much cheaper than the general case above." ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "fragment" } }, "source": [ "- In this lesson, we discuss how computationally efficient inference in *factorized* probability distributions can be automated by message passing-based inference in factor graphs." ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "### Factor Graph Construction Rules\n", "\n", "- Consider a function \n", "$$\n", "f(x_1,x_2,x_3,x_4,x_5) = f_a(x_1,x_2,x_3) \\cdot f_b(x_3,x_4,x_5) \\cdot f_c(x_4)\n", "$$" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "fragment" } }, "source": [ "- The factorization of this function can be graphically represented by a **Forney-style Factor Graph** (FFG):\n", "\n", "

" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "fragment" } }, "source": [ "- An FFG is an **undirected** graph subject to the following construction rules ([Forney, 2001](https://github.com/bertdv/BMLIP/blob/master/lessons/notebooks/files/Forney-2001-Codes-on-graphs-normal-realizations.pdf))\n", "\n", " 1. A **node** for every factor;\n", " 1. An **edge** (or **half-edge**) for every variable;\n", " 1. Node $f_\\bullet$ is connected to edge $x$ **iff** variable $x$ appears in factor $f_\\bullet$." ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "fragment" } }, "source": [ "- A **configuration** is an assigment of values to all variables.\n", "\n", "- A configuration $\\omega=(x_1,x_2,x_3,x_4,x_5)$ is said to be **valid** iff $f(\\omega) \\neq 0$\n", " " ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "### Equality Nodes for Branching Points\n", "\n", "\n", "- Note that a variable can appear in maximally two factors in an FFG (since an edge has only two end points)." ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "fragment" } }, "source": [ "- Consider the factorization (where $x_2$ appears in three factors) \n", "\n", "$$\n", " f(x_1,x_2,x_3,x_4) = f_a(x_1,x_2)\\cdot f_b(x_2,x_3) \\cdot f_c(x_2,x_4)\n", "$$" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "fragment" } }, "source": [ "- For the factor graph representation, we will instead consider the function $g$, defined as\n", "$$\\begin{align*}\n", " g(x_1,x_2&,x_2^\\prime,x_2^{\\prime\\prime},x_3,x_4) \n", " = f_a(x_1,x_2)\\cdot f_b(x_2^\\prime,x_3) \\cdot f_c(x_2^{\\prime\\prime},x_4) \\cdot f_=(x_2,x_2^\\prime,x_2^{\\prime\\prime})\n", "\\end{align*}$$\n", " where \n", "$$\n", "f_=(x_2,x_2^\\prime,x_2^{\\prime\\prime}) \\triangleq \\delta(x_2-x_2^\\prime)\\, \\delta(x_2-x_2^{\\prime\\prime})\n", "$$\n", "\n", "

\n", " " ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "subslide" } }, "source": [ "- Note that through introduction of auxiliary variables $X_2^{\\prime}$ and $X_2^{\\prime\\prime}$ and a factor $f_=(x_2,x_2^\\prime,x_2^{\\prime\\prime})$, each variable in $g$ appears in maximally two factors." ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "fragment" } }, "source": [ "- The constraint $f_=(x,x^\\prime,x^{\\prime\\prime})$ enforces that $X=X^\\prime=X^{\\prime\\prime}$ **for every valid configuration**." ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "fragment" } }, "source": [ "- Since $f$ is a marginal of $g$, i.e., \n", "$$\n", "f(x_1,x_2,x_3,x_4) = \\iint g(x_1,x_2,x_2^\\prime,x_2^{\\prime\\prime},x_3,x_4)\\, \\mathrm{d}x_2^\\prime \\mathrm{d}x_2^{\\prime\\prime}\n", "$$\n", "it follows that any inference problem on $f$ can be executed by a corresponding inference problem on $g$, e.g.,\n", "$$\\begin{align*}\n", "f(x_1 \\mid x_2) &\\triangleq \\frac{\\iint f(x_1,x_2,x_3,x_4) \\,\\mathrm{d}x_3 \\mathrm{d}x_4 }{ \\int\\cdots\\int f(x_1,x_2,x_3,x_4) \\,\\mathrm{d}x_1 \\mathrm{d}x_3 \\mathrm{d}x_4} \\\\\n", " &= \\frac{\\int\\cdots\\int g(x_1,x_2,x_2^\\prime,x_2^{\\prime\\prime},x_3,x_4) \\,\\mathrm{d}x_2^\\prime \\mathrm{d}x_2^{\\prime\\prime} \\mathrm{d}x_3 \\mathrm{d}x_4 }{ \\int\\cdots\\int g(x_1,x_2,x_2^\\prime,x_2^{\\prime\\prime},x_3,x_4) \\,\\mathrm{d}x_1 \\mathrm{d}x_2^\\prime \\mathrm{d}x_2^{\\prime\\prime} \\mathrm{d}x_3 \\mathrm{d}x_4} \\\\\n", " &= g(x_1 \\mid x_2)\n", "\\end{align*}$$" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "- $\\Rightarrow$ **Any factorization of a global function $f$ can be represented by a Forney-style Factor Graph**." ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "### Probabilistic Models as Factor Graphs\n", "\n", "- FFGs can be used to express conditional independence (factorization) in probabilistic models. " ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "fragment" } }, "source": [ "- For example, the (previously shown) graph for \n", "$f_a(x_1,x_2,x_3) \\cdot f_b(x_3,x_4,x_5) \\cdot f_c(x_4)$ \n", "could represent the probabilistic model\n", "$$\n", "p(x_1,x_2,x_3,x_4,x_5) = p(x_1,x_2|x_3) \\cdot p(x_3,x_5|x_4) \\cdot p(x_4)\n", "$$\n", "where we identify \n", "$$\\begin{align*}\n", "f_a(x_1,x_2,x_3) &= p(x_1,x_2|x_3) \\\\\n", "f_b(x_3,x_4,x_5) &= p(x_3,x_5|x_4) \\\\\n", "f_c(x_4) &= p(x_4)\n", "\\end{align*}$$" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "fragment" } }, "source": [ "- This is the graph\n", "\n", "

" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "### Inference by Closing Boxes\n", "\n", "- Factorizations provide opportunities to cut on the amount of needed computations when doing inference. In what follows, we will use FFGs to process these opportunities in an automatic way by message passing between the nodes of the graph. " ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "fragment" } }, "source": [ "- Assume we wish to compute the marginal\n", "$$\n", "\\bar{f}(x_3) \\triangleq \\sum\\limits_{x_1,x_2,x_4,x_5,x_6,x_7}f(x_1,x_2,\\ldots,x_7) \n", "$$\n", "for a model $f$ with given factorization \n", "$$\n", "f(x_1,x_2,\\ldots,x_7) = f_a(x_1) f_b(x_2) f_c(x_1,x_2,x_3) f_d(x_4) f_e(x_3,x_4,x_5) f_f(x_5,x_6,x_7) f_g(x_7)\n", "$$ " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "- Note that, if each variable $x_i$ can take on $10$ values, then the computing the marginal $\\bar{f}(x_3)$ takes about $10^6$ (1 million) additions. " ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "subslide" } }, "source": [ "- Due to the factorization and the [Generalized Distributive Law](https://en.wikipedia.org/wiki/Generalized_distributive_law), we can decompose this sum-of-products to the following product-of-sums:\n", "$$\\begin{align*}\\bar{f}&(x_3) = \\\\\n", " &\\underbrace{ \\Bigg( \\sum_{x_1,x_2} \\underbrace{f_a(x_1)}_{\\overrightarrow{\\mu}_{X_1}(x_1)}\\, \\underbrace{f_b(x_2)}_{\\overrightarrow{\\mu}_{X_2}(x_2)}\\,f_c(x_1,x_2,x_3)\\Bigg) }_{\\overrightarrow{\\mu}_{X_3}(x_3)} \n", " \\underbrace{ \\cdot\\Bigg( \\sum_{x_4,x_5} \\underbrace{f_d(x_4)}_{\\overrightarrow{\\mu}_{X_4}(x_4)}\\,f_e(x_3,x_4,x_5) \\cdot \\underbrace{ \\big( \\sum_{x_6,x_7} f_f(x_5,x_6,x_7)\\,\\underbrace{f_g(x_7)}_{\\overleftarrow{\\mu}_{X_7}(x_7)}\\big) }_{\\overleftarrow{\\mu}_{X_5}(x_5)} \\Bigg) }_{\\overleftarrow{\\mu}_{X_3}(x_3)}\n", "\\end{align*}$$\n", "which, in case $x_i$ has $10$ values, requires a few hundred additions and is therefore computationally (much!) lighter than executing the full sum $\\sum_{x_1,\\ldots,x_7}f(x_1,x_2,\\ldots,x_7)$\n", "\n", "

" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "fragment" } }, "source": [ "- Note that the auxiliary factor $\\overrightarrow{\\mu}_{X_3}(x_3)$ is obtained by multiplying all enclosed factors ($f_a$, $f_b, f_c$) by the red dashed box, followed by marginalization (summing) over all enclosed variables ($x_1$, $x_2$).\n", " " ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "fragment" } }, "source": [ "- This is the **Closing the Box**-rule, which is a general recipe for marginalization of latent variables (inside the box) and leads to a new factor that has the variables (edges) that cross the box as arguments. For instance, the argument of the remaining factor $\\overrightarrow{\\mu}_{X_3}(x_3)$ is the variable on the edge that crosses the red box ($x_3$).\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "- Hence, $\\overrightarrow{\\mu}_{X_3}(x_3)$ can be interpreted as a **message from the red box toward variable** $x_3$." ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "fragment" } }, "source": [ "- We drew _directed edges_ in the FFG in order to distinguish forward messages $\\overrightarrow{\\mu}_\\bullet(\\cdot)$ (in the same direction as the arrow of the edge) from backward messages $\\overleftarrow{\\mu}_\\bullet(\\cdot)$ (in opposite direction). This is just a notational convenience since an FFG is computationally an undirected graph. " ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "### Sum-Product Algorithm\n", "\n", "- Closing-the-box can also be interpreted as a **message update rule** for an outgoing message from a node. For a node $f(y,x_1,\\ldots,x_n)$ with incoming messages $\\overrightarrow{\\mu}_{X_1}(x_1), \\overrightarrow{\\mu}_{X_1}(x_1), \\ldots,\\overrightarrow{\\mu}_{X_n}(x_n)$, the outgoing message is given by ([Loeliger (2007), pg.1299](https://github.com/bertdv/BMLIP/blob/master/lessons/notebooks/files/Loeliger-2007-The-factor-graph-approach-to-model-based-signal-processing.pdf)): \n", "\n", "$$ \\boxed{\n", "\\underbrace{\\overrightarrow{\\mu}_{Y}(y)}_{\\substack{ \\text{outgoing}\\\\ \\text{message}}} = \\sum_{x_1,\\ldots,x_n} \\underbrace{\\overrightarrow{\\mu}_{X_1}(x_1)\\cdots \\overrightarrow{\\mu}_{X_n}(x_n)}_{\\substack{\\text{incoming} \\\\ \\text{messages}}} \\cdot \\underbrace{f(y,x_1,\\ldots,x_n)}_{\\substack{\\text{node}\\\\ \\text{function}}} }\n", "$$\n", "\n", "

\n", "\n", "- This is called the **Sum-Product Message** (SPM) update rule. (Look at the formula to understand why it's called the SPM update rule)." ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "fragment" } }, "source": [ "- Note that all SPM update rules can be computed from information that is **locally available** at each node." ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "fragment" } }, "source": [ "- If the factor graph for a function $f$ has **no cycles** (i.e., the graph is a tree), then the marginal $\\bar{f}(x_3) = \\sum_{x_1,x_2,x_4,x_5,x_6,x_7}f(x_1,x_2,\\ldots,x_7)$ is given by multiplying the forward and backward messages on that edge:\n", "\n", "$$ \\boxed{\n", "\\bar{f}(x_3) = \\overrightarrow{\\mu}_{X_3}(x_3)\\cdot \\overleftarrow{\\mu}_{X_3}(x_3)}\n", "$$" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "fragment" } }, "source": [ "- It follows that the marginal $\\bar{f}(x_3) = \\sum_{x_1,x_2,x_4,x_5,x_6,x_7}f(x_1,x_2,\\ldots,x_7)$ can be efficiently computed through sum-product messages. Executing inference through SP message passing is called the **Sum-Product Algorithm** (or alternatively, the **belief propagation** algorithm)." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "- Just as a final note, inference by sum-product message passing is much like replacing the sum-of-products\n", "$$\n", "ac + ad + bc + bd$$\n", "by the following product-of-sums:\n", "$$\n", "(a + b)(c + d) \\,.$$\n", "- Which of these two computations is cheaper to execute?" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Sum-Product Messages for the Equality Node\n", "\n", "- As an example, let´s evaluate the SP messages for the **equality node** $f_=(x,y,z) = \\delta(z-x)\\delta(z-y)$: \n", "\n", "

\n", "\n", "$$\\begin{align*}\n", "\\overrightarrow{\\mu}_{Z}(z) &= \\iint \\overrightarrow{\\mu}_{X}(x) \\overrightarrow{\\mu}_{Y}(y) \\,\\delta(z-x)\\delta(z-y) \\,\\mathrm{d}x \\mathrm{d}y \\\\\n", " &= \\overrightarrow{\\mu}_{X}(z) \\int \\overrightarrow{\\mu}_{Y}(y) \\,\\delta(z-y) \\,\\mathrm{d}y \\\\\n", " &= \\overrightarrow{\\mu}_{X}(z) \\overrightarrow{\\mu}_{Y}(z) \n", "\\end{align*}$$\n", "\n", "- By symmetry, this also implies (for the same equality node) that\n", "$$\\begin{align*}\n", "\\overleftarrow{\\mu}_{X}(x) &= \\overrightarrow{\\mu}_{Y}(x) \\overleftarrow{\\mu}_{Z}(x) \\quad \\text{and} \\\\\n", "\\overleftarrow{\\mu}_{Y}(y) &= \\overrightarrow{\\mu}_{X}(y) \\overleftarrow{\\mu}_{Z}(y)\\,.\n", "\\end{align*}$$\n", "\n", "- Let us now consider the case of Gaussian messages $\\overrightarrow{\\mu}_{X}(x) = \\mathcal{N}(x|\\overrightarrow{m}_X,\\overrightarrow{V}_X)$, $\\overrightarrow{\\mu}_{Y}(y) = \\mathcal{N}(y| \\overrightarrow{m}_Y,\\overrightarrow{V}_Y)$ and $\\overrightarrow{\\mu}_{Z}(z) = \\mathcal{N}(z|\\overrightarrow{m}_Z,\\overrightarrow{V}_Z)$. Let´s also define the precision matrices $\\overrightarrow{W}_X \\triangleq \\overrightarrow{V}_X^{-1}$ and similarly for $Y$ and $Z$. Then applying the SP update rule leads to multiplication of two Gaussian distributions (see [Roweis notes](https://github.com/bertdv/BMLIP/blob/master/lessons/notebooks/files/Roweis-1999-gaussian-identities.pdf)), resulting in \n", "\n", "$$\\begin{align*}\n", "\\overrightarrow{W}_Z &= \\overrightarrow{W}_X + \\overrightarrow{W}_Y \\qquad &\\text{(precisions add)}\\\\ \n", "\\overrightarrow{W}_Z \\overrightarrow{m}_z &= \\overrightarrow{W}_X \\overrightarrow{m}_X + \\overrightarrow{W}_Y \\overrightarrow{m}_Y \\qquad &\\text{(natural means add)}\n", "\\end{align*}$$\n", "\n", "- It follows that **message passing through an equality node is similar to applying Bayes rule**, i.e., fusion of two information sources. Does this make sense?" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "### Message Passing Schedules\n", "\n", "- In a non-cyclic (ie, tree) graph, start with messages from the terminals and keep passing messages through the internal nodes towards the \"target\" variable ($x_3$ in above problem) until you have both the forward and backward message for the target variable. " ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "fragment" } }, "source": [ "- In a tree graph, if you continue to pass messages throughout the graph, the Sum-Product Algorithm computes **exact** marginals for all hidden variables." ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "fragment" } }, "source": [ "- If the graph contains cycles, we have in principle an infinite tree by \"unrolling\" the graph. In this case, the SP Algorithm is not guaranteed to find exact marginals. In practice, if we apply the SP algorithm for just a few iterations (\"unrolls\"), then we often find satisfying approximate marginals. " ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "### Terminal Nodes and Processing Observations \n", "\n", "- We can use terminal nodes to represent observations, e.g., add a factor $f(y)=\\delta(y−3)$ to terminate the half-edge for variable $Y$ if $y=3$ is observed.\n", "\n", "

\n", "\n", "- Terminal nodes that carry observations are denoted by small black boxes." ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "fragment" } }, "source": [ "\n", " \n", "- The message out of a **terminal node** (attached to only 1 edge) is the factor itself. For instance, closing a box around terminal node $f_a(x_1)$ would lead to $$\\overrightarrow{\\mu}_{X_1}(x_1) \\triangleq \\sum_{ \\stackrel{ \\textrm{enclosed} }{ \\textrm{variables} } } \\;\\prod_{\\stackrel{ \\textrm{enclosed} }{ \\textrm{factors} }} f_a(x_1) = f_a(x_1)\\,$$\n", "since there are no enclosed variables. \n", "\n", "

\n", " " ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "fragment" } }, "source": [ "- The message from a half-edge is $1$ (one). You can verify this by imagining that a half-edge $x$ can be terminated by a node function $f(x)=1$ without affecting any inference issue." ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "### Automating Bayesian Inference by Message Passing\n", "\n", "- The foregoing message update rules can be worked out in closed-form and put into tables (e.g., see Tables 1 through 6 in [Loeliger (2007)](./files/Loeliger-2007-The-factor-graph-approach-to-model-based-signal-processing.pdf) for many standard factors such as essential probability distributions and operations such as additions, fixed-gain multiplications and branching (equality nodes).\n", "\n", "- In the optional slides below, we have worked out a few more update rules for the [addition node](#sp-for-addition-node) and the [multiplication node](#sp-for-multiplication-node).\n", "\n", "- If the update rules for all node types in a graph have been tabulated, then inference by message passing comes down to executing a set of table-lookup operations, thus creating a completely **automatable Bayesian inference framework**. \n", "\n", "- In our research lab [BIASlab](http://biaslab.org) (FLUX 7.060), we are developing [RxInfer](http://rxinfer.ml), which is a (Julia) toolbox for automating Bayesian inference by message passing in a factor graph.\n" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "### Example: Bayesian Linear Regression by Message Passing\n" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "fragment" } }, "source": [ "- Assume we want to estimate some function $f: \\mathbb{R}^D \\rightarrow \\mathbb{R}$ from a given data set $D = \\{(x_1,y_1), \\ldots, (x_N,y_N)\\}$, with model assumption $y_i = f(x_i) + \\epsilon_i$." ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "fragment" } }, "source": [ "#### model specification \n", "\n", "- We will assume a linear model with white Gaussian noise and a Gaussian prior on the coefficients $w$:\n", "$$\\begin{align*}\n", " y_i &= w^T x_i + \\epsilon_i \\\\\n", " \\epsilon_i &\\sim \\mathcal{N}(0, \\sigma^2) \\\\ \n", " w &\\sim \\mathcal{N}(0,\\Sigma)\n", "\\end{align*}$$\n", "or equivalently\n", "$$\\begin{align*}\n", "p(w,\\epsilon,D) &= \\overbrace{p(w)}^{\\text{weight prior}} \\prod_{i=1}^N \\overbrace{p(y_i\\,|\\,x_i,w,\\epsilon_i)}^{\\text{regression model}} \\overbrace{p(\\epsilon_i)}^{\\text{noise model}} \\\\\n", " &= \\mathcal{N}(w\\,|\\,0,\\Sigma) \\prod_{i=1}^N \\delta(y_i - w^T x_i - \\epsilon_i) \\mathcal{N}(\\epsilon_i\\,|\\,0,\\sigma^2) \n", "\\end{align*}$$" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "fragment" } }, "source": [ "#### Inference (parameter estimation)\n", "\n", "- We are interested in inferring the posterior $p(w|D)$. We will execute inference by message passing on the FFG for the model." ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "subslide" } }, "source": [ "- The left figure shows the factor graph for this model. \n", "- The right figure shows the message passing scheme. \n", " \n", "

\n" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "#### CODE EXAMPLE\n", "\n", "Let's solve this problem by message passing-based inference with Julia's FFG toolbox [RxInfer](https://biaslab.github.io/rxinfer-website/)." ] }, { "cell_type": "code", "execution_count": 15, "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "\u001b[32m\u001b[1m Activating\u001b[22m\u001b[39m project at `~/github/bertdv/BMLIP/lessons`\n" ] } ], "source": [ "using Pkg; Pkg.activate(\"../.\"); Pkg.instantiate();\n", "using IJulia; try IJulia.clear_output(); catch _ end" ] }, { "cell_type": "code", "execution_count": 16, "metadata": {}, "outputs": [ { "data": { "image/png": "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", "image/svg+xml": [ "\n", "\n", "\n", " \n", " \n", " \n", "\n", "\n", "\n", " \n", " \n", " \n", "\n", "\n", "\n", " \n", " \n", " \n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n" ], "text/html": [ "\n", "\n", "\n", " \n", " \n", " \n", "\n", "\n", "\n", " \n", " \n", " \n", "\n", "\n", "\n", " \n", " \n", " \n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "using Plots, LinearAlgebra, LaTeXStrings\n", "\n", "# Parameters\n", "Σ = 1e5 * Diagonal(I,3) # Covariance matrix of prior on w\n", "σ2 = 2.0 # Noise variance\n", "\n", "# Generate data set\n", "w = [1.0; 2.0; 0.25]\n", "N = 30\n", "z = 10.0*rand(N)\n", "x_train = [[1.0; z; z^2] for z in z] # Feature vector x = [1.0; z; z^2]\n", "f(x) = (w'*x)[1]\n", "y_train = map(f, x_train) + sqrt(σ2)*randn(N) # y[i] = w' * x[i] + ϵ\n", "scatter(z, y_train, label=\"data\", xlabel=L\"z\", ylabel=L\"f([1.0, z, z^2]) + \\epsilon\")" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "Now build the factor graph in RxInfer, perform sum-product message passing and plot results (mean of posterior)." ] }, { "cell_type": "code", "execution_count": 17, "metadata": {}, "outputs": [ { "data": { "image/png": "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", "image/svg+xml": [ "\n", "\n", "\n", " \n", " \n", " \n", "\n", "\n", "\n", " \n", " \n", " \n", "\n", "\n", "\n", " \n", " \n", " \n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n" ], "text/html": [ "\n", "\n", "\n", " \n", " \n", " \n", "\n", "\n", "\n", " \n", " \n", " \n", "\n", "\n", "\n", " \n", " \n", " \n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n" ] }, "metadata": {}, "output_type": "display_data" }, { "name": "stdout", "output_type": "stream", "text": [ "Posterior distribution of w: MvNormalWeightedMeanPrecision(\n", "xi: [229.90792480093597, 1448.9847509899066, 10683.625241700436]\n", "Λ: [15.00001 58.36745139887474 359.0190524950247; 58.36745139887474 359.0190624950247 2575.434877281413; 359.0190524950247 2575.434877281413 19921.345599681066]\n", ")\n", "\n" ] } ], "source": [ "using RxInfer, Random\n", "# Build model\n", "@model function linear_regression(N, Σ, σ2)\n", "\n", " w ~ MvNormalMeanCovariance(constvar(zeros(3)),constvar(Σ))\n", " x = datavar(Vector{Float64}, N)\n", " y = datavar(Float64, N)\n", " \n", " for i in 1:N\n", " y[i] ~ NormalMeanVariance(dot(w , x[i]), σ2)\n", " end\n", " return w, x, y\n", "end\n", "# Run message passing algorithm \n", "results = inference(\n", " model = linear_regression(length(x_train), Σ, σ2 ),\n", " data = (y = y_train, x = x_train),\n", " returnvars = (w = KeepLast()),\n", " iterations = 20\n", ");\n", "# Plot result\n", "w = results.posteriors[:w]\n", "println(\"Posterior distribution of w: $(w)\")\n", "plt = scatter(z, y_train, label=\"data\", xlabel=L\"z\", ylabel=L\"f([1.0, z, z^2]) + \\epsilon\")\n", "z_test = collect(0:0.2:12)\n", "x_test = [[1.0; z; z^2] for z in z_test]\n", "for i=1:10\n", " w_sample = rand(results.posteriors[:w])\n", " f_est(x) = (w_sample'*x)[1]\n", " plt = plot!(z_test, map(f_est, x_test), alpha=0.3, label=\"\");\n", "end\n", "display(plt)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Final thoughts: Modularity and Abstraction \n", "\n", "- The great Michael Jordan (no, not [this one](https://youtu.be/cuLprHh_BRg), but [this one](https://people.eecs.berkeley.edu/~jordan/)), wrote: \n", "\n", " > \"I basically know of two principles for treating complicated systems in simple ways: the first is the principle of **modularity** and the second is the principle of **abstraction**. I am an apologist for computational probability in machine learning because I believe that probability theory implements these two principles in deep and intriguing ways — namely through factorization and through averaging. Exploiting these two mechanisms as fully as possible seems to me to be the way forward in machine learning.\" — Michael Jordan, 1997 (quoted in [Fre98](https://mitpress.mit.edu/9780262062022/)).\n", "\n", "- Factor graphs realize these ideas nicely, both visually and computationally.\n", "\n", "- Visually, the modularity of conditional independencies in the model are displayed by the graph structure. Each node hides internal complexity and by closing-the-box, we can hierarchically move on to higher levels of abstraction. \n", "\n", "- Computationally, message passing-based inference uses the Distributive Law to avoid any unnecessary computations. \n", "\n", "- What is the relevance of this lesson? RxInfer is not yet a finished project. Still, my prediction is that in 5-10 years, this lesson on Factor Graphs will be the final lecture of part-A of this class, aimed at engineers who need to develop machine learning applications. In principle you have all the tools now to work out the 4-step machine learning recipe (1. model specification, 2. parameter learning, 3. model evaluation, 4. application) that was proposed in the [Bayesian machine learning lesson](https://nbviewer.org/github/bertdv/BMLIP/blob/master/lessons/notebooks/Bayesian-Machine-Learning.ipynb#Bayesian-design). You can propose any model and execute the (learning, evaluation, and application) stages by executing the corresponding inference task automatically in RxInfer. \n", "\n", "- Part-B of this class would be about on advanced methods on how to improve automated inference by RxInfer or a similar probabilistic programming package. The Bayesian approach fully supports separating model specification from the inference task. \n" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "##
OPTIONAL SLIDES
" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "### Sum-Product Messages for Multiplication Nodes\n", "- Next, let us consider a **multiplication** by a fixed (invertible matrix) gain $f_A(x,y) = \\delta(y-Ax)$\n", "\n", "

" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "fragment" } }, "source": [ "$$\\begin{align*}\n", "\\overrightarrow{\\mu}_{Y}(y) &= \\int \\overrightarrow{\\mu}_{X}(x) \\,\\delta(y-Ax) \\,\\mathrm{d}x \\\\\n", "&= \\int \\overrightarrow{\\mu}_{X}(x) \\,|A|^{-1}\\delta(x-A^{-1}y) \\,\\mathrm{d}x \\\\\n", "&= |A|^{-1}\\overrightarrow{\\mu}_{X}(A^{-1}y) \\,.\n", "\\end{align*}$$" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "fragment" } }, "source": [ "- For a Gaussian message input message $\\overrightarrow{\\mu}_{X}(x) = \\mathcal{N}(x|\\overrightarrow{m}_{X},\\overrightarrow{V}_{X})$, the output message is also Gaussian with \n", "$$\\begin{align*}\n", "\\overrightarrow{m}_{Y} = A\\overrightarrow{m}_{X} \\,,\\,\\text{and}\\,\\,\n", "\\overrightarrow{V}_{Y} = A\\overrightarrow{V}_{X}A^T\n", "\\end{align*}$$\n", "since \n", "$$\\begin{align*}\n", "\\overrightarrow{\\mu}_{Y}(y) &= |A|^{-1}\\overrightarrow{\\mu}_{X}(A^{-1}y) \\\\\n", " &\\propto \\exp \\left( -\\frac{1}{2} \\left( A^{-1}y - \\overrightarrow{m}_{X}\\right)^T \\overrightarrow{V}_{X}^{-1} \\left( A^{-1}y - \\overrightarrow{m}_{X}\\right)\\right) \\\\\n", " &= \\exp \\big( -\\frac{1}{2} \\left( y - A\\overrightarrow{m}_{X}\\right)^T \\underbrace{A^{-T}\\overrightarrow{V}_{X}^{-1} A^{-1}}_{(A \\overrightarrow{V}_{X} A^T)^{-1}} \\left( y - A\\overrightarrow{m}_{X}\\right)\\big) \\\\\n", " &\\propto \\mathcal{N}(y| A\\overrightarrow{m}_{X},A\\overrightarrow{V}_{X}A^T) \\,.\n", "\\end{align*}$$" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "subslide" } }, "source": [ "- Exercise: Proof that, for the same factor $\\delta(y-Ax)$ and Gaussian messages, the (backward) sum-product message $\\overleftarrow{\\mu}_{X}$ is given by \n", "$$\\begin{align*}\n", "\\overleftarrow{\\xi}_{X} &= A^T\\overleftarrow{\\xi}_{Y} \\\\\n", "\\overleftarrow{W}_{X} &= A^T\\overleftarrow{W}_{Y}A\n", "\\end{align*}$$\n", "where $\\overleftarrow{\\xi}_X \\triangleq \\overleftarrow{W}_X \\overleftarrow{m}_X$ and $\\overleftarrow{W}_{X} \\triangleq \\overleftarrow{V}_{X}^{-1}$ (and similarly for $Y$)." ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "\n", "### Code example: Gaussian forward and backward messages for the Addition node\n", "\n", "Let's calculate the Gaussian forward and backward messages for the addition node in RxInfer. \n", "

" ] }, { "cell_type": "code", "execution_count": 18, "metadata": { "slideshow": { "slide_type": "subslide" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Forward message on Z:\n" ] }, { "data": { "text/plain": [ "NormalMeanVariance{Float64}(μ=3.0, v=2.0)" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "println(\"Forward message on Z:\")\n", "@call_rule typeof(+)(:out, Marginalisation) (m_in1 = NormalMeanVariance(1.0, 1.0), m_in2 = NormalMeanVariance(2.0, 1.0))" ] }, { "cell_type": "code", "execution_count": 19, "metadata": { "slideshow": { "slide_type": "subslide" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Backward message on X:\n" ] }, { "data": { "text/plain": [ "NormalMeanVariance{Float64}(μ=1.0, v=2.0)" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "println(\"Backward message on X:\")\n", "@call_rule typeof(+)(:in1, Marginalisation) (m_out = NormalMeanVariance(3.0, 1.0), m_in2 = NormalMeanVariance(2.0, 1.0))" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "### Code Example: forward and backward messages for the Matrix Multiplication node\n", "\n", "In the same way we can also investigate the forward and backward messages for the matrix multiplication (\"gain\") node \n", "

" ] }, { "cell_type": "code", "execution_count": 20, "metadata": { "slideshow": { "slide_type": "subslide" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Forward message on Y:\n" ] }, { "data": { "text/plain": [ "NormalMeanVariance{Float64}(μ=4.0, v=16.0)" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "println(\"Forward message on Y:\")\n", "@call_rule typeof(*)(:out, Marginalisation) (m_A = PointMass(4.0), m_in = NormalMeanVariance(1.0, 1.0))" ] }, { "cell_type": "code", "execution_count": 21, "metadata": { "slideshow": { "slide_type": "subslide" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Backward message on X:\n" ] }, { "ename": "UndefVarError", "evalue": "UndefVarError: `TinyCorrection` not defined", "output_type": "error", "traceback": [ "UndefVarError: `TinyCorrection` not defined\n", "\n", "Stacktrace:\n", " [1] top-level scope\n", " @ ~/.julia/packages/ReactiveMP/IxcF2/src/rule.jl:504" ] } ], "source": [ "println(\"Backward message on X:\")\n", "@call_rule typeof(*)(:in, Marginalisation) (m_out = NormalMeanVariance(2.0, 1.0), m_A = PointMass(4.0))" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "fragment" } }, "source": [ " \n", " ### Example: Sum-Product Algorithm to infer a posterior\n", " \n", " - Consider a generative model \n", "$$p(x,y_1,y_2) = p(x)\\,p(y_1|x)\\,p(y_2|x) .$$ \n", " - This model expresses the assumption that $Y_1$ and $Y_2$ are independent measurements of $X$.\n", "\n", "

" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "subslide" } }, "source": [ " \n", "- Assume that we are interested in the posterior for $X$ after observing $Y_1= \\hat y_1$ and $Y_2= \\hat y_2$. The posterior for $X$ can be inferred by applying the sum-product algorithm to the following graph:\n", "\n", "

" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "fragment" } }, "source": [ " - (Note that) we usually draw terminal nodes for observed variables in the graph by smaller solid-black squares. This is just to help the visualization of the graph, since the computational rules are no different than for other nodes. " ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "### Code for Sum-Product Algorithm to infer a posterior\n", "\n", "We'll use RxInfer to build the above graph, and perform sum-product message passing to infer the posterior $p(x|y_1,y_2)$. We assume $p(y_1|x)$ and $p(y_2|x)$ to be Gaussian likelihoods with known variances:\n", "$$\\begin{align*}\n", " p(y_1\\,|\\,x) &= \\mathcal{N}(y_1\\,|\\,x, v_{y1}) \\\\\n", " p(y_2\\,|\\,x) &= \\mathcal{N}(y_2\\,|\\,x, v_{y2})\n", "\\end{align*}$$\n", "Under this model, the posterior is given by:\n", "$$\\begin{align*}\n", " p(x\\,|\\,y_1,y_2) &\\propto \\overbrace{p(y_1\\,|\\,x)\\,p(y_2\\,|\\,x)}^{\\text{likelihood}}\\,\\overbrace{p(x)}^{\\text{prior}} \\\\\n", " &=\\mathcal{N}(x\\,|\\,\\hat{y}_1, v_{y1})\\, \\mathcal{N}(x\\,|\\,\\hat{y}_2, v_{y2}) \\, \\mathcal{N}(x\\,|\\,m_x, v_x) \n", "\\end{align*}$$\n", "so we can validate the answer by solving the Gaussian multiplication manually." ] }, { "cell_type": "code", "execution_count": 22, "metadata": { "slideshow": { "slide_type": "subslide" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Sum-product message passing result: p(x|y1,y2) = 𝒩(1.1428571428571428,0.5714285714285714)\n", "Manual result: p(x|y1,y2) = 𝒩(1.1428571428571428, 0.5714285714285714)\n" ] } ], "source": [ "# Data\n", "y1_hat = 1.0\n", "y2_hat = 2.0\n", "\n", "# Construct the factor graph\n", "@model function my_model()\n", "\n", " # `x` is the hidden states\n", " x = randomvar()\n", " # `y1` and `y2` are \"clamped\" observations\n", " y1 = datavar(Float64,)\n", " y2 = datavar(Float64,)\n", " \n", " x ~ NormalMeanVariance(constvar(0.0), constvar(4.0))\n", " y1 ~ NormalMeanVariance(x, constvar(1))\n", " y2 ~ NormalMeanVariance(x, constvar(2))\n", " \n", " return x\n", "end\n", "\n", "result = inference(model=my_model(), data=(y1=y1_hat, y2 = y2_hat,))\n", "println(\"Sum-product message passing result: p(x|y1,y2) = 𝒩($(mean(result.posteriors[:x])),$(var(result.posteriors[:x])))\")\n", "\n", "# Calculate mean and variance of p(x|y1,y2) manually by multiplying 3 Gaussians (see lesson 4 for details)\n", "v = 1 / (1/4 + 1/1 + 1/2)\n", "m = v * (0/4 + y1_hat/1.0 + y2_hat/2.0)\n", "println(\"Manual result: p(x|y1,y2) = 𝒩($(m), $(v))\")" ] }, { "cell_type": "code", "execution_count": 23, "metadata": { "slideshow": { "slide_type": "skip" } }, "outputs": [ { "data": { "text/html": [ "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "open(\"../../styles/aipstyle.html\") do f display(\"text/html\", read(f, String)) end\n" ] } ], "metadata": { "celltoolbar": "Slideshow", "kernelspec": { "display_name": "Julia 1.9.3", "language": "julia", "name": "julia-1.9" }, "language_info": { "file_extension": ".jl", "mimetype": "application/julia", "name": "julia", "version": "1.9.3" } }, "nbformat": 4, "nbformat_minor": 4 }