diff --git a/docs/source/contrib.zuko.rst b/docs/source/contrib.zuko.rst new file mode 100644 index 0000000000..c7f2dbe7e1 --- /dev/null +++ b/docs/source/contrib.zuko.rst @@ -0,0 +1,5 @@ +Zuko in Pyro +============ + +.. automodule:: pyro.contrib.zuko + :members: diff --git a/docs/source/index.rst b/docs/source/index.rst index 82b70e684f..a5104fb9bc 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -45,6 +45,7 @@ Pyro Documentation contrib.randomvariable contrib.timeseries contrib.tracking + contrib.zuko Indices and tables diff --git a/pyro/contrib/zuko.py b/pyro/contrib/zuko.py new file mode 100644 index 0000000000..232b773389 --- /dev/null +++ b/pyro/contrib/zuko.py @@ -0,0 +1,81 @@ +# Copyright Contributors to the Pyro project. +# SPDX-License-Identifier: Apache-2.0 + +""" +This file contains helpers to use `Zuko `_-based +normalizing flows within Pyro piplines. + +Accompanying tutorials can be found at `tutorial/svi_flow_guide.ipynb` and +`tutorial/vae_flow_prior.ipynb`. +""" + +import torch +from torch import Size, Tensor + +import pyro + + +class ZukoToPyro(pyro.distributions.TorchDistribution): + r"""Wraps a Zuko distribution as a Pyro distribution. + + If ``dist`` has an ``rsample_and_log_prob`` method, like Zuko's flows, it will be + used when sampling instead of ``rsample``. The returned log density will be cached + for later scoring. + + :param dist: A distribution instance. + :type dist: torch.distributions.Distribution + + .. code-block:: python + + flow = zuko.flows.MAF(features=5) + + # flow() is a torch.distributions.Distribution + + dist = flow() + x = dist.sample((2, 3)) + log_p = dist.log_prob(x) + + # ZukoToPyro(flow()) is a pyro.distributions.Distribution + + dist = ZukoToPyro(flow()) + x = dist((2, 3)) + log_p = dist.log_prob(x) + + with pyro.plate("data", 42): + z = pyro.sample("z", dist) + """ + + def __init__(self, dist: torch.distributions.Distribution): + self.dist = dist + self.cache = {} + + @property + def has_rsample(self) -> bool: + return self.dist.has_rsample + + @property + def event_shape(self) -> Size: + return self.dist.event_shape + + @property + def batch_shape(self) -> Size: + return self.dist.batch_shape + + def __call__(self, shape: Size = ()) -> Tensor: + if hasattr(self.dist, "rsample_and_log_prob"): # fast sampling + scoring + x, self.cache[x] = self.dist.rsample_and_log_prob(shape) + elif self.has_rsample: + x = self.dist.rsample(shape) + else: + x = self.dist.sample(shape) + + return x + + def log_prob(self, x: Tensor) -> Tensor: + if x in self.cache: + return self.cache[x] + else: + return self.dist.log_prob(x) + + def expand(self, *args, **kwargs): + return ZukoToPyro(self.dist.expand(*args, **kwargs)) diff --git a/tests/contrib/test_zuko.py b/tests/contrib/test_zuko.py new file mode 100644 index 0000000000..cee04c177b --- /dev/null +++ b/tests/contrib/test_zuko.py @@ -0,0 +1,65 @@ +# Copyright Contributors to the Pyro project. +# SPDX-License-Identifier: Apache-2.0 + + +import pytest +import torch + +import pyro +from pyro.contrib.zuko import ZukoToPyro +from pyro.infer import SVI, Trace_ELBO +from pyro.optim import Adam + + +@pytest.mark.parametrize("multivariate", [True, False]) +@pytest.mark.parametrize("rsample_and_log_prob", [True, False]) +def test_ZukoToPyro(multivariate: bool, rsample_and_log_prob: bool): + # Distribution + if multivariate: + normal = torch.distributions.MultivariateNormal + mu = torch.zeros(3) + sigma = torch.eye(3) + else: + normal = torch.distributions.Normal + mu = torch.zeros(()) + sigma = torch.ones(()) + + dist = normal(mu, sigma) + + if rsample_and_log_prob: + + def dummy(self, shape): + x = self.rsample(shape) + return x, self.log_prob(x) + + dist.rsample_and_log_prob = dummy.__get__(dist) + + # Sample + x1 = pyro.sample("x1", ZukoToPyro(dist)) + + assert x1.shape == dist.event_shape + + # Sample within plate + with pyro.plate("data", 4): + x2 = pyro.sample("x2", ZukoToPyro(dist)) + + assert x2.shape == (4, *dist.event_shape) + + # SVI + def model(): + pyro.sample("a", ZukoToPyro(dist)) + + with pyro.plate("data", 4): + pyro.sample("b", ZukoToPyro(dist)) + + def guide(): + mu_ = pyro.param("mu", mu) + sigma_ = pyro.param("sigma", sigma) + + pyro.sample("a", ZukoToPyro(normal(mu_, sigma_))) + + with pyro.plate("data", 4): + pyro.sample("b", ZukoToPyro(normal(mu_, sigma_))) + + svi = SVI(model, guide, optim=Adam({"lr": 1e-3}), loss=Trace_ELBO()) + svi.step() diff --git a/tutorial/source/index.rst b/tutorial/source/index.rst index 5d4c0cc12c..442cb74878 100644 --- a/tutorial/source/index.rst +++ b/tutorial/source/index.rst @@ -97,6 +97,7 @@ List of Tutorials jit svi_horovod svi_lightning + svi_flow_guide .. toctree:: :maxdepth: 1 @@ -106,7 +107,8 @@ List of Tutorials vae ss-vae cvae - normalizing_flows_i + normalizing_flows_intro + vae_flow_prior dmm air cevae diff --git a/tutorial/source/normalizing_flows_i.ipynb b/tutorial/source/normalizing_flows_intro.ipynb similarity index 99% rename from tutorial/source/normalizing_flows_i.ipynb rename to tutorial/source/normalizing_flows_intro.ipynb index 87284ba4b4..3617edc98e 100644 --- a/tutorial/source/normalizing_flows_i.ipynb +++ b/tutorial/source/normalizing_flows_intro.ipynb @@ -4,10 +4,12 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "# Normalizing Flows - Introduction (Part 1)\n", + "# Normalizing Flows - Introduction\n", + "\n", + "This tutorial introduces Pyro's built-in normalizing flows. It is independent of most of Pyro, but users may want to read about distribution shapes in the [Tensor Shapes Tutorial](http://pyro.ai/examples/tensor_shapes.html).\n", + "\n", + "> The development of Pyro's built-in flows has stopped in favor of external libraries, such as [Zuko](https://github.com/probabilists/zuko), [nflows](https://github.com/bayesiains/nflows), [normflows](https://github.com/VincentStimper/normalizing-flows) or [FlowTorch](https://flowtorch.ai/). Some of these libraries may have interfaces that are not directly compatible with Pyro. See the [SVI with flow guide](svi_flow_guide.ipynb) and [VAE with flow prior](vae_flow_prior.ipynb) tutorials for example usages of [Zuko](https://github.com/probabilists/zuko) within Pyro.\n", "\n", - "This tutorial introduces Pyro's normalizing flow library. It is independent of much of Pyro, but users may want to read about distribution shapes in the [Tensor Shapes Tutorial](http://pyro.ai/examples/tensor_shapes.html).\n", - " \n", "## Introduction\n", "\n", "In standard probabilistic modeling practice, we represent our beliefs over unknown continuous quantities with simple parametric distributions like the normal, exponential, and Laplacian distributions. However, using such simple forms, which are commonly symmetric and unimodal (or have a fixed number of modes when we take a mixture of them), restricts the performance and flexibility of our methods. For instance, standard variational inference in the Variational Autoencoder uses independent univariate normal distributions to represent the variational family. The true posterior is neither independent nor normally distributed, which results in suboptimal inference and simplifies the model that is learnt. In other scenarios, we are likewise restricted by not being able to model multimodal distributions and heavy or light tails.\n", @@ -25,8 +27,7 @@ " \n", "Normalizing Flows are a family of methods for constructing flexible distributions. Let's first restrict our attention to representing univariate distributions. The basic idea is that a simple source of noise, for example a variable with a standard normal distribution, $X\\sim\\mathcal{N}(0,1)$, is passed through a bijective (i.e. invertible) function, $g(\\cdot)$ to produce a more complex transformed variable $Y=g(X)$.\n", "\n", - "For a given random variable, we typically want to perform two operations: sampling and scoring. Sampling $Y$ is trivial. First, we sample $X=x$, then calculate $y=g(x)$. Scoring $Y$, or rather, evaluating the log-density $\\log(p_Y(y))$, is more involved. How does the density of $Y$ relate to the density of $X$? We can use the substitution rule of integral calculus to answer this. Suppose we want to evaluate the expectation of some function of $X$. Then,\n", - "\n", + "For a given random variable, we typically want to perform two operations: sampling and scoring. Sampling $Y$ is trivial. First, we sample $X=x$, then calculate $y=g(x)$. Scoring $Y$, or rather, evaluating the log-density $\\log p_Y(y)$, is more involved. How does the density of $Y$ relate to the density of $X$? We can use the substitution rule of integral calculus to answer this. Suppose we want to evaluate the expectation of some function of $X$. Then,\n", "\n", "\\begin{align}\n", "\\mathbb{E}_{p_X(\\cdot)}\\left[f(X)\\right] &= \\int_{\\text{supp}(X)}f(x)p_X(x)dx\\\\\n", @@ -34,29 +35,22 @@ "&= \\mathbb{E}_{p_Y(\\cdot)}\\left[f(g^{-1}(Y))\\right],\n", "\\end{align}\n", "\n", - "\n", "where $\\text{supp}(X)$ denotes the support of $X$, which in this case is $(-\\infty,\\infty)$. Crucially, we used the fact that $g$ is bijective to apply the substitution rule in going from the first to the second line. Equating the last two lines we get,\n", "\n", - "\n", "\\begin{align}\n", - "\\log(p_Y(y)) &= \\log(p_X(g^{-1}(y)))+\\log\\left(\\left|\\frac{dx}{dy}\\right|\\right)\\\\\n", - "&= \\log(p_X(g^{-1}(y)))-\\log\\left(\\left|\\frac{dy}{dx}\\right|\\right).\n", + "\\log p_Y(y) & = \\log p_X(g^{-1}(y)) + \\log\\left|\\frac{dx}{dy}\\right| \\\\\n", + "& = \\log p_X(g^{-1}(y)) - \\log\\left|\\frac{dy}{dx}\\right|.\n", "\\end{align}\n", "\n", - "\n", "Inituitively, this equation says that the density of $Y$ is equal to the density at the corresponding point in $X$ plus a term that corrects for the warp in volume around an infinitesimally small length around $Y$ caused by the transformation.\n", "\n", - "If $g$ is cleverly constructed (and we will see several examples shortly), we can produce distributions that are more complex than standard normal noise and yet have easy sampling and computationally tractable scoring. Moreover, we can compose such bijective transformations to produce even more complex distributions. By an inductive argument, if we have $L$ transforms $g_{(0)}, g_{(1)},\\ldots,g_{(L-1)}$, then the log-density of the transformed variable $Y=(g_{(0)}\\circ g_{(1)}\\circ\\cdots\\circ g_{(L-1)})(X)$ is\n", - "\n", + "If $g$ is cleverly constructed (and we will see several examples shortly), we can produce distributions that are more complex than standard normal noise and yet have easy sampling and computationally tractable scoring. Moreover, we can compose such bijective transformations to produce even more complex distributions. By an inductive argument, if we have a sequence of $L$ transforms $(g_1, g_2, \\ldots, g_L)$ such that $Y = g(X) = g_L \\circ \\cdots g_2 \\circ g_1(X)$, then the log-density of $Y$ is\n", "\n", "\\begin{align}\n", - "\\log(p_Y(y)) &= \\log\\left(p_X\\left(\\left(g_{(L-1)}^{-1}\\circ\\cdots\\circ g_{(0)}^{-1}\\right)\\left(y\\right)\\right)\\right)+\\sum^{L-1}_{l=0}\\log\\left(\\left|\\frac{dg^{-1}_{(l)}(y_{(l)})}{dy'}\\right|\\right),\n", - "%\\left( g^{(l)}(y^{(l)})\n", - "%\\right).\n", + "\\log p_Y(y) = \\log p_X(y_0) + \\sum^{L}_{l=1} \\log \\left| \\frac{dg^{-1}_{l}(y_l)}{dy_{l}} \\right|\n", "\\end{align}\n", "\n", - "\n", - "where we've defined $y_{(0)}=x$, $y_{(L-1)}=y$ for convenience of notation.\n", + "where $y_{l} = y$ and $y_{l-1} = g^{-1}_l(y_{l})$.\n", "\n", "In a latter section, we will see how to generalize this method to multivariate $X$. The field of Normalizing Flows aims to construct such $g$ for multivariate $X$ to transform simple i.i.d. standard normal noise into complex, learnable, high-dimensional distributions. The methods have been applied to such diverse applications as image modeling, text-to-speech, unsupervised language induction, data compression, and modeling molecular structures. As probability distributions are the most fundamental component of probabilistic modeling we will likely see many more exciting state-of-the-art applications in the near future." ] @@ -71,13 +65,11 @@ "\n", "Let us begin by showing how to represent and manipulate a simple transformed distribution,\n", "\n", - "\n", "\\begin{align}\n", "X &\\sim \\mathcal{N}(0,1)\\\\\n", "Y &= \\text{exp}(X).\n", "\\end{align}\n", "\n", - "\n", "You may have recognized that this is by definition, $Y\\sim\\text{LogNormal}(0,1)$.\n", "\n", "We begin by importing the relevant libraries:" @@ -122,14 +114,12 @@ "source": [ "The class [ExpTransform](https://pytorch.org/docs/master/distributions.html#torch.distributions.transforms.ExpTransform) derives from [Transform](https://pytorch.org/docs/master/distributions.html#torch.distributions.transforms.Transform) and defines the forward, inverse, and log-absolute-derivative operations for this transform,\n", "\n", - "\n", "\\begin{align}\n", "g(x) &= \\text{exp(x)}\\\\\n", "g^{-1}(y) &= \\log(y)\\\\\n", - "\\log\\left(\\left|\\frac{dg}{dx}\\right|\\right) &= x.\n", + "\\log \\left|\\frac{dg}{dx}\\right| &= x.\n", "\\end{align}\n", "\n", - "\n", "In general, a transform class defines these three operations, from which it is sufficient to perform sampling and scoring.\n", "\n", "The class [TransformedDistribution](https://pytorch.org/docs/master/distributions.html#torch.distributions.transformed_distribution.TransformedDistribution) takes a base distribution of simple noise and a list of transforms, and encapsulates the distribution formed by applying these transformations in sequence. We use it as:" @@ -183,13 +173,11 @@ "source": [ "Our example uses a single transform. However, we can compose transforms to produce more expressive distributions. For instance, if we apply an affine transformation we can produce the general log-normal distribution,\n", "\n", - "\n", "\\begin{align}\n", "X &\\sim \\mathcal{N}(0,1)\\\\\n", "Y &= \\text{exp}(\\mu+\\sigma X).\n", "\\end{align}\n", "\n", - "\n", "or rather, $Y\\sim\\text{LogNormal}(\\mu,\\sigma^2)$. In Pyro this is accomplished, e.g. for $\\mu=3, \\sigma=0.5$, as follows:" ] }, @@ -282,13 +270,13 @@ "plt.show()\n", "\n", "plt.subplot(1, 2, 1)\n", - "sns.distplot(X[:,0], hist=False, kde=True, \n", + "sns.distplot(X[:,0], hist=False, kde=True,\n", " bins=None,\n", " hist_kws={'edgecolor':'black'},\n", " kde_kws={'linewidth': 2})\n", "plt.title(r'$p(x_1)$')\n", "plt.subplot(1, 2, 2)\n", - "sns.distplot(X[:,1], hist=False, kde=True, \n", + "sns.distplot(X[:,1], hist=False, kde=True,\n", " bins=None,\n", " hist_kws={'edgecolor':'black'},\n", " kde_kws={'linewidth': 2})\n", @@ -356,7 +344,7 @@ " loss.backward()\n", " optimizer.step()\n", " flow_dist.clear_cache()\n", - " \n", + "\n", " if step % 200 == 0:\n", " print('step: {}, loss: {}'.format(step, loss.item()))" ] @@ -407,24 +395,24 @@ "plt.show()\n", "\n", "plt.subplot(1, 2, 1)\n", - "sns.distplot(X[:,0], hist=False, kde=True, \n", + "sns.distplot(X[:,0], hist=False, kde=True,\n", " bins=None,\n", " hist_kws={'edgecolor':'black'},\n", " kde_kws={'linewidth': 2},\n", " label='data')\n", - "sns.distplot(X_flow[:,0], hist=False, kde=True, \n", + "sns.distplot(X_flow[:,0], hist=False, kde=True,\n", " bins=None, color='firebrick',\n", " hist_kws={'edgecolor':'black'},\n", " kde_kws={'linewidth': 2},\n", " label='flow')\n", "plt.title(r'$p(x_1)$')\n", "plt.subplot(1, 2, 2)\n", - "sns.distplot(X[:,1], hist=False, kde=True, \n", + "sns.distplot(X[:,1], hist=False, kde=True,\n", " bins=None,\n", " hist_kws={'edgecolor':'black'},\n", " kde_kws={'linewidth': 2},\n", " label='data')\n", - "sns.distplot(X_flow[:,1], hist=False, kde=True, \n", + "sns.distplot(X_flow[:,1], hist=False, kde=True,\n", " bins=None, color='firebrick',\n", " hist_kws={'edgecolor':'black'},\n", " kde_kws={'linewidth': 2},\n", @@ -452,36 +440,30 @@ "\n", "Sampling $Y$ is again trivial and involves evaluation of the forward pass of $g$. We can score $Y$ using the multivariate substitution rule of integral calculus,\n", "\n", - "\n", "\\begin{align}\n", "\\mathbb{E}_{p_X(\\cdot)}\\left[f(X)\\right] &= \\int_{\\text{supp}(X)}f(\\mathbf{x})p_X(\\mathbf{x})d\\mathbf{x}\\\\\n", - "&= \\int_{\\text{supp}(Y)}f(g^{-1}(\\mathbf{y}))p_X(g^{-1}(\\mathbf{y}))\\det\\left|\\frac{d\\mathbf{x}}{d\\mathbf{y}}\\right|d\\mathbf{y}\\\\\n", + "&= \\int_{\\text{supp}(Y)}f(g^{-1}(\\mathbf{y}))p_X(g^{-1}(\\mathbf{y}))\\left|\\det\\frac{d\\mathbf{x}}{d\\mathbf{y}}\\right|d\\mathbf{y}\\\\\n", "&= \\mathbb{E}_{p_Y(\\cdot)}\\left[f(g^{-1}(Y))\\right],\n", "\\end{align}\n", "\n", - "\n", - "where $d\\mathbf{x}/d\\mathbf{y}$ denotes the Jacobian matrix of $g^{-1}(\\mathbf{y})$. Equating the last two lines we get,\n", - "\n", + "where $\\det \\frac{d\\mathbf{x}}{d\\mathbf{y}}$ denotes the determinant of the Jacobian matrix of $g^{-1}(\\mathbf{y})$. Equating the last two lines we get,\n", "\n", "\\begin{align}\n", - "\\log(p_Y(y)) &= \\log(p_X(g^{-1}(y)))+\\log\\left(\\det\\left|\\frac{d\\mathbf{x}}{d\\mathbf{y}}\\right|\\right)\\\\\n", - "&= \\log(p_X(g^{-1}(y)))-\\log\\left(\\det\\left|\\frac{d\\mathbf{y}}{d\\mathbf{x}}\\right|\\right).\n", + "\\log p_Y(y) &= \\log p_X(g^{-1}(y)) + \\log\\left|\\det\\frac{d\\mathbf{x}}{d\\mathbf{y}}\\right|\\\\\n", + "&= \\log p_X(g^{-1}(y)) - \\log\\left|\\det\\frac{d\\mathbf{y}}{d\\mathbf{x}}\\right|.\n", "\\end{align}\n", "\n", "Inituitively, this equation says that the density of $Y$ is equal to the density at the corresponding point in $X$ plus a term that corrects for the warp in volume around an infinitesimally small volume around $Y$ caused by the transformation. For instance, in $2$-dimensions, the geometric interpretation of the absolute value of the determinant of a Jacobian is that it represents the area of a parallelogram with edges defined by the columns of the Jacobian. In $n$-dimensions, the geometric interpretation of the absolute value of the determinant Jacobian is that is represents the hyper-volume of a parallelepiped with $n$ edges defined by the columns of the Jacobian (see a calculus reference such as \\[7\\] for more details).\n", "\n", - "Similar to the univariate case, we can compose such bijective transformations to produce even more complex distributions. By an inductive argument, if we have $L$ transforms $g_{(0)}, g_{(1)},\\ldots,g_{(L-1)}$, then the log-density of the transformed variable $Y=(g_{(0)}\\circ g_{(1)}\\circ\\cdots\\circ g_{(L-1)})(X)$ is\n", - "\n", + "Similar to the univariate case, we can compose such bijective transformations to produce even more complex distributions. By an inductive argument, if we have a sequence of $L$ transforms $(g_1, g_2, \\ldots, g_L)$ such that $Y = g(X) = g_L \\circ \\cdots g_2 \\circ g_1(X)$, then the log-density of $Y$ is\n", "\n", "\\begin{align}\n", - "\\log(p_Y(y)) &= \\log\\left(p_X\\left(\\left(g_{(L-1)}^{-1}\\circ\\cdots\\circ g_{(0)}^{-1}\\right)\\left(y\\right)\\right)\\right)+\\sum^{L-1}_{l=0}\\log\\left(\\left|\\frac{dg^{-1}_{(l)}(y_{(l)})}{dy'}\\right|\\right),\n", - "%\\left( g^{(l)}(y^{(l)})\n", - "%\\right).\n", + "\\log p_Y(y) = \\log p_X(y_0) + \\sum^{L}_{l=1} \\log \\left| \\det \\frac{dg^{-1}_{l}(y_l)}{dy_{l}} \\right|\n", "\\end{align}\n", "\n", - "where we've defined $y_{(0)}=x$, $y_{(L-1)}=y$ for convenience of notation.\n", + "where $y_{l} = y$ and $y_{l-1} = g^{-1}_l(y_{l})$.\n", "\n", - "The main challenge is in designing parametrizable multivariate bijections that have closed form expressions for both $g$ and $g^{-1}$, a tractable Jacobian whose calculation scales with $O(D)$ rather than $O(D^3)$, and can express a flexible class of functions." + "The main challenge is in designing parametrizable multivariate bijections that have closed form expressions for both $g$ and $g^{-1}$, a tractable Jacobian determinant whose calculation scales with $O(D)$ rather than $O(D^3)$, and can express a flexible class of functions." ] }, { @@ -571,7 +553,7 @@ " loss.backward()\n", " optimizer.step()\n", " flow_dist.clear_cache()\n", - " \n", + "\n", " if step % 500 == 0:\n", " print('step: {}, loss: {}'.format(step, loss.item()))" ] @@ -613,24 +595,24 @@ "plt.show()\n", "\n", "plt.subplot(1, 2, 1)\n", - "sns.distplot(X[:,0], hist=False, kde=True, \n", + "sns.distplot(X[:,0], hist=False, kde=True,\n", " bins=None,\n", " hist_kws={'edgecolor':'black'},\n", " kde_kws={'linewidth': 2},\n", " label='data')\n", - "sns.distplot(X_flow[:,0], hist=False, kde=True, \n", + "sns.distplot(X_flow[:,0], hist=False, kde=True,\n", " bins=None, color='firebrick',\n", " hist_kws={'edgecolor':'black'},\n", " kde_kws={'linewidth': 2},\n", " label='flow')\n", "plt.title(r'$p(x_1)$')\n", "plt.subplot(1, 2, 2)\n", - "sns.distplot(X[:,1], hist=False, kde=True, \n", + "sns.distplot(X[:,1], hist=False, kde=True,\n", " bins=None,\n", " hist_kws={'edgecolor':'black'},\n", " kde_kws={'linewidth': 2},\n", " label='data')\n", - "sns.distplot(X_flow[:,1], hist=False, kde=True, \n", + "sns.distplot(X_flow[:,1], hist=False, kde=True,\n", " bins=None, color='firebrick',\n", " hist_kws={'edgecolor':'black'},\n", " kde_kws={'linewidth': 2},\n", @@ -799,7 +781,7 @@ " optimizer.step()\n", " dist_x1.clear_cache()\n", " dist_x2_given_x1.clear_cache()\n", - " \n", + "\n", " if step % 500 == 0:\n", " print('step: {}, loss: {}'.format(step, loss.item()))" ] @@ -845,24 +827,24 @@ "plt.show()\n", "\n", "plt.subplot(1, 2, 1)\n", - "sns.distplot(X[:,0], hist=False, kde=True, \n", + "sns.distplot(X[:,0], hist=False, kde=True,\n", " bins=None,\n", " hist_kws={'edgecolor':'black'},\n", " kde_kws={'linewidth': 2},\n", " label='data')\n", - "sns.distplot(X_flow[:,0], hist=False, kde=True, \n", + "sns.distplot(X_flow[:,0], hist=False, kde=True,\n", " bins=None, color='firebrick',\n", " hist_kws={'edgecolor':'black'},\n", " kde_kws={'linewidth': 2},\n", " label='flow')\n", "plt.title(r'$p(x_1)$')\n", "plt.subplot(1, 2, 2)\n", - "sns.distplot(X[:,1], hist=False, kde=True, \n", + "sns.distplot(X[:,1], hist=False, kde=True,\n", " bins=None,\n", " hist_kws={'edgecolor':'black'},\n", " kde_kws={'linewidth': 2},\n", " label='data')\n", - "sns.distplot(X_flow[:,1], hist=False, kde=True, \n", + "sns.distplot(X_flow[:,1], hist=False, kde=True,\n", " bins=None, color='firebrick',\n", " hist_kws={'edgecolor':'black'},\n", " kde_kws={'linewidth': 2},\n", @@ -897,13 +879,6 @@ "9. Laurent Dinh, Jascha Sohl-Dickstein, Samy Bengio. [*Density estimation using Real-NVP*](https://arxiv.org/abs/1605.08803). Conference paper at ICLR 2017.\n", "10. David Ha, Andrew Dai, Quoc V. Le. [*HyperNetworks*](https://arxiv.org/abs/1609.09106). Workshop contribution at ICLR 2017." ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] } ], "metadata": { diff --git a/tutorial/source/svi_flow_guide.ipynb b/tutorial/source/svi_flow_guide.ipynb new file mode 100644 index 0000000000..4b0fe1c89d --- /dev/null +++ b/tutorial/source/svi_flow_guide.ipynb @@ -0,0 +1,238 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# SVI with a Normalizing Flow guide\n", + "\n", + "Thanks to their expressiveness, normalizing flows (see [normalizing flow introduction](normalizing_flows_intro.ipynb)) are great guide candidates for stochastic variational inference (SVI). This notebook demonstrates how to perform amortized SVI with a normalizing flow as guide.\n", + "\n", + "> In this notebook we use [Zuko](https://zuko.readthedocs.io/) to implement normalizing flows, but similar results can be obtained with other PyTorch-based flow libraries." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [], + "source": [ + "import pyro\n", + "import torch\n", + "import zuko # pip install zuko\n", + "\n", + "from corner import corner, overplot_points # pip install corner\n", + "from pyro.contrib.zuko import ZukoToPyro\n", + "from pyro.optim import ClippedAdam\n", + "from pyro.infer import SVI, Trace_ELBO\n", + "from torch import Tensor" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Model\n", + "\n", + "We define a simple non-linear model $p(x | z)$ with a standard Gaussian prior $p(z)$ over the latent variables $z$." + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [], + "source": [ + "prior = pyro.distributions.Normal(torch.zeros(3), torch.ones(3)).to_event(1)\n", + "\n", + "def likelihood(z: Tensor):\n", + " mu = z[..., :2]\n", + " rho = z[..., 2].tanh() * 0.99\n", + "\n", + " cov = 1e-2 * torch.stack([\n", + " torch.ones_like(rho), rho,\n", + " rho, torch.ones_like(rho),\n", + " ], dim=-1).unflatten(-1, (2, 2))\n", + "\n", + " return pyro.distributions.MultivariateNormal(mu, cov)\n", + "\n", + "def model(x: Tensor):\n", + " with pyro.plate(\"data\", x.shape[1]):\n", + " z = pyro.sample(\"z\", prior)\n", + "\n", + " with pyro.plate(\"obs\", 5):\n", + " pyro.sample(\"x\", likelihood(z), obs=x)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We sample 64 reference latent variables and observations $(z^*, x^*)$. In practice, $z^*$ is unknown, and $x^*$ is your data." + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [], + "source": [ + "z_star = prior.sample((64,))\n", + "x_star = likelihood(z_star).sample((5,))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Guide\n", + "\n", + "We define the guide $q_\\phi(z | x)$ with a normalizing flow. We choose a conditional [neural spline flow](https://arxiv.org/abs/1906.04032) borrowed from the [Zuko](https://zuko.readthedocs.io/) library. Because Zuko distributions are very similar to Pyro distributions, a thin wrapper (`ZukoToPyro`) is sufficient to make Zuko and Pyro 100% compatible." + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [], + "source": [ + "flow = zuko.flows.NSF(features=3, context=10, transforms=1, hidden_features=(256, 256))\n", + "flow.transform = flow.transform.inv # inverse autoregressive flow (IAF) are fast to sample from\n", + "\n", + "def guide(x: Tensor):\n", + " pyro.module(\"flow\", flow)\n", + "\n", + " with pyro.plate(\"data\", x.shape[1]): # amortized\n", + " pyro.sample(\"z\", ZukoToPyro(flow(x.transpose(0, 1).flatten(-2))))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## SVI\n", + "\n", + "We train our guide with a standard stochastic variational inference (SVI) pipeline. We use 16 particles to reduce the variance of the ELBO and clip the norm of the gradients to make training more stable." + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "(0) 209195.08367919922\n", + "(256) -25.225540161132812\n", + "(512) -99.09033203125\n", + "(768) -102.66302490234375\n", + "(1024) -138.8058319091797\n", + "(1280) -92.15625\n", + "(1536) -136.78167724609375\n", + "(1792) -87.76119995117188\n", + "(2048) -116.21714782714844\n", + "(2304) -162.0266571044922\n", + "(2560) -91.13175964355469\n", + "(2816) -164.86270141601562\n", + "(3072) -98.17607116699219\n", + "(3328) -102.58432006835938\n", + "(3584) -151.61912536621094\n", + "(3840) -77.94436645507812\n", + "(4096) -121.82719421386719\n" + ] + } + ], + "source": [ + "pyro.clear_param_store()\n", + "\n", + "svi = SVI(model, guide, optim=ClippedAdam({\"lr\": 1e-3, \"clip_norm\": 10.0}), loss=Trace_ELBO(num_particles=16, vectorize_particles=True))\n", + "\n", + "for step in range(4096 + 1):\n", + " elbo = svi.step(x_star)\n", + "\n", + " if step % 256 == 0:\n", + " print(f'({step})', elbo)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Posterior predictive" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAdcAAAHXCAYAAADuhBDBAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjguMiwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8g+/7EAAAACXBIWXMAAA9hAAAPYQGoP6dpAACS40lEQVR4nO3dd3wT9f8H8FeStlld0LRAKWWPspcgGxEQHCyZUr8s2SJbBUUBgSKggoIMURBQEERw4QYEAVmFsgotUCildKSlM2naJp/fH/3dmaQZl/a638/HIw8ludx9cmnufZ/1/kgYYwyEEEIIEY20rAtACCGEVDYUXAkhhBCRUXAlhBBCREbBlRBCCBEZBVdCCCFEZBRcCSGEEJFRcCWEEEJE5lbWBRCTyWRCfHw8vLy8IJFIyro4hJQrjDFkZmYiMDAQUindVxNSkipVcI2Pj0edOnXKuhiElGsPHjxAUFBQWReDkEqtUgVXLy8vAAUXD29v7zIuDSHlS0ZGBurUqcP/TgghJadSBVeuKdjb25uCKyF2UJcJISWPOl4IIYQQkVFwJYQQQkRGwZUQQggRGQVXQgghRGQUXAkhhBCRUXAlhBBCREbBlRBCCBEZBVdCCCFEZBRcCSGEEJFRcCWEEEJERsGVEEIIERkFV0IIIURkFFwJIYQQkVFwJYQQQkRGwZUQQggRWaVaz5UUX2xsLLRardPtNBoNgoODS6FEhBBS8VBwJbzY2FiEhIRAp9M53ValUiEyMpICLCGE2EDBlfC0Wi10Oh327NmDkJAQu9tFRkYiNDQUWq2WgishhNhAwZUUEhISgvbt2zvdLjIy0uk21HxMCKmKKLgSl2k0GqhUKoSGhjrdlpqPCSFVEQVX4rLg4GBERkY6HfjENR+fPHnSYTMzQDVcQkjlQsGVFElwcLDTYEg1XEJIVUXBlZQYV2u4NECKEFJZUHAlJUpIDZcQQiobytBECCGEiIyCKyGEECIyCq6EEEKIyCi4EkIIISKj4EoIIYSIjIIrIYQQIjIKroQQQojIKLgSQgghIqPgSgghhIiMgishhBAiMgquhBBCiMgouBJCCCEio+BKCCGEiIyCKyGEECIyCq6EEEKIyCi4EkIIISKj4EoIIYSIjIIrIYQQIjIKroQQQojIKLgSQgghIqPgSgghhIiMgishhBAiMgquhBBCiMgouBJCCCEicyvrApDSERsbC61W63CbyMjIUioNIYRUbhRcq4DY2FiEhIRAp9M53ValUkGj0ZRCqQghpPKi4FoFaLVa6HQ67NmzByEhIQ631Wg0CA4OLqWSEUJI5UTBtQoJCQlB+/bty7oYhBBS6dGAJkIIIURkFFwJIYQQkVFwJYQQQkRGwZUQQggRGQVXQgghRGQ0WpiUG0KSWNBUIUJIRUDBlZQ5jUYDlUqF0NBQp9uqVCpERkZSgCWElGsUXEmZCw4ORmRkpKD0jKGhodBqtRRcCSHlGgVXUi4EBwdTwCSEVBo0oIkQQggRGQVXQgghRGQUXAkhhBCRUXAlhBBCREbBlRBCCBEZBVdCCCFEZBRcCSGEEJFRcCWEEEJERsGVEEIIERkFV0IIIURkFFwJIYQQkVFwJYQQQkRGifsruNjYWEGryRBCCCk9FFwrsNjYWISEhECn0zndVqVSQaPRlEKpCCGEUHCtwLRaLXQ6Hfbs2YOQkBCH22o0GlrSjRBCSgkF10ogJCQE7du3L+tiEEII+X80oIkQQggRGdVcSYUjZIAWNYMTQsoSBVdSYWg0GqhUKoSGhjrdVqVSITIykgIsIaRMUHAlFUZwcDAiIyMFTT0KDQ2FVqul4EoIKRMUXEmFEhwcTAGTEFLu0YAmQgghRGQUXAkhhBCRUXAlhBBCREbBlRBCCBEZBVdCCCFEZBRcCSGEEJFRcCWEEEJERsGVEEIIERkFV0IIIURkFFwJIYQQkVFwJYQQQkRGwZUQQggRGQVXQgghRGQUXAkhhBCRUXAlhBBCREbBlRBCCBEZBVdCCCFEZBRcCSGEEJFRcCWEEEJERsGVEEIIERkFV0IIIURkFFwJIYQQkVFwJYQQQkRGwZUQQggRGQVXQgghRGQUXAkhhBCRUXAlhBBCREbBlRBCCBEZBVdCCCFEZG5lXQBCSkpkZKTTbTQaDYKDg0uhNISQqoSCK6l0NBoNVCoVQkNDnW6rUqkQGRlJAZYQIioKrqTSCQ4ORmRkJLRarcPtIiMjERoaCq1WS8GVECIqCq7lVGxsrKDgQGwLDg6mgEkIKTMUXMuh2NhYhISEQKfTOd1WpVJBo9GUQqkIIYQIRcG1HNJqtdDpdNizZw9CQkIcbksDcgghpPyh4FqOhYSEoH379mVdDEIIIS6iea6EEEKIyCi4EkIIISKj4EoIIYSIjIIrIYQQIjIKroQQQojIKLgSQgghIqPgSgghhIiMgishhBAiMgquhBBCiMgouBJCCCEio+BKCCGEiIyCKyGEECIyCq6EEEKIyCi4EkIIISKj4EoIIYSIjIIrIYQQIjIKroQQQojIKLgSQgghIqPgSgghhIiMgishhBAiMgquhBBCiMgouBJCCCEio+BKCCGEiIyCKyGEECIyCq6EEEKIyCi4EkIIISKj4EoIIYSIjIIrIYQQIjIKroQQQojIKLgSQgghInMr6wIQUtYiIyOdbqPRaBAcHFwKpSGEVAYUXEmVpdFooFKpEBoa6nRblUqFyMhICrCEEEEouJIqKzg4GJGRkdBqtQ63i4yMRGhoKLRaLQVXQoggFFxJlRYcHEwBkxAiOgqupSw2NlZQTYkQQkjFRcG1FMXGxiIkJAQ6nc7ptiqVChqNphRKRQghRGwUXEuRVquFTqfDnj17EBIS4nBbGp1KCCEVFwXXMhASEoL27duXdTEIIYSUEEoiQQghhIiMgishhBAiMgquhBBCiMgouBJCCCEio+BKCCGEiIyCKyGEECIyCq6EEEKIyCi4EkIIISKj4EoIIYSIjIIrIYQQIjJKf0iIQEJWK6Kc0IQQgIIrIU5pNBqoVCqEhoY63ValUiEyMpICLCFVHAVXQpwIDg5GZGSkoHV4Q0NDodVqKbgSUsVRcBUJLYJeuQUHB1PAJIQIRsFVBLQIOiGEEHMUXEVAi6ATQggxR8FVRLQIOiGEEIDmuRJCCCGio+BKCCGEiIyCKyGEECIyCq6EEEKIyCi4EkIIISKj4EoIIYSIjIIrIYQQIjIKroQQQojIKLgSQgghIqPgSgghhIiMgishhBAiMsotTIjIhCwtSAs4EFK5UXB1gtZpJUJpNBqoVCqEhoY63ValUiEyMpICLCGVFAVXB2idVuKK4OBgREZGCroZCw0NhVarpeBKSCVVZYOr0BoprdNKXBEcHEx/B4SQqhlcXa2R9ujRgy6YhBBCBKuSwVWr1VKNlBBCSImpksGVExISgvbt25d1MQghhFQyNM+VEEIIEVmVrrkSUpbEnMJF3ReElC+VMrhevnwZnp6edl+neamkLLkyH1YomjdLSPlSKYNrr169nG5D81JJWRE6H1YomjdLSPlTKYPrtm3b0KFDB4fbUDMaKUs0H5aQyq1SBVfGGAAgKCgIjRo1crp9RkZGSReJkBKXlZUFALh48SL//7ZkZ2cD+O93QggpORJWiX5pcXFxqFOnTlkXg5By7cGDBwgKCirrYhBSqVWq4GoymRAfHw8vLy9IJJJi7SsjIwN16tTBgwcP4O3tLVIJxUflFFdFKSfgelkZY8jMzERgYCCkUpqFR0hJqlTNwlKpVPQ7cm9v73J/kQWonGKrKOUEXCurj49PCZeGEAJQEglCCCFEdBRcCSGEEJFRcLVDLpfj3XffhVwuL+uiOETlFFdFKSdQscpKSFVTqQY0EUIIIeUB1VwJIYQQkVFwJYQQQkRWqabiiDnPlZDKRug8V/odEWKf0N9RpQqu8fHxlKGJECecZWii3xEhzjn7HVWq4Orl5QUATjPWuDKGS8w7d7GPW1afQyjz8ul0Ouh0OqhUKqhUqkLbUg2p5HEZnbjfiT1Cf0eEVEVCf0eVKrhyF2hnGWvKKihlZ2cjOzsbarXaZoBx9bgVKbg6u0hTcC09zs610N8RIVWZs98RDWgqRdnZ2cjPz+dXJyGEEFI5UXAtRWq1Gm5ublCr1WVdFEIIISWoUjULl3f2+hsJIYRULlRzrcR0Oh2Sk5Oh0+nKuiiEEFKlUHCtxKiPlxBCygYFVxsqS42P+ngJIaRsUJ+rDeY1voocmKiPlxBCygYFVxvUarVLgVXofNPyPi9VKFc+b3lOwkEIISWlSgZXZxdetVpdYjVWMS/65TGA6HQ6i0QZ5bGMhBBS0qjPlYiKBlERQggFVyIyGkRFCCFVtFmYlBwaREUIIVRzLXe4aUDUrEoIIRUXBddyhvosHassc5AJIZUbBddyhvosHaObD0JIRUB9riXAejqKK7g+S5rCYpurc5AJqQxiY2Oh1WpF259Go0FwcLBo+yOFUXAtAea1KxrcIy4aMEWqmtjYWISEhIjaFaJSqRAZGUkBtgRVyuDKGHOYzceVzEFCtrM+lnntyvo1V7IMCTmu0PKJvZ1er7eoQXL/r1QqHb7XVq1e7PIJRRmfSEWg1Wqh0+mwZ88ehISEFHt/kZGRCA0NhVarpeBagiplcC1t1hdee7UrV4KII9nZ2dDpdFCpVC43jxanydq6DOZ9n7Zq6rY+h7NavaPySSQSCnKkygoJCUH79u3LuhhEIAquEC/gmO8HgKB9Wh+bGw0LFPSL2AqeOp0O+fn50Ol0LgVXnU6H+/fvQy6XA0CxPqt136fQflBnfabUpE4IqQwouEK8C7qQ2pyzY2dnZyMzMxMA7NZMuSBsvV9nNwnZ2dmQy+UwGAyoWbNmkT8nVwbzYwg9b876TGnAEiGkMqgSwdVZ0FGr1UhKSkJaWhqys7MREBBQpCBblNqc9XvUajW8vLwAFA5Y5s3B/v7+fLMr9/m44Gkd0Ln+UQDw8vJCzZo1nX4+sWrzrqIBS4SQyqBKBFdnNVOuhpiVlYWsrKwiB5Si1OZsvSc4ONhm36K95mDu8wGwOUeWe93NzQ3+/v6CPouQ2nxZBWBCCCnvqkQSCSGJGdRqNTw9PeHp6VlumyRVKhXc3NwKBTLu8wUEBMDf399moEtLS3PpWELOGSV0IIQQ26pEzVVIU6NKpUK9evVKp0BFZG+dWSGfz9fX16VjCdlnZewfpdo4IUQMVaLmWpaKmgs3OzvbbgJ/7jWh+1Sr1TAYDHyfrVi4vl+hQagi5AWm2jghRAwUXEtYUS/W5v2r9l7TarWCgpVSqYRareYHO5WVihC4KLczIUQMlbJZWEiGJqH7EZK0wGg0Qiq1fZ+SnZ2NR48eoVatWvDx8bG7nbn8/HwoFAro9XooFAp+sBKHew0A8vLykJWVxc9dtVc+AEhPT4efnx9MJpPN7RhjNsun0+n4UcoqlQomkwk5OTn8c/ayMplMJshkMv7f5s3I5s2vCoUCACyyPtnbZ0knkaDRyqQ0CckZHBkZWUqlIWKqlMFVSCYfMS/Sjo6n1+uhUqmg1+shkUig1+st5qjaClASicTmRV6n00Gv10OpVMLPzw9ZWVl4/PgxcnNzHQYFLmD6+Pjw+3f0WaxZj1KWSCQWzzk6rvn+zMuYnJzM12KVSiUkEkmpJ5CgbE+kLLmSM1ilUkGj0ZRCqYhYyjy4ip0vtrzRaDTQarXQaDTQ6/V4+PAhPDw8+NeNRiN0Op3DnLxcUNXpdPDw8OADtlKphMFgcBrkgMKJJ6xro47YSlphL5GFULYGQ1XGAVKE2ONKzmBaxabiKZPgeuHCBXz88cfYtWtXiQfW0hz9aStgaTQa/o7z1q1bePjwIT8Q6NSpU/j555/h4+OD4OBg1KxZE4GBgQgICECtWrX4JlW9Xm8xj1WpVPLHkkgkNqfnmJdFoVAUCqJCap4cW6OUi9t8av5+rpmemmRJZeFKcy/lDK6cSj24RkREoFevXhg/fnyx92UwGGAwGPh/Z2RkFNqmJJoa7dX6nAWsnJwcuLu74/fff8eECRMQHR1t9xgymYyft8oF3TFjxqBPnz4AgJSUFBiNRuTm5vLH1mg0FjVTrixcn6a54tY8rdk6J1xtm6a1kKqEmnsJUMrBNSIiAl27dsWMGTOwdu1am9uYTCZBg34AICwsDMuWLXO4TXGaGnU6HX/3yQUunU6H2NjYQsnvuRoyAHh7e/PPmQccX19fbNmyBdu2bQMAeHp6YtiwYfDw8EB8fDwePXqEhIQEJCYmwmg04tGjR3j06BGuXLkCANi5cyeeeuopzJ07F506dUJubi5SU1NhMpmgVCotApuz4Mlty31GRwOTONxUHm70sfln5JqsrZuei3tjQ/NOSUVDzb0EKMXg+ujRI3Tt2hUjR47E2rVrYTAYsGTJEkRFRSElJQWDBw/G6NGjERQUJLgfdtGiRZg3bx7/74yMDNSpU8dim+I0Nep0Oosk+lww4pLf16hRw2JbuVxu0UTLBRdulZsPPviAD6xvvPEGXn/9dT6PsDmDwYDU1FQ+uMbHx+PcuXP46quvcOzYMRw7doy/SWnTpg0MBkOh4GP+ua1HB5sHffMarrPgqtVqkZmZCS8vL4vgat5kbatftjh9qLRKDqmoqLm3aiu1ea7x8fF44okncOHCBdy+fRvDhg3DmTNnUL9+fdSuXRv79+/H3LlzkZiYKLgfVi6Xw9vb2+LhKkeJDVQqFby8vODl5WVRI/Ty8kJwcHChQGIruLi5Fdy/rF+/Hps2bQIALFu2DO+9957NwAoUBKnAwEB06NABzz//PKZMmYLt27cjMjISkyZNgpubG06fPo3Q0FBMnToVqamp/N0vd9fs7DNz82S1Wi0ePXpkd1tHySzMPyPXt2z++ZVKJZ/LuKjJI2jeKSGkIiq14NqhQwd88MEHCAwMRJMmTQAAhw8fxkcffYR9+/Zh6tSpuHz5MsLDw0U7ppCMQI4SG3BJ9M0DKddHYl47TU1NBYBCwYXbdufOnfj4448BFATWRYsWFenz1KtXD1u2bCkUZPv3749+/frh+PHjyMzMxIMHDxx+ZvOgbzKZ7M6RvXnzJpYsWYJLly7xfbo1atSAn59foc9oa0BVSkoK36xb1OQRrmaBIoSQ8qBEgys32CYnJwdAQYBdtmwZFixYgPnz58PPz49PcDBp0iSkp6fj33//Fe34Qi7qjmpGXH+ko0BlK5OS+fvCwsKwfPlyAMULrOa4IHvt2jW88sorcHNzw7FjxzBo0CCEhobi+PHjiIqKsltuLiBqNBp+sQKgYJBUcnIy7t27h3nz5qFNmzZYv349Bg4ciLfffhu5ublOAx3XJx0REYHY2FgkJSUVq/ZZEVImEkKItRLrc7158ybef/993LlzBy1atMDYsWPRvXt3PPnkkwgMDEStWrUAFIyKNRqNSE1NRZMmTdC6detiH5vL0GQ+mMk8K5P5/yuVSr6v0Tpzk06nQ1ZWFlJSUhAUFGQzOCiVSn4d1by8PABAVlYWjEYjVq5cidWrVwMAli5ditmzZ/M3HI5w/bfOaDQafPLJJ1iwYAHWrFmDXbt24ezZszh79iw6d+6MRYsW4ZlnnkF+fr7F3FqO0WiEUqmEQqFATk4O0tLS8M033+DTTz9FYmIiAKB58+a4ceMGPvvsM3z33XdYvnw5xowZY3Eu9Ho9cnJyoFAo8PjxY8THxyMzM5OfSuToHAOOB7GZ3yApFAqL7ewNdnJl7rSQ7YRm9BK6P0JI5VciNderV6+ia9eukMvlaN++PW7evIl9+/bxwSc4OBju7u789jKZDJs2bUJSUhI6duxY7ONzGZPMmxS558wfer0eWq2Wz55k/VCpVMjJyeETN9jaxtPTE35+flCpVJBKpZBKpVCpVNi4cSMfWJctW4bFixfDzc1N8IPbl6OHm5sbZDIZGjRogC1btuDGjRt8c/HZs2cxZMgQvPDCC/jnn3/w+PFj5OTk8O/NycnBw4cPkZmZiZycHMTGxmLcuHF49913kZiYiHr16mH//v04e/Ysjhw5gmbNmiElJQUzZ85E37598e+///L7Sk1NRVJSElJTU/mA5+7ujho1aiAgIMDmeTN/cJmcbD3Ma73Wgau0chVT7ZkQ4irRg+u9e/cwZMgQzJgxA1u2bMH69evxzDPPIC0tDSaTic+Jy/nll18wa9YsrF+/Hvv370fdunXFLpJdji7O5iNqzefSOqLX65GSkoKvvvrKIrCK0RQsRL169bB582aLIPvnn39i4MCBGDp0KH777Te+LzQlJQUeHh54/PgxwsLC0LVrV5w9exYKhQKLFy/GuXPnMHDgQABAjx49cPr0aaxatQqenp4IDw9H7969MWvWLDx+/Nji8+v1evj6+qJBgwYICgoqdl+poz7X0hrsVBEWHCCElC+iBlfGGC5evIj+/ftj1qxZ/PNarRbXrl1Dhw4d8OKLL+KLL77gX7t27RpiY2Pxzz//oG3btmIWxylHF2fugqrT6QSvharT6fD48WMsWbIEQMFUodIKrOZsBdkzZ85g5MiRGDRoEM6cOQPGGI4ePYohQ4Zgw4YNyM/Px4ABA3Du3DksWrSo0LQcd3d3zJo1C+Hh4RgxYgQYY9iyZQuaN2+OI0eOQKPRQKlU8osT6PV6xMXF8TcpFbnmRyOWCSGukjBXOpQESEpKwuPHj9G0aVMAwHvvvYf3338fYWFh8PDwwJ07d7B7924cOHAA3bt3B1AwP7Uo02isZWRkwMfHB2lpaQ73J6RPzjwpBFBQg7J3cc3IyOBr5O+99x4+/fRTNG3aFOHh4RbN37m5ufwoXWfHNn+fPUajUVDN8O7du1i7di2+/PJLfk5qUFAQ4uLiAAD169fHRx99hN69e1usYmOPyWTC+fPn8dprr+HGjRsAgPbt22PNmjVo2bIlXzNWKpX8FKH8/Hy4ubnxU3PMmX8fjpJGWH9vXPJ/6/1Sn6tt3O8jPT3d4e9D6HbEtvDwcHTo0AEXL14sl/Ncy3v5yjuhvw/Rm4UDAgL4wAoU1FoPHDiAWbNmYerUqZg4cSIkEgnu37/Pb1PWP2BbNStuRK2/vz/8/f0LBVbz+Z96vZ4f9XzixAkAwGuvvSYoQJaGunXrFprCExcXB4VCgXfeeQcRERF47rnnXNpnr169cOHCBaxZs4ZvKn7mmWcQFhbGNxVLpVJ+UNndu3cFrT/rShMs1SgJIeVViU3F4e72N2zYgIEDB/JZgry9vVG3bl3UrFmzpA7tcjNkUfrUrDMbyWQyqFQq9OzZE0BBX3J5Yz5Pdt26dYiIiMCSJUucZmayx93dHXPnzsX169cxfPhwGI1GbNiwAWPGjEFUVBQaNWrEL7enVquRmprq0tQoZ9+jkDmwFb1JmhBSMZVYcOWax7ggy02h2LRpE7Kzs9G8efOSOrRFTlshilIDMs/IpFKp4OfnB6VSienTpwMAfv75Z9y9e7dI5S9p9erVw+zZs9GgQQNR9hcYGIi9e/fi0KFDCAoKwoMHD/DKK6/g1VdfRUpKCjQaDeRyOWrXru3wPFs3CYsxkIgGIxFCykKRgyvXDOoMF2SjoqKwYMECbN68Gbt37+bnuZYELvAJDZauZAHimoMB2Gwubtq0Kfr37w/GGD777DPXC1+BPf/884iIiMCMGTMgkUiwe/duNG/eHH/88QeaNWuGevXqOTzP1oFQyE2Ps5opNR0TQspCkYJrVFQU1q9f7zAnrfkgkBs3bmDz5s04ffo0jh07hjZt2hTlsII5C5Z6vb7QBVlo86F5czAXaM3fo9frMWLECADAt99+69JgGHsSExOxcuVKnDlzptj7Kmne3t7YsGED/v77bzRv3hzJyckIDQ3Fc889h3v37jl8r3UgFHLT46xmSukTCSFlweUMTbdv30aXLl3w+PFjpKSkYN68eYXWI7Qerdm8eXNMmDABixYtQkBAQPFL7QSXocke66w/er0eDx484LMYMcYKDW6ytZSbVqtFVlYWPD09+TVTs7Oz0bt3b6jVaty/fx/nz5/nE2Pk5OQIGi386NEjviyZmZkYM2YMbt26hdWrV6Nbt26YNWsWOnToAIPBYHP0rbX09HS7iwSYS0tL41MhOpKbm+t0Dco2bdrgzz//xNatWxEWFoZff/0VLVu2xDvvvINXX32VH5Wck5MDvV4Pb29vp5mc9Ho932zMbWcrC5fYo3tFHlAv+LiEkIrLpZprdnY2wsLCMGjQIHzyySdYvXo11qxZw695yuEuHGvXruXXW23dunWpBFbu+I4e5v2lEomEX4vUYDBYLMOWkpLC11LNMwZxzcFcEGeMwc3NDbm5ucjJyYFMJsMzzzwDAPjuu+/4rEtyuRzu7u6CHh4eHpBKpZg7dy5u3boFb29vuLm54dSpUxg9ejQmTpyIy5cvC8rkJOScSCQSuLm5QaFQOH1wZXP2UCgUWLRoES5cuIDu3btDp9PhzTffxIQJE/gbML1ez9/oOCuf+U2RRCKBVqvlR53by8Ll7CHG35Mr+yKEVA0uBVepVIoOHTpgwIABmDlzJvbt24d169bZDLCpqam4ePEifvnlF6SkpIhaaFdZN/lar+RivYycSqXicwBza6XaEhAQgBo1avC1OJ1Ox2ehGjp0KADg4MGDRar5MMbw7rvv4u+//4ZCocBXX32F48ePY8yYMXBzc8PJkycRGhqKESNG4OzZsy7vvzQ1adIEv//+OzZu3Ah3d3ccOHAA8+fP5xNM5ObmCuoTtW421mq1MBgMhf72CCGkrLnULKxUKjFu3Dj+4jZy5EgwxjBmzBgwxvDmm2/yK91IpVJ8+umnMBgMFkuUlQWuxsOt0GK9gLq9f3t4eBRao9Uctx03uEulUiElJQVyuRzdunWzaBru1KmTS2X+/PPPsWfPHkgkEmzYsIFf0GD16tWYOXMmNm3ahAMHDuD48eM4fvw4evfujQULFqBz584W+8nIyMCtW7eQlpaGuLg4PHz4EA8fPkRycjL8/PxQu3Zt1K5dG0FBQfD19UVISAh8fX1Fr4lJpVK88sor8Pb2xrhx47B582ao1Wq89tprhRaYT0pKAlBw82K9Pqz5vzUaDbRardMmakIIKW0u97lygZULoKNGjQJjDC+99BIkEgnmzJmDtWvX4t69e9i3bx+qV68ueqFdxfXLGQwGvpnX2QAXrnnY1ek5derU4ff/3HPPYf/+/fj2229dCq7Hjh3DihUrAACLFy/GgAEDLF6vU6cOVq9ejYkTJ2LXrl3Yu3cvH2S7dOkCtVqNhw8fIi4uDpmZmYKPa/45goKCULt2bdStWxdDhgxB165dRQm4I0eOREpKCubOnYt169ZBoVBgxowZ/OvZ2dnIysoCAJtZmsxxCT7KC/M+YRpARUjVVuQl52QyGRhjMJlMGD16NCQSCV5++WX88MMPuHPnDs6dOydo2bTSwNUwuSAr5MKnUqmgVCoFLXFm61g6nQ5PP/009u/fj+PHj7tU3j179oAxhgEDBmDy5Ml2t6tduzY+/PBDzJkzB+vXr8fevXttjij29fVFnTp1+Fpq7dq14e/vj9TUVL4m++DBA8TFxfEr20RFRSEqKgoAsHPnTrRr1w6vvvqqy5mcbJk+fTqSk5OxatUqrFq1CiEhIRgzZgyAgoDKDaoyv7ERcu7LmnmfcHktIyGkdBRrPVfzRBGjRo3Ctm3bcPnyZYSHh6NVq1aiFFBMXOAr6uhPVy6eOp0OgYGBAOByn3OvXr1w8uRJ3LlzR9D2wcHBfJD96aef4OXlhaCgIAQFBSEwMBC5ubmCUkxmZmZCLpcjPj4ecXFxiIuLQ3h4OPbv349Lly5h0qRJqF+/PqZMmYJJkyYVObMTACxZsgRxcXHYtWsXpk+fjrZt2yIkJAQqlQr16tUrtL0r576sapDmI5cJIVVbsTM0SSQSmEwmzJs3D8eOHcOxY8fKZWAVgysJCVQqFapVqwagYIqLK4YNGwa1Wo3o6GicOnVK8PuCg4MxY8YMvPzyy3jqqafQuHFjly/0SqUSDRs2RK9evTB27Fh88MEHCA8Px/z58+Hr64uYmBgsWrQILVq0wNq1a5GamurS/jkSiQSffPIJunbtioyMDAwaNMjhTYj1uXc0L7mssjIplUqaU0sIASBi+sMWLVogPDycH3hTHghJDKHT6aDVagXlnnWUkMB8P9yDy0KVkZHB51YWwtPTE8OHDwdQ0CRb1vz9/fHmm2/i0qVLWLlyJWrXro3k5GQsX74czZs3x5tvvokHDx64vF+5XI5vvvkGdevWxZ07dzBixAh+lLY16xHejgIoZWUihJQ1UYKrTCbDxIkTS309VmeE1GB0Oh0yMzMRGxtbrOTuOp0ORqMRWq0WDx48QFZWFp8wgjGGjIwMl/Y3btw4AMCff/6J2NjYIpdLTJ6enpgyZQpOnjyJzz//HK1atUJ2djY2bdqE1q1bY+XKlYLTYnL8/f1x8OBBeHp64vjx43j11VftNtubZ9ZyFEBLsgZJCwEQQoQoVp+rufI0iZ5L7GArew9QsB4pN1CJy7Qkl8sLjSIWWtvMz8+HQqFAamoq0tLS4OHhwc/dVCgUyMnJQUpKCqpXry4oQ1NGRgYCAgLQrVs3nDp1Ctu3b8fChQsLbXf37l2HKSg5Dx8+FJR5KS8vD3Xq1HG6XU5ODnr06IHu3bvj1KlT2LZtG/7991+sXr0aJ0+exAcffIDq1asjNzcXNWrUcLq/oKAg7N69G8OGDcP27dtRs2ZNTJgwgV8MgZOZmQmTyYTs7Gz4+fnx2bWSk5P5AWhAwfdvPhDNESH97+YZx8xv2Gz1OQvtzy9PvxdCiPhKbFWcsmSeiYmrwZhn0pFKpRYZl+rWrQsvLy+o1Wq72zl6yGQySKVSPttQQkICGGPQ6/Xw8fEBUBAwVSqVoAxItWrVQo0aNTBz5kwAwKFDh6BWq1GjRg2LB3dcZ4/Hjx9bZJOy98jOzoaHh4fTBwA+i9RTTz2FvXv34qOPPoJSqcSpU6cwdOhQXL16FTKZjM9O5ezx3HPPYfXq1QCAVatW4bfffoNer7c4zwqFgr85SUlJgV6vt8j1zG2n1+v5JnqxMiqZ/71wNWZb+6NsToQQoJIGV1fZ6kvV6XRISUnhm/+c9c3qdDrI5XJ+ZC5X6+WCq6uDmgDg6aefRv369ZGRkYFvv/3W5feXpiFDhuDQoUNo0KABHj16hFGjRmH37t0ujcyeM2cOxo0bB5PJhDfffBPh4eE2t+Nqj1xLg3WiD+4mpySabu31u7vSd08IqfwouNphfgHX6XSIjY1FZmamw4W7PT090ahRI2g0GqjVamg0Gn7EcHp6ustlkEqlmDRpEgDgs88+EyWBvMlkQmxsLM6cOVOkgO9I06ZN8f333+PZZ59Ffn4+Vq1ahUmTJglOZCGRSLBx40Z0794dGRkZeO655/DMM8/g9OnTAAqao/Pz8wHAYi1d84FOHEefjVvNyFZffFGDpHlzMfXLkoogMjIS4eHhDh/lZbxHRSRan2tlY55wgssZnJCQYNFX6yiFIofr63z8+HGRyjFmzBiEhYUhKioKJ06cQK9evVzeB2MMZ86cwd9//42bN2/ywc7NzQ09e/bE+PHjRRtZ6+npiY0bN2Lnzp1YtWoVDh06hOvXr2Pr1q2CBrzJ5XIcPHgQixcvxpdffslP7+rbty+WLVuGRo0awdvbm/9etFqtzXPv6+trc//Z2dmIjY3lE5yo1Wr+Bsp80QYhWbzMmffvUzIJUp5xN6OhoaFOt1WpVIiMjERwcHAplKxyoZqrHSqVCn5+fvyFWyqVombNmi43ORan5goUrI/K5Qs+f/68y++PjY3Fp59+inXr1uH8+fPIzMyEh4cHAgMDkZ+fj6NHj2LdunUuj/J1RCKRYMKECdi1axcCAwMRFRWFPn36YM6cOYKS7FerVg2bN2/GjRs3MGnSJLi5ueHPP/9Enz598OWXX/IDicwDoTmlUmk3JzTXfG8wGOzux7zWa909IARNBSLlWXBwMCIjI3Hx4kWHjz179vA3sMR1VbrmKjSlnkqlQnBwMHQ6Hb8uqnUfn16vt9ie27+7uzsA8M2ZroqLi8OJEycAAH379hX8Pr1ej927d+PAgQPIz8+Hh4cHnn/+eTzxxBOoX78+3N3dcfXqVYSFheHKlSv4/OuD6NT3edTWGeGnkhWprNbatWuH48eP46233sKBAwfw5Zdf4vDhw/jwww8xbNgwp++vV68eNm/ejDfeeAOzZ8/GL7/8gtdffx1Hjx7FF198AS8vL5s1TK6JHgDfTMulv+S2rVGjhsX70tLS+AUAzGu9XOB1Vgs1r61SIglS3gUHB1NttIRV6ZqrK5l8uL497gGA75vT6/UwGo1ISUmB0WjkmxnNa4NFrRlu3rwZ+fn56NGjh6BmVcYYjh8/jnHjxmHv3r3Iz89Hy5Yt8dFHH2Hs2LFo0qQJH/BbtWqFWbNmwbN1P0Q2HotdD6ph6s/J+DNGvL5Cf39/bNu2DUeOHEGrVq2Qnp6OSZMmYcWKFYKnOtWrVw+HDx/GRx99BLlcjl9//RVt2rTB8ePHbfa3mrOulZqvx2uOC6jWA6S4fzurhVJtlRBirkoHV1cuiNYDXZKTk5GUlITk5GQolUrIZDL4+flBJpPxTckymYzv2ytKzTU1NRV79uwBALz22mtOt09LS8PChQuxbNkyJCcno1atWli1ahUmT56MmjVr2nxPSIcu8BswC5L/70tmALZezECKTrxmYgDo0qULjh07hlmzZgEAPvjgA/zvf//jV8BxRiKRYPr06Th37hxatWqF5ORkvPDCC5gyZQpOnz4NrVZrc8CXrdHEjrYxHyBl3hcrZBUlZ4GeEFJ1VKlmYetmYCEXTfP32hvoolQqbQZobl4rULSa6+effw6dTodWrVoJGsi0fft2XLx4ER4eHnjppZcwevRoyOVyXLlyxe57knMkgMTyHsvEgEdZ+aI1D3NkMhmfMnH27Nn4+eef8cwzz2D37t2C81G3bNkS//77LxYtWoSPP/4Yn3/+OT7//HMABd9DcHAw6tSpwzd7cY+GDRvavYmy93dg/p1TjZQQ4opKGVy5pAjWnGXXsWaeyUmpVEKv10OpVIIxxk+3AQpqmEql0uY+uffn5eVBr9dDJnMesJKSkpCXl4etW7cCKEiFyC0gbi4qKopPSKDT6fD7778DAF555RU0adIEN2/eBADcuXMHd+/etXksPeSAvAdgntjAZETq/Vu4obX958Elb3AmNzfX5nadOnXCtm3bMH/+fNy4cQNPP/00vvjiC3Tt2tXh/oxGI6pVqwaJRILVq1fjqaeewpo1a3D37l0kJiZCr9fj1q1buHXrls33z5gxA2+99RaUSiUUCgUYY/zNjy1c7dXZSkqursLjypQqSjhBSMVUKYOrvSw45tMlhFy0ZDIZv52np6dFCkHu38nJyTAajcjJybGZYpDr3zSZTJDL5YLS8qlUKnz11VdIT0/nFyu3FZTd3d35AVbnzp1DXl4egoKC0KlTJ4vPp9Vq7TYLq5GH9sZIXJKFgEkkYCYjUn/biO+r5WLBggU2z1NcXJzDoMTR6XR20z22a9cOe/fuxZw5c3Djxg2MHDkS77//Pv73v//Z3Z91WsOBAwdi4MCBAACDwYC4uDg8ePAAMTExSEhIwIMHD/jHzZs38emnn6J27doYP348lEql02xJarWaT53paDvzmzaq4RJCgEoaXO0p7nqu9vbpaE4kF1xcaRbOy8vD9u3bAQBTpkxxWts1mUz466+/ABSMKHa1tlPP9AgBplToZGrU8VXg/etH8bfRiHr16mHEiBEu7csVNWrUwBdffIElS5bgjz/+4Guy7733Hn9TIpRcLkfDhg3RsGFDdOnShU/TyFm9ejWWLVuGd955B82aNcOzzz5rd1+uLsxO67hWHbGxsU6npkRGRpZSaUh5VqWCqyOOLqjW0znMqdVqvhZkCxcYXRnQ9PPPPyM+Ph4ajQYvvvii0+2vX7+OxMREKJVKdOnSRfBxzKlggCfLQ9e2T2Hq1Kn49NNPsWvXLtStWxedOnUq0j6FUCqVWLFiBTp27IiwsDB8/vnniIqKwueff87PERbDG2+8gaioKOzduxcTJ07E8ePH0axZM5vbupoEwpW+e1JxxcbGIiQkRPDylNysAlI1lUlwzczMRGZmJry8vKBQKODu7m7RvymUwWCAwWDg/+3qsm7mAdXRBVWr1SIpKQkymQxNmzZ1qYbC1VyFBleTycTXWidOnMiPNnbk6NGjAIDu3bsLaq515tlnn8Xdu3fx66+/Yu3atRg2bBj8/f35aUjm51wMEokE8+bNQ7NmzTB9+nScPHkS/fv3x969e9GoUSPRjrF582bcu3cPZ86cwZAhQ/DTTz8hICAAACxuqqgmSmzhZgvs2bMHISEhDrfVaDQ0j7SKK/XgevXqVUyYMAHZ2dkwGo3o1asXFixYgKZNm8JoNAoa8MMJCwvDsmXLilwW676ypKQkGAwGm7VXbjCTqyNHuWAnNPDfv38f0dHRcHNzw9ixYwW95+rVqwCAHj16CC6XM1OnTkVcXByuXbuGr776qtDrXl5e0Gg08Pf3h7+/PwICAtC4cWM0b95c0PJ2tjz77LM4cuQIXn75Zdy7dw+DBw/G4cOH0bhx4+J+HAD/Lc7eq1cvxMTEYP369Zg2bRoCAwMtbqqoJkocCQkJQfv27cu6GKScK9XgGhsbi6effhpjxozB0KFDcfr0aRw7dgxPP/00fvzxR7Rr186lALto0SLMmzeP/3dGRoag9Ug55jUUrsnXVu3VvHnHWcKC5ORki337+/sDACIiIgSXCSio6Qq9wKtUqiKnV7TH3d0dS5YswS+//IL4+HgkJycjJSUFycnJ0Ov1fOtDTEyMxfskEgkaNGiAFi1aoF69evD29katWrUEH7dFixb47bffMGLECFy/fh1DhgwRNcD6+/tj5cqVeOmll/D9999j1qxZLid/cLVPlhBS9ZRqcD1//jyaNGmC1atXQ6lUonfv3nj22WexfPly9OnTB3///Tdat27tdHQmRy6X22w21el08Pb2dvp+6xqKveZAbtSoM9xFFyio6ZpMJjRs2BBAQb9oTk6O04uxn58fJBIJGGN4/PgxH5wd8fX1RXp6Oh4/foy6des63V4otVqN4cOHF3o+OjoaXl5eSE5O5h+PHj3CjRs3EB8fjzt37uDOnTsAgI8//hg1a9ZEu3bt0LZtW7Rv3x6NGzd2+P36+/vj4MGDePHFFy0CbP369UX5XM899xz8/f2RnJyMmzdvonXr1i693zqzFwVaQoi1Us3QlJaWhsuXL1ukG2zbti3CwsLQu3dvTJw4EfHx8cWe21fUpb7srdVpztFyZVztl1tuTq1Wo2HDhvDz80N+fj6uXbvmtAwymYwfyCM0YTaXuk/sJeTsUSqVqFevHp544gk8++yzGDduHN58803s2rUL33zzDd555x0MGzYMDRs2hEwmQ0JCAn755ReEhYVhxIgRWLx4MXJzcx0ew8/PDwcPHkSLFi2QlJSEIUOGIDo6WpTye3h48CuC7N692+X3m2f2ciWFJiGk6iiV4MpNfenQoQMaNWqE7777zmJQTNOmTTF9+nTk5+fj0qVLxT5eSdYg7K3EwuFy12o0GtStWxf16tVDmzZtAACXL18WdAw/Pz8AQEpKiqDtSzu4OuLn54eePXtixowZCAsLwz///INt27ZhxowZ6NKlC9zc3HDkyBFMnz7daT+0dYAdPny43QQRrpowYQIA4JdffkFcXJzFa87WYzW/CStuTmFa+5WQyqlUmoW5mmjbtm3RrFkzbNiwAY0bN0bPnj35/tX+/ftj9uzZOHr0KJ577rliHc9ZcGWMWWTVsZetydYIZltZe/Lz8yGVSvlBWtnZ2XxztV6vR0hICI4ePYoLFy5g4sSJTsvPJYaIiYlBkyZN7G736NEjJCcn8+WIjY21mYnp8ePHghYsVyqVWLt2rdPtmjVrJqgWrlQq+RsKX19fPPXUU6hXrx4OHjyICxcuYMiQIRg9ejSaNGnicATy6tWrMWfOHNy5cwf9+vXDt99+67APNicnx+56rpw6deqga9euOH36NL744gu89dZbAAq+r7i4OHh4eIAxBrlcDpPJZDcZBve3w9VchWZo4n4TjkapUyYnQiquEguut2/fxr59+3D9+nX0798fTz75JEJCQrBv3z507twZ06ZNw0cffYR+/frxCQMaNWqEoKCgYh/bWeYdLhA6m8toMBgK9afZ6n/lMjlxgRcoSImo0+kQHx/PB4KrV68KSrvIZVPKzc11OFeufv368Pb2xsOHD3HixAlIJBK0aNGi0Ha3bt0S1Hf7ySefON0GABITE9GxY0en2924caPQzYGPjw8GDx6MI0eOICUlBTt27MDw4cMdzqXVaDTYuHEjXn31Vdy5cwfDhw93OMjJw8OjUBIJWyZMmIDTp09j27ZtCA0NhaenJ3Q6HTw8PGAwGBAQEACJRAKpVOo0Q1NGRga0Wi3q1q3rNMCa/326mjXMmvngKpo6REj5USLNwteuXUP37t0RHh6OpKQkrFy5Etu2beNXQDl79ixq166N119/HVOnTsW2bdswc+ZMnDhxwmHmHDEJac5ztT/NfOK40WhEfHw8ZDIZf8Nw48YN5OTkON2Pq83C1atXB1BQQ60I/Pz8MHToUPj5+UGn02Hv3r04ffq0w/f4+vpi/fr1ovbBDh48GL6+voiPj8e3337Lj4T29PREnTp1BHcvqNVq5ObmwsPDg18pSWgzr1qtRkBAQJEDI/X5ElI+iR5c4+LiMGrUKEyYMAHfffcd/vrrL7z99tv48ssvLQboHD16FGPGjEFiYiI2bNiA27dv48SJE2jatKnYRbJJyOClovanccvNBQYGwtvbG926dYNGo0F+fj4/J9URV4MrNwCqtIJrURd+N+fp6YnBgwcjKCgIeXl5eP3113H48GGH7/H19RV1kJNSqeTnEv/xxx/8c64uHadSqVC3bl1+hHppBjtaR5aQ8knU4MoYw9GjRxESEoKpU6fyi2G/9NJLqF27Nu7fvw+gIHcuALz11lv46aefcPr0aXz//ff8wJ/SZm9QiVKpdBqAbeFqsMHBwWjatCn8/f3RoUMHAMCFCxecvp+riboaXNPS0gQvQG7NlffduXMHZ8+eLfIC8BwPDw8MHDgQrVq1gtFoxPvvv88vH2ePrVHE3LSfopg0aRIA4MSJEwBQ5JR13HceEBBQqsGOu0mk4EpI+SJqcJVIJKhZsya6d++OevXq8YOBGGPIysrCo0ePAMAiKbtEIoGPj48oafuKqqSb1nQ6HZ8u7a233sL333/vcHuuf5Q7X874+PhAJpPBZDLh4cOHRSpjYmKi4G0ZY4iOjsaNGzeKdCxzMpkMAwYMwOTJkwEAX3zxhcujiCdNmuR0ao89rVu3RseOHZGXl4dTp04Ve6S5kBYRQkjlJ3qzcP/+/TFnzhwA/4129PDwQLVq1SyC6t69e3Hx4kWxD18kJdm0ptPp8ODBAwwaNAidOnVCVlYWRowYgaVLl9qtLXJN49HR0YLy+MpkMr7Wf+rUqSKV09noWnM1atTgy1fUmrI5iUSCiRMnQqVSwWQyCco25efnh2+++QbVq1fH9evX8cEHHxT5+H379gUA/Pvvv0XeByGEmCvRea7c6EeJRAK1Ws3XThctWoTp06e7dEEvSSVZ29DpdJDL5fD29sZnn33Gz69ctWoVhg0bZnNuau3ateHr64u8vDxERUUJOk737t0BFATXoiypJ2QUM8fHxwdyuRw6na7QHNHi4G5uhA4GqlGjBj91aMOGDQgPDy/ScZ988kkAFFwJIeIplSQSeXl5SE1NRW5uLlasWIENGzbgzz//5FMDlmdFmeSv0+n4FTRUKhU8PT3RpEkTeHp6Yu7cuVi3bh0UCgWOHDmCrl274t69exbvl0gkaNmyJQAImk8KAB07doRcLkdiYmKx+iCFkEql/DQYsZI6AP8FV1ea5wcNGoQXX3wRRqMRM2fOhF6vd/m4XHC9efMmP4VKq9Xyj6LskxBStRU5uLoymEUikcDX1xeLFy/GqlWrcOLECUHzJMsDrj/WlSkWWq0WycnJ0Gq1/EAXrlas1+vRu3dvfPPNNwgMDMTt27cRFhZWaB+uBleFQsGf0w8//BCnT58WdVF4a1x+4MTERNEyQxUluAIFqyPVqFEDt2/fxsqVK10+rkaj4W8Wzp49C51OB6PRiJSUFBiNRpvfOWVWIoQ4UqQkElFRUfjxxx/x0ksv2V3xxDwLTV5eHiQSCbRaLf7991+XE6WXFcYYP8nfYDDwg56sm1AZYxaBTCKRICcnB3l5efDz8+O39/X1RU5ODuRyOapVq4a3334bM2bMwKlTpyz6LtPT0/kk9REREXaDV3JyskXmpR49euD69evQarVYv349vv/+ewwZMgTZ2dkur5XrTGxsLHx8fJCWloazZ8/aXLvSzc1N0GpAISEhePDgAZ+tKzY2Fg8ePCi0XW5urkW/vbk33ngD8+bNw9atW9G6dWuMGjXK6XGNRiOfbKJTp06Ijo7GmTNn0KNHD+j1en7UNmMMycnJUKlUFhmZuL+HkhqMRxmaCKm4XA6ut2/fRpcuXfD48WOkpKRg3rx5haYvWK9qo1QqMWXKFDz55JMO0/mVFqEXIqlUyq+c4yiTjvUSeRqNhu9rzcnJ4dc39fb2Rn5+PrRaLTQaDZ/mMSoqCunp6fx5rFmzJnr27AmgYNpL9erVbWYcatGiBZ8qkdOrVy9888032LdvH2JiYvDRRx/hySefxPDhw/n5s/bs37+fn8MqlUrtBuSMjAz4+PigZs2aSEtLQ0pKCoKDgwulCHz06JGgzFA5OTmoW7cuXz6lUmlzdZ9Hjx7ZzbzUu3dvDB06FIcOHcKKFSswbNiwQufGmtFo5D/jk08+ia+++grnzp0rlO0oJSXFIp809183NzeoVCqLvwchf1sUNAmp/FyqzmRnZyMsLAyDBg3CJ598gtWrV2PNmjWFVm/hLghr167lFzP/3//+Vy4Ca1HZGvRk3jRo3c8aHBwMLy8vm4OkfH19+RoPNzL4zJkzFtvUqVMHvr6+yM3NdalfU6FQYNy4cdi1axf69esHoGCgztSpU/HNN984HX3MfXdCAoC3tzcUCgVMJpPgFXwc4QIal8nLVfPmzUNgYCAePXqERYsWufRert/13LlzhUZAKxQKPpByCzcAcDnZBCGk6nApuEqlUnTo0AEDBgzAzJkzsW/fPqxbt85mgE1NTcXFixfxyy+/CE6GUNFwTYNccDWv3Vj3tXJUKhVfw8vPz+eTS1gHV4lEglatWgEArly54nLZ/P39sXjxYmzatAn169dHTk4Odu/ejenTp+PkyZN2g6crwVUikfDTchITEwu9hzGGvLw8fnH11NRUvh/TlqL2uZq/n7uZ27lzJ3777TfB723ZsiXUajUyMjIKjTo2z9rEfX+uBlXqoyWkanEpuCqVSowbN47vzxo5ciT27t2LdevW4f333+eDKNfc9umnn+LQoUNOmyMrKm5+LHehtdc3ytVqzUedcu/t1q0bANjMrcv1TRcluHKaN2+OhQsXYuHChfD390dSUhLef/99vPHGG0hISCi0vXkzpJAAyzVl5+Tk4OLFi7hx4wYiIiJw8eJFPHjwAFeuXMGNGzcQFRWFmJgY3Lt3z26ii+IGV6BgWcOXXnoJADBz5kykpqYKep+bmxueeOIJAI7nCtu7aXKGcgATUrW4PMqFuwAajUYwxjBq1Ch8/fXX+OCDD/D+++8jPj4eCxYswIQJE6BWq+0OeKoMuKZioGCEMLfMHGA5HYer1er1+kLNik8//TQA4OTJk/jqq68s9t++fXsAwMGDB4s8hxMoCJi9evXC5s2bMXbsWMjlcty4ccNpLl97wZUxhvT0dMTExFgMWDIajcjMzEROTo5F/mGZTGYx6Mfe8nc+Pj4AgPj4eKEfzaaZM2eicePGSEhIsDkS2x7uRufLL79EQkKCRS3T/Pt09Jw9lAOYkKqlyENIuUE8JpMJo0ePxt69e7F+/Xr06dMHGzduxDvvvGMRbCozbvCSwWDgazTmzcRcU6JSqSzUrNi4cWPMnTsXADBx4kT8+uuv/H779u2L3r17Q6/X4+WXXy72KjAKhQJjxozB//73PwCwmQmJW2INKPhuuQDLjYg2Go1ITEzEzZs3kZSUVCiJv1qtRrNmzdCqVSvUrl0b7du3R9u2bdGiRQt+OTx7qQq5qUQXL14sVg1PoVDgww8/BABs377d5hq3tkybNg3Vq1fH1atXsWHDBr6lQa/XIzY2FklJSYiNjbUY2GTeFeCIeZ+9Xq+nJmJCKrlizc/g1qXkarA9evRAcnIywsPD0a5dO7HKWO6pVCp4eXkhODiYD5rmQZRrSuSCK9esqNVqcfPmTbz++usYNWoU8vPzMWLECFy6dAlAQVPltm3b0K5dO6SlpWHkyJG4fft2scvLrd5irwZpvt6o0Wi0eDDGYDKZIJPJ4O/vj2bNmqFTp078wKzs7GwwxviVgcybmblpNCaTyWa/a8OGDREUFITc3NxiZ0t66qmn0K9fP+Tl5fH9sM7UrFkT69evB1CQ8YlLxsFNn0pPT+czUwEocv8rNRETUvkVe/KjRCKByWTCvHnzcOzYMRw7dowfiFNV2OqHE9I3p9VqkZubi9TUVKxbtw7dunWDTqfDuHHj+CCqVquxa9cuhISEICkpCSNGjCh2gOWmqDgKrubTcKzn8FavXh3t27dHgwYN4OPjwycJ4QY33b17l1/5yJxMJuP3a+t1iUSC3r17AwCOHz9epM9m7r333oNEIsHBgwcFrUYEAKNHj8bgwYORl5eHKVOmID09HQqFAl5eXmjcuDE/Apxr7udunlxBTcSkIomMjER4eLjDR2xsbFkXs9wRLbNAixYtEB4eXmESRJQHGo0GHh4e0Gg08PX1xZYtW9CmTRs8fvwYY8aM4fseq1evjv3794sWYJ0FV8AywHL/L5PJ+P5TqVQKo9wLBt+6MMoL9hccHAylUom8vDzcvXvXZp8tV3u1FVwB8MH11KlTdrcRqmXLlvzgprffflvwCOiNGzfyCwKsXLmSHy3MPcyn5HCZuFxp4i3qUoYAjTompYf7Ww8NDUWHDh0cPkJCQijAWilShiZrMpkMEydOrDAT3s0vsjqdjk8OYX2xEzrZ3zpphi3Z2dnQ6/V80zBQsLKL+UjqkJAQ/Pbbb+jWrRvu3LmDsWPH4ttvv0W1atXg6+uLffv2YfTo0YiMjMSIESPw5ptvolGjRk7Lp9VqLQIVt6h6enq6RX+k9eeVSCSFEmRwfa/JXo1hbD8SkEgBZoIsfD9k98/C398fDx48QFpaWqHMVebMA0SdOnX4pnCTyQQfHx+kp6dj+/bt6Ny5M39uMzMznS5HB4DPgAUA06dPx4EDB/DPP/9g165dGDhwIL9dfn4+X9s25+3tjTVr1uCVV17Bxo0b8eKLL6Jz584W2yiVSuj1euTm5vL9rnK5vND5Ept5kzLNsSUlKTg4GJGRkU7nsEdGRiI0NBRardZmpraqSpTgClTcTDLOLlZCP5ez7fR6PfLz85GSkgK9Xs9nfbIWEBCA3377DT179kRUVBSmTp2K3377DSqVCrVq1cKxY8fQv39/XLlyBatXr8aBAwecBljzLFHAf0vGGQwG1KtXj593+9prr9kMNtb+On0RUc1GAdxnlkhhbD8S3RpUg4Ll4Nq1azhz5gzS09P5FXQ4Hh4eMBgMkEqlfCrB/Px8i/J17NgRf/31F3bu3Invv/8ebdq0Qdu2bVGtWjV+RLEjJpOJryHXrVsXkydPxqZNmzBv3jxUr16dX0EIKJxdizNq1CgcPnwYP/30E6ZPn47z589bpF7ksjiZNw9LpVLR/l7sbeMoUxghYgsODqaAWUSlsipOeVZa/V8qlQq5ublISUlBZmamw2a9unXr4siRI/D19cWZM2cwZswYvuap0Wjw+++/o3Xr1kVuIjZP2VeUbEi5cp//AitHIoVOVnAOW7RogTp16gAAEhISLDIemSfQsOeFF17AE088AblcjsePH+P48eNYv349li9fjnfffRc//vij4IXkAWDu3Lno27cvDAYDxo8fj3/++cfpeyQSCTZs2MCPHn7//ff513Q6HWJjY/lmMHt96yXRhKtWqxEQEED9tYSUc1U+uJbkWq7muGZnX19fiyk79rRs2RLff/89FAoFfv75Z0ybNo1vYuUCbFH7YGUyGX9xFtLMas3DkA5YN/cyE1TGgtGv3LxaqVTK31BwuOBqPVo4PU+Cu1luSM+TQKPRYPbs2diyZQveeOMNPPPMMwgICIDRaMTly5exfft2TJs2DTNmzMCOHTuc3iDI5XJs2bLF5QBbo0YNrFu3DgCwYsUKPpmHTqdDVlZWoak51rKzs5GRkYF79+5RHykhVUyVD65iys7ORnJyst0pFkqlkp+yY6vmwb2fuxB369YNe/fuhUwmw5dffok33niD31aj0WDfvn0WAdZ6XVhHuEFNtua6OuOen43m2ZcB9v81UmZC8+wIKFiOxWflmnDT0tL4c2Kr5prq2wxrb3rj8xhPrL3pjQupBcn53d3d0apVK7z88sv44IMPMH/+fEycOBGtW7eGTCbDw4cPcfjwYWzYsMFpmW0F2LNnzzp934gRI/Dcc88hLy8P48ePR35+Pt8EnJGRAZPJxDcNp6SkWARRtVoNg8EAuVxO024IqWIouIpIp9MhMzMTsbGxyM7OLhRs1Wo1/P397TbpcSNQU1NT+WbHp59+GsuXLwdQsE5rTs5/Acx6FPGnn34quKxc3+r7778vKMhYC8qNRc+0P9Ax4x/0TPsDQbmFRwoqFAr4+voCKMg9bB5QuaZipvTBw8CeYPj/nMaQ4PBDJdLzLJudJRIJ/P39MXjwYLz33nvYvXs3pk2bBqAg2X5MTIzTMlsHWK5W6gjXPKxWqxEREYFLly7x06zq16/Pr5zEfXfmQVSlUqFevXrw9vamZlxCqhgKrkVkqz9NpVLxNRVbyfydMU/qzwXn7OxsxMXFAQA6d+5caO3Q6tWrY/78+QDAj7gVYsqUKahTpw5SU1OxbNkyrFu3ziJwC6FgOaien2JRY7Xm5+cHDw8PPrMTF3y4wUxM7V8w4tgMgwQpBsejbtVqNQYOHMgPTvr+++8FlVkul/Mr5kRGRhZaAceWGjVq8MGRW/LOOnEI991ZB9HS6nYghJQvFFyLyFaWHbVabbHUHDeIiZvu4wxXs61evTqkUilycnKwbNkybN68GUDBkmq2cHOLo6KiBAfIevXq4eOPP8aLL74IqVSKo0ePYteuXbhx44ag9wsllUpRs2ZNSCQS6HQ6vo+XC0KS7OT/mpf/nwQMfnLbK+dYGzJkCADgxIkTgpe9a9CgAX8DJCQ1ImOMn77ETfGxThKiUqng5+dHQZQQAoCCa5HZG2Vs3vTLDWLy8PDgA6yjPlngv/mwCoUC27dvx5YtWwCAn29pS2BgIKpXr478/HxERkYK/gxyuRyTJk3CunXrEBQUhOzsbOzatQvffPONqANw5HI5v3oO8N8i9AAg0aejdvwJSFAwQEoChiG19fBxFzbHuHHjxmjZsiWMRiN+/vlnQe9xc3Pj1xa+evWq0+11Oh0/WpsLrra2se5zJRVHbGys0yxErvy2CBFtnmtVY572zlGyCa4/zrxfTqfTOex3zcvLQ1hYGLZv3w6gILBy/Yu2SCQStGnTBseOHUNERITLeZ2bNWuGTz75BCtWrEB4eDguXbqE27dvY+jQoWjevLlL+7LHx8cH2dnZ0Ol08PT0tJijWT3tJsY+3Q4pBhn85EbBgZUzZMgQXLt2Db/++itGjBhRqOnclubNm+Pq1au4du0ahg4d6nBbbilBmUxmMR/XnHmfK9VeK5bY2FiEhIQIXoDB/EaREHsqZXDNzs7mR8PaIjTzktBtnQVXxhh0Oh0YY8jNzUVubi6ysrL4fkduKTqVSgWFQoHFixfj888/BwB89NFHGD9+vM3m3sePH/OJDZo0aYJjx47h4sWLGDZsmMV26enpglIJcmnMjhw5gtTUVOzatQstWrRA3759LQKWXq8XNM+UW4KO4+npCTc3NygUCovnc3NzcenUMQCAowRqcrncZsJ/rmas1Wqxe/du9O3bl1+cwB7uAnnlyhWHc26BgmxSwH83CNz3BhScC271HJlMBpVKZbcf1/x7thekSenj0lfu2bMHISEhDrfVaDSUVIEIUimDq5A7UCHZbYSkNRSyPy47k0wmg1KptBjkxA188vDwQHZ2NtasWcMH1vXr12PKlCl298sNFgKALl26YOvWrbh161ahO+uOHTsKupj7+vpCo9Fg8uTJ2LZtG/bu3Yvr169Dr9dj/fr1/AhjuVyO6tWrO91fZmYmv96tIwkJCTaDpjWdTmd3feDnnnsOX375Jc6ePYv+/fs7rT1yNfIbN25YBEtbuBsbb29v6PV6vv+YWz7OaDRCJpPBz8/PYsEDW+XnvnsKruVPSEgIv4YyIcVVKftcy1uznL31XM0XTpfJZFi1ahU++ugjAM4Dq7U2bdoAAG7dusXXpIpKoVDgtddew7Zt26DRaHD37l1MnjxZ0HSXstK1a1d4e3sjJSVF0KhpLmXk/fv3+WZfe7jBTL6+vvz3ZjQa+QDL1Vg59hZRL+oSdYSQiqfKBtfSXF2EG+Rkvrar+fQNPz8/rFmzBp988gmAgqZgVwIrANSuXRt+fn7Iz88XbcRvq1at8NlnnyE4OBiJiYmYMmWKoAFAZcHd3R19+vQBABw9etRpc763tzdq1qwJwPmgJi74ck3PKSkpfJYtW0sL2puCJWQZQkJI5VDmwdWV/k8xlcaC1eajg7OzswuNJuUutuY11o0bN2Ly5MkuH4sb1ARAUGo/oQIDA/HZZ5+hRYsWyMjIwMyZM3Hr1i3R9i+mp556Ch4eHnjw4IGg2mvjxo0BgE9raA9Xc/Xy8kJ2djZMJpPDdVyphkoIKZPgeuHCBfzvf/8DULzVdAwGAzIyMiweQrmasL8oNV2tVovExES+idBWbcZoNGLjxo0ACvpNX3nlFcH7t9apUycABXlwR4wYUaTMS7b4+vpi06ZN6NKlCwwGA7755hscOnTI6UCg0ubl5YUWLVoAAC5evOh0e65p+Nq1aw634wYoxcTEQK/XW0wlsoVqqISQUg+uERER6NWrl8PRvEKFhYXBx8eHf3ArsQjhauac4tZ07dVmDAYD3n77bUgkEpw5cwbDhg0r0ko1ADBjxgy8/PLLcHNzw/Hjx/H8889jxIgRuHz5cpH2Z06pVGLdunUYOXIkAODvv//G+vXr+ZG05UFeXh6ioqIA/NcH7Ui9evUAwOmiByNHjoSnpycuX76Mo0ePOkwWYa+/lRBStZRqcI2IiEDXrl0xY8YMbNq0yeY2QtLRcRYtWoT09HT+8eDBA7GKWugiWZSl6TQaDWrUqAGNRgO1Wl3ooqzT6fDgwQMMHz4cn3/+ORQKBY4cOYKBAwciISHB5TIrlUp8+OGHOHv2rEWQnTBhAsaPHy+oNueIm5sb5s+fj1GjRkGtViMuLg7r1q3D+fPni7XfosrIkyJG546MvII/4ytXriA7Oxu+vr7o0KGD0/dzUyq4gGxPYGAg3nnnHQAFuZgNBoPdbV1NeUkIqZxKLbg+evQIXbt2xciRI7F27VoYDAa8/vrrGDJkCHr06IF169YhLi4OUqlUcD+sXC6Ht7e3xUMs1jXVouSI5QYyAQVzJVNSUiwCtk6ng8lkQmJiIp599ln8+eef0Gg0uHTpEnr37o2bN28WqezBwcGFguypU6cwevRoUYJs06ZNsXDhQjRs2BAGgwFfffUV9uzZU+Qad1GEpymw/q4fdj2ohvV3/RCepsDp06cBFEw9srcIujkuuCYkJDjtUpg1axaaN2+Ox48fW6ztas1RfyvVagmpOkotuMbHx+OJJ57AhQsXcPv2bQwbNgxnzpxB/fr1Ubt2bezfvx9z585FYmJisfphxSLmIurmq92Y12q4pcu4+aOtW7fGjz/+iLp16/Ir4hRncBIXZL///nuMGjWqUJB9+PBhkfft6+uLmTNnYsCAAZBIJLhw4QKWLl2Kr776Cvfv3y/RgWoZeVL8lOhlsZLOT4leuH634PM88cQTgvbj6enJn3tnTcPu7u780nZbtmxxaZEE4L8F1jMzMym4ElIFlFoSiQ4dOuCDDz7A4sWL0aRJEwwcOBCHDx+Gn58fAODzzz/H6tWrER4ejoEDB5ZWsexSqVRQKpWCk00425dOp+MTLyiVSphMJigUCvj5+SE1NRVAwQXY398fO3fuxLx583Dp0iU8//zz2Lp1K0aMGFFovwaDQVAzerVq1fDuu+/ilVdewbZt23Do0CGcOnUK8+fPx65du/jP+OjRI74sjqSnp/OZobp164batWvjl19+QXx8PM6fP4/z588jMDAQSqUSHh4eTmuReXl5gpLuq1Qq3Lt3D49MXmCwTJTBIIHEuwZqqWRQKBRISkpyuj+ZTIYGDRogMTERV65c4fMNW8vPz4ePjw969OiBkSNHYv/+/Zg1axaOHj1qkTSCW9uVu4Gy7gKQy+XIzc3ls3YJIcbfn6vbEUKKr0SDa35+Ptzc3PhMRx06dMC7776L1q1bY+DAgfDz8+Oz20yaNAmLFi3Cv//+W+zgKpFIHF6UxMi6ZM5RVh4AfBJ/k8lUaJ8SiQQ+Pj788wkJCfD398d3332HefPm4dChQ5gwYQISEhKwYMECi/drNBqnxwb+u1GoV68eunXrhrfeegtdunTBxYsXERERwa8sExMTwy9550ivXr34UbmchQsX4sqVK/jqq69w5MgRxMfHAwCSkpIwcOBADB48GEFBQTb3d+7cObuZl8zFxsaiadOmqJsL/HGZ8TVXAIDJhPy0eAwYPggNGzbkRwI7kp+fj0aNGuHMmTMOP7tEIuHP85o1a3DkyBH8+++/2Lt3L15++WWLtIZqtRrZ2dlQq9UW3xXXAqJWq51mhCKEVHwl1ix848YNTJs2DX369MHChQtx6NAhAAWZdGbOnMmvwymTyWA0GpGcnIwmTZrwy6dVFeZruGq1Wvj4+EAqlcLPzw9ffPEFpk6dCqBg8Ba3aHpxNWnSBLNnzwYAvP322w4H6AjFzbNds2YNTpw4gfnz5yMgIACZmZnYv38/xo4di4ULF+LUqVPFnsLj6wGMqJ9vsZJOym+fgGU/Ro8ePVzaFzfX1VmzMCcwMBCvv/46gILm4ZSUFERHRyM6Oprfh62+efPAW9wMWoSQ8q9EguvNmzfRrVs3mEwmNGjQAHfu3MHYsWPx3nvvASiYAsHlxAUKAuymTZuQlJSEjh07lkSRyhQ3R9bWRdV8lQ25XA6pVIratWvzC6+/8847WLZsGQDgvffew+HDh0Up07x581CjRg3cvXsXW7duFWWfnOrVq2PKlCnYtGkTVq9ejc6dO0MikeDcuXNYvHgxhg4dirCwMJw+fbrIgf1JfxOWtM3FjGa5aBWzH1lX/kC7du3g4+Pj0n64Gm50dLTg94SGhgIAzp8/zy+4np6eDpPJ5HQ5wZJOXEIIKR9KpFl4+/bt6NWrF7744gsABTWy/fv3Y86cOTAYDFixYgW/7S+//IIjR45g9+7dOH78OOrWrVsSRSpT3EXVuh/OHPd8jRo1IJfL+ed0Oh3mzp2LuLg4fPbZZxg/fjzOnDnjdPUOZzw9PbF06VJMnz4dYWFhGDNmTLH2Z4tMJkOXLl3QpUsXPHz4ED/88AN+/fVXpKWl4ddff8Wvv/4KpVKJJk2aYNiwYWjYsKFL+/f1ALzdjFh79BcAQO/evV0uIxdc79y5I3ihhqCgIHTt2hWnT5/GqVOnMHbsWL6519EAOPMmY0JI5SZ6zZUxhrt371rUTDUaDaZNm4YtW7YgLCwMn376Kf/atWvXEBsbi3/++Qdt27YVuzjlAjfy2NWsPubPzZw5E23btkVWVhamTJkiyuCU0NBQtGrVCunp6Rg8eLDFMnBiq127NqZPn46DBw9i/fr1GDZsGPz9/aHX6xEREYF3330XmzZtcjkpxU8//QStVgu1Wl2kVo/69etDJpMhKyvLpbnFXB7ja9euISgoCI0bN+ZbHOzhpnNRnyshlZ/owVUikaBnz564fPkyIiMj/zuQVIqXXnoJb7/9NjZv3ow7d+4AKBgIs3v3brRs2VLsopQbzi6qQuY/ent7480334RKpcKZM2fwzTffFLtcMpkMO3fuhL+/Py5fvoy33nrLpRSSReHm5oZ27dph9uzZ2L9/PzZv3owOHTrwGapef/117Nu3j286NZlMePz4Me7du4dbt27hr7/+wrfffovPPvsMa9euxZ49ewAU3CiY39AJ5eHhwWdqciVnMteM3LBhQ6SkpAjuR6W5roRUDSXSLNyxY0f4+Phgx44deO211/hRogqFAgMGDMAnn3yCxMREvhlQzOQPFQm3liu3nquzZuMePXpg1qxZeP/99/Hmm2+ib9++xV4XNCQkBL/88gsGDhyIO3fuYNGiRQgLCyuV70QqlaJ58+YYPXo0hg4diq+//ho3btzATz/9hL/++gseHh7IyMhwWkvv0qUL+vXrV+RyNG3aFHfu3EF0dLTgpmUub3PTpk2RnJwMT09P1K5d2+n7zPtdKfcwIZVXiQxo6t69O1566SXs378f27Ztw927d/nXmjZtiqCgIFFGqFZk5kkFAAhqNpbJZJg3bx4CAwMRFxeH9evXi1KW5s2b45dffoGPjw9u376NRYsWlXgN1lq9evWwaNEizJ8/H4GBgdDr9UhPT+f7QX18fBAYGIh27dqhT58+ePHFF/npW3Pnzi1W4hFufqvQmmtCQgLu3bsHiUSCZs2aIS0tTXDN1To5iZhLH5bmMoqEEMdEr7maTCZIpVLMnTsXer0eu3btQnR0NCZMmIAGDRpg27ZtSE1NRdOmTcU+dLmi0+n4wSv2UuHJ5XIYDAYEBwfb3YarzZovcbZs2TJMnjwZ69evx7hx41xasMCe5s2bIywsDIsWLeIDbGnVYDkSiQTt2rVD69atcefOHXh4eMDX1xfe3t6QSqX8PFd70nKB5BwJvOBaoHU1uP77778ACs6Zv78/srKybDb52/obsE5OImZNlmrFhJQfRQ6uXPIHa1KplA+wixcvRu3atXHo0CEMGDAALVq0QFZWFn788UcEBgYWq+Dlia1mS/MLHXfhNR+Naj462NaFkDEGnU4Ho9EInU5ncfEeN24cdu/ejRMnTuCtt97iR2Xbk5ubKyjXbq1atSwC7JtvvomVK1cWWsFIr9cLqtkKvchnZ2fza6ZyuJzMjDGkp6cDADIzM+1mXorIVOPXlGoFmZrgjmTZQ/QMcvznbTKZUK1aNb459+bNm/yxrLcz/xzh4eEACtIsKhQK5OTkQKFQwGQyWWTMys7ORmZmJlJSUlCnTh0olUo+4YT56GIuABd1kBr3d0WjkQkpP4oUXKOiovDjjz/ipZdesplZRyqV8tmZxo0bh+HDhyMmJgZSqRQajQYBAQHFLnh5YqtJ0lamHplMxv+/p6en3f5SvV6P7OxsSCQSGAwG/oIcEBDAX+TXr1+Pjh074rvvvsOrr77KJ+Wwxc3NTVDmJW6x8U6dOvF9sGFhYfjqq6/QoEEDfjutVito8FC1atXg6+vrdLvq1asLCggxMTGoWbNmoedTdEa8/3MyuNDEIMGXN3LRt2Ug/NXudveXk5MDlUqFVq1aASjIJpWfn1+ots4Yszh/9+7dA1AwjUcqlfI1azc3N0gkEou+9EePHsHHx4dvgeDSI3I3HrYWXXfW6mFrO/OHPeUhZzchVYXLwfX27dvo0qULHj9+jJSUFMybN49PgsCxvhip1epKPRrYFlsXTaG4Wi/XN5eVlYWsrCyLi23btm0xceJEbN++HfPmzcOZM2cE1U6F4PpgBw4ciIiICLRs2RIdO3bEiBEjMGzYMH4ebnkRn5UP6zqfiQHxGXkOgyvH29sbNWvWREJCAqKjo50uV8eNIahVqxYfMK3zCHPzmmvVqgWDwWDRLMzVXO0R2rxrvh3VVklZM58dYo9Go+FXo6rsXAqu2dnZCAsLw6BBg9CxY0fMmjUL+fn5eP311y0CLHeHvHbtWuTk5GDJkiXilrqSs27e42q4arXaoraydOlSHDhwAJcvX8auXbswYcIE0crQvHlz/Pbbb1i4cCGOHTuGCxcu4MKFC3jzzTfRqVMnDB06FM8//3yhGytnuJsGMQV6ukECWARYqQQI9HYeWDmNGzdGQkICoqKinAbXmJgYAEDdunUt+sTNm3XT0tL4582b/rk+V0c5oYU271IzMCkPuLn4XOYyR1QqFSIjI6tEgHXpKieVStGhQwf4+flh1KhR8Pf3x+jRowGgUIBNTU3FxYsXce/ePcycOZNfEaaqENq0Z4t1rZebhwkUrAvL1Vb8/f2xZMkSLFiwAEuWLMGgQYP4VYbE0KxZM/z4449ITEzEoUOH8O233+L06dM4e/Yszp49i7feegs9evTAnDlz8OSTT1q812g04vr167h69SouX76MmJgY3L17F+np6RgyZAjWrVsn2mApP5UM0zp6Y+vFDJhYwRD42d0CBNVaOU2aNMHJkyedLpyemZnJJ7rw9/dHcnIy1Gp1oYuFr68v3NzcXL75AGAzWDvajpCyFBwcjMjISKcrW0VGRiI0NBRarZaCqzWlUolx48bxd8ojR44EYwxjxowBYwxvvvkmv9KNVCrFp59+CoPBUOUCK1C4ac+6f6yorGsr06dPx2effYZbt26hR48eOHjwYLFTI1qrUaMGpk2bhmnTpiEuLg67du3Cjz/+iIiICBw/fhynTp3Cxx9/jEaNGuH06dM4ffo0/v33X5uDgwDgu+++Q3R0NPbv388PXCquvvVVaFdDjkdZ+VDkZaJdE+E5hv/66y98//33AOD0ApGYmAig4LeQl5cHk8lU6Pu01VRMSGUWHBxcJQKmK1xun+MuJFwAHTVqFBhjeOmllyCRSDBnzhysXbsW9+7dw759+6pkYAUKB0Gx+sfMayt5eXlwd3fHzp07MWLECNy+fRvdu3fH2LFj0alTJ3Tu3FnQ0muuCAoKwpQpU/Dqq68iJiYGK1euxE8//YTp06cX2latVqNLly58ORo0aIDU1FRMnjwZV69exfPPP4+DBw+KVjY/lQx+KhlSUrIEbZ+Xl8enXQSAFi1aYP78+Q7fww3g4+a1qtXqQrVTsWqU3MA289aP4rSIEEJKT5E7v2QyGRhjMJlMGD16NCQSCV5++WX88MMPuHPnDs6dO1fuBr6UJusLbEn2jwUEBGDPnj1YuHAhzp8/jy1btmDLli0ACkbiduzYEZ07d0anTp3wxBNPCBrBK0T9+vWxdetWvP3229ixYwc8PT3RuXNndO3aFV27dkX9+vVtNov+9NNPGD58OO7cuYNnn30W69evL7Q+bEl78OABZs+ejevXrwMAJk2ahGXLlkGhUDh8n1qtRo0aNZCYmIiHDx/yI41Lgq2BTTSXlZCKoVgjS7iBS4wxjBo1Ctu2bcPly5cRHh5eohediogLtiU1HaJ69er45JNPcOXKFYSHh+PKlSu4ePEiUlNT8fvvv+P333/nt23WrBk6d+6MadOmoV27dsU6rkwmQ1hYGGbNmoWAgACLwUpZWbZrkI0aNcKRI0fw4osvIjo6GlOmTMHWrVtLbUT5kSNHsGTJEmRlZcHX1xcbNmzAc889J/j9DRo0QGJiIh48eMBPUbJuErNOAFIUtm7IaBATIRVDsYdtSiQSGI1GflTp5cuXKbCWMj8/P/4CXrduXYwcORIqlQppaWm4ePEiIiIicPnyZZw7dw53797FzZs3cfPmTezatQtTpkzB0qVLUa1atWKVwdWkIIGBgfjpp58watQoXL58GRMmTMCmTZvQqVOnYpXDEb1ej5UrV+LAgQMACqYz7dy5k899LVSDBg1w5swZxMbG8mkagYLvgRvZbT4dx172LWfNu0ql0uai61RjJaT8E21ORIsWLRAeHo7WrVuLtcsqS2imHi4TllKpLJR+jzEGDw8PtG3bFh07duQv+snJybhw4QL27t2LAwcOYOvWrTh48CBWrFiBF198UdBcWaF5oQ0GA4xGo93XfX19cfDgQQwfPhyXLl3C1KlTsWrVKvTq1cvu/vLy8vh/5+XlIS4uDgDg7u4ODw8PuLu7IzMzExkZGXB3d+dbCqKjo/H666/j7t27kEgkeOWVVzBu3DjUrFkT+fn5Dj9Hfn6+xXG50dtRUVFwc3ODVCpFdnY2FAoFf54dDWpijNnM4GWN+36FoAQRhJQvogRXmUyGiRMnVtkfuNifW+j+PDw8HG6blpbGp+Tj+r+DgoJQrVo1dOvWDRMmTMCCBQtw48YNTJs2DTt27MDHH3+M9u3bOzxurVq1BAXhatWqOe13r1atGr7//ntMmzYNR44cwRtvvIFPPvmEn+LF4VL8RUZG4vz58zh37hwuX76MnJwch/t3d3eHXC6HXq+H0WhEzZo1sX37dvTs2RP5+fmCmlfz8/MtPi+3buzly5f5fUulUotsXI5GhXPbWmfwsrVdVf1NEVLRiVZzpYtA+cNdwG2l18vPz0dISAh+//137Nq1C6tWrcLZs2fx5JNPYurUqVi+fHmxm4qFUigU2LlzJ2bPno29e/dixowZ0Gq1aNu2LS5evIgLFy7g4sWLNhcz9/Lygru7OwwGA3Jzcy1qmEBB7ZZ7rn///tiyZUuR5p6a69y5M4CCNIgGgwE1a9bkgy8379VZ0y017xJSuZXIeq6kfLCXjIBrsszNzYVEIsGkSZPwv//9D6+99hq+++47bNmyBd9++y1WrVqFcePGCW6aLA43Nzd88skn8PHxwZYtW/DOO+/Y3KZly5b8qOdOnTqhUaNGFjd23OLqQEFgNRgMMBgMkMlkCA4OFuUmUKPRoFGjRrh9+zbu3r1rkQbRZDLRSF5CCAXXisZ8IIy9vjpnuCbL7Oxsvl9QrVZjx44dGDt2LBYtWoSoqChMmTIFn3/+OXbu3Cn6fFlbpFIpVq5cCT8/P6xatQq1atVCx44d+UezZs1sJu633odCoSjxaWBPPvkkbt++jfDwcPTr149fQ9V8rVZCSNVFwbWCETIQRijrfkGlUolBgwZh4MCB2LhxI5YvX46zZ89i0KBBOH36tGjzYx2RSCSYP38+Xn311UIBMjc3t8SPLxS3rmx0dDS/NKBMJit2xikxbp4IIWWv5Nv7iKjUarXD2pFOp0NycjJfk3ImOTkZkZGRfL5coGAQ0NSpU3HixAkEBQUhOjoa48aNs1irtKSV9wQk165dA1CwwIFKpbLocxV67jnm35n5zRMhpOKi4FrBqFQq+Pv72+3Tc/XirNVqYTAYCuXU1ev1CAgIwKZNm6BQKHDkyBEsX7682OUvz4xGI65cuYKHDx86nQ519uxZAEC7du0sptwUJTBap8Z0pWmZC8wUjAkpXyi4VkDcBZXLb2tOyMU5OzubvyBrNBrI5XJ+BK1Op0NKSgqAgv7DVq1a8UsGrly5EocPHy5yuR8+fIhly5bxgak8OXnyJLp06YInnngCDRo0QI0aNdCrVy9Mnz4dn3zyCf744w8+6CYkJODevXuQSCRo3rw5jEYjH2CL0udq/p05u3myRjVdQson6nOtgBzllxUyxcM8e5C/vz/fT5ifn4+UlBRkZWXB09MTderUgVKpxJgxYxAdHY2dO3diwoQJOHbsmMupCmNjY9G/f3/cvXsXK1euxAsvvIBly5aVWspDe+7du4fFixfzCwgoFArk5eUhPT0d//77L/7991+L7X18fPhBVU2aNIGXlxeAgvOuUCj4f3OEZGISO0UiIaTsVcngKjQDEiDu/F1XMi85Oq75BdV8n+arqJgPhuEy/XD5boGCWqn1NB3u/81zRgMFF/9169YhJiYGf//9N0aPHo3jx487HeDErdoTGxuLZ555BjExMdBoNEhNTcWPP/6In376CWPGjMFrr70maDSy0WgUdA6NRqPDzFBAQd7jjz76CJs2bYLBYIBUKsXw4cOxcOFCNG3aFNHR0Th37hyuX7+OqKgo3L17FzExMUhPT+fTHbZv3x56vZ5P/WgymQqVz9YANKGZl4R8Vi47l0Qicbq9K3/3lUVsbKygdUYJEVuVDK5CiLX+alFIJBKHwdV8/qr5dvZqtFymH67G6ubmVmhUq/Xn5Zoqc3JyYDQaIZFIsH//fjzxxBO4c+cOpk6diu+++85hkHB3d8ejR48wYMAAxMTEoH79+ti9ezdSU1P5ptavv/4aBw4cwJQpU7B48WLUqFHD7v64oHTz5k0cPXoUf/31FyIiItC0aVP07NkTPXr0QMeOHVG9enW75TKZTNi7dy8WLVqE+Ph4AEC3bt0wf/58NGrUCF5eXvD09ES7du3QpEkT6HQ6KBQK6PV6xMfH4969e4iLi+Nr4gD4xQqsvw8ANjMxuZJ5ydZ21rXhqhg0hYiNjUVISIigAWYqlarYyUUIMSdhleiXmZGRAR8fH6Snp8Pb29vudkI+cnJyMh+IAgICRCmf0FNt6yItZDt7TZDcdo6aKM0/r3ngtX5PeHg4unfvjpycHCxatAjLli2zW7579+5hwIABuHv3Lho0aIBvv/0W7u7uAAoCzL179/D+++/jxIkTAAoucLNnz8b8+fMtasUPHjzgg+nx48f5oGiLQqFA586d0aNHD/To0QNPPvkkf3N09uxZzJ07F+fOnQNQsJLN0qVL0b9/f+Tl5fE3LdY3U0ajEXq93qIGZDQakZubizp16vDnSaVS8bmFHSnq98ux/q6E7i8jIwO+vr5Ofx9Cf0flXXh4ODp06IA9e/YgJCTE4bYajYYW+y5h3Pdx8eJFpylWyzOhvw8KrnaURM21pIOrs+0cBVdXFuHeuXMnJk6cCADYv38/hgwZUmib2NhY9OvXDzExMWjQoAGOHj0KPz8/fsoPd05VKhVOnz6Nt99+G+fPnwdQkG945syZSE5OxtGjRxEdHW2xb7lcji5duqBXr15o2bIlYmJicOrUKfzzzz/8YCyOm5sbOnTogICAAPz444/8sWfPno05c+ZAqVRaJNKwhau5m58r8+XktFqtwxsxWzVNWwuhW7P3/draHwXXwirLxbyyqCzfh9DfBzUL21HS66/a4kqAK8o+nA2E4vrunBk7diwuXbqETz75BBMnTkTDhg0tlhk8e/Ysxo0bZxFYuRpe3bp1C+3vqaeewunTp7F7926sXr0aUVFRWLFiBf+6VCpF+/bt0a1bNzzxxBPo0qULX0PMz89Hz549MX/+fDDGcOvWLfz999/4559/cOLECcTFxVmMTh43bhxWrFiBWrVqgTEGmUxW7JsnRyvgAOIvek55iQkp/8osuHKDP8xXGxF6B15ZFeeCy0lKSuJH+3JLo3HEHFm6evVqXLlyBX///Tf69euHw4cPIykpCR9++CFOnz4NAKhfv75FYDVnXvvjVpbp3bs3evTogT///BOHDh1CYGAgunbtioEDB8Ld3R35+fnIzc21uHFITk6GwWDg/63RaDB27FhMmTIFjDHcv38fJ0+exK1btzB48GA88cQTFuWwTgEphPVarfZyOHMcLXrOfQZbN0NCareEkPKpTIJrZGQkNmzYgNu3b6NLly7o2LEjBg8ezI94FBpguaTsnIyMjJIqcqko6WkVYtZ48vLy8Omnn+Lll19GeHg4evbsyb/m7u6OESNGYMWKFTYDKwB+ni0XOK5duwYPDw8EBQVh8uTJGDp0KN/UyjW9Aig0B1StVvOBDiioyer1er4ZPCAgAC+//LLdz2EeKLl/Owu0zmqqtra3N2WK6z+1dUMlxs0WIaRslHpwvXnzJrp27YrnnnsO9evXx6lTp/DVV1/hwoULeO+991wKsGFhYQ4H1FQ05k2G5v92hXmfnysBwJqzJmq9Xg9vb2/s378fEyZMwMmTJ+Hj44OXX34Z48aN49eNFSI1NRV5eXnQ6XQICgoCYBnAdDodPDw8+EBr7dGjR1AqldBoNHBzc4NCoShUuzT/XObPcUHV29vb4j3WwdV8GhNXPiHn1tl5dHRDpVarkZSUBIPBIErt1dW0jISUBCFTnyrDALNSDa6MMWzbtg39+/fHnj17AABxcXH49ttvsXjxYhgMBqxZs0ZwzXXRokWYN28e/++MjAy7NaWKori1Fa7WVdwaj7NyKJVK6PV6eHl54dChQ/j777/RqVMneHl5Qa/XF0o6bz0IyN/fnw8YXNMsFyC5z2EdFO0lspfL5XzeY41Gg6ysLIugaV0O85oqF7S54GbvhkSn0yEzMxP379+Ht7e34B+/s/PoKEgrlUpRvkvzz0BIWdFoNFCpVAgNDXW6rUqlQmRkZIUOsKUaXCUSCW7fvs1PxwCAoKAgTJo0CR4eHliyZAlq1aqFuXPnCtqfXC4v9wneXeWoJiN0wFNRmpet9+2s1mQdFF544QWL1wDwAU+n0yE2NtbiuzIPtObpF23htrO1cIB5Ey53XL1ezwdNoCB/sq2aKrct95qjkeEqlQopKSnw8PBATk6O3bJaK25Tv5hdBdS0TMpScHAwIiMjBSX1CA0NhVarpeAqBNfU27NnTxw6dAhRUVFo0qQJAMDLywsjR47EvXv3cPjwYYwZM8bpup2VlaNRu9a1IHsDaLisPUDBeReSEcg6k5B1rclWxidnuEFrOp0OcrkcBoMBNWrUsKg9KpVKpxmpgIKgyAVCrix6vR56vR4ajcYi+5FCoUBOTg7fpGyrpsodlwta5ucyOzubr31zwS0nJwd+fn58gKpevXqhYM8Y48+LeU2du3HgjsG95mxZOZPJVOi7LA4KrqSsBQcHV+iA6YpSS9zPXTzbtGmD+Ph4fPXVV0hNTeVf12g0GDx4MP7991/ExMSUeFmEPsriuFwGH+uHeYJ3R9txCQ+4gT32tnO0b3vPSSQSeHh4wN3dHXl5eUhLS+PTHFo/5HI5pFIp1Go1vLy8ULduXYvsT2q1GlKpFO7u7pDJZBaPnJwcpKamIicnh/83l8xBKpVCKpUiJSUFSUlJSElJ4Z+TSqXw9PREQEAAPD09IZFIkJ6ezge03NxceHp6QiaTwc3Nze75Mx8YZTAY+Lmu3MWB24f1Q6fTQavV8nNfdTpdof1zAT87O7tIfwfFeRBCSkeJ1lzv3buHU6dOIS0tDU2bNkXfvn3Rr18/zJ49GwsWLIBcLsf48eP53KyNGzd2mkmlKhOa0MK6hitkSoetGrOt/kDz5mOh/cO2Bmq5srgAV26u9mnezCuEr68v0tLS4Ovra3dQlHV5uVonV4vlPqOzQWJcuQHYPRa3H0q2T0jlVWLB9erVq3j66afRpUsXXL9+HZ6envDz88MPP/yAOXPmID8/H8uXL8f9+/cxaNAgtGzZEhs3boRWqy00P5O4xrqfTswpHdZrjwrtD3S1DNaBjGtCTUlJsRgF7CzYcWXkmmZdDWg6nY4fI+Dh4eH0eFyZvL29HfbfUmAlpHIrkeCakpKCl19+GRMnTsTq1auRnp6OQ4cOYeLEiXjmmWdw6NAhLFiwAH5+fvj6668xcuRI1KtXD9nZ2fjxxx9Rq1atkihWlWFdMxRzUIz5vlyZN+tqGezV0q2n6DhbmYcrI1fjNmfeL2p+LOtpPDqdzmIUc1HK7UhycjK0Wi00Gk2hBRUIIRVTiQTXhw8fwmg0YvLkyQAK1sDs168fmjVrhtu3b2PAgAG4ePEiJkyYgOeffx6JiYnIy8tDYGCgw1VRSNEolUrRBrO4kibR+n1ilMHWFB0h+7VVc7Y3r9U8gIsxv9Q8CxS3GL2fnx8fSLVaLQwGA7RaLQVXQiqJEhvQlJGRgatXr/L/zsrKgoeHB9avX4/U1FS8//77AAoGMrVs2RLt2rWjwEpcwo3EFRL8zAdRmb/fVr+oWq3m5+Ga4wYrmWdzMv+3PeZB3DyQcjQajdPpSISQiqVEaq6BgYFo2LAhdu3ahejoaLRo0QJjx47FhAkTMHr0aBw8eJBf6YRGMFZOxVmEgKvpcVzN+2s+CMp8H7bm6ro6F9i6udhWFihr5jVhjUbD11w5/v7+RaqxluWaw4QQx0SvuTLGoNFo8PHHHwMAtm/fjjlz5mDmzJn48MMPARSk6Hv48KHYhybliHkzrKu4oGU+ncXRtta1yaIe17zsXO5jc9Y1XZVKhdzc3EKpEa2Z14T9/f3RtGlTUZp/i3OOCSElS/Saq0QigclkQsuWLbFz504wxpCWlsYvNcYYQ0JCAlq3bi32oUk5UpxBVNaDiICCQT9cUgdzWq2WXwUoODhYlGkujvpizftrzRNTOErP6OxYrtTwrWurJbnQAyGk6IoVXPPz88EYs0hnaJ69h0sx5+PjAwC4ffs2duzYgWPHjmHlypXFOTQp58wzKXGZhczn25oHIuuFGqybObmVY8xTFZrT6/XIzc3lMzUpFAqLpQztsZfxyLwZ116GK1vzWRljgrIome/POjOWdfmsu03Mt7deIagqiY2NFZRGj5CyUuTgeuPGDSxbtgzx8fFo1KgR+vfvjzFjxkAqlcJoNBa6uCUlJeGrr77C7t278ddff6FZs2bFLnxVJ7S/uqy2s2Zvrquz7EHmNbScnByLmp6/vz90Oh2MRiNiY2NRt25dqFTCFrm3lcLRVp8tlynJVpl8fHz47YSu5mS+P/PPZv1eW+fF0fZVRWxsLEJCQgQtRGCefpKQ0lSk4BoVFYWuXbvihRdeQL9+/fDXX39h7dq1+P3337Fjxw7IZDLk5ubCw8ODf4+vry8mTJiAyZMn8xmZSNVS1GZMribLGCu0/qlKpULdunVx//59yOXyYifKcCXrVElMLXKGBi+B72Pfs2eP04xulWHpMlIxuRxcGWPYtWsX+vXrh927dwMAFixYgB07dmDr1q0YNWoUvvnmGz6w7tixA08//XSVSthMbBMjINkK0FyAFaP/kfoxK46QkBC0b9++rItBiE0uB1eJRIKHDx8iISGBf06lUmHixIlQKBTYtGkTFi1ahLCwMJw+fRqrVq3C0aNHsXPnTkH9YIQ4Yi9AFydwWw8qErsf096gpeJMVyKksqvoi6q7FFy5PqX27dvj1q1buHnzJt93qlQqMWLECERFReHYsWN4/Pgxunbtitdffx19+/alwEoKKco8zZIISGLmXnZl/yV9XEIqosqyqLpL81y5ARTPPvssoqOjsWbNGmRmZvKve3t7Y86cOTh//jyOHj0KAJg8eTLq168vYpFJZVGUeZpizu3U6XRITk4GgELZm8RkKzuUo+cJqcq4RdUvXrzo8LFnzx5+nnt5VKQBTQ0bNsT+/fsxcOBAqFQqLF26lB+R5+HhgXbt2llkoCHElqL0b4rZJ8oFajc3txLN6VsSTdmEVGaVYYxOkafiPPXUUzhw4ABGjBiB+Ph4jBgxAq1bt8bu3bsRFxeHhg0billOUgkVJbiIGZCKE6gdNU9TXyohpFhJJF544QWcPn0a8+bNw5tvvgk3Nze4u7vjl19+QZ06dcQqIyElojiB2lF/KfWlEkKKnf6wffv2+OGHH5CamoqsrCzUrFmTJm0Xk5AsP9x2QhIJCN0OKN8LKbhyXmwliLC1XVE/r61aL1c+89eEltle+YQqz98bIVWRKLmFvb29+VSHpGIpySZMsS/4JbE/Ifu0tY2tEc5cQBdSI6ZgSEjlVmLruZKKgVZWIYQQ8VFwreJoOgghhIivRBZLJxUH13zJ1VwpyJYcWtyckKqDgmsFUlL9o+ZNw3TRLzl0np2jpeRIZUHBtQIpqSkelKy+dBT3PFf2+bO0lBypTCi4ViAlFQQrU6ag8hyAinueK/v8WVpKjlQmFFwrEHsX5/IcUEpbZQ5AVaWFgZaSI5UBBddKoDIHFFfZC0Dl4QakuAOaKlMLAyFiKa9L01FwrQTKa42mLDJNqVQqKJXKQtvZugERu3wmk8nhdjSgiRDxlPel6Si4lkOupip0VhOqLNmAiptRiQtqJXU+nJXP1ZugyvK9EVISuKXphIwuDw0NhVarpeBKiDkx5oeWh7ml1KxLiLjK89J0lKGJlHuUopEQUtGUeXA1mUxlXQRSxvR6PZKTk+3Ob6QUjYSQiqbMmoWvXr2K/Px8tGvXrqyKQMoJZ6OdqTm14qPMS6SqKZPgeuXKFbRt2xZLly6l4ErKdLRzeZiiU5EJCZrJyckYNmwYZV4iVUqpB9fLly+ja9euWLhwId55551i7ctgMMBgMPD/zsjIKG7xSBlQKpVlFthojnDRuZqu8Ndff4W/v7/D7SjzEqksSjW43r59Gx06dMB7772HxYsXIy8vDwcPHsTNmzfRsGFDhISEoGPHjoL3FxYWhmXLlpVgiUllJ0atubLWfi9fvgxPT0+7r0dGRlK6QlJhiNXtkJWVJWi7UguuRqMRv/76KxhjCAwMBAAMHDgQWq0WeXl5yMjIQHBwMGbMmIGxY8cK2ueiRYswb948/t/p6ekIDg6mGmw5IXaSBkDcuZ9c+eRyOfLz8+3+3Tgrn1arRX5+PjIzM6HRaKDT6aDT6Rz2FZfFHFbu8zn7XrjXe/Xq5XSfSqUSbdu2RZ06dQQfn5DSJJfLoVQqBSWbcIXT6xsrRbGxsWz16tXM29ubBQQEsGHDhrHo6GjGGGMRERFs9OjRrE+fPiwhIaFI+3/w4AEDQA960MPB48GDB/Q7ogc9ivlw9juSMCawelFEt27dwtatW/Hhhx8CAJKSkrB9+3acPHkSq1evRps2bfht//jjDzzzzDM4e/YsnnjiCZePZTKZEB8fDy8vr2LXDDIyMlCnTh08ePAA3t7exdpXSaJyiquilBNwvayMMWRmZiIwMBBSqf1ZeNzviDGG4ODgcn0uKsL3RWUsvvJUPqG/oxJtFr5y5Qr69OkDvV6PCRMmoFWrVggICMCkSZPQv39/NG/eHEDBj1kqlcLLywshISHw8/Mr0vGkUimCgoLE/Ajw9vYu8y9TCCqnuCpKOQHXyurj4+N0G+53xDXjVoRzQWUUR3kvY3kpn6DfUUkdPCIiAp07d8bIkSOh0WjwzTff8K/VqFEDHTp0gLu7e0Eh/j/6Hz58GN7e3vD19S2pYhFCCCElrkSC66VLl9ClSxfMmTMHn376KaZPn469e/fi+vXr/DbmzbY3b97EwoULsWXLFmzduhXVq1cviWIRQgghpUL04Prw4UMMGjQIs2bNQlhYGACgW7duyMnJwYULFwAUjBzmREdHY/ny5fjzzz9x/PhxtG7dWuwiFYlcLse7774LuVxe1kVxiMopropSTqDky1oRzgWVURzlvYzlvXy2iD6gKSEhAefPn8cLL7xg8fwrr7yC48ePIyIiotCcwitXrsDf3x+1atUSsyiEEEJImRC15soYQ82aNS0CK5eYn5tj9Ouvv1o8DwCtW7emwEoIIaTSKPGpOBzGGLp06QJ/f3/8+OOPpXFIQgghpEyUypJzRqMREokES5cuxcWLF/Hzzz+XxmEJIYSQMlGs4Jqfn4+8vDyL52ytzyqTyQAArVq1gkwmw8mTJ2kdV0IIIZVWkZuFb9y4gWXLliE+Ph6NGjVC//79MWbMGAAFNVUuoFr78ssv0bFjR7Ro0aLopSaEEELKsSIF16ioKHTq1AkvvPACGjdujL/++guZmZlo06YNduzYAQDIzc2Fh4cH/578/Hy4uZXZ2uxVEpf5ioiDzud/6FwQ4pjLwZUxhiVLluDWrVs4cOAAgIIlt3bs2IGtW7ciJCTEIhvTjh070LdvX0GrZpRnJpMJjDGLGjlzYTWX0nT16lXk5+eX+4XoK8o5pfP5n4pwLirK3xVQfstV0ZTH8+jyradEIsHDhw+RkJDAP6dSqTBx4kTMnj0b0dHRWLRoEQDg9OnTWLVqFRYvXmyROKKiiYyMxIwZM/DMM89gyZIl+P777wEUnItSGmwt2JUrV9CmTZtyPyK7opxTOp//qQjnoiL8XV24cAH/+9//AJTN0oNCZGZmIj4+HpmZmfy4mvI2Tqa8n0eXgiv3x9m+fXsYjUbcvHmTf02pVGLEiBHo168fjh07hsePH6Nr1654/fXXsXz5crt9sOXdzZs30bVrV2RlZaF+/fo4deoU5s6diyVLlgAoXz/ay5cv48knn8TChQvxzjvvlHVx7Koo55TO538qwrmoCH9XERER6NWrF7y8vMq0HI5cvXoVTz31FJ5++ml06NABM2bMwK1btyCVSstNJakinMcired6+/ZtptFo2IQJE1hGRobFa/Hx8UwqlbJvv/22KLsuV0wmE5s7dy4bOXIk/9yDBw/YRx99xJRKJVu4cGEZls5SdHQ0k0qlbOXKlYwxxnJzc9nevXvZu+++y3bt2sXOnz9fxiUsUFHOKZ3P/1SEc1ER/q4uX77MVCoVW7Bggd1tjEZjKZaosPv37zN/f3/22muvsWPHjrGVK1eyvn37stq1a7Pw8HDGGGP5+fllWsaKcB4ZY6xII4waNmyI/fv3Y+DAgVCpVFi6dCk0Gg0AwMPDA+3atSvysnHliUQiwe3bt/nVewAgKCgIkyZNgoeHB5YsWYJatWph7ty5ZVjKgtHZv/76KxhjCAwMBAAMHDgQWq0WeXl5yMjIQHBwMGbMmIGxY8eWaVkrwjml8/mfinIuyvvf1aNHj9C1a1eMHDkSa9euhcFgwJIlSxAVFYWUlBQMHjwYo0ePRlBQUJn2H54/fx5NmjTB6tWroVQq0bt3bzz77LNYvnw5+vTpg7///hutW7cuszJWlPMIoGg1V84PP/zA5HI5Gzp0KPv666/ZtWvX2BtvvMFq1KjBYmNjix/6yxB357N27VrWtWtXduvWLYvXk5OT2cKFC1nPnj3Zo0ePyqKIFuLj49nq1auZt7c3CwgIYMOGDWPR0dGMMcYiIiLY6NGjWZ8+fVhCQkIZl7RinNPY2Nhyfz5NJhNjrOTPZ3k/F6V1HorjwoULrFevXqxly5YsOjqaPfvss6x79+5szpw5bNSoUeyJJ55gw4cPL/Pf5/bt25larWbJyckWz9+8eZMNGTKEdejQgT18+LCMSldxziNjjBUruDLG2MWLF1mvXr1YcHAwa9CgAWvatCnffFAZ/PHHH6xevXrsnXfeYSkpKRav/fPPP8zDw4OdPn26TMqWnZ1t8e+kpCS2cuVKNmDAAHb58mWL137//XcmkUjYuXPnSrOIjLGCwH/27Fn+30ePHi2X5/TmzZts7ty5/L8TExPL5fm09vvvv4t+PiviuSiJ8yCmCxcusP79+zOJRMKeffZZptVq+de2b9/OGjVqxI4cOVImZeNuUC5dusTatGnDtm7dynJyciy2+e2331ibNm3YTz/9VBZF5JXn82iu2MGVMcbS09NZTEwMu3r1aqE7nork7t277OOPP2bvvfceO3jwIP/8xx9/zGQyGVu5cqXFXVtiYiJr06ZNmfxgr127xlq3bl2obzshIYGdP3+e5ebmMsb+q4GfOXOGNW/enN25c6dUyxkREcEaNWrE1qxZY9GaUd7OaUREBPPz82MqlYpduXKFf768nc+YmBi2Z88etnHjRvbHH3/wz3/00Ueinc+KcC5K4zwUV15eHmPsv8DFGGOnTp1iCxYsYH/99RdjzLL/0t/fn73zzjulVj57Ro0axZo3b86OHj1aqH+1WbNmbN68eaVaHoPBwBhjTK/X88+dOXOGLVy4sFyfR1GCa2Vw5coVVrNmTfbcc8+xRo0asS5durDvvvuOf33dunXMy8uLTZkyhf3000/s3r17bMGCBax27dosPj6+1Mu7cOFCplKpWJs2bdg333xj8Zr5j5nzxhtvsCeffLLQHX1Jun37NvP392dz5861OQji/fffLxfn9PLly0yhULDp06ez4OBg9tZbb1m8Xl7O55UrV5i/vz8bNGgQa9iwIWvTpg3r06cPy8rKYowVNIsW93xWhHNRGuehuK5fv84mTZrEnnrqKTZ//nyLa0lMTAwfMBgrCAxJSUmsW7dupToQNDo6mr333nts9OjR7IsvvmA3btzgX+vUqRNr0qQJ+/nnn/mbKcYYe/7559mHH35YamWMjIxk48ePZz169GDTpk1jJ0+e5F+7f/++RdnK6jzaQ8GVMXbr1i0WGBjI3nrrLWYymVhiYiJr1aoV27Jli8V2X3zxBevbty9TqVSsefPmrG7dumXWBP7uu++ybt26sVmzZrGQkBC2b98+/jXzQBYZGckWLFjAfHx8WERERKmW8b333mMvvvgiY6yglvPpp5+yZcuWseXLl/O1ni+//LJMz2l4eDhTKpXszTffZIwxFhYWxho0aMCuXbtmc/uyOp9arZa1adOGvfHGG4wxxtLS0tiOHTuYRCJh3bp1Y0lJSYyx4v2NVoRzURrnobgiIyOZr68vmzBhAps0aRIbMmQIUyqVbPny5Xbf8+6777LGjRuze/fulUoZr169ymrUqMGGDh3K+vTpwxo2bMjmzJnDMjMz+W2eeuop1qJFCzZhwgS2detWNmPGDObt7c1u3rxZKmW8cuUKq1atGps6dSqbPXs26927N5s5c6ZFQLVW2ufRkSofXHNyctjcuXPZhAkTWG5uLn9nPnbsWDZjxgw2d+5ctnr1an77hIQEdvXqVRYeHl6mnebHjx9nr732Grt16xZ7+eWXWfPmzdnvv//OVqxYwU6dOsWMRiOLiopiY8aMYW3btmWXLl0q9TJOmTKFv1B36tSJ9ezZk3Xu3JnVrVuXNWjQgN2/f58xxtijR4/K5JzGxcWxoKAg9vrrr/PPnThxggUGBrKdO3cyxixvVMryfEZERLCWLVuy27dv88/FxcWxkJAQVqNGDda+fXv++aSkJJfPZ0U5FyV9HsQwf/58NnjwYP7fycnJbNOmTczd3b1QS8CRI0fYq6++ynx8fErtPD548IA1b96c/20yxtiOHTtYtWrVWExMjMW2K1asYM8++yxr3rw569+/f6H+9pISExPDGjRoYHG+wsLC2NixY1lOTg7T6XQW25fFeXSmygfX/Px8dubMGYsvZMWKFUwqlbLJkyezF198kTVr1owNGzas7Appw6lTp1hISAjT6/Xs2rVrbObMmaxatWpMIpHwd++MFVyMyqLZmjHGJk+ezAYOHMj27dvHBgwYwNLT01l2djZLSEhgXbt2Ze3atSuTcnEePXrEfvjhh0LPT5o0iTVs2JBvZjRXVuczIiKCBQcHs0OHDvHP3bx5k7Vp04bt3buX1atXj78JtNV060xFORclfR6Ky2QysaFDh7IRI0ZYPG80Gtnnn3/OpFIp27RpE//8mjVr2KBBg9jVq1dLrXxffvkle/HFF1lMTAzfgmQwGFjLli3Z8ePHGWPMonZoMplYWlqaRZ9nSZfx22+/ZdOmTbO4KZo/fz5r06YNa9GiBRs4cCD7/PPP+ddK+zwKUeWDK2P/DTxgrOCOPDAwkP3444/8c9u2bWMNGzYsteYQIR4/fsy6du3K1yaee+45plarWf369dnhw4fLtGzcRe3vv/9mXbt2ZV26dGGTJ09mjP03CObs2bOsTp06ZZaAwNaFlyvbsWPHWMOGDfl+m/IwIT05OZk99dRTbOjQoWzNmjXs559/Zr6+vvyI3uHDh7NJkyYVad8V6VyU5HkQy0cffcQaN25s0YfJWMGAnHfeeadQzTs9Pb1Uy/fbb7+xjz76yOK5nJwcVq9ePbZ3795SLYs9iYmJFtfb5cuXM7VazT7++GO2ZcsWtnDhQlazZk2LPtjSPo/O0LIWgMVqPY0bN0ZERASef/55PpemRqOBh4cHfH19y6iEhfn6+kIul+P8+fMYP348Ll26hO3bt+OZZ57B1KlT+ZyqZYGbuN28eXM0btwYFy5cwP379wGAX0lFqVTC09MTKpWqTMtojitbr169oNFosHPnTovnywpjDBqNBh9//DEAYPv27ZgzZw5mzpyJDz/8EAAQEBCAhw8fFmn/FeVclPR5EEvHjh3h4+ODHTt2IC4ujn9eoVBgwIABePjwIRITE/nnvb29S7V8/fv3x5w5cwD8l9LWw8MD1apVs0jCsXfvXly8eLFUy8YJCAhA06ZN+X9rtVocOHAAs2bNwtSpUzFx4kRIJBL+ugKU/nl0htaAM8P+P6MHl12Ku5CcPn0aDRo0gFqtLsvi8bjlvjQaDQYOHAhfX1/8/PPPaNu2LZo0aQJ3d3e0bNmyTMvIXQiXLVuGjIwM/PLLL5g+fTo2b96M1NRUHD58GAqFAv7+/mVaTmvcWsRLly7FxIkT8fPPP+O5554r0zJJJBKYTCa0bNkSO3fuBGMMaWlpqFu3LoCCc52QkIDWrVuLetzydi7K6jy4qnv37njppZewYcMGKBQKjB8/Hg0aNAAANG3aFEFBQTAYDGVaRg53YyWRSKBWq6FQKAAAixYtwubNm8ssuHK4a/KGDRsA/Hft8/b2Rt26dVGzZs0yLZ9DZVVlLgt5eXmFRpo5auZKSUlhixYtYn5+fhbz/UqDkLIePXqUderUqVDTqvXk75LkqJzcfx88eMAWLlzIatWqxXx9fVn79u1ZjRo1SnVUsKvfPTfA54033ijVplBXyxkdHc0WL17MqlWrxiIjI0vkGGVxLkrjPJQE8zKuXLmSNW3alI0ePZr99ttvLDo6mi1cuJDVrl27TLMc2ZKbm8uaN2/OvvvuO/bee+8xpVJZLnJGc6y7LhYvXsxatWpVZuNJhKgywfX69ets5MiRrHv37mz8+PHs66+/5l+zNQfz999/Z1OmTGH169cv9dFnQsrK/YjNszSV9gAOV8sZHx/Ptm/fzo4cOVKqQ+Vd/e45O3futDsNpSS4Ws7ExES2dOlSVqdOHcE3KhXhXJTGeSguR+fKPMDu3LmTDR48mEkkEtayZUtWr169clFGa3l5eaxr166sWbNmpRpYXV0E4NatW2z+/PmsWrVqpTZyuahcXiy9IoqKikKnTp3wwgsvoHHjxvjrr7+QmZmJNm3aYMeOHQCA3NxceHh48O+Jj4/H0aNH0b17d9SrV69cldVgMEAul/Pv4ZpKSlNRzmlZKEo58/PzLfrhy2s5c3NzkZCQADc3Nz6pvtjHKO1zURrnQYwy/vjjj3jppZdQq1Ytm9uYn7fs7GzExMTwXTkBAQHloozMLLG9Xq9Hv379cOvWLfz111+l0rTuahlv3LiBzz77DGfPnsXmzZvRpk2bEi9jsZRtbC95JpOJvfXWW2z48OH8c9nZ2Wzjxo2sVatWFktUMVYw+ZyrVZV2TbAoZS2LBRKonOIqSjm5OcIleYzSPhelcR6KKzo6mlWvXp1JJBK2aNEim+ley2IKkLmilvHLL78stOhBSSlqGSMiIlhiYmJpFLHYKn1wZYyx8ePHs+7du1s8p9Pp2Pbt21m7du34ydSnTp1ijRo1YmPHjmV5eXll8iNxtayhoaFlsr4ilbPilbMinIvyXMasrCw2ceJENn78eLZx40YmkUjYwoUL7eZTX7NmjcOsTOWljEuXLqUyloBKHVy54Pjxxx+zLl26FBrokJ6ezl5//XXWuXNnlpqayhgrmNN69+5dKiuVs9KUsyKci4pQRp1OxzZt2sSnGv3mm2/sBoaUlBQ2atQo1rlz51LNP13UMpqvLENlFEelDq6c27dvM41GwyZMmMAyMjIsXouPj2dSqbRcJHpmrOKUlcoprtIoZ0U4F+W9jNaZqvbt28ckEglbsGABf/HPz89njx8/ZikpKWUympXKWD5UieDKWMG0FblczmbOnGlxd6TValmHDh3YsWPHyq5wVipKWamc4iqNclaEc1ERypifn8/Xtvfu3cvXvB4+fMjmzJnDhgwZUqpT4qiM5U+VCa6MMfbDDz8wuVzOhg4dyr7++mt27do19sYbb7AaNWqUyUAWRypKWamc4iqNclaEc1ERymgymfhpN/v27WPu7u6sadOmzM3NrcxWy7JGZSw7VSq4MsbYxYsXWa9evVhwcDBr0KABa9q0abn9AitKWamc4iqNclaEc1ERymgymfiaV58+fVj16tVLPeGMM1TGslEl5rlay8jIQGpqKrKyslCzZk1oNJqyLpJdFaWsVE5xlUY5K8K5qAhlNBqNWLhwIdavX4/Lly+XefpFW6iMpa9KBldCCBGL0WjEzp070aFDB7Rt27asi2MTlbH0UXAlhJBiYmaZhMorKmPpouBKCCGEiIzWcyWEEEJERsGVEEIIERkFV0IIIURkFFwJIYQQkVFwJYQQQkRGwZUQQggRGQVXQgghRGQUXAkhhBCRUXAlhBBCREbBlRBCCBHZ/wEOp/KogV5fHQAAAABJRU5ErkJggg==", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "z = flow(x_star[:, 0].flatten()).sample((4096,))\n", + "x = likelihood(z).sample()\n", + "\n", + "fig = corner(x.numpy())\n", + "\n", + "overplot_points(fig, x_star[:, 0].numpy())" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "z = flow(x_star[:, 1].flatten()).sample((4096,))\n", + "x = likelihood(z).sample()\n", + "\n", + "fig = corner(x.numpy())\n", + "\n", + "overplot_points(fig, x_star[:, 1].numpy())" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "pyro", + "language": "python", + "name": "pyro" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.18" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/tutorial/source/vae_flow_prior.ipynb b/tutorial/source/vae_flow_prior.ipynb new file mode 100644 index 0000000000..345a37d94f --- /dev/null +++ b/tutorial/source/vae_flow_prior.ipynb @@ -0,0 +1,336 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Variational Autoencoder with a Normalizing Flow prior\n", + "\n", + "Using a normalizing flow as prior for the latent variables instead of the typical standard Gaussian is an easy way to make a variational autoencoder (VAE) more expressive. This notebook demonstrates how to implement a VAE with a normalizing flow as prior for the MNIST dataset. We strongly recommend to read [Pyro's VAE tutorial](vae.ipynb) first.\n", + "\n", + "> In this notebook we use [Zuko](https://zuko.readthedocs.io/) to implement normalizing flows, but similar results can be obtained with other PyTorch-based flow libraries." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [], + "source": [ + "import pyro\n", + "import torch\n", + "import torch.nn as nn\n", + "import torch.utils.data as data\n", + "import zuko\n", + "\n", + "from pyro.contrib.zuko import ZukoToPyro\n", + "from pyro.optim import Adam\n", + "from pyro.infer import SVI, Trace_ELBO\n", + "from torch import Tensor\n", + "from torchvision.datasets import MNIST\n", + "from torchvision.transforms.functional import to_tensor, to_pil_image\n", + "from tqdm import tqdm" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Data\n", + "\n", + "The [MNIST](https://wikipedia.org/wiki/MNIST_database) dataset consists of 28 x 28 grayscale images representing handwritten digits (0 to 9)." + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [], + "source": [ + "trainset = MNIST(root='', download=True, train=True, transform=to_tensor)\n", + "trainloader = data.DataLoader(trainset, batch_size=256, shuffle=True)" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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", + "text/plain": [ + "" + ] + }, + "execution_count": 3, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "x = [trainset[i][0] for i in range(16)]\n", + "x = torch.cat(x, dim=-1)\n", + "\n", + "to_pil_image(x)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Model\n", + "\n", + "As for the [previous tutorial](vae.ipynb), we choose a (diagonal) Gaussian model as encoder $q_\\psi(z | x)$ and a Bernoulli model as decoder $p_\\phi(x | z)$. " + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [], + "source": [ + "class GaussianEncoder(nn.Module):\n", + " def __init__(self, features: int, latent: int):\n", + " super().__init__()\n", + "\n", + " self.hyper = nn.Sequential(\n", + " nn.Linear(features, 1024),\n", + " nn.ReLU(),\n", + " nn.Linear(1024, 1024),\n", + " nn.ReLU(),\n", + " nn.Linear(1024, 2 * latent),\n", + " )\n", + "\n", + " def forward(self, x: Tensor):\n", + " phi = self.hyper(x)\n", + " mu, log_sigma = phi.chunk(2, dim=-1)\n", + "\n", + " return pyro.distributions.Normal(mu, log_sigma.exp()).to_event(1)\n", + "\n", + "\n", + "class BernoulliDecoder(nn.Module):\n", + " def __init__(self, features: int, latent: int):\n", + " super().__init__()\n", + "\n", + " self.hyper = nn.Sequential(\n", + " nn.Linear(latent, 1024),\n", + " nn.ReLU(),\n", + " nn.Linear(1024, 1024),\n", + " nn.ReLU(),\n", + " nn.Linear(1024, features),\n", + " )\n", + "\n", + " def forward(self, z: Tensor):\n", + " phi = self.hyper(z)\n", + " rho = torch.sigmoid(phi)\n", + "\n", + " return pyro.distributions.Bernoulli(rho).to_event(1)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "However, we choose a [masked autoregressive flow](https://arxiv.org/abs/1705.07057) (MAF) as prior $p_\\phi(z)$ instead of the typical standard Gaussian $\\mathcal{N}(0, I)$. Instead of implementing the MAF ourselves, we borrow it from the [Zuko](https://github.com/probabilists/zuko) library. Because Zuko distributions are very similar to Pyro distributions, a thin wrapper (`ZukoToPyro`) is sufficient to make Zuko and Pyro 100% compatible." + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "VAE(\n", + " (encoder): GaussianEncoder(\n", + " (hyper): Sequential(\n", + " (0): Linear(in_features=784, out_features=1024, bias=True)\n", + " (1): ReLU()\n", + " (2): Linear(in_features=1024, out_features=1024, bias=True)\n", + " (3): ReLU()\n", + " (4): Linear(in_features=1024, out_features=32, bias=True)\n", + " )\n", + " )\n", + " (decoder): BernoulliDecoder(\n", + " (hyper): Sequential(\n", + " (0): Linear(in_features=16, out_features=1024, bias=True)\n", + " (1): ReLU()\n", + " (2): Linear(in_features=1024, out_features=1024, bias=True)\n", + " (3): ReLU()\n", + " (4): Linear(in_features=1024, out_features=784, bias=True)\n", + " )\n", + " )\n", + " (prior): MAF(\n", + " (transform): LazyComposedTransform(\n", + " (0): MaskedAutoregressiveTransform(\n", + " (base): MonotonicAffineTransform()\n", + " (order): [0, 1, 2, 3, 4, ..., 11, 12, 13, 14, 15]\n", + " (hyper): MaskedMLP(\n", + " (0): MaskedLinear(in_features=16, out_features=256, bias=True)\n", + " (1): ReLU()\n", + " (2): MaskedLinear(in_features=256, out_features=256, bias=True)\n", + " (3): ReLU()\n", + " (4): MaskedLinear(in_features=256, out_features=32, bias=True)\n", + " )\n", + " )\n", + " (1): MaskedAutoregressiveTransform(\n", + " (base): MonotonicAffineTransform()\n", + " (order): [15, 14, 13, 12, 11, ..., 4, 3, 2, 1, 0]\n", + " (hyper): MaskedMLP(\n", + " (0): MaskedLinear(in_features=16, out_features=256, bias=True)\n", + " (1): ReLU()\n", + " (2): MaskedLinear(in_features=256, out_features=256, bias=True)\n", + " (3): ReLU()\n", + " (4): MaskedLinear(in_features=256, out_features=32, bias=True)\n", + " )\n", + " )\n", + " (2): MaskedAutoregressiveTransform(\n", + " (base): MonotonicAffineTransform()\n", + " (order): [0, 1, 2, 3, 4, ..., 11, 12, 13, 14, 15]\n", + " (hyper): MaskedMLP(\n", + " (0): MaskedLinear(in_features=16, out_features=256, bias=True)\n", + " (1): ReLU()\n", + " (2): MaskedLinear(in_features=256, out_features=256, bias=True)\n", + " (3): ReLU()\n", + " (4): MaskedLinear(in_features=256, out_features=32, bias=True)\n", + " )\n", + " )\n", + " )\n", + " (base): Unconditional(DiagNormal(loc: torch.Size([16]), scale: torch.Size([16])))\n", + " )\n", + ")" + ] + }, + "execution_count": 5, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "class VAE(nn.Module):\n", + " def __init__(self, features: int, latent: int = 16):\n", + " super().__init__()\n", + "\n", + " self.encoder = GaussianEncoder(features, latent)\n", + " self.decoder = BernoulliDecoder(features, latent)\n", + "\n", + " self.prior = zuko.flows.MAF(\n", + " features=latent,\n", + " transforms=3,\n", + " hidden_features=(256, 256),\n", + " )\n", + "\n", + " def model(self, x: Tensor):\n", + " pyro.module(\"prior\", self.prior)\n", + " pyro.module(\"decoder\", self.decoder)\n", + "\n", + " with pyro.plate(\"batch\", len(x)):\n", + " z = pyro.sample(\"z\", ZukoToPyro(self.prior()))\n", + " x = pyro.sample(\"x\", self.decoder(z), obs=x)\n", + "\n", + " def guide(self, x: Tensor):\n", + " pyro.module(\"encoder\", self.encoder)\n", + "\n", + " with pyro.plate(\"batch\", len(x)):\n", + " z = pyro.sample(\"z\", self.encoder(x))\n", + "\n", + "vae = VAE(784, 16).cuda()\n", + "vae" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Training\n", + "\n", + "We train our VAE with a standard stochastic variational inference (SVI) pipeline." + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "100%|██████████| 96/96 [24:04<00:00, 15.05s/it, loss=63.1]\n" + ] + } + ], + "source": [ + "pyro.clear_param_store()\n", + "\n", + "svi = SVI(vae.model, vae.guide, Adam({'lr': 1e-3}), loss=Trace_ELBO())\n", + "\n", + "for epoch in (bar := tqdm(range(96))):\n", + " losses = []\n", + "\n", + " for x, _ in trainloader:\n", + " x = x.round().flatten(-3).cuda()\n", + "\n", + " losses.append(svi.step(x))\n", + "\n", + " losses = torch.tensor(losses)\n", + "\n", + " bar.set_postfix(loss=losses.sum().item() / len(trainset))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "After training, we can generate MNIST images by sampling latent variables from the prior and decoding them." + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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", + "text/plain": [ + "" + ] + }, + "execution_count": 7, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "z = vae.prior().sample((16,))\n", + "x = vae.decoder(z).mean.reshape(-1, 28, 28)\n", + "\n", + "to_pil_image(x.movedim(0, 1).reshape(28, -1))" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "pyro", + "language": "python", + "name": "pyro" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.18" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +}