diff --git a/README.md b/README.md index 148fa7fd0d..37d89481b2 100644 --- a/README.md +++ b/README.md @@ -1,24 +1,48 @@ # pydelfi -**NOTE:** currently only works with tensorflow <= 1.15; tensorflow 2 update coming soon. - **Density Estimation Likelihood-Free Inference** with neural density estimators and adaptive acquisition of simulations. The implemented methods are described in detail in [Alsing, Charnock, Feeney and Wandelt 2019](https://arxiv.org/abs/1903.00007), and are based closely on [Papamakarios, Sterratt and Murray 2018](https://arxiv.org/pdf/1805.07226.pdf), [Lueckmann et al 2018](https://arxiv.org/abs/1805.09294) and [Alsing, Wandelt and Feeney, 2018](https://academic.oup.com/mnras/article-abstract/477/3/2874/4956055?redirectedFrom=fulltext). Please cite these papers if you use this code! **Installation:** -The code is in python3 and has the following dependencies:
+The code is in python3. There is a Tensorflow 1 (most stable, see below) and Tensorflow 2 version that can be installed as follows:
+ +**Tensorflow 1 (stable)** + +This can be found on the master branch and has the following dependencies:
[tensorflow](https://www.tensorflow.org) (<=1.15)
[getdist](http://getdist.readthedocs.io/en/latest/)
-[emcee](http://dfm.io/emcee/current/)
+[emcee](http://dfm.io/emcee/current/) (>=3.0.2)
[tqdm](https://github.com/tqdm/tqdm)
[mpi4py](https://mpi4py.readthedocs.io/en/stable/) (if MPI is required)
You can install the requirements and this package with, + ``` +pip install tensorflow==1.15 pip install git+https://github.com/justinalsing/pydelfi.git ``` +(`tensorflow-gpu==1.15` for GPU acceleration instead of `tensorflow==1.15`) + or alternatively, pip install the requirements and then clone the repo and run `python setup.py install` +**Tensorflow 2** + +The Tensorflow 2 version can be found on the `tf2-tom` branch and can be installed as follows. We reccommend you do the install inside a virtual environment to keep version conflicts under control, ie., + +``` +mkdir ~/envs +virtualenv ~/envs/pydelfi +source ~/envs/pydelfi/bin/activate +``` + +Followed by a pip install of pydelfi: + +``` +pip install git+https://github.com/justinalsing/pydelfi.git@tf2-tom +``` + +Note: the Mixture Density Networks (MDN) in the tf2 version are currently not performing as well as in the tf1 version (but the Masked Autoregressive Flows are fine). We are getting ot the bottom of this, and also working on expanding the suite of conditional density estimators in a coming update. Watch this space. + **Documentation and tutorials:** Once everything is installed, try out either `cosmic_shear.ipynb` or `jla_sne.ipynb` as example templates for how to use the code; plugging in your own simulator and letting pydelfi do it's thing. diff --git a/examples/cosmic_shear.ipynb b/examples/cosmic_shear.ipynb index fb920fe798..f10076768c 100644 --- a/examples/cosmic_shear.ipynb +++ b/examples/cosmic_shear.ipynb @@ -6,6 +6,8 @@ "metadata": {}, "outputs": [], "source": [ + "import sys\n", + "import tensorflow as tf\n", "import numpy as np\n", "import matplotlib.pyplot as plt\n", "import getdist\n", @@ -13,12 +15,19 @@ "import pydelfi.priors as priors\n", "import pydelfi.ndes as ndes\n", "import pydelfi.delfi as delfi\n", - "import tensorflow as tf\n", + "import pydelfi.score as score\n", "import simulators.cosmic_shear.cosmic_shear as cosmic_shear\n", "import pickle\n", - "import pydelfi.score as score\n", - "tf.logging.set_verbosity(tf.logging.ERROR)\n", - "%matplotlib inline" + "import tensorflow_probability as tfp\n", + "tfd = tfp.distributions" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Set up the simulator\n", + "This must have the signature `simulator(parameters, seed, args, batch)` -> `np.array([batch, ndata])`" ] }, { @@ -27,37 +36,45 @@ "metadata": {}, "outputs": [], "source": [ - "### SET UP THE SIMULATOR ###\n", - "\n", - "# Set up the tomography simulations\n", "CosmicShearSimulator = cosmic_shear.TomographicCosmicShear(pz = pickle.load(open('simulators/cosmic_shear/pz_5bin.pkl', 'rb')),\n", " lmin = 10, lmax = 1000, n_ell_bins = 5, \n", " sigma_e = 0.3, nbar = 30, Area = 15000)\n", "\n", - "# Simulator function: This must be of the form simulator(theta, seed, args) -> simulated data vector\n", "def simulator(theta, seed, simulator_args, batch=1):\n", " return CosmicShearSimulator.simulate(theta, seed)\n", "\n", - "# Simulator arguments\n", "simulator_args = None" ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Set up the prior" + ] + }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [], "source": [ - "### SET UP THE PRIOR ###\n", + "lower = np.array([0, 0.4, 0, 0.4, 0.7]).astype('float32')\n", + "upper = np.array([1, 1.2, 0.1, 1.0, 1.3]).astype('float32')\n", + "prior_mean = np.array([0.3, 0.8, 0.05, 0.70, 0.96]).astype('float32')\n", + "prior_covariance = (np.eye(5)*np.array([0.1, 0.1, 0.05, 0.3, 0.3])**2).astype('float32')\n", + "prior_stddev = np.sqrt(np.diag(prior_covariance))\n", "\n", - "# Define the priors parameters\n", - "lower = np.array([0, 0.4, 0, 0.4, 0.7])\n", - "upper = np.array([1, 1.2, 0.1, 1.0, 1.3])\n", - "prior_mean = np.array([0.3, 0.8, 0.05, 0.70, 0.96])\n", - "prior_covariance = np.eye(5)*np.array([0.1, 0.1, 0.05, 0.3, 0.3])**2\n", - "\n", - "# Prior\n", - "prior = priors.TruncatedGaussian(prior_mean, prior_covariance, lower, upper)" + "prior = tfd.Blockwise([tfd.TruncatedNormal(loc=prior_mean[i], scale=prior_stddev[i], low=lower[i], high=upper[i]) for i in range(5)])" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Set up the compressor\n", + "Must have the signature `compressor(data, args)` -> `np.array([n_summaries])`
\n", + "In this case we are going to do Wishart score compression." ] }, { @@ -66,8 +83,6 @@ "metadata": {}, "outputs": [], "source": [ - "### SET UP THE COMPRESSOR ###\n", - "\n", "# Fiducial parameters\n", "theta_fiducial = np.array([0.3, 0.8, 0.05, 0.70, 0.96])\n", "\n", @@ -94,39 +109,85 @@ "compressor_args = None" ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Generate mock data vector" + ] + }, { "cell_type": "code", - "execution_count": 6, + "execution_count": 5, "metadata": {}, "outputs": [], "source": [ - "### GENERATE MOCK DATA VECTOR ###\n", - "\n", "seed = 0\n", "data = simulator(theta_fiducial, seed, simulator_args)\n", "compressed_data = compressor(data, compressor_args)" ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Create ensemble of NDEs" + ] + }, { "cell_type": "code", - "execution_count": null, + "execution_count": 6, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "WARNING:tensorflow:From /obs/njeffrey/envs/delfi2env/lib/python3.7/site-packages/tensorflow/python/ops/linalg/linear_operator_lower_triangular.py:167: calling LinearOperator.__init__ (from tensorflow.python.ops.linalg.linear_operator) with graph_parents is deprecated and will be removed in a future version.\n", + "Instructions for updating:\n", + "Do not pass `graph_parents`. They will no longer be used.\n", + "WARNING:tensorflow:From /obs/njeffrey/envs/delfi2env/lib/python3.7/site-packages/tensorflow_probability/python/distributions/distribution.py:334: calling TransformedDistribution.__init__ (from tensorflow_probability.python.distributions.transformed_distribution) with batch_shape is deprecated and will be removed after 2020-06-01.\n", + "Instructions for updating:\n", + "`batch_shape` and `event_shape` args are deprecated. Please use `tfd.Sample`, `tfd.Independent`, and broadcasted parameters of the base distribution instead. For example, replace `tfd.TransformedDistribution(tfd.Normal(0., 1.), tfb.Exp(), batch_shape=[2, 3], event_shape=[4])` with `tfd.TransformedDistrbution(tfd.Sample(tfd.Normal(tf.zeros([2, 3]), 1.),sample_shape=[4]), tfb.Exp())` or `tfd.TransformedDistribution(tfd.Independent(tfd.Normal(tf.zeros([2, 3, 4]), 1.), reinterpreted_batch_ndims=1), tfb.Exp())`.\n", + "WARNING:tensorflow:From /obs/njeffrey/envs/delfi2env/lib/python3.7/site-packages/tensorflow_probability/python/distributions/distribution.py:334: calling TransformedDistribution.__init__ (from tensorflow_probability.python.distributions.transformed_distribution) with event_shape is deprecated and will be removed after 2020-06-01.\n", + "Instructions for updating:\n", + "`batch_shape` and `event_shape` args are deprecated. Please use `tfd.Sample`, `tfd.Independent`, and broadcasted parameters of the base distribution instead. For example, replace `tfd.TransformedDistribution(tfd.Normal(0., 1.), tfb.Exp(), batch_shape=[2, 3], event_shape=[4])` with `tfd.TransformedDistrbution(tfd.Sample(tfd.Normal(tf.zeros([2, 3]), 1.),sample_shape=[4]), tfb.Exp())` or `tfd.TransformedDistribution(tfd.Independent(tfd.Normal(tf.zeros([2, 3, 4]), 1.), reinterpreted_batch_ndims=1), tfb.Exp())`.\n" + ] + } + ], "source": [ - "# Create an ensemble of NDEs\n", - "NDEs = [ndes.ConditionalMaskedAutoregressiveFlow(n_parameters=5, n_data=5, n_hiddens=[50,50], n_mades=5, act_fun=tf.tanh, index=0),\n", - " ndes.MixtureDensityNetwork(n_parameters=5, n_data=5, n_components=1, n_hidden=[30,30], activations=[tf.tanh, tf.tanh], index=1),\n", - " ndes.MixtureDensityNetwork(n_parameters=5, n_data=5, n_components=2, n_hidden=[30,30], activations=[tf.tanh, tf.tanh], index=2),\n", - " ndes.MixtureDensityNetwork(n_parameters=5, n_data=5, n_components=3, n_hidden=[30,30], activations=[tf.tanh, tf.tanh], index=3),\n", - " ndes.MixtureDensityNetwork(n_parameters=5, n_data=5, n_components=4, n_hidden=[30,30], activations=[tf.tanh, tf.tanh], index=4),\n", - " ndes.MixtureDensityNetwork(n_parameters=5, n_data=5, n_components=5, n_hidden=[30,30], activations=[tf.tanh, tf.tanh], index=5)]\n", + "NDEs = [ndes.ConditionalMaskedAutoregressiveFlow(\n", + " n_parameters=5,\n", + " n_data=5,\n", + " n_mades=5,\n", + " n_hidden=[30,30], \n", + " activation=tf.keras.layers.LeakyReLU(0.01),\n", + " kernel_initializer=tf.keras.initializers.RandomNormal(mean=0.0, stddev=1e-5, seed=None),\n", + " all_layers=True)]\n", + "\n", + "NDEs += [ndes.MixtureDensityNetwork(\n", + " n_parameters=5,\n", + " n_data=5, \n", + " n_components=i+1,\n", + " n_hidden=[30], \n", + " activation=tf.keras.layers.LeakyReLU(0.01))\n", + " for i in range(5)]\n", "\n", - "# Create the DELFI object\n", - "DelfiEnsemble = delfi.Delfi(compressed_data, prior, NDEs, Finv=Finv, theta_fiducial=theta_fiducial, \n", - " param_limits = [lower, upper],\n", - " param_names = ['\\Omega_m', 'S_8', '\\Omega_b', 'h', 'n_s'], \n", - " results_dir = \"simulators/cosmic_shear/results/\",\n", - " input_normalization='fisher')" + "NDEs += [ndes.SinhArcSinhMADE(\n", + " n_parameters=5,\n", + " n_data=5,\n", + " n_hidden=[64],\n", + " activation=tf.tanh,\n", + " kernel_initializer=tf.keras.initializers.RandomNormal(mean=0.0, stddev=1e-5, seed=None),\n", + " bias_initializer=tf.keras.initializers.RandomNormal(mean=0.0, stddev=1e-5, seed=None)\n", + " )]" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Create DELFI object" ] }, { @@ -135,8 +196,42 @@ "metadata": {}, "outputs": [], "source": [ - "# Do the Fisher pre-training\n", - "DelfiEnsemble.fisher_pretraining()" + "DelfiEnsemble = delfi.Delfi(compressed_data, prior, NDEs, \n", + " Finv=Finv, \n", + " theta_fiducial=theta_fiducial,\n", + " param_limits = [lower, upper],\n", + " param_names=['\\Omega_m', 'S_8', '\\Omega_b', 'h', 'n_s'], \n", + " results_dir=\"simulators/cosmic_shear/results\",\n", + " filename=\"cosmic_shear\",\n", + " optimiser=tf.keras.optimizers.Adam(lr=1e-4),\n", + " optimiser_arguments=None,\n", + " dtype=tf.float32,\n", + " posterior_chain_length=200,\n", + " nwalkers=500,\n", + " input_normalization=\"fisher\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Fisher pre-training to initialize NDEs" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [], + "source": [ + "# DelfiEnsemble.fisher_pretraining(n_batch=5000, epochs=1000, patience=20, plot=True)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Sequential Neural Likelihood" ] }, { @@ -151,22 +246,15 @@ "n_populations = 39\n", "\n", "# Do the SNL training\n", - "DelfiEnsemble.sequential_training(simulator, compressor, n_initial, n_batch, n_populations, patience=10, save_intermediate_posteriors=True)" + "DelfiEnsemble.sequential_training(simulator, compressor, n_initial, n_batch, n_populations, patience=10, plot=True, save_intermediate_posteriors=True)" ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] } ], "metadata": { "kernelspec": { - "display_name": "Python 3", + "display_name": "delfi2env", "language": "python", - "name": "python3" + "name": "delfi2env" }, "language_info": { "codemirror_mode": { @@ -178,7 +266,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.6.5" + "version": "3.7.3" } }, "nbformat": 4, diff --git a/examples/cosmic_shear_prerun_sims.ipynb b/examples/cosmic_shear_prerun_sims.ipynb index 95462cd77e..cde2503369 100644 --- a/examples/cosmic_shear_prerun_sims.ipynb +++ b/examples/cosmic_shear_prerun_sims.ipynb @@ -13,18 +13,21 @@ }, { "cell_type": "code", - "execution_count": 1, + "execution_count": 6, "metadata": {}, "outputs": [], "source": [ + "import sys\n", + "sys.path.append('/Users/justinalsing/Dropbox/science/pydelfi-tf2/pydelfi/pydelfi')\n", + "\n", "import numpy as np\n", "import matplotlib.pyplot as plt\n", - "import pydelfi.priors as priors\n", - "import pydelfi.ndes as ndes\n", - "import pydelfi.delfi as delfi\n", + "import priors as priors\n", + "import ndes as ndes\n", + "import delfi as delfi\n", "import tensorflow as tf\n", - "tf.logging.set_verbosity(tf.logging.ERROR)\n", - "%matplotlib inline" + "import tensorflow_probability as tfp\n", + "tfd = tfp.distributions" ] }, { @@ -40,13 +43,13 @@ }, { "cell_type": "code", - "execution_count": 2, + "execution_count": 7, "metadata": {}, "outputs": [], "source": [ "lower = np.array([0, 0.4, 0, 0.4, 0.7])\n", "upper = np.array([1, 1.2, 0.1, 1.0, 1.3])\n", - "prior = priors.Uniform(lower, upper)" + "prior = tfd.Blockwise([tfd.Uniform(low=lower[i], high=upper[i]) for i in range(5)])" ] }, { @@ -65,7 +68,7 @@ }, { "cell_type": "code", - "execution_count": 3, + "execution_count": 8, "metadata": {}, "outputs": [], "source": [ @@ -93,17 +96,35 @@ }, { "cell_type": "code", - "execution_count": 4, + "execution_count": 9, "metadata": {}, "outputs": [], "source": [ - "NDEs = [ndes.ConditionalMaskedAutoregressiveFlow(n_parameters=5, n_data=5, n_hiddens=[50,50], n_mades=5, act_fun=tf.tanh, index=0),\n", - " ndes.MixtureDensityNetwork(n_parameters=5, n_data=5, n_components=1, n_hidden=[30,30], activations=[tf.tanh, tf.tanh], index=1),\n", - " ndes.MixtureDensityNetwork(n_parameters=5, n_data=5, n_components=2, n_hidden=[30,30], activations=[tf.tanh, tf.tanh], index=2),\n", - " ndes.MixtureDensityNetwork(n_parameters=5, n_data=5, n_components=3, n_hidden=[30,30], activations=[tf.tanh, tf.tanh], index=3),\n", - " ndes.MixtureDensityNetwork(n_parameters=5, n_data=5, n_components=4, n_hidden=[30,30], activations=[tf.tanh, tf.tanh], index=4),\n", - " ndes.MixtureDensityNetwork(n_parameters=5, n_data=5, n_components=5, n_hidden=[30,30], activations=[tf.tanh, tf.tanh], index=5)]\n", - " " + "NDEs = [ndes.ConditionalMaskedAutoregressiveFlow(\n", + " n_parameters=5,\n", + " n_data=5,\n", + " n_mades=5,\n", + " n_hidden=[30,30], \n", + " activation=tf.keras.layers.LeakyReLU(0.01),\n", + " kernel_initializer=tf.keras.initializers.RandomNormal(mean=0.0, stddev=1e-5, seed=None),\n", + " all_layers=True)]\n", + "\n", + "NDEs += [ndes.MixtureDensityNetwork(\n", + " n_parameters=5,\n", + " n_data=5, \n", + " n_components=i+1,\n", + " n_hidden=[30], \n", + " activation=tf.keras.layers.LeakyReLU(0.01))\n", + " for i in range(5)]\n", + "\n", + "NDEs += [ndes.SinhArcSinhMADE(\n", + " n_parameters=5,\n", + " n_data=5,\n", + " n_hidden=[64],\n", + " activation=tf.tanh,\n", + " kernel_initializer=tf.keras.initializers.RandomNormal(mean=0.0, stddev=1e-5, seed=None),\n", + " bias_initializer=tf.keras.initializers.RandomNormal(mean=0.0, stddev=1e-5, seed=None)\n", + " )]" ] }, { @@ -119,16 +140,22 @@ }, { "cell_type": "code", - "execution_count": 5, + "execution_count": 11, "metadata": {}, "outputs": [], "source": [ "DelfiEnsemble = delfi.Delfi(compressed_data, prior, NDEs, \n", - " Finv = Finv, \n", - " theta_fiducial = theta_fiducial, \n", + " Finv=Finv, \n", + " theta_fiducial=theta_fiducial,\n", " param_limits = [lower, upper],\n", - " param_names = ['\\Omega_m', 'S_8', '\\Omega_b', 'h', 'n_s'], \n", - " results_dir = \"simulators/cosmic_shear/results_prerun/\",\n", + " param_names=['\\Omega_m', 'S_8', '\\Omega_b', 'h', 'n_s'], \n", + " results_dir=\"simulators/cosmic_shear/results\",\n", + " filename=\"cosmic_shear\",\n", + " optimiser=tf.keras.optimizers.Adam(lr=1e-4),\n", + " optimiser_arguments=None,\n", + " dtype=tf.float32,\n", + " posterior_chain_length=200,\n", + " nwalkers=500,\n", " input_normalization=\"fisher\")" ] }, @@ -141,7 +168,7 @@ }, { "cell_type": "code", - "execution_count": 6, + "execution_count": 12, "metadata": {}, "outputs": [], "source": [ @@ -161,7 +188,7 @@ }, { "cell_type": "code", - "execution_count": 7, + "execution_count": 13, "metadata": { "scrolled": true }, @@ -169,111 +196,37 @@ { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "1c72c67e70074f2993bc72ba749641a4", - "version_major": 2, - "version_minor": 0 - }, - "text/plain": [ - "HBox(children=(IntProgress(value=0, description='Training', max=300, style=ProgressStyle(description_width='in…" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "application/vnd.jupyter.widget-view+json": { - "model_id": "c3c5cdf85dc3456aaa10d2107e6e1451", - "version_major": 2, - "version_minor": 0 - }, - "text/plain": [ - "HBox(children=(IntProgress(value=0, description='Training', max=300, style=ProgressStyle(description_width='in…" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "application/vnd.jupyter.widget-view+json": { - "model_id": "a753a535fd6b44bfa221041012f01962", - "version_major": 2, - "version_minor": 0 - }, - "text/plain": [ - "HBox(children=(IntProgress(value=0, description='Training', max=300, style=ProgressStyle(description_width='in…" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "application/vnd.jupyter.widget-view+json": { - "model_id": "37edf6bc26894aff9543aff9221017ce", - "version_major": 2, - "version_minor": 0 - }, - "text/plain": [ - "HBox(children=(IntProgress(value=0, description='Training', max=300, style=ProgressStyle(description_width='in…" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "application/vnd.jupyter.widget-view+json": { - "model_id": "399f328087304188b37750acd9924966", - "version_major": 2, - "version_minor": 0 - }, - "text/plain": [ - "HBox(children=(IntProgress(value=0, description='Training', max=300, style=ProgressStyle(description_width='in…" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "application/vnd.jupyter.widget-view+json": { - "model_id": "7475a662a88c4f1592e01b8c563946fc", + "model_id": "a973d13d0325400bb3f6064e713a4f43", "version_major": 2, "version_minor": 0 }, "text/plain": [ - "HBox(children=(IntProgress(value=0, description='Training', max=300, style=ProgressStyle(description_width='in…" + "HBox(children=(FloatProgress(value=0.0, description='Training', max=1000.0, style=ProgressStyle(description_wi…" ] }, "metadata": {}, "output_type": "display_data" }, { - "name": "stdout", - "output_type": "stream", - "text": [ - "Sampling approximate posterior...\n", - "Done.\n", - "Removed no burn in\n" + "ename": "KeyboardInterrupt", + "evalue": "", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mKeyboardInterrupt\u001b[0m Traceback (most recent call last)", + "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mDelfiEnsemble\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mfisher_pretraining\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mn_batch\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;36m5000\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mbatch_size\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;36m100\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[0;32m~/Dropbox/science/pydelfi-tf2/pydelfi/pydelfi/delfi.py\u001b[0m in \u001b[0;36mfisher_pretraining\u001b[0;34m(self, n_batch, plot, batch_size, validation_split, epochs, patience, mode)\u001b[0m\n\u001b[1;32m 581\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mmode\u001b[0m \u001b[0;34m==\u001b[0m \u001b[0;34m\"regression\"\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 582\u001b[0m \u001b[0;31m# Train the networks on these initial simulations\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 583\u001b[0;31m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mtrain_ndes\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mtraining_data\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mfisher_x_train\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mfisher_y_train\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mnp\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0matleast_2d\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mfisher_logpdf_train\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mreshape\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m-\u001b[0m\u001b[0;36m1\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;36m1\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mvalidation_split\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mvalidation_split\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mepochs\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mepochs\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mbatch_size\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mbatch_size\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mpatience\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mpatience\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mmode\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;34m'regression'\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 584\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mmode\u001b[0m \u001b[0;34m==\u001b[0m \u001b[0;34m\"samples\"\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 585\u001b[0m \u001b[0;31m# Train the networks on these initial simulations\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m~/Dropbox/science/pydelfi-tf2/pydelfi/pydelfi/delfi.py\u001b[0m in \u001b[0;36mtrain_ndes\u001b[0;34m(self, training_data, batch_size, validation_split, epochs, patience, mode)\u001b[0m\n\u001b[1;32m 495\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0mn\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mrange\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mn_ndes\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 496\u001b[0m \u001b[0;31m# Train the NDE\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 497\u001b[0;31m \u001b[0mval_loss\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mtrain_loss\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mtrainer\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mn\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mtrain\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mtraining_data\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mf_val\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mvalidation_split\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mepochs\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mepochs\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mn_batch\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mbatch_size\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mprogress_bar\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mprogress_bar\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mpatience\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mpatience\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mfile_name\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mgraph_restore_filename\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mmode\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mmode\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 498\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 499\u001b[0m \u001b[0;31m# Save the training and validation losses\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m~/Dropbox/science/pydelfi-tf2/pydelfi/pydelfi/train.py\u001b[0m in \u001b[0;36mtrain\u001b[0;34m(self, train_data, f_val, epochs, n_batch, patience, file_name, progress_bar, mode)\u001b[0m\n\u001b[1;32m 105\u001b[0m \u001b[0;31m# Retrieve the gradients of the trainable variables wrt the loss and\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 106\u001b[0m \u001b[0;31m# pass to optimizer.\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 107\u001b[0;31m \u001b[0mgrads\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mtape\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mgradient\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mtrain_loss\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mmodel\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mtrainable_variables\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 108\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0moptimizer\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mapply_gradients\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mzip\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mgrads\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mmodel\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mtrainable_variables\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 109\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m/usr/local/lib/python3.7/site-packages/tensorflow_core/python/eager/backprop.py\u001b[0m in \u001b[0;36mgradient\u001b[0;34m(self, target, sources, output_gradients, unconnected_gradients)\u001b[0m\n\u001b[1;32m 1027\u001b[0m \u001b[0moutput_gradients\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0moutput_gradients\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 1028\u001b[0m \u001b[0msources_raw\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mflat_sources_raw\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m-> 1029\u001b[0;31m unconnected_gradients=unconnected_gradients)\n\u001b[0m\u001b[1;32m 1030\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 1031\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0;32mnot\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_persistent\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m/usr/local/lib/python3.7/site-packages/tensorflow_core/python/eager/imperative_grad.py\u001b[0m in \u001b[0;36mimperative_grad\u001b[0;34m(tape, target, sources, output_gradients, sources_raw, unconnected_gradients)\u001b[0m\n\u001b[1;32m 75\u001b[0m \u001b[0moutput_gradients\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 76\u001b[0m \u001b[0msources_raw\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 77\u001b[0;31m compat.as_str(unconnected_gradients.value))\n\u001b[0m", + "\u001b[0;32m/usr/local/lib/python3.7/site-packages/tensorflow_core/python/eager/backprop.py\u001b[0m in \u001b[0;36m_gradient_function\u001b[0;34m(op_name, attr_tuple, num_inputs, inputs, outputs, out_grads, skip_input_indices)\u001b[0m\n\u001b[1;32m 117\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 118\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 119\u001b[0;31m def _gradient_function(op_name, attr_tuple, num_inputs, inputs, outputs,\n\u001b[0m\u001b[1;32m 120\u001b[0m out_grads, skip_input_indices):\n\u001b[1;32m 121\u001b[0m \"\"\"Calls the gradient function of the op.\n", + "\u001b[0;31mKeyboardInterrupt\u001b[0m: " ] - }, - { - "data": { - "image/png": "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\n", - "text/plain": [ - "
" - ] - }, - "metadata": { - "needs_background": "light" - }, - "output_type": "display_data" } ], "source": [ - "DelfiEnsemble.fisher_pretraining(n_batch=5000, batch_size=100)" + "DelfiEnsemble.fisher_pretraining(n_batch=5000, epochs=1000, patience=20, plot=True)" ] }, { @@ -378,7 +331,7 @@ } ], "source": [ - "DelfiEnsemble.train_ndes()" + "DelfiEnsemble.train(f_val=0.1, epochs=300, n_batch=256, patience=20)" ] }, { @@ -398,7 +351,7 @@ "metadata": {}, "outputs": [], "source": [ - "posterior_samples = DelfiEnsemble.emcee_sample()" + "posterior_samples, posterior_weights = DelfiEnsemble.emcee_sample()" ] }, { @@ -439,7 +392,7 @@ } ], "source": [ - "DelfiEnsemble.triangle_plot(samples=[posterior_samples])" + "DelfiEnsemble.triangle_plot(samples=[posterior_samples], weights=[posterior_weights])" ] } ], @@ -459,7 +412,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.6.5" + "version": "3.7.4" } }, "nbformat": 4, diff --git a/examples/jla_sne-restore.ipynb b/examples/jla_sne-restore.ipynb new file mode 100644 index 0000000000..c3319c1fd7 --- /dev/null +++ b/examples/jla_sne-restore.ipynb @@ -0,0 +1,321 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## How to restore a DELFI object from a previous run\n", + "\n", + "## Import and create everything exactly as you did before on the previous run, then, when you create the DELFI object, just set \"restore=True\". It will load in all of the saved attributes of the DELFI object as they were at the end of your previous run, and load in the saved weights of all of the trained NDEs\n", + "\n", + "## In the below example we will imagine you have already run the \"jla_sne.ipynb\" tutorial notebook, and you want to restore everything without having to re-run from the beginning.\n", + "\n", + "### NOTE: you must have already run the jla_sne.ipynb tutorial through to completion for this notebook to work" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "## Import your stuff" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [], + "source": [ + "import numpy as np\n", + "import simulators.jla_supernovae.jla_simulator as jla\n", + "import ndes as ndes\n", + "import delfi as delfi\n", + "import score as score\n", + "import priors as priors\n", + "import tensorflow as tf\n", + "import tensorflow_probability as tfp\n", + "tfd = tfp.distributions" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "## Set up the simulator\n", + "This must have the signature `simulator(parameters, seed, args, batch)` -> `np.array([batch, ndata])`" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/Users/justinalsing/Dropbox/science/pydelfi-tf2/pydelfi/examples/simulators/jla_supernovae/jla_parser.py:9: VisibleDeprecationWarning: Reading unicode strings without specifying the encoding argument is deprecated. Set the encoding, use None for the system default.\n", + " dtype = None, names = True)\n" + ] + } + ], + "source": [ + "JLASimulator = jla.JLA_Model()\n", + "\n", + "def simulator(theta, seed, simulator_args, batch):\n", + " \n", + " return JLASimulator.simulation(theta, seed)\n", + "\n", + "simulator_args = None" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "## Set up the prior" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [], + "source": [ + "lower = np.array([0, -1.5, -20, 0, 0, -0.5])\n", + "upper = np.array([0.6, 0, -18, 1, 6, 0.5])\n", + "prior = tfd.Blockwise([tfd.Uniform(low=lower[i], high=upper[i]) for i in range(6)])" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "## Set up the compressor\n", + "Must have the signature `compressor(data, args)` -> `np.array([n_summaries])`
\n", + "In this case we are going to do Gaussian score compression $$\\mathbf{t} = \\boldsymbol\\theta_* + \\mathbf{F}^{-1}\\nabla_\\theta^T\\boldsymbol\\mu_*\\mathbf{C}^{-1}(\\mathbf{d}-\\boldsymbol\\mu_*)$$ using the class `score.Gaussian`. For this we'll need some fiducial parameters, the mean its derivative at the fiducial parameters, the inverse covariance, and the inverse Fisher matrix" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [], + "source": [ + "theta_fiducial = np.array([0.2, -0.75, -19.05, 0.125, 2.65, -0.05])\n", + "\n", + "mu = JLASimulator.apparent_magnitude(theta_fiducial)\n", + "Cinv = JLASimulator.Cinv\n", + "\n", + "h = np.array(abs(theta_fiducial))*0.01\n", + "dmudt = JLASimulator.dmudt(theta_fiducial, h)\n", + "\n", + "Compressor = score.Gaussian(len(JLASimulator.data), theta_fiducial, mu = mu, Cinv = Cinv, dmudt = dmudt)\n", + "Compressor.compute_fisher()\n", + "Finv = Compressor.Finv\n", + "\n", + "def compressor(d, compressor_args):\n", + " return Compressor.scoreMLE(d)\n", + "compressor_args=None" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "## Load in the compressed data" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [], + "source": [ + "compressed_data = compressor(JLASimulator.data, compressor_args)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "## Define ensemble of NDEs\n", + "\n", + "### These must be defined exactly as you did for the previous run, in this case in jla_sne.ipynb" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [], + "source": [ + "NDEs = [ndes.ConditionalMaskedAutoregressiveFlow(\n", + " n_parameters=6,\n", + " n_data=6,\n", + " n_mades=5,\n", + " n_hidden=[30,30], \n", + " activation=tf.keras.layers.LeakyReLU(0.01),\n", + " kernel_initializer=tf.keras.initializers.RandomNormal(mean=0.0, stddev=1e-5, seed=None),\n", + " all_layers=True)]\n", + "\n", + "NDEs += [ndes.MixtureDensityNetwork(\n", + " n_parameters=6,\n", + " n_data=6, \n", + " n_components=i+1,\n", + " n_hidden=[30], \n", + " activation=tf.keras.layers.LeakyReLU(0.01))\n", + " for i in range(5)]\n", + "\n", + "NDEs += [ndes.SinhArcSinhMADE(\n", + " n_parameters=6,\n", + " n_data=6,\n", + " n_hidden=[64],\n", + " activation=tf.tanh,\n", + " kernel_initializer=tf.keras.initializers.RandomNormal(mean=0.0, stddev=1e-5, seed=None),\n", + " bias_initializer=tf.keras.initializers.RandomNormal(mean=0.0, stddev=1e-5, seed=None)\n", + " )]" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "## Create DELFI object\n", + "\n", + "### Do this exactly as we did in jla_sne.ipynb, but with \"restore=True\"" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [], + "source": [ + "DelfiEnsemble = delfi.Delfi(compressed_data, prior, NDEs, \n", + " Finv=Finv, \n", + " theta_fiducial=theta_fiducial, \n", + " param_names=['\\\\Omega_m', 'w_0', 'M_\\mathrm{B}', '\\\\alpha', '\\\\beta', '\\\\delta M'], \n", + " results_dir=\"simulators/jla_supernovae/results\",\n", + " filename=\"jla\",\n", + " optimiser=tf.keras.optimizers.Adam(lr=1e-4),\n", + " optimiser_arguments=None,\n", + " dtype=tf.float32,\n", + " posterior_chain_length=200,\n", + " nwalkers=500,\n", + " input_normalization=\"fisher\",\n", + " restore=True,\n", + " restore_filename=\"restore\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Plot the current posterior approximation (based on the current state of the posterior_samples from the restored DELFI object)" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Removed no burn in\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/usr/local/lib/python3.7/site-packages/matplotlib/figure.py:2359: UserWarning: This figure includes Axes that are not compatible with tight_layout, so results might be incorrect.\n", + " warnings.warn(\"This figure includes Axes that are not compatible \"\n" + ] + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": { + "needs_background": "light" + }, + "output_type": "display_data" + } + ], + "source": [ + "DelfiEnsemble.triangle_plot(samples=[DelfiEnsemble.posterior_samples], weights=[DelfiEnsemble.posterior_weights])" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "celltoolbar": "Slideshow", + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "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.7.4" + } + }, + "nbformat": 4, + "nbformat_minor": 1 +} diff --git a/examples/jla_sne.ipynb b/examples/jla_sne.ipynb index d2b4086c0e..e979200410 100644 --- a/examples/jla_sne.ipynb +++ b/examples/jla_sne.ipynb @@ -13,23 +13,17 @@ }, { "cell_type": "code", - "execution_count": null, - "metadata": { - "slideshow": { - "slide_type": "-" - } - }, + "execution_count": 1, + "metadata": {}, "outputs": [], "source": [ "import numpy as np\n", - "import simulators.jla_supernovae.jla_simulator as jla\n", - "import pydelfi.ndes as ndes\n", - "import pydelfi.delfi as delfi\n", - "import pydelfi.score as score\n", - "import pydelfi.priors as priors\n", "import tensorflow as tf\n", - "tf.logging.set_verbosity(tf.logging.ERROR)\n", - "%matplotlib inline" + "import tensorflow_probability as tfp\n", + "tfd = tfp.distributions\n", + "\n", + "from pydelfi import delfi\n", + "from pydelfi import ndes" ] }, { @@ -46,14 +40,25 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 2, "metadata": { "slideshow": { "slide_type": "-" } }, - "outputs": [], + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/Users/justinalsing/Dropbox/science/pydelfi/tf2-tom/pydelfi/examples/simulators/jla_supernovae/jla_parser.py:9: VisibleDeprecationWarning: Reading unicode strings without specifying the encoding argument is deprecated. Set the encoding, use None for the system default.\n", + " dtype = None, names = True)\n" + ] + } + ], "source": [ + "from simulators.jla_supernovae import jla_simulator as jla\n", + "\n", "JLASimulator = jla.JLA_Model()\n", "\n", "def simulator(theta, seed, simulator_args, batch):\n", @@ -76,13 +81,13 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 3, "metadata": {}, "outputs": [], "source": [ - "lower = np.array([0, -1.5, -20, 0, 0, -0.5])\n", - "upper = np.array([0.6, 0, -18, 1, 6, 0.5])\n", - "prior = priors.Uniform(lower, upper)" + "lower = np.array([0, -1.5, -20, 0, 0, -0.5]).astype(np.float32)\n", + "upper = np.array([0.6, 0, -18, 1, 6, 0.5]).astype(np.float32)\n", + "prior = tfd.Blockwise([tfd.Uniform(low=lower[i], high=upper[i]) for i in range(lower.shape[0])])" ] }, { @@ -104,6 +109,7 @@ "metadata": {}, "outputs": [], "source": [ + "from pydelfi import score\n", "theta_fiducial = np.array([0.2, -0.75, -19.05, 0.125, 2.65, -0.05])\n", "\n", "mu = JLASimulator.apparent_magnitude(theta_fiducial)\n", @@ -149,7 +155,9 @@ } }, "source": [ - "## Define ensemble of NDEs" + "## Define ensemble of NDEs\n", + "\n", + "For this example let's define a stack of 6 NDEs; one Masked Autoregressive Flow (MAF) and five Mixture Density Networks (MDNs) with 1-5 full-rank Gaussian components respectively" ] }, { @@ -158,12 +166,31 @@ "metadata": {}, "outputs": [], "source": [ - "NDEs = [ndes.MixtureDensityNetwork(n_parameters=6, n_data=6, n_components=1, n_hidden=[30,30], activations=[tf.tanh, tf.tanh], index=0),\n", - " ndes.MixtureDensityNetwork(n_parameters=6, n_data=6, n_components=2, n_hidden=[30,30], activations=[tf.tanh, tf.tanh], index=1),\n", - " ndes.MixtureDensityNetwork(n_parameters=6, n_data=6, n_components=3, n_hidden=[30,30], activations=[tf.tanh, tf.tanh], index=2),\n", - " ndes.MixtureDensityNetwork(n_parameters=6, n_data=6, n_components=4, n_hidden=[30,30], activations=[tf.tanh, tf.tanh], index=3),\n", - " ndes.MixtureDensityNetwork(n_parameters=6, n_data=6, n_components=5, n_hidden=[30,30], activations=[tf.tanh, tf.tanh], index=4),\n", - " ndes.ConditionalMaskedAutoregressiveFlow(n_parameters=6, n_data=6, n_hiddens=[50,50], n_mades=5, act_fun=tf.tanh, index=5)]" + "NDEs = [ndes.ConditionalMaskedAutoregressiveFlow(\n", + " n_parameters=6,\n", + " n_data=6,\n", + " n_mades=5,\n", + " n_hidden=[30,30], \n", + " activation=tf.keras.layers.LeakyReLU(0.01),\n", + " kernel_initializer=tf.keras.initializers.RandomNormal(mean=0.0, stddev=1e-5, seed=None),\n", + " all_layers=True)]\n", + "\n", + "NDEs += [ndes.MixtureDensityNetwork(\n", + " n_parameters=6,\n", + " n_data=6, \n", + " n_components=i+1,\n", + " n_hidden=[30], \n", + " activation=tf.keras.layers.LeakyReLU(0.01))\n", + " for i in range(5)]\n", + "\n", + "NDEs += [ndes.SinhArcSinhMADE(\n", + " n_parameters=6,\n", + " n_data=6,\n", + " n_hidden=[32, 32],\n", + " activation=tf.tanh,\n", + " kernel_initializer=tf.keras.initializers.RandomNormal(mean=0.0, stddev=1e-5, seed=None),\n", + " bias_initializer=tf.keras.initializers.RandomNormal(mean=0.0, stddev=1e-5, seed=None)\n", + " )]" ] }, { @@ -184,11 +211,16 @@ "outputs": [], "source": [ "DelfiEnsemble = delfi.Delfi(compressed_data, prior, NDEs, \n", - " Finv = Finv, \n", - " theta_fiducial = theta_fiducial, \n", - " param_limits = [lower, upper],\n", - " param_names = ['\\\\Omega_m', 'w_0', 'M_\\mathrm{B}', '\\\\alpha', '\\\\beta', '\\\\delta M'], \n", - " results_dir = \"simulators/jla_supernovae/results/\",\n", + " Finv=Finv, \n", + " theta_fiducial=theta_fiducial, \n", + " param_names=['\\\\Omega_m', 'w_0', 'M_\\mathrm{B}', '\\\\alpha', '\\\\beta', '\\\\delta M'], \n", + " results_dir=\"simulators/jla_supernovae/results\",\n", + " filename=\"jla\",\n", + " optimiser=tf.keras.optimizers.Adam(lr=1e-4),\n", + " optimiser_arguments=None,\n", + " dtype=tf.float32,\n", + " posterior_chain_length=200,\n", + " nwalkers=500,\n", " input_normalization=\"fisher\")" ] }, @@ -209,7 +241,7 @@ "metadata": {}, "outputs": [], "source": [ - "DelfiEnsemble.fisher_pretraining()" + "DelfiEnsemble.fisher_pretraining(n_batch=5000, epochs=1000, patience=20, plot=True)" ] }, { @@ -231,10 +263,9 @@ "source": [ "n_initial = 200\n", "n_batch = 200\n", - "n_populations = 10\n", + "n_populations = 12\n", "\n", - "DelfiEnsemble.sequential_training(simulator, compressor, n_initial, n_batch, n_populations, patience=20,\n", - " save_intermediate_posteriors=False)" + "DelfiEnsemble.sequential_training(simulator, compressor, n_initial, n_batch, n_populations, patience=10, plot=True, save_intermediate_posteriors=True)" ] }, { @@ -250,7 +281,7 @@ "metadata": {}, "outputs": [], "source": [ - "posterior_samples = DelfiEnsemble.emcee_sample()" + "posterior_samples, posterior_weights = DelfiEnsemble.affine_sample()" ] }, { @@ -267,8 +298,15 @@ "metadata": {}, "outputs": [], "source": [ - "DelfiEnsemble.triangle_plot(samples=[posterior_samples])" + "DelfiEnsemble.triangle_plot(samples=[posterior_samples], weights=[posterior_weights])" ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] } ], "metadata": { @@ -288,9 +326,9 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.6.5" + "version": "3.7.4" } }, "nbformat": 4, - "nbformat_minor": 1 + "nbformat_minor": 4 } diff --git a/examples/jla_sne_marginalized.ipynb b/examples/jla_sne_marginalized.ipynb index 058768856b..0dd02547bc 100644 --- a/examples/jla_sne_marginalized.ipynb +++ b/examples/jla_sne_marginalized.ipynb @@ -23,8 +23,8 @@ "import pydelfi.priors as priors\n", "import tensorflow as tf\n", "from scipy.linalg import block_diag\n", - "tf.logging.set_verbosity(tf.logging.ERROR)\n", - "%matplotlib inline" + "import tensorflow_probability as tfp\n", + "tfd = tfp.distributions" ] }, { @@ -43,11 +43,11 @@ "source": [ "lower = np.array([0, -1.5])\n", "upper = np.array([0.6, 0])\n", - "prior = priors.Uniform(lower, upper)\n", + "prior = tfd.Blockwise([tfd.Uniform(low=lower[i], high=upper[i]) for i in range(2)])\n", "\n", "eta_lower = np.array([-20, 0, 0, -0.5])\n", "eta_upper = np.array([-18, 1, 6, 0.5])\n", - "eta_prior = priors.Uniform(eta_lower, eta_upper)" + "eta_prior = tfd.Blockwise([tfd.Uniform(low=eta_lower[i], high=eta_upper[i]) for i in range(4)])" ] }, { @@ -71,7 +71,7 @@ "def simulator(theta, seed, simulator_args, batch):\n", " \n", " eta_prior = simulator_args[0]\n", - " eta = eta_prior.draw()\n", + " eta = eta_prior.sample().numpy()\n", " \n", " return JLASimulator.simulation(np.concatenate([theta, eta]), seed)\n", "\n", @@ -145,12 +145,31 @@ "metadata": {}, "outputs": [], "source": [ - "NDEs = [ndes.ConditionalMaskedAutoregressiveFlow(n_parameters=2, n_data=2, n_hiddens=[50,50], n_mades=5, act_fun=tf.tanh, index=0),\n", - " ndes.MixtureDensityNetwork(n_parameters=2, n_data=2, n_components=1, n_hidden=[30,30], activations=[tf.tanh, tf.tanh], index=1),\n", - " ndes.MixtureDensityNetwork(n_parameters=2, n_data=2, n_components=2, n_hidden=[30,30], activations=[tf.tanh, tf.tanh], index=2),\n", - " ndes.MixtureDensityNetwork(n_parameters=2, n_data=2, n_components=3, n_hidden=[30,30], activations=[tf.tanh, tf.tanh], index=3),\n", - " ndes.MixtureDensityNetwork(n_parameters=2, n_data=2, n_components=4, n_hidden=[30,30], activations=[tf.tanh, tf.tanh], index=4),\n", - " ndes.MixtureDensityNetwork(n_parameters=2, n_data=2, n_components=5, n_hidden=[30,30], activations=[tf.tanh, tf.tanh], index=5)]" + "NDEs = [ndes.ConditionalMaskedAutoregressiveFlow(\n", + " n_parameters=6,\n", + " n_data=6,\n", + " n_mades=5,\n", + " n_hidden=[30,30], \n", + " activation=tf.keras.layers.LeakyReLU(0.01),\n", + " kernel_initializer=tf.keras.initializers.RandomNormal(mean=0.0, stddev=1e-5, seed=None),\n", + " all_layers=True)]\n", + "\n", + "NDEs += [ndes.MixtureDensityNetwork(\n", + " n_parameters=6,\n", + " n_data=6, \n", + " n_components=i+1,\n", + " n_hidden=[30], \n", + " activation=tf.keras.layers.LeakyReLU(0.01))\n", + " for i in range(5)]\n", + "\n", + "NDEs += [ndes.SinhArcSinhMADE(\n", + " n_parameters=6,\n", + " n_data=6,\n", + " n_hidden=[64],\n", + " activation=tf.tanh,\n", + " kernel_initializer=tf.keras.initializers.RandomNormal(mean=0.0, stddev=1e-5, seed=None),\n", + " bias_initializer=tf.keras.initializers.RandomNormal(mean=0.0, stddev=1e-5, seed=None)\n", + " )]" ] }, { @@ -166,11 +185,18 @@ "metadata": {}, "outputs": [], "source": [ - "DelfiEnsemble = delfi.Delfi(compressed_data, prior, NDEs, Finv = Finv, theta_fiducial = theta_fiducial, \n", - " param_limits = [lower, upper],\n", - " param_names = ['\\Omega_m', 'w_0'], \n", - " results_dir = \"simulators/jla_supernovae/results_marginal/\",\n", - " input_normalization=\"fisher\")" + "DelfiEnsemble = delfi.Delfi(compressed_data, prior, NDEs, \n", + " Finv=Finv, \n", + " theta_fiducial=theta_fiducial, \n", + " param_names=['\\\\Omega_m', 'w_0', 'M_\\mathrm{B}', '\\\\alpha', '\\\\beta', '\\\\delta M'], \n", + " results_dir=\"simulators/jla_supernovae/results_marginal\",\n", + " filename=\"jla\",\n", + " optimiser=tf.keras.optimizers.Adam(lr=1e-4),\n", + " optimiser_arguments=None,\n", + " dtype=tf.float32,\n", + " posterior_chain_length=200,\n", + " nwalkers=500,\n", + " input_normalization=\"fisher\")" ] }, { @@ -186,7 +212,7 @@ "metadata": {}, "outputs": [], "source": [ - "DelfiEnsemble.fisher_pretraining()" + "DelfiEnsemble.fisher_pretraining(n_batch=5000, epochs=1000, patience=20, plot=True)" ] }, { @@ -226,7 +252,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.6.5" + "version": "3.7.4" } }, "nbformat": 4, diff --git a/pydelfi/__init__.py b/pydelfi/__init__.py index 05509b2ebb..585d28dfc8 100644 --- a/pydelfi/__init__.py +++ b/pydelfi/__init__.py @@ -1,3 +1,5 @@ #from .delfi import Delfi #from . import ndes, compression, simulators +__version__ = "v0.2" +__author__ = "Justin Alsing, Tom Charnock and Stephen Feeney" diff --git a/pydelfi/affine.py b/pydelfi/affine.py new file mode 100644 index 0000000000..c0aeba334d --- /dev/null +++ b/pydelfi/affine.py @@ -0,0 +1,81 @@ +import tensorflow as tf +from tqdm.auto import tqdm +import numpy as np + +def sample(log_prob, n_params, n_walkers, n_steps, walkers1, walkers2): + + # progress-bar + pbar = tqdm(total=n_steps, desc="Sampling") # Jupyter notebook or qtconsole + + # initialize current state + current_state1 = tf.Variable(walkers1) + current_state2 = tf.Variable(walkers2) + + # initial target log prob for the walkers (and set any nans to -inf)... + logp_current1 = log_prob(current_state1) + logp_current2 = log_prob(current_state2) + logp_current1 = tf.where(tf.math.is_nan(logp_current1), tf.ones_like(logp_current1)*tf.math.log(0.), logp_current1) + logp_current2 = tf.where(tf.math.is_nan(logp_current2), tf.ones_like(logp_current2)*tf.math.log(0.), logp_current2) + + # holder for the whole chain + chain = [tf.concat([current_state1, current_state2], axis=0)] + + # MCMC loop + for epoch in range(n_steps): + + # first set of walkers: + + # proposals + partners1 = tf.gather(current_state2, np.random.randint(0, n_walkers, n_walkers)) + z1 = 0.5*(tf.random.uniform([n_walkers], minval=0, maxval=1)+1)**2 + proposed_state1 = partners1 + tf.transpose(z1*tf.transpose(current_state1 - partners1)) + + # target log prob at proposed points + logp_proposed1 = log_prob(proposed_state1) + logp_proposed1 = tf.where(tf.math.is_nan(logp_proposed1), tf.ones_like(logp_proposed1)*tf.math.log(0.), logp_proposed1) + + # acceptance probability + p_accept1 = tf.math.minimum(tf.ones(n_walkers), z1**(n_params-1)*tf.exp(logp_proposed1 - logp_current1) ) + + # accept or not + accept1_ = (tf.random.uniform([n_walkers], minval=0, maxval=1) <= p_accept1) + accept1 = tf.cast(accept1_, tf.float32) + + # update the state + current_state1 = tf.transpose( tf.transpose(current_state1)*(1-accept1) + tf.transpose(proposed_state1)*accept1) + logp_current1 = tf.where(accept1_, logp_proposed1, logp_current1) + + # second set of walkers: + + # proposals + partners2 = tf.gather(current_state1, np.random.randint(0, n_walkers, n_walkers)) + z2 = 0.5*(tf.random.uniform([n_walkers], minval=0, maxval=1)+1)**2 + proposed_state2 = partners2 + tf.transpose(z2*tf.transpose(current_state2 - partners2)) + + # target log prob at proposed points + logp_proposed2 = log_prob(proposed_state2) + logp_proposed2 = tf.where(tf.math.is_nan(logp_proposed2), tf.ones_like(logp_proposed2)*tf.math.log(0.), logp_proposed2) + + # acceptance probability + p_accept2 = tf.math.minimum(tf.ones(n_walkers), z2**(n_params-1)*tf.exp(logp_proposed2 - logp_current2) ) + + # accept or not + accept2_ = (tf.random.uniform([n_walkers], minval=0, maxval=1) <= p_accept2) + accept2 = tf.cast(accept2_, tf.float32) + + # update the state + current_state2 = tf.transpose( tf.transpose(current_state2)*(1-accept2) + tf.transpose(proposed_state2)*accept2) + logp_current2 = tf.where(accept2_, logp_proposed2, logp_current2) + + # append to chain + chain.append(tf.concat([current_state1, current_state2], axis=0)) + + # update the progressbar + pbar.update(1) + + # stack up the chain + chain = tf.stack(chain, axis=0) + + return chain[1:,:,:] + + diff --git a/pydelfi/delfi.py b/pydelfi/delfi.py index cd83cee824..6afea9adb8 100644 --- a/pydelfi/delfi.py +++ b/pydelfi/delfi.py @@ -1,89 +1,80 @@ import tensorflow as tf import getdist from getdist import plots, MCSamples -import pydelfi.ndes -import pydelfi.train -import emcee +#import emcee import matplotlib.pyplot as plt import matplotlib as mpl -import pydelfi.priors as priors import numpy as np from tqdm.auto import tqdm import scipy.optimize as optimization from scipy.stats import multivariate_normal import pickle +import tensorflow_probability as tfp +tfd = tfp.distributions + +# pydelfi imports +from pydelfi import ndes +from pydelfi import affine + +__version__ = "v0.2" +__author__ = "Justin Alsing, Tom Charnock and Stephen Feeney" class Delfi(): - def __init__(self, data, prior, nde, \ - Finv = None, theta_fiducial = None, param_limits = None, param_names = None, nwalkers = 100, \ - posterior_chain_length = 1000, proposal_chain_length = 100, \ - rank = 0, n_procs = 1, comm = None, red_op = None, \ - show_plot = True, results_dir = "", progress_bar = True, input_normalization = None, - graph_restore_filename = "graph_checkpoint", restore_filename = "restore.pkl", restore = False, save = True): - - # Input validation - for i in range(len(nde)): - - # Check all NDEs expect same number of parameters - if nde[0].n_parameters != nde[i].n_parameters: - err_msg = 'NDEs have inconsistent parameter counts. ' + \ - 'NDE 0: {:d} pars; NDE {:d}: {:d} pars.' - raise ValueError(err_msg.format(nde[0].n_parameters, \ - i, nde[i].n_parameters)) - - # Check all NDEs expect same data length - if nde[0].n_data != nde[i].n_data: - err_msg = 'NDEs have inconsistent data counts. ' + \ - 'NDE 0: {:d} data; NDE {:d}: {:d} data.' - raise ValueError(err_msg.format(nde[0].n_data, \ - i, nde[i].n_data)) - - # Check length of data provided is consistent with NDE - # expectations - if nde[i].n_data != len(data): - err_msg = 'inconsistent compressed data lengths. ' + \ - 'Compressed data have shape' + \ - str(data.shape) + \ - '; NDE {:d} expects length {:d}.' - raise ValueError(err_msg.format(i, nde[i].n_data)) + def __init__(self, data, prior, nde, Finv=None, theta_fiducial=None, + param_limits=None, param_names=None, nwalkers=100, + posterior_chain_length=100, proposal_chain_length=100, + rank=0, n_procs=1, comm=None, red_op=None, show_plot=True, + results_dir="", filename=None, progress_bar=True, input_normalization=None, + save=True, restore=False, **kwargs): # Data - self.data = data + self.data = tf.convert_to_tensor(data.astype(np.float32), dtype=tf.float32) self.D = len(data) - + # Prior self.prior = prior # Number of parameters - self.npar = nde[0].n_parameters + self.npar = prior.event_shape[0] - # Initialize the NDEs, trainers, and stacking weights (for stacked density estimators) - self.n_ndes = len(nde) - self.nde = nde - self.trainer = [pydelfi.train.ConditionalTrainer(nde[i]) for i in range(self.n_ndes)] - self.stacking_weights = np.zeros(self.n_ndes) + self.NDEs = ndes.NDE(nde, self.prior, **kwargs) - # Tensorflow session for the NDE training - self.sess = tf.Session(config = tf.ConfigProto()) - self.sess.run(tf.global_variables_initializer()) - + #TC - some work... # Parameter limits - if param_limits is not None: - # Set to provided prior limits if provided - self.lower = param_limits[0] - self.upper = param_limits[1] - else: - # Else set to max and min float32 - self.lower = np.ones(self.npar)*np.finfo(np.float32).min - self.upper = np.ones(self.npar)*np.finfo(np.float32).max + if ((not hasattr(self.prior, "low")) + and (not hasattr(self.prior, "high"))): + if hasattr(self.prior, "distributions"): + if hasattr(self.prior.distributions[0], "low"): + low = np.zeros((self.npar,), dtype=np.float32) + high = np.zeros((self.npar,), dtype=np.float32) + for i in range(self.npar): + low[i] = self.prior.distributions[i].low + high[i] = self.prior.distributions[i].high + self.prior.low = tf.convert_to_tensor(low, dtype=tf.float32) + self.prior.high = tf.convert_to_tensor(high, dtype=tf.float32) + elif param_limits is None: + raise ValueError("Please provide a prior whose distributions have `low` and `high` limits.") + elif param_limits is not None: + # Set to provided prior limits if provided + self.prior.low = tf.convert_to_tensor(param_limits[0].astype(np.float32), dtype=tf.float32) + self.prior.high = tf.convert_to_tensor(param_limits[1].astype(np.float32), dtype=tf.float32) + else: + # Else set to max and min float32 + self.prior.low = tf.convert_to_tensor(np.ones(self.npar)*np.finfo(np.float32).min, dtype=tf.float32) + self.prior.high = tf.convert_to_tensor(np.ones(self.npar)*np.finfo(np.float32).max, dtype=tf.float32) # Fisher matrix and fiducial parameters if Finv is not None: - self.Finv = Finv - self.fisher_errors = np.sqrt(np.diag(self.Finv)) - self.theta_fiducial = theta_fiducial - self.asymptotic_posterior = priors.TruncatedGaussian(self.theta_fiducial, self.Finv, self.lower, self.upper) + self.Finv = Finv.astype(np.float32) + self.fisher_errors = np.sqrt(np.diag(self.Finv)).astype(np.float32) + self.theta_fiducial = theta_fiducial.astype(np.float32) + scale = tf.linalg.cholesky(self.Finv) + self.asymptotic_posterior = ndes.TruncatedMultivariateNormalTriL( + loc=self.theta_fiducial, scale_tril=scale, + low=self.prior.low, high=self.prior.high, + validate_args=False, allow_nan_stats=True, + name='AsymptoticPosterior') else: self.Finv = None self.fisher_errors = None @@ -93,52 +84,56 @@ def __init__(self, data, prior, nde, \ # Re-scaling for inputs to NDE self.input_normalization = input_normalization if input_normalization is None: - self.x_mean = np.zeros(self.D) - self.x_std = np.ones(self.D) - self.p_mean = np.zeros(self.npar) - self.p_std = np.ones(self.npar) - elif input_normalization is 'fisher': - self.x_mean = self.theta_fiducial - self.x_std = self.fisher_errors - self.p_mean = self.theta_fiducial - self.p_std = self.fisher_errors + self.data_shift = tf.convert_to_tensor(np.zeros(self.D).astype(np.float32), dtype=tf.float32) + self.data_scale = tf.convert_to_tensor(np.ones(self.D).astype(np.float32), dtype=tf.float32) + self.theta_shift = tf.convert_to_tensor(np.zeros(self.npar).astype(np.float32), dtype=tf.float32) + self.theta_scale = tf.convert_to_tensor(np.ones(self.npar).astype(np.float32), dtype=tf.float32) + elif input_normalization is "fisher": + self.data_shift = tf.convert_to_tensor(self.theta_fiducial.astype(np.float32), dtype=tf.float32) + self.data_scale = tf.convert_to_tensor(self.fisher_errors.astype(np.float32), dtype=tf.float32) + self.theta_shift = tf.convert_to_tensor(self.theta_fiducial.astype(np.float32), dtype=tf.float32) + self.theta_scale = tf.convert_to_tensor(self.fisher_errors.astype(np.float32), dtype=tf.float32) + elif input_normalization is "auto": + self.input_normalization_set = False else: - self.x_mean, self.x_std, self.p_mean, self.p_std = input_normalization + self.data_shift = tf.convert_to_tensor(input_normalization[0].astype(np.float32), dtype=tf.float32) + self.data_scale = tf.convert_to_tensor(input_normalization[1].astype(np.float32), dtype=tf.float32) + self.theta_shift = tf.convert_to_tensor(input_normalization[2].astype(np.float32), dtype=tf.float32) + self.theta_scale = tf.convert_to_tensor(input_normalization[3].astype(np.float32), dtype=tf.float32) # Training data [initialize empty] - self.ps = np.array([]).reshape(0,self.npar) - self.xs = np.array([]).reshape(0,self.D) - self.x_train = tf.placeholder(tf.float32, shape = (None, self.npar)) - self.y_train = tf.placeholder(tf.float32, shape = (None, self.D)) + self.theta_realizations = np.array([], dtype=np.float32).reshape(0,self.npar) + self.data_realizations = np.array([], dtype=np.float32).reshape(0,self.D) + self.theta_train = [] + self.data_train = [] self.n_sims = 0 - + # MCMC chain parameters for EMCEE self.nwalkers = nwalkers self.posterior_chain_length = posterior_chain_length self.proposal_chain_length = proposal_chain_length - + # Initialize MCMC chains for posterior and proposal if self.asymptotic_posterior is not None: - self.posterior_samples = np.array([self.asymptotic_posterior.draw() for i in range(self.nwalkers*self.posterior_chain_length)]) - self.proposal_samples = np.array([self.asymptotic_posterior.draw() for i in range(self.nwalkers*self.proposal_chain_length)]) + self.posterior_samples = self.asymptotic_posterior.sample(2*self.nwalkers*self.posterior_chain_length).numpy() + self.proposal_samples = self.asymptotic_posterior.sample(2*self.nwalkers*self.proposal_chain_length).numpy() else: - self.posterior_samples = np.array([self.prior.draw() for i in range(self.nwalkers*self.posterior_chain_length)]) - self.proposal_samples = np.array([self.prior.draw() for i in range(self.nwalkers*self.proposal_chain_length)]) - self.posterior_weights = np.ones(len(self.posterior_samples))*1.0/len(self.posterior_samples) - self.proposal_weights = np.ones(len(self.proposal_samples))*1.0/len(self.proposal_samples) - + self.posterior_samples = self.prior.sample(2*self.nwalkers*self.posterior_chain_length).numpy() + self.proposal_samples = self.prior.sample(2*self.nwalkers*self.proposal_chain_length).numpy() + self.posterior_weights = (np.ones(len(self.posterior_samples))*1.0/len(self.posterior_samples)).astype(np.float32) + self.proposal_weights = (np.ones(len(self.proposal_samples))*1.0/len(self.proposal_samples)).astype(np.float32) + self.log_posterior_values = None#(np.ones(len(self.posterior_samples))*1.0/len(self.posterior_samples)).astype(np.float32) + self.log_proposal_values = None#(np.ones(len(self.proposal_samples))*1.0/len(self.proposal_samples)).astype(np.float32) + # Parameter names and ranges for plotting with GetDist self.names = param_names self.labels = param_names - self.ranges = dict(zip(param_names, [ [self.lower[i], self.upper[i]] for i in range(self.npar) ])) + self.ranges = dict(zip(param_names, [(self.prior.low[i].numpy().astype(np.float64), self.prior.high[i].numpy().astype(np.float64)) for i in range(self.npar) ])) self.show_plot = show_plot - - # Results directory - self.results_dir = results_dir - + # Training loss, validation loss - self.training_loss = [np.array([]) for i in range(self.n_ndes)] - self.validation_loss = [np.array([]) for i in range(self.n_ndes)] + self.training_loss = np.zeros((0, self.NDEs.n_stack), dtype=np.float32) + self.validation_loss = np.zeros((0, self.NDEs.n_stack), dtype=np.float32) self.stacked_sequential_training_loss = [] self.stacked_sequential_validation_loss = [] self.sequential_nsims = [] @@ -155,31 +150,32 @@ def __init__(self, data, prior, nde, \ # Show progress bars? self.progress_bar = progress_bar - + # Filenames for saving/restoring graph and attributes - self.graph_restore_filename = results_dir + graph_restore_filename - self.restore_filename = results_dir + restore_filename - + self.results_dir = results_dir + self.filename = filename + # Save attributes of the ojbect as you go? self.save = save # Restore the graph and dynamic object attributes if restore = True if restore == True: - - # Restore the graph - saver = tf.train.Saver() - saver.restore(self.sess, self.graph_restore_filename) # Restore the dynamic object attributes - self.stacking_weights, self.posterior_samples, self.proposal_samples, self.training_loss, self.validation_loss, self.stacked_sequential_training_loss, self.stacked_sequential_validation_loss, self.sequential_nsims, self.ps, self.xs, self.x_mean, self.x_std, self.p_mean, self.p_std = pickle.load(open(self.restore_filename, 'rb')) + self.NDEs.weighting, self.posterior_samples, self.posterior_weights, self.proposal_samples, self.proposal_weights, self.training_loss, self.validation_loss, self.stacked_sequential_training_loss, self.stacked_sequential_validation_loss, self.sequential_nsims, self.theta_realizations, self.data_realizations = pickle.load(open(self.results_dir + "/" + self.filename + ".pkl", 'rb')) + + # Restore the NDE models + self.NDEs.load_models(self.NDEs.stack, directory=self.results_dir, filename=self.filename) # Save object attributes def saver(self): - - f = open(self.restore_filename, 'wb') - pickle.dump([self.stacking_weights, self.posterior_samples, self.proposal_samples, self.training_loss, self.validation_loss, self.stacked_sequential_training_loss, self.stacked_sequential_validation_loss, self.sequential_nsims, self.ps, self.xs, self.x_mean, self.x_std, self.p_mean, self.p_std], f) + + f = open(self.results_dir + "/" + self.filename + ".pkl", 'wb') + pickle.dump([self.NDEs.weighting.numpy(), self.posterior_samples, self.posterior_weights, self.proposal_samples, self.proposal_weights, self.training_loss, self.validation_loss, self.stacked_sequential_training_loss, self.stacked_sequential_validation_loss, self.sequential_nsims, self.theta_realizations, self.data_realizations], f) f.close() - + + self.NDEs.save_models(self.NDEs.stack, directory=self.results_dir, filename=self.filename) + # Divide list of jobs between MPI processes def allocate_jobs(self, n_jobs): n_j_allocated = 0 @@ -204,100 +200,83 @@ def complete_array(self, target_distrib): else: target = target_distrib return target - - # NDE log likelihood (individual NDE) - def log_likelihood_individual(self, i, theta, data): - - lnL = self.nde[i].eval((np.atleast_2d((theta-self.p_mean)/self.p_std), np.atleast_2d((data-self.x_mean)/self.x_std)), self.sess) - - return lnL - - # NDE log likelihood (stacked) - def log_likelihood_stacked(self, theta, data): - - # Stack the likelihoods - L = 0 - for n in range(self.n_ndes): - L += self.stacking_weights[n]*np.exp(self.nde[n].eval((np.atleast_2d((theta-self.p_mean)/self.p_std), np.atleast_2d((data-self.x_mean)/self.x_std)), self.sess)) - lnL = np.log(L) - lnL[np.isnan(lnL)[:,0],:] = -1e300 - return lnL - - # Log posterior (stacked) - def log_posterior_stacked(self, theta, data): - - return self.log_likelihood_stacked(theta, data) + self.prior.logpdf(np.atleast_2d(theta)) - - # Log posterior (individual) - def log_posterior_individual(self, i, theta, data): - - return self.log_likelihood_individual(i, theta, data) + self.prior.logpdf(np.atleast_2d(theta)) - - # Log posterior - def log_geometric_mean_proposal_stacked(self, x, data): - - return 0.5 * (self.log_likelihood_stacked(x, data) + 2 * self.prior.logpdf(np.atleast_2d(x)) ) # Bayesian optimization acquisition function def acquisition(self, theta): # Compute log_posteriors - Ls = np.array([self.log_posterior_individual(i, theta) for i in range(self.n_ndes)]) - + P = self.NDEs.weighted_log_prob((self.data - self.data_shift)/self.data_scale, conditional=(theta - self.theta_shift)/self.theta_scale) + P_mean, P_variance = self.NDEs.variance(P) + # Check whether prior is zero or not - return self.log_posterior_stacked(theta)*np.sqrt(np.average((Ls - np.average(Ls, weights = self.stacking_weights, axis=0))**2, weights=self.stacking_weights, axis=0)) - + return tf.multiply(self.NDEs.weighted_log_posterior(theta), tf.sqrt(P_variance)) + + + # train the NDEs in the stack + def train(self, training_data=None, f_val=0.1, epochs=300, n_batch=100, patience=20): + + # default training data + if training_data is None: + training_data = [self.theta_train, self.data_train] + + # train the NDEs + val_loss, train_loss = self.NDEs.fit(data=training_data, f_val=f_val, epochs=epochs, n_batch=n_batch, patience=patience) + + # save the training/validation loss + self.training_loss = np.vstack([self.training_loss, train_loss]) + self.validation_loss = np.vstack([self.validation_loss, val_loss]) + # Bayesian optimization training def bayesian_optimization_training(self, simulator, compressor, n_batch, n_populations, n_optimizations = 10, \ simulator_args = None, compressor_args = None, plot = False, batch_size = 100, \ validation_split = 0.1, epochs = 300, patience = 20, seed_generator = None, \ save_intermediate_posteriors = False, sub_batch = 1): - + # Loop over n_populations for i in range(n_populations): - + # Find acquisition point... print('Finding optimal acquisition point...') A_optimal = 0 theta_optimal = self.theta_fiducial for i in range(n_optimizations): - res = optimization.basinhopping(lambda x: -self.acquisition(x), x0=self.theta_fiducial) + res = optimization.basinhopping(lambda x: -self.acquisition(x).numpy(), x0=self.theta_fiducial) if res.fun < A_optimal: A_optimal = res.fun theta_optimal = res.x - + # Array of parameters to run simulations - ps = np.array([theta_optimal for k in range(n_batch)]) - + thetas = np.array([theta_optimal for k in range(n_batch)], dtype=np.float32) + # Run a small batch of simulations at the acquisition point - xs_batch, ps_batch = self.run_simulation_batch(n_batch, ps, simulator, compressor, simulator_args, compressor_args, seed_generator = seed_generator, sub_batch = sub_batch) - + data_batch, theta_batch = self.run_simulation_batch(n_batch, thetas, simulator, compressor, simulator_args, compressor_args, seed_generator = seed_generator, sub_batch = sub_batch) + # Augment the training data - self.add_simulations(xs_batch, ps_batch) - - # Re-train the networks - self.train_ndes(training_data=[self.x_train, self.y_train], batch_size=max(self.n_sims//8, batch_size), validation_split=validation_split, epochs=epochs, patience=patience) - - # Save the losses - self.stacked_sequential_training_loss.append(np.sum(np.array([self.training_loss[n][-1]*self.stacking_weights[n] for n in range(self.n_ndes)]))) - self.stacked_sequential_validation_loss.append(np.sum(np.array([self.validation_loss[n][-1]*self.stacking_weights[n] for n in range(self.n_ndes)]))) + self.add_simulations(data_batch, theta_batch) + + #TC - we should add maximising the ELBO between propsal distribution and NDEs, that would be the most correct thing to do and would be really quick (not yet implemented in ndes) + # Train the networks on these initial simulations + self.train(training_data=[self.theta_train, self.data_train], f_val=validation_split, epochs=epochs, n_batch=max(self.n_sims//8, batch_size), patience=patience) + + self.stacked_sequential_training_loss.append(np.sum(self.NDEs.weighting * self.training_loss[-1])) + self.stacked_sequential_validation_loss.append(np.sum(self.NDEs.weighting * self.validation_loss[-1])) self.sequential_nsims.append(self.n_sims) # Save attributes if save == True - if self.save == True: + if self.save is True: self.saver() - + # Run n_batch simulations - def run_simulation_batch(self, n_batch, ps, simulator, compressor, simulator_args, compressor_args, seed_generator = None, sub_batch = 1): - + def run_simulation_batch(self, n_batch, thetas, simulator, compressor, simulator_args, compressor_args, seed_generator = None, sub_batch = 1): + # Random seed generator: set to unsigned 32 bit int random numbers as default if seed_generator is None: seed_generator = lambda: np.random.randint(2147483647) - + # Dimension outputs data_samples = np.zeros((n_batch*sub_batch, self.D)) parameter_samples = np.zeros((n_batch*sub_batch, self.npar)) - + # Run samples assigned to each process, catching exceptions # (when simulator returns np.nan). i_prop = self.inds_prop[0] @@ -307,51 +286,100 @@ def run_simulation_batch(self, n_batch, ps, simulator, compressor, simulator_arg pbar = tqdm(total = self.inds_acpt[-1], desc = "Simulations") while i_acpt <= self.inds_acpt[-1]: try: - sims = simulator(ps[i_prop,:], seed_generator(), simulator_args, sub_batch) - + sims = simulator(thetas[i_prop,:], seed_generator(), simulator_args, sub_batch) + # Make sure the sims are the right shape if sub_batch == 1 and len(sims) != 1: sims = np.array([sims]) compressed_sims = np.array([compressor(sims[k], compressor_args) for k in range(sub_batch)]) if np.all(np.isfinite(compressed_sims.flatten())): data_samples[i_acpt*sub_batch:i_acpt*sub_batch+sub_batch,:] = compressed_sims - parameter_samples[i_acpt*sub_batch:i_acpt*sub_batch+sub_batch,:] = ps[i_prop,:] + parameter_samples[i_acpt*sub_batch:i_acpt*sub_batch+sub_batch,:] = thetas[i_prop,:] i_acpt += 1 if self.progress_bar: pbar.update(1) else: - print(err_msg.format('NaN/inf', ps[i_prop,:], self.rank)) + print(err_msg.format('NaN/inf', thetas[i_prop,:], self.rank)) except: - print(err_msg.format('exception', ps[i_prop,:], self.rank)) + print(err_msg.format('exception', thetas[i_prop,:], self.rank)) i_prop += 1 # Reduce results from all processes and return data_samples = self.complete_array(data_samples) parameter_samples = self.complete_array(parameter_samples) - return data_samples, parameter_samples + return data_samples.astype(np.float32), parameter_samples.astype(np.float32) + + # weighted log posterior + def weighted_log_posterior(self, theta): + + #lnP = self.NDEs.weighted_log_prob((self.data.astype(np.float32) - self.data_shift)/self.data_scale, conditional=(theta.astype(np.float32) - self.theta_shift)/self.theta_scale ).numpy() + self.prior.log_prob(theta.astype(np.float32)).numpy() + + #if np.isnan(lnP): + # return -1e100 + #else: + # return lnP + return self.NDEs.weighted_log_prob((self.data - self.data_shift)/self.data_scale, conditional=(theta - self.theta_shift)/self.theta_scale ) + self.prior.log_prob(theta) + + # weighted log posterior + def log_proposal(self, theta): + + #lnP = 0.5*self.NDEs.weighted_log_prob((self.data.astype(np.float32) - self.data_shift)/self.data_scale, conditional=(theta.astype(np.float32) - self.theta_shift)/self.theta_scale ).numpy() + self.prior.log_prob(theta.astype(np.float32)).numpy() + + #if np.isnan(lnP): + # return -1e100 + #else: + # return lnP + return 0.5*self.NDEs.weighted_log_prob((self.data - self.data_shift)/self.data_scale, conditional=(theta - self.theta_shift)/self.theta_scale ) + self.prior.log_prob(theta) + # EMCEE sampler - def emcee_sample(self, log_likelihood=None, x0=None, burn_in_chain=100, main_chain=1000): - - # Set the log likelihood (default to the posterior if none given) - if log_likelihood is None: - log_likelihood = lambda x: self.log_posterior_stacked(x, self.data)[0] - + #def emcee_sample(self, log_target=None, x0=None, burn_in_chain=100, main_chain=1000): + + # default log target + # if log_target is None: + # log_target = self.weighted_log_posterior + # Set up default x0 - if x0 is None: - x0 = [self.posterior_samples[-i,:] for i in range(self.nwalkers)] - + # if x0 is None: + # x0 = self.posterior_samples[np.random.choice(np.arange(len(self.posterior_samples)), p=self.posterior_weights.astype(np.float32)/sum(self.posterior_weights), replace=False, size=self.nwalkers),:] + # Set up the sampler - sampler = emcee.EnsembleSampler(self.nwalkers, self.npar, log_likelihood) - + # sampler = emcee.EnsembleSampler(self.nwalkers, self.npar, log_target) + # Burn-in chain - state = sampler.run_mcmc(x0, burn_in_chain) - sampler.reset() - + # state = sampler.run_mcmc(x0, burn_in_chain) + # sampler.reset() + # Main chain - sampler.run_mcmc(state.coords, main_chain) - - return sampler.flatchain + # sampler.run_mcmc(state, main_chain) + + # pull out the unique samples and weights + # chain, weights = np.unique(sampler.get_chain(flat=True), axis=0, return_counts=True) + + # pull out the log probabilities + # log_prob, _ = np.unique(sampler.get_log_prob(flat=True), axis=0, return_counts=True) + + # return chain, weights, log_prob + + # run affine sampler + def affine_sample(self, log_target=None, x0=None, burn_in_chain=100, main_chain=1000): + + # default log target + if log_target is None: + log_target = self.weighted_log_posterior + + # Set up default x0 + if x0 is None: + x0 = self.posterior_samples[np.random.choice(np.arange(len(self.posterior_samples)), p=self.posterior_weights.astype(np.float32)/sum(self.posterior_weights), replace=False, size=2*self.nwalkers),:] + + # run chain + chain = affine.sample(log_target, self.npar, self.nwalkers, burn_in_chain + main_chain, x0[0:self.nwalkers,:], x0[self.nwalkers:,:]) + + # cut out birn-in and flatten + chain = chain.numpy()[burn_in_chain:,:,:].reshape(-1, chain.shape[-1]).astype(np.float32) + + # return weighted samples + return np.unique(chain, axis=0, return_counts=True) def sequential_training(self, simulator, compressor, n_initial, n_batch, n_populations, proposal = None, \ simulator_args = None, compressor_args = None, safety = 5, plot = True, batch_size = 100, \ @@ -360,13 +388,16 @@ def sequential_training(self, simulator, compressor, n_initial, n_batch, n_popul # Set up the initial parameter proposal density if proposal is None: - if self.input_normalization is 'fisher': - proposal = priors.TruncatedGaussian(self.theta_fiducial, 9*self.Finv, self.lower, self.upper) - elif self.Finv is not None: - proposal = priors.TruncatedGaussian(self.theta_fiducial, 9*self.Finv, self.lower, self.upper) + if self.Finv is not None: + scale = tf.linalg.cholesky((9. *self.Finv).astype(np.float32)) + proposal = ndes.TruncatedMultivariateNormalTriL( + loc=self.theta_fiducial.astype(np.float32), scale_tril=scale, + low=self.prior.low, high=self.prior.high, + validate_args=False, allow_nan_stats=True, + name='Proposal') else: proposal = self.prior - + # Generate initial theta values from some broad proposal on # master process and share with other processes. Overpropose # by a factor of safety to (hopefully) cope gracefully with @@ -374,56 +405,59 @@ def sequential_training(self, simulator, compressor, n_initial, n_batch, n_popul # proposal array (self.inds_prop) and accepted arrays # (self.inds_acpt) to allow for easy MPI communication. if self.rank == 0: - ps = np.array([proposal.draw() for i in range(safety * n_initial)]) + theta_batch = proposal.sample(safety * n_initial).numpy() else: - ps = np.zeros((safety * n_initial, self.npar)) + theta_batch = np.zeros((safety * n_initial, self.npar)) if self.use_mpi: - self.comm.Bcast(ps, root=0) + self.comm.Bcast(theta_batch, root=0) self.inds_prop = self.allocate_jobs(safety * n_initial) self.inds_acpt = self.allocate_jobs(n_initial) # Run simulations at those theta values - xs_batch, ps_batch = self.run_simulation_batch(n_initial, ps, simulator, compressor, simulator_args, compressor_args, seed_generator = seed_generator, sub_batch = sub_batch) + data_batch, theta_batch = self.run_simulation_batch(n_initial, theta_batch, simulator, compressor, simulator_args, compressor_args, seed_generator = seed_generator, sub_batch = sub_batch) # Train on master only if self.rank == 0: # Construct the initial training-set - self.load_simulations(xs_batch, ps_batch) + self.load_simulations(data_batch, theta_batch) - # Train the network on these initial simulations - self.train_ndes(training_data=[self.x_train, self.y_train], batch_size=max(self.n_sims//8, batch_size), validation_split=validation_split, epochs=epochs, patience=patience) - self.stacked_sequential_training_loss.append(np.sum(np.array([self.training_loss[n][-1]*self.stacking_weights[n] for n in range(self.n_ndes)]))) - self.stacked_sequential_validation_loss.append(np.sum(np.array([self.validation_loss[n][-1]*self.stacking_weights[n] for n in range(self.n_ndes)]))) + #TC - we should add maximising the ELBO between propsal distribution and NDEs, that would be the most correct thing to do and would be really quick (not yet implemented in ndes) + # Train the networks on these initial simulations + self.train(training_data=[self.theta_train, self.data_train], f_val=validation_split, epochs=epochs, n_batch=max(self.n_sims//8, batch_size), patience=patience) + + self.stacked_sequential_training_loss.append(np.sum(self.NDEs.weighting * self.training_loss[-1])) + self.stacked_sequential_validation_loss.append(np.sum(self.NDEs.weighting * self.validation_loss[-1])) self.sequential_nsims.append(self.n_sims) - + # Generate posterior samples if save_intermediate_posteriors: print('Sampling approximate posterior...') - self.posterior_samples = self.emcee_sample(log_likelihood = lambda x: self.log_posterior_stacked(x, self.data)[0], - x0=[self.posterior_samples[-i,:] for i in range(self.nwalkers)], \ - main_chain=self.posterior_chain_length) - + #x0 = self.posterior_samples[np.random.choice(np.arange(len(self.posterior_samples)), p=self.posterior_weights.astype(np.float32)/sum(self.posterior_weights), replace=False, size=2*self.nwalkers),:] + #self.posterior_samples, self.posterior_weights, self.log_posterior_values = self.emcee_sample(x0=x0, main_chain=self.posterior_chain_length) + #x0 = self.posterior_samples[-2*self.nwalkers:,:] + x0 = self.posterior_samples[np.random.choice(np.arange(len(self.posterior_samples)), p=self.posterior_weights.astype(np.float32)/sum(self.posterior_weights), replace=False, size=2*self.nwalkers),:] + self.posterior_samples, self.posterior_weights = self.affine_sample(log_target=self.weighted_log_posterior, main_chain=self.posterior_chain_length, x0=x0) + # Save posterior samples to file - f = open('{}posterior_samples_0.dat'.format(self.results_dir), 'w') + f = open(self.results_dir + "/" + 'posterior_samples_0.dat', 'w') np.savetxt(f, self.posterior_samples) f.close() - + print('Done.') # If plot == True, plot the current posterior estimate if plot == True: - self.triangle_plot([self.posterior_samples], \ - savefig=True, \ - filename='{}seq_train_post_0.pdf'.format(self.results_dir)) - + self.triangle_plot([self.posterior_samples], weights=[self.posterior_weights], savefig=True, \ + filename=self.results_dir + "/" + 'seq_train_post_0.pdf') + # Save attributes if save == True - if self.save == True: + if self.save is True: self.saver() # Loop through a number of populations for i in range(n_populations): - + # Propose theta values on master process and share with # other processes. Again, ensure we propose more sets of # parameters than needed to cope with bad params. @@ -431,47 +465,59 @@ def sequential_training(self, simulator, compressor, n_initial, n_batch, n_popul # Current population print('Population {}/{}'.format(i+1, n_populations)) - + # Sample the current posterior approximation print('Sampling proposal density...') - self.proposal_samples = \ - self.emcee_sample(log_likelihood = lambda x: self.log_geometric_mean_proposal_stacked(x, self.data)[0], \ - x0=[self.proposal_samples[-j,:] for j in range(self.nwalkers)], \ - main_chain=self.proposal_chain_length) - ps_batch = self.proposal_samples[-safety * n_batch:,:] + #x0 = [self.proposal_samples[-j,:] for j in range(self.nwalkers)] + #x0 = self.proposal_samples[np.random.choice(np.arange(len(self.proposal_samples)), p=self.proposal_weights.astype(np.float32)/sum(self.proposal_weights), replace=False, size=2*self.nwalkers),:] + + #self.proposal_samples, self.proposal_weights, self.log_proposal_values = \ + # self.emcee_sample(log_target = self.log_proposal, + # x0=x0, + # main_chain=self.proposal_chain_length) + #x0 = self.proposal_samples[-2*self.nwalkers:,:] + x0 = self.proposal_samples[np.random.choice(np.arange(len(self.proposal_samples)), p=self.proposal_weights.astype(np.float32)/sum(self.proposal_weights), replace=False, size=2*self.nwalkers),:] + + self.proposal_samples, self.proposal_weights = self.affine_sample(log_target=self.log_proposal, main_chain=self.proposal_chain_length, x0=x0) + + theta_batch = self.proposal_samples[-safety * n_batch:,:] print('Done.') else: - ps_batch = np.zeros((safety * n_batch, self.npar)) + theta_batch = np.zeros((safety * n_batch, self.npar)) if self.use_mpi: - self.comm.Bcast(ps_batch, root=0) + self.comm.Bcast(theta_batch, root=0) # Run simulations self.inds_prop = self.allocate_jobs(safety * n_batch) self.inds_acpt = self.allocate_jobs(n_batch) - xs_batch, ps_batch = self.run_simulation_batch(n_batch, ps_batch, simulator, compressor, simulator_args, compressor_args, seed_generator = seed_generator, sub_batch = sub_batch) + data_batch, theta_batch = self.run_simulation_batch(n_batch, theta_batch, simulator, compressor, simulator_args, compressor_args, seed_generator = seed_generator, sub_batch = sub_batch) # Train on master only if self.rank == 0: - + # Augment the training data - self.add_simulations(xs_batch, ps_batch) - - # Train the network on these initial simulations - self.train_ndes(training_data=[self.x_train, self.y_train], batch_size=max(self.n_sims//8, batch_size), validation_split=0.1, epochs=epochs, patience=patience) - self.stacked_sequential_training_loss.append(np.sum(np.array([self.training_loss[n][-1]*self.stacking_weights[n] for n in range(self.n_ndes)]))) - self.stacked_sequential_validation_loss.append(np.sum(np.array([self.validation_loss[n][-1]*self.stacking_weights[n] for n in range(self.n_ndes)]))) + self.add_simulations(data_batch, theta_batch) + + # Train the networks on these initial simulations + self.train(training_data=[self.theta_train, self.data_train], f_val=validation_split, epochs=epochs, n_batch=max(self.n_sims//8, batch_size), patience=patience) + + self.stacked_sequential_training_loss.append(np.sum(self.NDEs.weighting * self.training_loss[-1])) + self.stacked_sequential_validation_loss.append(np.sum(self.NDEs.weighting * self.validation_loss[-1])) self.sequential_nsims.append(self.n_sims) # Generate posterior samples if save_intermediate_posteriors: print('Sampling approximate posterior...') - self.posterior_samples = self.emcee_sample(log_likelihood = lambda x: self.log_posterior_stacked(x, self.data)[0], - x0=[self.posterior_samples[-i,:] for i in range(self.nwalkers)], \ - main_chain=self.posterior_chain_length) - + #x0 = self.posterior_samples[np.random.choice(np.arange(len(self.posterior_samples)), p=self.posterior_weights.astype(np.float32)/sum(self.posterior_weights), replace=False, size=2*self.nwalkers),:] + #self.posterior_samples, self.posterior_weights, self.log_posterior_values = \ + # self.emcee_sample(x0=x0, main_chain=self.posterior_chain_length) + #x0 = self.posterior_samples[-2*self.nwalkers:,:] + x0 = self.posterior_samples[np.random.choice(np.arange(len(self.posterior_samples)), p=self.posterior_weights.astype(np.float32)/sum(self.posterior_weights), replace=False, size=2*self.nwalkers),:] + self.posterior_samples, self.posterior_weights = self.affine_sample(log_target=self.weighted_log_posterior, main_chain=self.posterior_chain_length, x0=x0) + # Save posterior samples to file - f = open('{}posterior_samples_{:d}.dat'.format(self.results_dir, i+1), 'w') + f = open(self.results_dir + "/" + 'posterior_samples_{:d}.dat'.format(i+1), 'w') np.savetxt(f, self.posterior_samples) f.close() @@ -480,136 +526,105 @@ def sequential_training(self, simulator, compressor, n_initial, n_batch, n_popul # If plot == True if plot == True: # Plot the posterior - self.triangle_plot([self.posterior_samples], \ + self.triangle_plot([self.posterior_samples], weights=[self.posterior_weights], \ savefig=True, \ - filename='{}seq_train_post_{:d}.pdf'.format(self.results_dir, i + 1)) + filename=self.results_dir + "/" + 'seq_train_post_{:d}.pdf'.format(i + 1)) # Plot training convergence if plot == True: # Plot the training loss convergence - self.sequential_training_plot(savefig=True, filename='{}seq_train_loss.pdf'.format(self.results_dir)) + self.sequential_training_plot(savefig=True, filename=self.results_dir + "/" + 'seq_train_loss.pdf') - def train_ndes(self, training_data=None, batch_size=100, validation_split=0.1, epochs=500, patience=20, mode='samples'): - - # Set the default training data if none - if training_data is None: - training_data = [self.x_train, self.y_train] - - # Train the networks - for n in range(self.n_ndes): - # Train the NDE - val_loss, train_loss = self.trainer[n].train(self.sess, training_data, validation_split = validation_split, epochs=epochs, batch_size=batch_size, progress_bar=self.progress_bar, patience=patience, saver_name=self.graph_restore_filename, mode=mode) - - # Save the training and validation losses - self.training_loss[n] = np.concatenate([self.training_loss[n], train_loss]) - self.validation_loss[n] = np.concatenate([self.validation_loss[n], val_loss]) + # Save attributes if save == True + if self.save is True: + self.saver() - # Update weights for stacked density estimator - self.stacking_weights = np.exp(-np.array([self.training_loss[i][-1] for i in range(self.n_ndes)])) - self.stacking_weights = self.stacking_weights/sum(self.stacking_weights) + def load_simulations(self, data_batch, theta_batch): - # if save == True, save everything - if self.save == True: - self.saver() + if self.input_normalization is "auto": + if self.input_normalization is False: + self.data_shift = np.mean(data_batch, axis = 0).astype(np.float32) + self.data_scale = np.std(data_batch, axis = 0).astype(np.float32) + self.theta_shift = np.mean(theta_batch, axis = 0).astype(np.float32) + self.theta_scale = np.std(theta_batch, axis = 0).astype(np.float32) - def load_simulations(self, xs_batch, ps_batch): - - # Set the input normalizations if None specified - if self.input_normalization is None: - self.p_mean = np.mean(ps_batch, axis = 0) - self.p_std = np.std(ps_batch, axis = 0) - self.x_mean = np.mean(xs_batch, axis = 0) - self.x_std = np.std(xs_batch, axis = 0) - - ps_batch = (ps_batch - self.p_mean)/self.p_std - xs_batch = (xs_batch - self.x_mean)/self.x_std - self.ps = np.concatenate([self.ps, ps_batch]) - self.xs = np.concatenate([self.xs, xs_batch]) - self.x_train = self.ps.astype(np.float32) - self.y_train = self.xs.astype(np.float32) - self.n_sims += len(ps_batch) - - def add_simulations(self, xs_batch, ps_batch): - - ps_batch = (ps_batch - self.p_mean)/self.p_std - xs_batch = (xs_batch - self.x_mean)/self.x_std - self.ps = np.concatenate([self.ps, ps_batch]) - self.xs = np.concatenate([self.xs, xs_batch]) - self.x_train = self.ps.astype(np.float32) - self.y_train = self.xs.astype(np.float32) - self.n_sims += len(ps_batch) - - def fisher_pretraining(self, n_batch=5000, plot=True, batch_size=100, validation_split=0.1, epochs=1000, patience=20, mode='regression'): + self.theta_realizations = np.concatenate([self.theta_realizations, theta_batch]) + self.data_realizations = np.concatenate([self.data_realizations, data_batch]) + self.theta_train = tf.convert_to_tensor((self.theta_realizations.astype(np.float32) - self.theta_shift)/self.theta_scale, dtype=tf.float32) + self.data_train = tf.convert_to_tensor((self.data_realizations.astype(np.float32) - self.data_shift)/self.data_scale, dtype=tf.float32) + self.n_sims += len(theta_batch) + + def add_simulations(self, data_batch, theta_batch): + self.theta_realizations = np.concatenate([self.theta_realizations, theta_batch]) + self.data_realizations = np.concatenate([self.data_realizations, data_batch]) + self.theta_train = tf.convert_to_tensor((self.theta_realizations.astype(np.float32) - self.theta_shift)/self.theta_scale, dtype=tf.float32) + self.data_train = tf.convert_to_tensor((self.data_realizations.astype(np.float32) - self.data_shift)/self.data_scale, dtype=tf.float32) + self.n_sims += len(theta_batch) + + def fisher_pretraining(self, n_batch=5000, plot=True, batch_size=100, validation_split=0.1, epochs=1000, patience=20): # Train on master only if self.rank == 0: # Generate fisher pre-training data - + # Broader proposal - proposal = priors.TruncatedGaussian(self.theta_fiducial, 9*self.Finv, self.lower, self.upper) - - # Anticipated covariance of the re-scaled data - Cdd = np.zeros((self.npar, self.npar)) - for i in range(self.npar): - for j in range(self.npar): - Cdd[i,j] = self.Finv[i,j]/(self.fisher_errors[i]*self.fisher_errors[j]) - Ldd = np.linalg.cholesky(Cdd) - Cddinv = np.linalg.inv(Cdd) - ln2pidetCdd = np.log(2*np.pi*np.linalg.det(Cdd)) - + proposal = ndes.TruncatedMultivariateNormalTriL( + loc=self.theta_fiducial, scale_tril=tf.linalg.cholesky(9*self.Finv), + low=self.prior.low, high=self.prior.high, + validate_args=False, allow_nan_stats=True, + name='AsymptoticPosterior') + + # Cholesky of inverse Fisher information matrix + L = np.linalg.cholesky(self.Finv) + # Sample parameters from some broad proposal - ps = np.zeros((3*n_batch, self.npar)) - for i in range(0, n_batch): - # Draws from prior - ps[i,:] = (self.prior.draw() - self.theta_fiducial)/self.fisher_errors - - # Draws from asymptotic posterior - ps[n_batch + i,:] = (self.asymptotic_posterior.draw() - self.theta_fiducial)/self.fisher_errors - - # Drawn from Gaussian with 3x anticipated covariance matrix - ps[2*n_batch + i,:] = (proposal.draw() - self.theta_fiducial)/self.fisher_errors - + theta_batch = np.zeros((3*n_batch, self.npar)) + theta_batch[:n_batch,:] = self.prior.sample(n_batch) + theta_batch[n_batch:2*n_batch,:] = self.asymptotic_posterior.sample(n_batch) + theta_batch[2*n_batch:,:] = proposal.sample(n_batch) + # Sample data assuming a Gaussian likelihood - xs = np.array([pss + np.dot(Ldd, np.random.normal(0, 1, self.npar)) for pss in ps]) - - # Evaluate the logpdf at those values - fisher_logpdf_train = np.array([-0.5*np.dot(xs[i,:]-ps[i,:], np.dot(Cddinv, xs[i,:]-ps[i,:])) - 0.5*ln2pidetCdd for i in range(len(xs))]) - + data_batch = np.array([theta + np.dot(L, np.random.normal(0, 1, self.npar)) for theta in theta_batch], dtype=np.float32) + # Construct the initial training-set - fisher_x_train = ps.astype(np.float32).reshape((3*n_batch, self.npar)) - fisher_y_train = xs.astype(np.float32).reshape((3*n_batch, self.npar)) - - # Train the networks depending on the chosen mode (regression = default) - - if mode == "regression": - # Train the networks on these initial simulations - self.train_ndes(training_data=[fisher_x_train, fisher_y_train, np.atleast_2d(fisher_logpdf_train).reshape(-1,1)], validation_split = validation_split, epochs=epochs, batch_size=batch_size, patience=patience, mode='regression') - if mode == "samples": - # Train the networks on these initial simulations - self.train_ndes(training_data=[fisher_x_train, fisher_y_train], validation_split = validation_split, epochs=epochs, batch_size=batch_size, patience=patience, mode='samples') + fisher_theta_train = tf.convert_to_tensor((theta_batch.astype(np.float32).reshape((3*n_batch, self.npar)) - self.theta_shift)/self.theta_scale, dtype=tf.float32) + fisher_data_train = tf.convert_to_tensor((data_batch.astype(np.float32).reshape((3*n_batch, self.npar)) - self.data_shift)/self.data_scale, dtype=tf.float32) + + # Train the networks on these initial simulations + self.train(training_data=[fisher_theta_train, fisher_data_train], f_val=validation_split, epochs=epochs, n_batch=batch_size, patience=patience) # Generate posterior samples if plot==True: print('Sampling approximate posterior...') - self.posterior_samples = self.emcee_sample(log_likelihood = lambda x: self.log_posterior_stacked(x, self.data)[0], - x0=[self.posterior_samples[-i,:] for i in range(self.nwalkers)], \ - main_chain=self.posterior_chain_length) + #x0 = self.posterior_samples[np.random.choice(np.arange(len(self.posterior_samples)), p=self.posterior_weights.astype(np.float32)/sum(self.posterior_weights), replace=False, size=2*self.nwalkers),:] + #self.posterior_samples, self.posterior_weights, self.log_posterior_values = \ + # self.emcee_sample(x0=x0, main_chain=self.posterior_chain_length) + #x0 = self.posterior_samples[-2*self.nwalkers:,:] + x0 = self.posterior_samples[np.random.choice(np.arange(len(self.posterior_samples)), p=self.posterior_weights.astype(np.float32)/sum(self.posterior_weights), replace=False, size=2*self.nwalkers),:] + #x0 = self.posterior_samples[np.random.choice(np.arange(len(self.posterior_samples)), replace=False, size=2*self.nwalkers),:] + self.posterior_samples, self.posterior_weights = self.affine_sample(log_target=self.weighted_log_posterior, main_chain=self.posterior_chain_length, x0=x0) + print('Done.') # Plot the posterior - self.triangle_plot([self.posterior_samples], \ + self.triangle_plot([self.posterior_samples], weights=[self.posterior_weights], \ savefig=True, \ - filename='{}fisher_train_post.pdf'.format(self.results_dir)) + filename=self.results_dir + "/" + 'fisher_train_post.pdf') - def triangle_plot(self, samples = None, weights = None, savefig = False, filename = None): + # save current state if save=True + if self.save is True: + self.saver() + def triangle_plot(self, samples = None, weights = None, savefig = False, filename = None): # Set samples to the posterior samples by default if samples is None: samples = [self.posterior_samples] - mc_samples = [MCSamples(samples=s, weights = None, names = self.names, labels = self.labels, ranges = self.ranges) for s in samples] + weights = [self.posterior_weights] + mc_samples = [MCSamples(samples=s, weights=weights[i], names=self.names, labels=self.labels, ranges=self.ranges) for i, s in enumerate(samples)] # Triangle plot + plt.close() with mpl.rc_context(): g = plots.getSubplotPlotter(width_inch = 12) g.settings.figure_legend_frame = False @@ -622,12 +637,11 @@ def triangle_plot(self, samples = None, weights = None, savefig = False, filenam for j in range(0, i+1): ax = g.subplots[i,j] xtl = ax.get_xticklabels() - ax.set_xticklabels(xtl, rotation=45) - plt.tight_layout() + #ax.set_xticklabels(xtl, rotation=45) plt.subplots_adjust(hspace=0, wspace=0) - + if savefig: - plt.savefig(filename) + plt.savefig(filename, bbox_inches='tight') if self.show_plot: plt.show() else: @@ -651,7 +665,7 @@ def sequential_training_plot(self, savefig = False, filename = None): 'lines.linewidth': .5, 'axes.linewidth': .5, 'axes.edgecolor': 'black'}): - + # Trace plot of the training and validation loss as a function of the number of simulations ran plt.plot(self.sequential_nsims, self.stacked_sequential_training_loss, markersize=5, marker='o', lw=2, alpha=0.7, label = 'training loss') plt.plot(self.sequential_nsims, self.stacked_sequential_validation_loss, markersize=5, marker='o', lw=2, alpha=0.7, label = 'validation loss') diff --git a/pydelfi/ndes.py b/pydelfi/ndes.py index 59ccb73957..b6467875a9 100755 --- a/pydelfi/ndes.py +++ b/pydelfi/ndes.py @@ -1,353 +1,907 @@ -import numpy as np -import numpy.random as rng import tensorflow as tf -dtype = tf.float32 - -class ConditionalGaussianMade: - """ - Implements a Made, where each conditional probability is modelled by a single gaussian component. - """ - - def __init__(self, n_parameters, n_data, n_hiddens, act_fun, output_order='sequential', mode='sequential', input_parameters=None, input_data=None, logpdf=None): - """ - Constructor. - :param n_inputs: number of (conditional) inputs - :param n_outputs: number of outputs - :param n_hiddens: list with number of hidden units for each hidden layer - :param act_fun: tensorflow activation function - :param output_order: order of outputs - :param mode: strategy for assigning degrees to hidden nodes: can be 'random' or 'sequential' - :param input: tensorflow placeholder to serve as input; if None, a new placeholder is created - :param output: tensorflow placeholder to serve as output; if None, a new placeholder is created - """ - - # save input arguments - self.n_parameters = n_parameters - self.n_data = n_data - self.n_hiddens = n_hiddens - self.act_fun = act_fun - self.mode = mode - - # create network's parameters - degrees = self.create_degrees(output_order) - Ms, Mmp = self.create_masks(degrees) - Wx, Ws, bs, Wm, bm, Wp, bp = self.create_weights_conditional(None) - self.parms = [Wx] + Ws + bs + [Wm, bm, Wp, bp] - self.output_order = degrees[0] - - # activation function - f = self.act_fun +import tensorflow_probability as tfp +import tqdm +import pickle +import os +import numpy as np - # input matrices - self.parameters = tf.placeholder(dtype=dtype,shape=[None,n_parameters],name='parameters') if input_parameters is None else input_parameters - self.data = tf.placeholder(dtype=dtype,shape=[None,n_data],name='data') if input_data is None else input_data - self.logpdf = tf.placeholder(dtype=dtype,shape=[None,1],name='logpdf') if logpdf is None else logpdf +from tensorflow_probability.python.internal import distribution_util +from tensorflow_probability.python.internal import dtype_util +from tensorflow_probability.python.internal import tensor_util - # feedforward propagation - h = f(tf.matmul(self.parameters, Wx) + tf.matmul(self.data, Ms[0] * Ws[0]) + bs[0],name='h1') - for l, (M, W, b) in enumerate(zip(Ms[1:], Ws[1:], bs[1:])): - h = f(tf.matmul(h, M * W) + b,name='h'+str(l + 2)) +tfd = tfp.distributions +tfb = tfp.bijectors +dtype = tf.float32 - # output means - self.m = tf.add(tf.matmul(h, Mmp * Wm), bm, name='m') +__version__ = "v0.2" +__author__ = "Justin Alsing, Tom Charnock and Stephen Feeney" - # output log precisions - self.logp = tf.add(tf.matmul(h, Mmp * Wp), bp, name='logp') +class NDE(): + def __init__(self, model, prior, optimiser=tf.keras.optimizers.Adam(lr=1e-4), optimiser_arguments=None, dtype=tf.float32, **kwargs): + self.dtype = dtype + if self.dtype == tf.float32: + self.itype = tf.int32 + else: + self.itype = tf.int64 - # random numbers driving made - self.u = tf.exp(0.5 * self.logp) * (self.data - self.m) + if type(model) is list: + self.n_stack = len(model) + else: + self.n_stack = 1 + model = [model] - # log likelihoods - self.L = tf.multiply(-0.5,self.n_data * np.log(2 * np.pi) + \ - tf.reduce_sum(self.u ** 2 - self.logp, axis=1,keepdims=True),name='L') + self.error_stack = None + self.set_stack() - # train objective - self.trn_loss = -tf.reduce_mean(self.L,name='trn_loss') - self.reg_loss = tf.losses.mean_squared_error(self.L, self.logpdf) + # model weighting + self.weighting = tf.ones((self.n_stack,), name="weighting") + self.model = model + self.prior = prior - def create_degrees(self, input_order): + if optimiser_arguments is not None: + self.optimiser = optimiser(optimiser_arguments) + else: + self.optimiser = optimiser + super(NDE, self).__init__(**kwargs) + + @tf.function + def single_train_epoch(self, dataset, stack, variables_list, stack_size, n_batch): + loss = tf.zeros((stack_size,)) + for step, xy_batch_train in enumerate(dataset): + # Unpack batched training data + x_batch_train, y_batch_train = xy_batch_train + # Open a GradientTape to record the operations run + # during the forward pass, which enables autodifferentiation. + with tf.GradientTape() as tape: + # Compute the loss for this batch. + neg_log_prob = -tf.reduce_mean(self.log_prob(y_batch_train, conditional=x_batch_train, stack=stack), -1) + neg_total_log_prob = tf.reduce_sum(neg_log_prob) + # Retrieve the gradients of the trainable variables wrt the loss and + # pass to optimizer. + grads = tape.gradient(neg_total_log_prob, variables_list) + self.optimiser.apply_gradients(zip(grads, variables_list)) + loss = tf.add(loss, neg_log_prob) + return tf.divide(loss, n_batch) + + @tf.function + def single_validate_epoch(self, dataset, stack, stack_size, n_batch): + loss = tf.zeros((stack_size,)) + for step, xy_batch_train in enumerate(dataset): + # Unpack batched training data + x_batch_train, y_batch_train = xy_batch_train + # Compute the loss for this batch. + neg_log_prob = -tf.reduce_mean(self.log_prob(y_batch_train, conditional=x_batch_train, stack=stack), -1) + loss = tf.add(loss, neg_log_prob) + return tf.divide(loss, n_batch) + + def fit(self, data, f_val=0.1, epochs=1000, n_batch=100, + patience=20, file_name=None, progress_bar=True): """ - Generates a degree for each hidden and input unit. A unit with degree d can only receive input from units with - degree less than d. - :param n_hiddens: a list with the number of hidden units - :param input_order: the order of the inputs; can be 'random', 'sequential', or an array of an explicit order - :param mode: the strategy for assigning degrees to hidden nodes: can be 'random' or 'sequential' - :return: list of degrees + Training function to be called with desired parameters. + :param data: a tuple/list of (X,Y) with data where Y is conditioned on X. + :param f_val: fraction of training data randomly selected to be used for validation + :param epochs: maximum number of epochs for training. + :param n_batch: size of each batch within an epoch. + :param patience: number of epochs for early stopping criteria. + :param file_name: string of name (with or without folder) where model is saved. + :param progress_bar: display progress bar? """ - degrees = [] - - # create degrees for inputs - if isinstance(input_order, str): - - if input_order == 'random': - degrees_0 = np.arange(1, self.n_data + 1) - rng.shuffle(degrees_0) - - elif input_order == 'sequential': - degrees_0 = np.arange(1, self.n_data + 1) - + stack = list(range(self.n_stack)) + stack_size = self.n_stack + variables_list = self.get_flat_variables_list(stack) + + # Parse full training data and determine size of training set + data_X, data_Y = data + data_X = tf.convert_to_tensor(data_X, dtype=self.dtype) + data_Y = tf.convert_to_tensor(data_Y, dtype=self.dtype) + + n_sims = data_X.shape[0] + + #is_train = tfd.Categorical(probs=[f_val, 1. - f_val], dtype=tf.bool).sample(n_sims) + n_val = int(n_sims * f_val) + n_train = n_sims - n_val + is_train = tf.random.shuffle([True] * n_train + [False] * n_val) + #n_train = tf.reduce_sum(tf.cast(is_train, dtype=tf.int64)) + + n_train_batches = n_train // n_batch + if n_train_batches == 0: + n_train_batches = 1 + n_train_batches = tf.cast(n_train_batches, dtype=self.dtype) + + n_val_batches = int(n_val / n_batch) + if n_val_batches == 0: + n_val_batches = 1 + n_val_batches = tf.cast(n_val_batches, dtype=self.dtype) + + # Create training and validation Dataset objects, shuffling and batching the training data. Note + # the default behaviour of Dataset.shuffle() sets reshuffle_each_iteration=True, and + # Dataset.batch() sets drop_remainder=False + train_dataset = tf.data.Dataset.from_tensor_slices((data_X[is_train], + data_Y[is_train])) + val_dataset = tf.data.Dataset.from_tensor_slices((data_X[~is_train], + data_Y[~is_train])) + train_dataset = train_dataset.shuffle(n_train).batch(n_batch) + val_dataset = val_dataset.batch(n_val) + + # Early stopping variables + es_count = tf.zeros((self.n_stack,), dtype=tf.int64) + temp_train_loss = tf.zeros((self.n_stack,), dtype=self.dtype) + temp_val_loss = tf.divide(tf.ones(self.n_stack, dtype=self.dtype), tf.convert_to_tensor(0, dtype=self.dtype)) + + temp_variables = [self.model[i].trainable_variables for i in self.stack] + + # Validation and training losses + train_losses = [] + val_losses = [] + + # Progress bar, if desired + if progress_bar: + if self.isnotebook(): + pbar = tqdm.tnrange(epochs, desc="Training") else: - raise ValueError('invalid output order') - - else: - input_order = np.array(input_order) - assert np.all(np.sort(input_order) == np.arange(1, self.n_data + 1)), 'invalid input order' - degrees_0 = input_order - degrees.append(degrees_0) - - # create degrees for hiddens - if self.mode == 'random': - for N in self.n_hiddens: - min_prev_degree = min(np.min(degrees[-1]), self.n_data - 1) - degrees_l = rng.randint(min_prev_degree, self.n_data, N) - degrees.append(degrees_l) - - elif self.mode == 'sequential': - for N in self.n_hiddens: - degrees_l = np.arange(N) % max(1, self.n_data - 1) + min(1, self.n_data - 1) - degrees.append(degrees_l) - + pbar = tqdm.trange(epochs, desc="Training") + pbar.set_postfix(ordered_dict={"train loss":0, "val loss":0}, refresh=True) + + # Main training loop + for epoch in range(epochs): + # Iterate over the batches of the dataset. + this_train_loss = self.single_train_epoch(train_dataset, stack, variables_list, stack_size, n_train_batches) + this_val_loss = self.single_validate_epoch(val_dataset, stack, stack_size, 1) + + # early stopping + state = this_val_loss < tf.gather(temp_val_loss, stack) + + improving = tf.where(state) + es_count = tf.squeeze( + tf.tensor_scatter_nd_update( + tf.expand_dims(es_count, 1), + improving, + tf.zeros( + (tf.reduce_sum( + tf.cast(state, dtype=tf.int64)), + 1), + dtype=tf.int64)), + 1) + improving = tf.squeeze(improving, 1) + improving_stack = tf.gather(stack, improving) + temp_variables = self.save_models(improving_stack.numpy(), variables=temp_variables) + temp_train_loss = tf.tensor_scatter_nd_update( + temp_train_loss, + tf.expand_dims(improving_stack, 1), + tf.gather(this_train_loss, improving)) + temp_val_loss = tf.tensor_scatter_nd_update( + temp_val_loss, + tf.expand_dims(improving_stack, 1), + tf.gather(this_val_loss, improving)) + + not_improving = tf.where(~state) + es_count = tf.squeeze( + tf.tensor_scatter_nd_add( + tf.expand_dims(es_count, 1), + not_improving, + tf.ones( + (tf.reduce_sum( + tf.cast(~state, dtype=tf.int64)), + 1), + dtype=tf.int64)), + 1) + + ended = es_count >= patience + if tf.reduce_any(ended): + model_indices = tf.gather(stack, tf.squeeze(tf.where(ended), 1)).numpy() + remaining_indices = tf.squeeze(tf.where(~ended), 1) + es_count = tf.gather(es_count, remaining_indices) + self.load_models(model_indices, variables=temp_variables) + stack = self.remove_from_stack(stack, model_indices, epoch=epoch) + stack_size = len(stack) + variables_list = self.get_flat_variables_list(stack) + if len(stack) == 0: + break + + train_losses.append(temp_train_loss) + val_losses.append(temp_val_loss) + + # Update progress if desired. + if progress_bar: + pbar.update(1) + pbar.set_postfix( + ordered_dict={ + "train loss":[float("{0:.3g}".format(this_train_loss.numpy()[i]))for i in range(len(this_train_loss.numpy()))], + "val loss":[float("{0:.3g}".format(this_val_loss.numpy()[i])) for i in range(len(this_train_loss.numpy()))], + "patience counter":es_count.numpy(), + "stack":stack}, + refresh=True) + self.weighting = tf.nn.softmax(-temp_train_loss - tf.math.reduce_max(-temp_train_loss)) + self.set_stack() + return tf.stack(val_losses), tf.stack(train_losses) + + def set_stack(self, train=False, error=None): + stack = list(range(self.n_stack)) + if train: + self.stack = stack else: - raise ValueError('invalid mode') - - return degrees - - def create_masks(self, degrees): + if error is not None: + for i in error: + stack.pop(i) + self.error_stack = stack + self.stack = stack + if self.error_stack is not None: + self.stack = self.error_stack + + def get_flat_variables_list(self, stack): + variable_list = [] + for i in stack: + for variable in self.model[i].trainable_variables: + variable_list.append(variable) + return variable_list + + def save_models(self, models, variables=None, directory=None, filename=None): + if (filename is not None) or (variables is not None): + for model in models: + these_variables = self.model[model].trainable_variables + if filename is not None: + if not os.path.isdir(directory): + raise ValueError(directory + " does not exist.") + with open(directory + "/" + filename + "_model_" + str(model) + ".pkl", "wb") as outfile: + pickle.dump([variable.numpy() for variable in these_variables], outfile) + if variables is not None: + variables[model] = these_variables + if variables is not None: + return variables + + def load_models(self, models, variables=None, directory=None, filename=None): + if (filename is not None) or (variables is not None): + for model in models: + if filename is not None: + file = directory + "/" + filename + "_model_" + str(model) + ".pkl" + if not os.path.isfile(file): + raise ValueError(file + " does not exist.") + with open(file, "rb") as outfile: + for model_variable, temp_variable in zip(self.model[model].trainable_variables, tuple(pickle.load(outfile))): + model_variable.assign(temp_variable) + if variables is not None: + for model_variable, temp_variable in zip(self.model[model].trainable_variables, variables[model]): + model_variable.assign(temp_variable) + + def remove_from_stack(self, stack, models, epoch=None): + for model in models: + stack.remove(model) + if epoch is not None: + print("Training terminated for model {:d} at epoch {:d}.".format(model, epoch + 1)) + return stack + + @tf.function + def log_prob(self, data, conditional=None, stack=None): + if stack is None: + stack = self.stack """ - Creates the binary masks that make the connectivity autoregressive. - :param degrees: a list of degrees for every layer - :return: list of all masks, as theano shared variables + log probability, returns log density ln P(d | \theta) + :param data: data vectors to evaluate density at + :param parameters: (conditional) input parameters to evaluate density at """ - - Ms = [] - - for l, (d0, d1) in enumerate(zip(degrees[:-1], degrees[1:])): - M = d0[:, np.newaxis] <= d1 - M = tf.constant(M, dtype=dtype, name='M' + str(l+1)) - Ms.append(M) - - Mmp = degrees[-1][:, np.newaxis] < degrees[0] - Mmp = tf.constant(Mmp, dtype=dtype, name='Mmp') - - return Ms, Mmp - - def create_weights_conditional(self, n_comps): + return tf.stack([ + self.model[element].conditional_log_prob(data, conditional=conditional) + for element in stack], axis=0) + + @tf.function + def weighted_log_prob(self, data, conditional=None, stack=None): + if stack is None: + stack = self.stack + return tf.transpose(tf.math.reduce_logsumexp(tf.add(tf.transpose(self.log_prob(data, conditional=conditional, stack=stack)), tf.math.log(self.weighting)), axis=1)) + + @tf.function + def prob(self, data, conditional=None, stack=None): + if stack is None: + stack = self.stack """ - Creates all learnable weight matrices and bias vectors. - :param n_comps: number of gaussian components - :return: weights and biases, as tensorflow variables + probability, returns density P(d | \theta) + :param data: data vectors to evaluate density at + :param parameters: (conditional) input parameters to evaluate density at """ - - Ws = [] - bs = [] - - n_units = np.concatenate(([self.n_data], self.n_hiddens)) - - Wx = tf.get_variable("Wx", [self.n_parameters, self.n_hiddens[0]], initializer = tf.random_normal_initializer(0., np.sqrt(1./(self.n_parameters + 1))) ) - - for l, (N0, N1) in enumerate(zip(n_units[:-1], n_units[1:])): - - W = tf.get_variable("W"+str(l), [N0, N1], initializer = tf.random_normal_initializer(0., np.sqrt(1./(1+N0))) ) - b = tf.get_variable("b"+str(l), [1, N1], initializer = tf.constant_initializer(0.0) ) - Ws.append(W) - bs.append(b) - - if n_comps is None: - - Wm = tf.get_variable("Wm", [n_units[-1], self.n_data], initializer = tf.random_normal_initializer(0., np.sqrt(1./(n_units[-1] + 1))) ) - Wp = tf.get_variable("Wp", [n_units[-1], self.n_data], initializer = tf.random_normal_initializer(0., np.sqrt(1./(n_units[-1] + 1))) ) - bm = tf.get_variable("bm", [1, self.n_data], initializer = tf.constant_initializer(0.0) ) - bp = tf.get_variable("bp", [1, self.n_data], initializer = tf.constant_initializer(0.0) ) - - return Wx, Ws, bs, Wm, bm, Wp, bp - - else: - - Wm = tf.get_variable("Wm", [n_units[-1], self.n_data, n_comps], initializer = tf.random_normal_initializer(0., np.sqrt(1./(n_units[-1] + 1))) ) - Wp = tf.get_variable("Wp", [n_units[-1], self.n_data, n_comps], initializer = tf.random_normal_initializer(0., np.sqrt(1./(n_units[-1] + 1))) ) - Wa = tf.get_variable("Wa", [n_units[-1], self.n_data, n_comps], initializer = tf.random_normal_initializer(0., np.sqrt(1./(n_units[-1] + 1))) ) - bm = tf.get_variable("bm", [self.n_data, n_comps], initializer = tf.random_normal_initializer() ) - bp = tf.get_variable("bp", [self.n_data, n_comps], initializer = tf.random_normal_initializer() ) - ba = tf.get_variable("ba", [self.n_data, n_comps], initializer = tf.random_normal_initializer() ) - - return Wx, Ws, bs, Wm, bm, Wp, bp, Wa, ba - - def eval(self, xy, sess, log=True): + return tf.stack([ + self.model[element].conditional_prob(data, conditional=conditional) + for element in stack], axis=0) + + @tf.function + def weighted_prob(self, data, conditional=None, stack=None): + if stack is None: + stack = self.stack + return tf.reduce_sum(tf.multiply(self.weighting, self.prob(data, conditional=conditional, stack=stack)), axis=0) + + @tf.function + def sample(self, n=None, conditional=None, stack=None): + if stack is None: + stack = self.stack + if n is None: + n = 1 + return tf.stack([ + self.model[element].sample(n, conditional=conditional) + for element in stack], 0) + + @tf.function + def weighted_sample(self, n=None, conditional=None, stack=None): + if stack is None: + stack = self.stack """ - Evaluate log probabilities for given input-output pairs. - :param xy: a pair (x, y) where x rows are inputs and y rows are outputs - :param sess: tensorflow session where the graph is run - :param log: whether to return probabilities in the log domain - :return: log probabilities: log p(y|x) + sample, returns samples {d} from P(d | \theta) for some input values of \theta + :param parameters: (conditional) input parameters to draw samples at + :param n: number of samples to draw (for each parameter set input) """ - - x, y = xy - lprob = sess.run(self.L,feed_dict={self.parameters:x,self.data:y}) - - return lprob if log else np.exp(lprob) - - -class ConditionalMaskedAutoregressiveFlow: + if n is None: + n = 1 + samples = self.sample(n, conditional=None, stack=stack) + return self.variance(samples) + + @tf.function + def log_posterior(self, data, conditional=None, stack=None): + if stack is None: + stack = self.stack + return tf.add( + self.log_prob(data, conditional=conditional, stack=stack), + tf.cast(self.prior.log_prob(conditional), dtype=self.dtype)) + + @tf.function + def weighted_log_posterior(self, data, conditional=None, stack=None): + if stack is None: + stack = self.stack + data = tf.cast(data, dtype=self.dtype) + conditional = tf.cast(conditional, dtype=self.dtype) + return tf.add(self.weighted_log_prob(data, conditional=conditional, stack=stack), + tf.cast(self.prior.log_prob(conditional), dtype=self.dtype)) + + @tf.function + def geometric_mean(self, data, conditional=None, stack=None): + if stack is None: + stack = self.stack + half = tf.cast(0.5, dtype=self.dtype) + two = tf.cast(2., dtype=self.dtype) + data = tf.cast(data, dtype=self.dtype) + conditional = tf.cast(conditional, dtype=self.dtype) + return tf.multiply(half, + tf.add(self.weighted_log_prob(data, conditional=conditional, stack=stack), + tf.multiply(two, self.prior.log_prob(conditional)))) + + @tf.function + def variance(self, x): + weighted_sum = tf.reduce_sum(self.weighting) + mean = tf.divide( + tf.einsum("i...,i->...", + x, + self.weighting), + weighted_sum) + variance = tf.divide( + tf.reduce_sum( + tf.square( + tf.subtract( + x, + tf.expand_dims(mean, 0))), + 0), + weighted_sum) + return mean, variance + + def isnotebook(self): + try: + shell = get_ipython().__class__.__name__ + if shell == 'ZMQInteractiveShell': + return True # Jupyter notebook or qtconsole + elif shell == 'TerminalInteractiveShell': + return False # Terminal running IPython + else: + return False # Other type (?) + except NameError: + return False + +def ConditionalMaskedAutoregressiveFlow( + n_parameters, n_data, n_mades=1, n_hidden=[50,50], input_order="random", + activation=tf.keras.layers.LeakyReLU(0.01), all_layers=True, + kernel_initializer=tf.keras.initializers.RandomNormal(mean=0.0, stddev=1e-5, seed=None), bias_initializer='zeros', + kernel_regularizer=None, bias_regularizer=None, kernel_constraint=None, + bias_constraint=None): """ Conditional Masked Autoregressive Flow. """ - - def __init__(self, n_parameters, n_data, n_hiddens, act_fun, n_mades, - output_order='sequential', mode='sequential', input_parameters=None, input_data=None, logpdf=None, index=1): + if all_layers == True: + all_layers = "all_layers" + else: + all_layers = "first_layer" + # construct stack of MADEs + MADEs = [tfb.MaskedAutoregressiveFlow( + shift_and_log_scale_fn=tfb.AutoregressiveNetwork( + params=2, + hidden_units=n_hidden, + activation=activation, + event_shape=[n_data], + conditional=True, + conditional_event_shape=[n_parameters], + conditional_input_layers=all_layers, + input_order=input_order, + kernel_initializer=kernel_initializer, + bias_initializer=bias_initializer, + kernel_regularizer=kernel_regularizer, + bias_regularizer=bias_regularizer, + kernel_constraint=kernel_constraint, + bias_constraint=bias_constraint), + name="MADE_{}".format(i)) + for i in range(n_mades)] + bijector = tfb.Chain(MADEs) + distribution = tfd.TransformedDistribution( + distribution=tfd.Normal(loc=0., scale=1.), + bijector=bijector) + put_conditional = lambda conditional : dict( + zip(["MADE_{}".format(i) for i in range(n_mades)], + [{"conditional_input": tf.convert_to_tensor(conditional, dtype=tf.float32)} for i in range(n_mades)])) + distribution.conditional_log_prob = lambda a, conditional : distribution.log_prob(a, bijector_kwargs=put_conditional(conditional)) + distribution.conditional_prob = lambda a, conditional : distribution.prob(a, bijector_kwargs=put_conditional(conditional)) + distribution.conditional_sample = lambda a, conditional : distribution.sample(a, bijector_kwargs=put_conditional(conditional)) + _ = distribution.conditional_log_prob(np.random.normal(0, 1, (1, n_data)), + conditional=np.random.normal(0, 1, (1, n_parameters))) + return distribution + +class MixtureDensityNetwork(tfd.Distribution): + """ + Implements a gaussian Mixture Density Network for modeling a conditional density p(d|\theta) (d="data", \theta="parameters") + """ + def __init__(self, n_parameters, n_data, n_components=3, n_hidden=[50,50], activation=tf.keras.layers.LeakyReLU(0.01), dtype=tf.float32, reparameterization_type=None, validate_args=False, allow_nan_stats=True, kernel_initializer=tf.keras.initializers.RandomNormal(mean=0.0, stddev=1e-5, seed=None)): """ Constructor. :param n_parameters: number of (conditional) inputs - :param n_data: number of outputs + :param n_data: number of outputs (ie dimensionality of distribution you're parameterizing) :param n_hiddens: list with number of hidden units for each hidden layer - :param act_fun: tensorflow activation function - :param n_mades: number of mades in the flow - :param output_order: order of outputs of last made - :param mode: strategy for assigning degrees to hidden nodes: can be 'random' or 'sequential' - :param input_parameters: tensorflow placeholder to serve as input for the parameters part of the training data; if None, a new placeholder is created - :param input_data: tensorflow placeholder to serve as input for data-realizations part of the training data; if None, a new placeholder is created - :param index: index of the NDE; crucial when using ensembles of NDEs to keep their scopes separate + :param activation: activation function for network + :param dtype: tensorflow type """ + super(MixtureDensityNetwork, self).__init__( + dtype=dtype, + reparameterization_type=reparameterization_type, + validate_args=validate_args, + allow_nan_stats=allow_nan_stats) - # save input arguments + # dimension of data and parameter spaces self.n_parameters = n_parameters self.n_data = n_data - self.n_hiddens = n_hiddens - self.act_fun = act_fun - self.n_mades = n_mades - self.mode = mode - - self.parameters = tf.placeholder(dtype=dtype,shape=[None,n_parameters],name='parameters') if input_parameters is None else input_parameters - self.data = tf.placeholder(dtype=dtype,shape=[None,n_data],name='data') if input_data is None else input_data - self.logpdf = tf.placeholder(dtype=dtype,shape=[None,1],name='logpdf') if logpdf is None else logpdf - - self.parms = [] - - self.mades = [] - self.bns = [] - self.u = self.data - self.logdet_dudy = 0.0 - - for i in range(n_mades): - - # create a new made - with tf.variable_scope('nde_' + str(index) + '_made_' + str(i + 1)): - made = ConditionalGaussianMade(n_parameters, n_data, n_hiddens, act_fun, - output_order, mode, self.parameters, self.u) - self.mades.append(made) - self.parms += made.parms - output_order = output_order if output_order is 'random' else made.output_order[::-1] - # inverse autoregressive transform - self.u = made.u - self.logdet_dudy += 0.5 * tf.reduce_sum(made.logp, axis=1,keepdims=True) + # number of mixture components and network architecture + self.n_components = n_components - self.output_order = self.mades[0].output_order - - # log likelihoods - self.L = tf.add(-0.5 * n_data * np.log(2 * np.pi) - 0.5 * tf.reduce_sum(self.u ** 2, axis=1,keepdims=True), self.logdet_dudy,name='L') + # required size of output layer for a Gaussian mixture density network + self.n_hidden = n_hidden + self.activation = activation + self.architecture = [self.n_parameters] + self.n_hidden - # train objective - self.trn_loss = -tf.reduce_mean(self.L,name='trn_loss') - self.reg_loss = tf.losses.mean_squared_error(self.L, self.logpdf) + self._network = self.build_network(kernel_initializer) + self.conditional_log_prob = self.log_prob + self.conditional_prob = self.prob + self.conditional_sample = self.sample - def eval(self, xy, sess, log=True): + def build_network(self, kernel_initializer): """ - Evaluate log probabilities for given input-output pairs. - :param xy: a pair (x, y) where x rows are inputs and y rows are outputs - :param sess: tensorflow session where the graph is run - :param log: whether to return probabilities in the log domain - :return: log probabilities: log p(y|x) + Individual network constructor. Builds a single mixture of Gaussians. """ + model = tf.keras.models.Sequential([ + tf.keras.layers.Dense( + self.architecture[layer + 1], + input_shape=(size,), + activation=self.activation, + kernel_initializer=kernel_initializer) + for layer, size in enumerate(self.architecture[:-1])]) - x, y = xy - lprob = sess.run(self.L,feed_dict={self.parameters:x,self.data:y}) - - return lprob if log else np.exp(lprob) + model.add( + tf.keras.layers.Dense( + tfp.layers.MixtureSameFamily.params_size( + self.n_components, + component_params_size=tfp.layers.MultivariateNormalTriL.params_size(self.n_data)), + kernel_initializer=kernel_initializer)) + + model.add( + tfp.layers.MixtureSameFamily(self.n_components, tfp.layers.MultivariateNormalTriL(self.n_data))) + + return model + + def log_prob(self, x, **kwargs): + if len(x.shape) == 1: + x = x[tf.newaxis, ...] + if len(kwargs["conditional"].shape) == 1: + kwargs["conditional"] = kwargs["conditional"][tf.newaxis, ...] + squeeze = True + else: + squeeze = False + log_prob = self._network(kwargs["conditional"]).log_prob(x) + if squeeze: + log_prob = tf.squeeze(log_prob, 0) + return log_prob + + def prob(self, x, **kwargs): + if len(x.shape) == 1: + x = x[tf.newaxis, ...] + if len(kwargs["conditional"].shape) == 1: + kwargs["conditional"] = kwargs["conditional"][tf.newaxis, ...] + squeeze = True + else: + squeeze = False + prob = self._network(kwargs["conditional"]).prob(x) + if squeeze: + prob = tf.squeeze(prob, 0) + return prob + + def sample(self, n, **kwargs): + if len(kwargs["conditional"].shape) == 1: + kwargs["conditional"] = kwargs["conditional"][tf.newaxis, ...] + squeeze = True + else: + squeeze = False + samples = self._network(kwargs["conditional"]).sample(n) + if squeeze: + samples = tf.squeeze(samples, 1) + return samples -class MixtureDensityNetwork: - """ - Implements a Mixture Density Network for modeling p(y|x) - """ - def __init__(self, n_parameters, n_data, n_components = 3, n_hidden=[50,50], activations=[tf.tanh, tf.tanh], - input_parameters=None, input_data=None, logpdf=None, index=1): - """ - Constructor. - :param n_parameters: number of (conditional) inputs - :param n_data: number of outputs (ie dimensionality of distribution you're parameterizing) - :param n_hiddens: list with number of hidden units for each hidden layer - :param activations: tensorflow activation functions for each hidden layer - :param input: tensorflow placeholder to serve as input; if None, a new placeholder is created - :param output: tensorflow placeholder to serve as output; if None, a new placeholder is created - """ +class SinhArcSinhMADE(tf.keras.Model): + + def __init__(self, n_parameters, n_data, n_hidden=[50,50], activation=tf.keras.layers.LeakyReLU(0.01), kernel_initializer=tf.keras.initializers.RandomNormal(mean=0.0, stddev=1e-5, seed=None), bias_initializer=tf.keras.initializers.RandomNormal(mean=0.0, stddev=1e-5, seed=None), input_order="random", all_layers=True): - # save input arguments + super(SinhArcSinhMADE, self).__init__() + + # pull out parameters self.n_parameters = n_parameters self.n_data = n_data - self.M = n_components - self.N = int((self.n_data + self.n_data * (self.n_data + 1) / 2 + 1)*self.M) self.n_hidden = n_hidden - self.activations = activations + self.activation = activation - self.parameters = tf.placeholder(dtype=dtype,shape=[None,self.n_parameters],name='parameters') if input_parameters is None else input_parameters - self.data = tf.placeholder(dtype=dtype,shape=[None,self.n_data],name='data') if input_data is None else input_data - self.logpdf = tf.placeholder(dtype=dtype,shape=[None,1],name='logpdf') if logpdf is None else logpdf + # put conditional into all layers (default), or not? + if all_layers == True: + all_layers = "all_layers" + else: + all_layers = "first_layer" - # Build the layers of the network - self.layers = [self.parameters] - self.weights = [] - self.biases = [] - for i in range(len(self.n_hidden)): - with tf.variable_scope('nde_' + str(index) + '_layer_' + str(i + 1)): - if i == 0: - self.weights.append(tf.get_variable("weights", [self.n_parameters, self.n_hidden[i]], initializer = tf.random_normal_initializer(0., np.sqrt(2./self.n_parameters)))) - self.biases.append(tf.get_variable("biases", [self.n_hidden[i]], initializer = tf.constant_initializer(0.0))) - elif i == len(self.n_hidden) - 1: - self.weights.append(tf.get_variable("weights", [self.n_hidden[i], self.N], initializer = tf.random_normal_initializer(0., np.sqrt(2./self.n_hidden[i])))) - self.biases.append(tf.get_variable("biases", [self.N], initializer = tf.constant_initializer(0.0))) - else: - self.weights.append(tf.get_variable("weights", [self.n_hidden[i], self.n_hidden[i + 1]], initializer = tf.random_normal_initializer(0., np.sqrt(2/self.n_hidden[i])))) - self.biases.append(tf.get_variable("biases", [self.n_hidden[i + 1]], initializer = tf.constant_initializer(0.0))) - if i < len(self.n_hidden) - 1: - self.layers.append(self.activations[i](tf.add(tf.matmul(self.layers[-1], self.weights[-1]), self.biases[-1]))) - else: - self.layers.append(tf.add(tf.matmul(self.layers[-1], self.weights[-1]), self.biases[-1])) - - # Map the output layer to mixture model parameters - self.mu, self.sigma, self.alpha = tf.split(self.layers[-1], [self.M * self.n_data, self.M * self.n_data * (self.n_data + 1) // 2, self.M], 1) - self.mu = tf.reshape(self.mu, (-1, self.M, self.n_data)) - self.sigma = tf.reshape(self.sigma, (-1, self.M, self.n_data * (self.n_data + 1) // 2)) - self.alpha = tf.nn.softmax(self.alpha) - self.Sigma = tf.contrib.distributions.fill_triangular(self.sigma) - self.Sigma = self.Sigma - tf.linalg.diag(tf.linalg.diag_part(self.Sigma)) + tf.linalg.diag(tf.exp(tf.linalg.diag_part(self.Sigma))) - self.det = tf.reduce_prod(tf.linalg.diag_part(self.Sigma), axis=-1) - - self.mu = tf.identity(self.mu, name = "mu") - self.Sigma = tf.identity(self.Sigma, name = "Sigma") - self.alpha = tf.identity(self.alpha, name = "alpha") - self.det = tf.identity(self.det, name = "det") + # constant for normal densities + self.halfln2pi = tf.cast(0.5 * tf.math.log(2 * np.pi), tf.float32) - # Log likelihoods - self.L = tf.log(tf.reduce_sum(tf.exp(-0.5*tf.reduce_sum(tf.square(tf.einsum("ijlk,ijk->ijl", self.Sigma, tf.subtract(tf.expand_dims(self.data, 1), self.mu))), 2) + tf.log(self.alpha) + tf.log(self.det) - self.n_data*np.log(2. * np.pi) / 2.), 1, keepdims=True) + 1e-37, name = "L") - - # Objective loss function - self.trn_loss = -tf.reduce_mean(self.L, name = "trn_loss") - self.reg_loss = tf.losses.mean_squared_error(self.L, self.logpdf) + # build autoregressive network + self.autoregressive_network = tfb.AutoregressiveNetwork( + params=4, + hidden_units=n_hidden, + activation=activation, + event_shape=[n_data], + conditional=True, + conditional_event_shape=[n_parameters], + conditional_input_layers=all_layers, + input_order=input_order, + kernel_initializer=kernel_initializer, + bias_initializer=bias_initializer) + + # small number for regularizing things + self.epsilon = tf.cast(1e-20, tf.float32) + + # conditional functions + self.conditional_log_prob = self.log_prob + self.conditional_prob = self.prob + _ = self.conditional_log_prob(np.random.normal(0, 1, (1, n_data)).astype(np.float32), + conditional=np.random.normal(0, 1, (1, n_parameters)).astype(np.float32)) + + # compute the parameters of the conditional SinhArcSinh distributions + #@tf.function + def call(self, x, conditional=None): - def eval(self, xy, sess, log=True): - """ - Evaluate log probabilities for given input-output pairs. - :param xy: a pair (x, y) where x rows are inputs and y rows are outputs - :param sess: tensorflow session where the graph is run - :param log: whether to return probabilities in the log domain - :return: log probabilities: log p(y|x) - """ + # pull bijector parameters out of autoregressive network + mu_, logp_, logtau_, k_ = tf.split(self.autoregressive_network(x, conditional_input=conditional), [1, 1, 1, 1], axis=-1) + + # transform things to usual parameterization + sigma = tf.squeeze(tf.exp(-0.5*logp_), -1) # std-deviations + tau = tf.squeeze(tf.exp(logtau_), -1) # tailweights + mu = tf.squeeze(mu_, -1) # means + k = tf.squeeze(k_, -1) # skewnesses + m = 2. / tf.math.sinh(tau * (tf.math.asinh(2.) + k) ) # multipliers + + return mu, sigma, tau, k, m + + #@tf.function + def log_prob(self, x, conditional=None): + + # pull bijector parameters out of autoregressive network + mu_, logp_, logtau_, k_ = tf.split(self.autoregressive_network(x, conditional_input=conditional), [1, 1, 1, 1], axis=-1) + + # transform things to usual parameterization + sigma = tf.squeeze(tf.exp(-0.5*logp_), -1) # std-deviations + tau = tf.squeeze(tf.exp(logtau_), -1) # tailweights + mu = tf.squeeze(mu_, -1) # means + k = tf.squeeze(k_, -1) # skewnesses + m = 2. / tf.math.sinh(tau * (tf.math.asinh(2.) + k) ) # multipliers + + # transform x to unit normal base random variates + u = tf.math.sinh((1./tau) * tf.math.asinh((x - mu)/(m * sigma)) - k ) - x, y = xy - lprob = sess.run(self.L,feed_dict={self.parameters:x,self.data:y}) + # log probability of unit normal random variates + lnN = tf.reduce_sum(-0.5 * u**2 - self.halfln2pi, axis=-1) + + # log jacobian term + lnJ = tf.math.reduce_sum(0.5 * tf.math.log(1. + u**2) - 0.5 * tf.math.log(1. + ((x - mu)/(m*sigma))**2) - tf.math.log(tf.math.abs(tau * m * sigma) + self.epsilon), axis=-1) + + # total log probability + return lnN + lnJ + + #@tf.function + def prob(self, x, conditional=None): + + # probability + return tf.exp(self.log_prob(x, conditional=conditional)) + + +class TruncatedMultivariateNormalTriL(): + + + def __init__(self, + loc, + scale_tril, + low, + high, + validate_args=False, + allow_nan_stats=True, + dtype=tf.float32, + name='truncatedMultivariateNormalTriL'): + + super(TruncatedMultivariateNormalTriL, self).__init__() + + parameters = dict(locals()) + with tf.name_scope(name) as name: + + dtype = dtype_util.common_dtype([loc, scale_tril, low, high], dtype) - return lprob if log else np.exp(lprob) + self.loc = tensor_util.convert_nonref_to_tensor(loc, name='loc',dtype=dtype) + self.scale_tril = tensor_util.convert_nonref_to_tensor(scale_tril, name='scale_tril', dtype=dtype) + self.high = tensor_util.convert_nonref_to_tensor(high, name='high', dtype=dtype) + self.low = tensor_util.convert_nonref_to_tensor(low, name='low', dtype=dtype) + + self.mvn = tfd.MultivariateNormalTriL( + loc=self.loc, scale_tril=self.scale_tril, validate_args=validate_args, + allow_nan_stats=allow_nan_stats) + + self.u = tfd.Blockwise( + [tfd.Uniform(low=low[i], high=high[i]) + for i in range(self.low.shape[0])]) + + self._parameters = parameters + + def prob(self, x, **kwargs): + return tf.multiply(self.mvn.prob(x, **kwargs), + self.u.prob(x, **kwargs)) + + def log_prob(self, x, **kwargs): + return tf.math.log(self.prob(x, **kwargs)) + + def sample(self, n, seed=None, **kwargs): + samples = self.mvn.sample(n, seed=seed, **kwargs) + too_low = samples < self.low + too_high = samples > self.high + rejected = tf.reduce_any(tf.logical_or(too_low, too_high), -1) + while tf.reduce_any(rejected): + new_n = tf.reduce_sum(tf.cast(rejected, dtype=tf.int32)) + new_samples = self.mvn.sample(new_n, seed=seed, **kwargs) + samples = tf.tensor_scatter_nd_update(samples, tf.where(rejected), + new_samples) + too_low = samples < self.low + too_high = samples > self.high + rejected = tf.reduce_any(tf.logical_or(too_low, too_high), -1) + return samples + + +class TruncatedMultivariateNormalTriL_(tfd.MultivariateNormalLinearOperator): + """The multivariate normal distribution on `R^k`. + + The Multivariate Normal distribution is defined over `R^k` and parameterized + by a (batch of) length-`k` `loc` vector (aka "mu") and a (batch of) `k x k` + `scale` matrix; `covariance = scale @ scale.T` where `@` denotes + matrix-multiplication. + + #### Mathematical Details + + The probability density function (pdf) is, + + ```none + + pdf(x; loc, scale) = exp(-0.5 ||y||**2) / Z, + y = inv(scale) @ (x - loc), + Z = (2 pi)**(0.5 k) |det(scale)|, + ``` + + where: + + * `loc` is a vector in `R^k`, + * `scale` is a matrix in `R^{k x k}`, `covariance = scale @ scale.T`, + * `Z` denotes the normalization constant, and, + * `||y||**2` denotes the squared Euclidean norm of `y`. + A (non-batch) `scale` matrix is: + + ```none + scale = scale_tril + ``` + + where `scale_tril` is lower-triangular `k x k` matrix with non-zero diagonal, + i.e., `tf.diag_part(scale_tril) != 0`. + + Additional leading dimensions (if any) will index batches. + + Note that in the truncated multivariate is not correctly normalised (yet). + + The MultivariateNormal distribution is a member of the [location-scale + family](https://en.wikipedia.org/wiki/Location-scale_family), i.e., it can be + constructed as, + + ```none + X ~ MultivariateNormal(loc=0, scale=1) # Identity scale, zero shift. + Y = scale @ X + loc + ``` + + Trainable (batch) lower-triangular matrices can be created with + `tfp.distributions.matrix_diag_transform()` and/or + `tfp.distributions.fill_triangular()` + + #### Examples + + ```python + tfd = tfp.distributions + + # Initialize a single 3-variate Gaussian. + mu = [1., 2, 3] + cov = [[ 0.36, 0.12, 0.06], + [ 0.12, 0.29, -0.13], + [ 0.06, -0.13, 0.26]] + scale = tf.cholesky(cov) + # ==> [[ 0.6, 0. , 0. ], + # [ 0.2, 0.5, 0. ], + # [ 0.1, -0.3, 0.4]]) + mvn = tfd.TruncatedMultivariateNormalTriL( + loc=mu, + scale_tril=scale) + + mvn.mean().eval() + # ==> [1., 2, 3] + + # Covariance agrees with cholesky(cov) parameterization. + mvn.covariance().eval() + # ==> [[ 0.36, 0.12, 0.06], + # [ 0.12, 0.29, -0.13], + # [ 0.06, -0.13, 0.26]] + + # Compute the pdf of an observation in `R^3` ; return a scalar. + mvn.prob([-1., 0, 1]).eval() # shape: [] + + # Initialize a 2-batch of 3-variate Gaussians. + mu = [[1., 2, 3], + [11, 22, 33]] # shape: [2, 3] + tril = ... # shape: [2, 3, 3], lower triangular, non-zero diagonal. + mvn = tfd.TruncatedMultivariateNormalTriL( + loc=mu, + scale_tril=tril) + + # Compute the pdf of two `R^3` observations; return a length-2 vector. + x = [[-0.9, 0, 0.1], + [-10, 0, 9]] # shape: [2, 3] + mvn.prob(x).eval() # shape: [2] + + # Instantiate a "learnable" MVN. + dims = 4 + mvn = tfd.TruncatedMultivariateNormalTriL( + loc=tf.Variable(tf.zeros([dims], dtype=tf.float32), name="mu"), + scale_tril=tfp.utils.DeferredTensor( + tfp.bijectors.ScaleTriL().forward, + tf.Variable(tf.zeros([dims * (dims + 1) // 2], dtype=tf.float32), + name="raw_scale_tril"))) + ``` + + """ + + def __init__(self, + loc, + scale_tril, + low, + high, + validate_args=False, + allow_nan_stats=True, + dtype=tf.float32, + name='truncatedMultivariateNormalTriL'): + """Construct Multivariate Normal distribution on `R^k` with samples + from a truncated boundary. + + The `batch_shape` is the broadcast shape between `loc` and `scale` + arguments. + + The `event_shape` is given by last dimension of the matrix implied by + `scale`. The last dimension of `loc` (if provided) must broadcast with + this. + + Recall that `covariance = scale @ scale.T`. A (non-batch) `scale` + matrix is: + + ```none + scale = scale_tril + ``` + + where `scale_tril` is lower-triangular `k x k` matrix with non-zero + diagonal, i.e., `tf.diag_part(scale_tril) != 0`. + + Additional leading dimensions (if any) will index batches. + + Args: + loc: Floating-point `Tensor`. Must have shape `[B1, ..., Bb, k]` + where `b >= 0` and `k` is the event size. + scale_tril: Floating-point, lower-triangular `Tensor` with non-zero + diagonal elements. `scale_tril` has shape `[B1, ..., Bb, k, k]` + where `b >= 0` and `k` is the event size. + low: Floating-point `Tensor`. Must have `[B1, ..., Bb, k]` where + `b >= 0` and `k` is the event size. Defines the lower boundary for + the samples. + high: Floating-point `Tensor`. Must have `[B1, ..., Bb, k]` where + `b >= 0` and `k` is the event size. Defines the upper boundary for + the samples. + validate_args: Python `bool`, default `False`. When `True` + distribution + parameters are checked for validity despite possibly degrading + runtime performance. When `False` invalid inputs may silently + render incorrect outputs. + allow_nan_stats: Python `bool`, default `True`. When `True`, + statistics (e.g., mean, mode, variance) use the value "`NaN`" to + indicate the result is undefined. When `False`, an exception is + raised if one or more of the statistic's batch members are + undefined. + name: Python `str` name prefixed to Ops created by this class. + + Raises: + ValueError: if neither `loc` nor `scale_tril` are specified. + """ + parameters = dict(locals()) + with tf.name_scope(name) as name: + dtype = dtype_util.common_dtype([loc, scale_tril, low, high], dtype) + loc = tensor_util.convert_nonref_to_tensor(loc, name='loc', + dtype=dtype) + scale_tril = tensor_util.convert_nonref_to_tensor( + scale_tril, name='scale_tril', dtype=dtype) + self.high = tensor_util.convert_nonref_to_tensor(high, name='high', + dtype=dtype) + self.low = tensor_util.convert_nonref_to_tensor(low, name='low', + dtype=dtype) + scale = tf.linalg.LinearOperatorLowerTriangular( + scale_tril, is_non_singular=True, is_self_adjoint=False, + is_positive_definite=False) + self.mvn = tfd.MultivariateNormalLinearOperator( + loc=loc, scale=scale, validate_args=validate_args, + allow_nan_stats=allow_nan_stats, name=name) + self.u = tfd.Blockwise( + [tfd.Uniform(low=low[i], high=high[i]) + for i in range(self.low.shape[0])]) + super(TruncatedMultivariateNormalTriL, self).__init__( + loc=loc, + scale=scale, + validate_args=validate_args, + allow_nan_stats=allow_nan_stats, + name=name) + self._parameters = parameters + + def _log_prob(self, x, **kwargs): + return tf.math.log(self._log_prob(x, **kwargs)) + + def _prob(self, x, **kwargs): + return tf.multiply(self.mvn.prob(x, **kwargs), + self.u.prob(x, **kwargs)) + + def _sample_n(self, n, seed=None, **kwargs): + samples = self.mvn.sample(n, seed=seed, **kwargs) + too_low = samples < self.low + too_high = samples > self.high + rejected = tf.reduce_any(tf.logical_or(too_low, too_high), -1) + while tf.reduce_any(rejected): + new_n = tf.reduce_sum(tf.cast(rejected, dtype=tf.int32)) + new_samples = self.mvn.sample(new_n, seed=seed, **kwargs) + samples = tf.tensor_scatter_nd_update(samples, tf.where(rejected), + new_samples) + too_low = samples < self.low + too_high = samples > self.high + rejected = tf.reduce_any(tf.logical_or(too_low, too_high), -1) + return samples + + @classmethod + def _params_event_ndims(cls): + return dict(loc=1, scale_tril=2) diff --git a/pydelfi/priors.py b/pydelfi/priors.py deleted file mode 100755 index 3f6ec3ce71..0000000000 --- a/pydelfi/priors.py +++ /dev/null @@ -1,62 +0,0 @@ -from scipy.stats import multivariate_normal -import numpy as np - -class TruncatedGaussian(): - - def __init__(self, mean, C, lower, upper): - - self.mean = mean - self.C = C - self.Cinv = np.linalg.inv(C) - self.lower = lower - self.upper = upper - self.L = np.linalg.cholesky(C) - self.logdet = np.log(np.linalg.det(C)) - - def loguniform(self, x): - - inrange = np.prod(x > self.lower)*np.prod(x < self.upper) - return inrange*np.log(np.prod(self.upper-self.lower)) - (1 - inrange)*1e300 - - def uniform(self, x): - - inrange = np.prod(x > self.lower)*np.prod(x < self.upper) - return inrange*np.prod(self.upper-self.lower) - - def draw(self): - - P = 0 - while P == 0: - x = self.mean + np.dot(self.L, np.random.normal(0, 1, len(self.mean))) - P = self.uniform(x) - return x - - def pdf(self, x): - - return np.exp(self.logpdf(x)) - - def logpdf(self, x): - - return np.array([self.loguniform(xx) - 0.5*self.logdet - 0.5*np.dot((xx - self.mean), np.dot(self.Cinv,(xx - self.mean)) ) for xx in x]) - - -class Uniform(): - - def __init__(self, lower, upper): - - self.lower = lower - self.upper = upper - - def logpdf(self, x): - - inrange = lambda y: np.prod(y > self.lower)*np.prod(y < self.upper) - return np.array([inrange(xx)*np.log(np.prod(self.upper-self.lower)) - (1 - inrange(xx))*1e300 for xx in x]) - - def pdf(self, x): - - inrange = lambda y: np.prod(y > self.lower)*np.prod(y < self.upper) - return np.array([inrange(xx)*np.prod(self.upper-self.lower) for xx in x]) - - def draw(self): - - return np.random.uniform(self.lower, self.upper) diff --git a/pydelfi/score.py b/pydelfi/score.py index d311e6274d..b1d66cd444 100755 --- a/pydelfi/score.py +++ b/pydelfi/score.py @@ -2,6 +2,9 @@ import numpy as np import tqdm +__version__ = "v0.2" +__author__ = "Justin Alsing, Tom Charnock and Stephen Feeney" + def isnotebook(): try: shell = get_ipython().__class__.__name__ diff --git a/pydelfi/train.py b/pydelfi/train.py deleted file mode 100755 index cbe2673589..0000000000 --- a/pydelfi/train.py +++ /dev/null @@ -1,119 +0,0 @@ -import tensorflow as tf -import numpy as np -import numpy.random as rng -import os -from tqdm.auto import tqdm - -class ConditionalTrainer(): - - def __init__(self, model, optimizer=tf.train.AdamOptimizer, optimizer_arguments={}): - """ - Constructor that defines the training operation. - :param model: made/maf instance to be trained. - :param optimizer: tensorflow optimizer class to be used during training. - :param optimizer_arguments: dictionary of arguments for optimizer intialization. - """ - - self.model = model - self.train_optimizer = optimizer(**optimizer_arguments).minimize(self.model.trn_loss) - self.train_reg_optimizer = optimizer(**optimizer_arguments).minimize(self.model.reg_loss) - - """ - Training class for the conditional MADEs/MAFs classes using a tensorflow optimizer. - """ - def train(self, sess, train_data, validation_split = 0.1, epochs=1000, batch_size=100, - patience=20, saver_name='tmp_model', progress_bar=True, mode='samples'): - """ - Training function to be called with desired parameters within a tensorflow session. - :param sess: tensorflow session where the graph is run. - :param train_data: a tuple/list of (X,Y) with training data where Y is conditioned on X. - :param validation_split: percentage of training data randomly selected to be used for validation - :param epochs: maximum number of epochs for training. - :param batch_size: batch size of each batch within an epoch. - :param early_stopping: number of epochs for early stopping criteria. - :param check_every_N: check every N iterations if model has improved and saves if so. - :param saver_name: string of name (with or without folder) where model is saved. If none is given, - a temporal model is used to save and restore best model, and removed afterwards. - """ - - # Training data - if mode == 'samples': - train_data_X, train_data_Y = train_data - elif mode == 'regression': - train_data_X, train_data_Y, train_data_PDF = train_data - train_idx = np.arange(train_data_X.shape[0]) - - # validation data using p_val percent of the data - rng.shuffle(train_idx) - N = train_data_X.shape[0] - val_data_X = train_data_X[train_idx[-int(validation_split*N):]] - train_data_X = train_data_X[train_idx[:-int(validation_split*N)]] - val_data_Y = train_data_Y[train_idx[-int(validation_split*N):]] - train_data_Y = train_data_Y[train_idx[:-int(validation_split*N)]] - if mode == 'regression': - val_data_PDF = train_data_PDF[train_idx[-int(validation_split*N):]] - train_data_PDF = train_data_PDF[train_idx[:-int(validation_split*N)]] - train_idx = np.arange(train_data_X.shape[0]) - - # Early stopping variables - bst_loss = np.infty - early_stopping_count = 0 - saver = tf.train.Saver() - - # Validation and training losses - validation_losses = [] - training_losses = [] - - # Main training loop - if progress_bar: - pbar = tqdm(total = epochs, desc = "Training") - pbar.set_postfix(ordered_dict={"train loss":0, "val loss":0}, refresh=True) - for epoch in range(epochs): - # Shuffel training indices - rng.shuffle(train_idx) - for batch in range(len(train_idx)//batch_size): - # Last batch will have maximum number of elements possible - batch_idx = train_idx[batch*batch_size:np.min([(batch+1)*batch_size,len(train_idx)])] - - if mode == 'samples': - sess.run(self.train_optimizer,feed_dict={self.model.parameters:train_data_X[batch_idx], - self.model.data:train_data_Y[batch_idx]}) - elif mode == 'regression': - sess.run(self.train_reg_optimizer,feed_dict={self.model.parameters:train_data_X[batch_idx], - self.model.data:train_data_Y[batch_idx], - self.model.logpdf:train_data_PDF[batch_idx]}) - # Early stopping check - if mode == 'samples': - val_loss = sess.run(self.model.trn_loss,feed_dict={self.model.parameters:val_data_X, - self.model.data:val_data_Y}) - train_loss = sess.run(self.model.trn_loss,feed_dict={self.model.parameters:train_data_X, - self.model.data:train_data_Y}) - elif mode == 'regression': - val_loss = sess.run(self.model.reg_loss,feed_dict={self.model.parameters:val_data_X, - self.model.data:val_data_Y, - self.model.logpdf:val_data_PDF}) - train_loss = sess.run(self.model.reg_loss,feed_dict={self.model.parameters:train_data_X, - self.model.data:train_data_Y, - self.model.logpdf:train_data_PDF}) - if progress_bar: - pbar.update() - pbar.set_postfix(ordered_dict={"train loss":train_loss, "val loss":val_loss}, refresh=True) - validation_losses.append(val_loss) - training_losses.append(train_loss) - - if val_loss < bst_loss: - bst_loss = val_loss - if saver_name is not None: - saver.save(sess,"./"+saver_name) - early_stopping_count = 0 - else: - early_stopping_count += 1 - if early_stopping_count >= patience: - #pbar.set_postfix(str="Early stopping: terminated", refresh=True) - break - - # Restore best model - if saver_name is not None: - saver.restore(sess, saver_name) - - return np.array(validation_losses), np.array(training_losses) diff --git a/setup.py b/setup.py index 291afd591a..553675473a 100644 --- a/setup.py +++ b/setup.py @@ -4,17 +4,16 @@ import sys setup(name='pydelfi', - version='v0.1', + version='v0.2', description='LFI in TensorFlow', - author='Justin Alsing', + author='Justin Alsing, Tom Charnock, Stephen Feeney', url='https://github.com/justinalsing/pydelfi', packages=find_packages(), install_requires=[ - "tensorflow>=v1.1.0", - "getdist", - "emcee", - "mpi4py", - "scipy", - "tqdm" + "jupyter", + "getdist>=1.1.0", + "emcee>=3.0.2", + "tqdm>=4.41.1", + "tensorflow-probability==0.11.0", + "tensorflow==2.4.1" ]) -