├── .gitignore ├── Dockerfile ├── jupyter_notebook_config.py ├── notebooks ├── 00-original-dataset.ipynb ├── 00-original-dataset.py ├── 01-sklearn-example.ipynb ├── 01-sklearn-example.py ├── 02-sklearn-example-lightgbm.ipynb ├── 02-sklearn-example-lightgbm.py ├── 03-sklearn-example-pytorch.ipynb ├── 03-sklearn-example-pytorch.py └── mcycle └── readme.md /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | env/ 12 | build/ 13 | develop-eggs/ 14 | dist/ 15 | downloads/ 16 | eggs/ 17 | .eggs/ 18 | lib/ 19 | lib64/ 20 | parts/ 21 | sdist/ 22 | var/ 23 | wheels/ 24 | *.egg-info/ 25 | .installed.cfg 26 | *.egg 27 | 28 | # PyInstaller 29 | # Usually these files are written by a python script from a template 30 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 31 | *.manifest 32 | *.spec 33 | 34 | # Installer logs 35 | pip-log.txt 36 | pip-delete-this-directory.txt 37 | 38 | # Unit test / coverage reports 39 | htmlcov/ 40 | .tox/ 41 | .coverage 42 | .coverage.* 43 | .cache 44 | nosetests.xml 45 | coverage.xml 46 | *.cover 47 | .hypothesis/ 48 | 49 | # Translations 50 | *.mo 51 | *.pot 52 | 53 | # Django stuff: 54 | *.log 55 | local_settings.py 56 | 57 | # Flask stuff: 58 | instance/ 59 | .webassets-cache 60 | 61 | # Scrapy stuff: 62 | .scrapy 63 | 64 | # Sphinx documentation 65 | docs/_build/ 66 | 67 | # PyBuilder 68 | target/ 69 | 70 | # Jupyter Notebook 71 | .ipynb_checkpoints 72 | 73 | # pyenv 74 | .python-version 75 | 76 | # celery beat schedule file 77 | celerybeat-schedule 78 | 79 | # SageMath parsed files 80 | *.sage.py 81 | 82 | # dotenv 83 | .env 84 | 85 | # virtualenv 86 | .venv 87 | venv/ 88 | ENV/ 89 | 90 | # Spyder project settings 91 | .spyderproject 92 | .spyproject 93 | 94 | # Rope project settings 95 | .ropeproject 96 | 97 | # mkdocs documentation 98 | /site 99 | 100 | # mypy 101 | .mypy_cache/ 102 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM tensorflow/tensorflow:1.8.0-gpu-py3 2 | 3 | MAINTAINER CeShine Lee 4 | 5 | ARG USERNAME=docker 6 | ARG USERID=1000 7 | 8 | ENV LANG C.UTF-8 9 | 10 | # Instal basic utilities 11 | RUN apt-get update && \ 12 | apt-get install -y --no-install-recommends git wget unzip bzip2 sudo build-essential libjpeg-dev && \ 13 | apt-get clean && \ 14 | rm -rf /var/lib/apt/lists/* 15 | 16 | # Create the user 17 | RUN useradd --create-home -s /bin/bash --no-user-group -u $USERID $USERNAME && \ 18 | adduser $USERNAME sudo && \ 19 | echo "$USERNAME ALL=(ALL) NOPASSWD: ALL" >> /etc/sudoers 20 | 21 | RUN pip install --upgrade pip && \ 22 | pip install -U jupyter h5py pandas==0.22.0 sklearn matplotlib seaborn plotly pillow-simd joblib tqdm 23 | 24 | # Install LightGBM 25 | RUN pip install lightgbm 26 | 27 | # Install PyTorch 28 | RUN pip install http://download.pytorch.org/whl/cu90/torch-0.4.0-cp35-cp35m-linux_x86_64.whl 29 | RUN pip install torchvision 30 | 31 | USER $USERNAME 32 | WORKDIR /home/$USERNAME 33 | 34 | # Copy notebooks 35 | COPY notebooks /home/$USERNAME 36 | 37 | # Jupyter 38 | EXPOSE 8888 39 | CMD jupyter notebook --ip=0.0.0.0 --port=8888 40 | -------------------------------------------------------------------------------- /jupyter_notebook_config.py: -------------------------------------------------------------------------------- 1 | # Reference: https://svds.com/jupyter-notebook-best-practices-for-data-science/ 2 | import os 3 | from subprocess import check_call 4 | 5 | def post_save(model, os_path, contents_manager): 6 | """post-save hook for converting notebooks to .py scripts""" 7 | if model['type'] != 'notebook': 8 | return # only do this for notebooks 9 | d, fname = os.path.split(os_path) 10 | check_call(['jupyter', 'nbconvert', '--to', 'script', fname], cwd=d) 11 | # check_call(['jupyter', 'nbconvert', '--to', 'html', fname], cwd=d) 12 | 13 | c.FileContentsManager.post_save_hook = post_save 14 | -------------------------------------------------------------------------------- /notebooks/00-original-dataset.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": 1, 6 | "metadata": {}, 7 | "outputs": [], 8 | "source": [ 9 | "import tensorflow as tf\n", 10 | "import pandas as pd\n", 11 | "import numpy as np\n", 12 | "import matplotlib.pyplot as plt\n", 13 | "\n", 14 | "%matplotlib inline\n", 15 | "\n", 16 | "# Initialize session\n", 17 | "sess = tf.Session()" 18 | ] 19 | }, 20 | { 21 | "cell_type": "code", 22 | "execution_count": 2, 23 | "metadata": {}, 24 | "outputs": [ 25 | { 26 | "data": { 27 | "text/plain": [ 28 | "((94, 1), (94, 1))" 29 | ] 30 | }, 31 | "execution_count": 2, 32 | "metadata": {}, 33 | "output_type": "execute_result" 34 | } 35 | ], 36 | "source": [ 37 | "# Load data\n", 38 | "mcycle = pd.read_csv('mcycle',delimiter='\\t')\n", 39 | "mcycle.times = (mcycle.times - mcycle.times.mean())/mcycle.times.std()\n", 40 | "mcycle.accel = (mcycle.accel - mcycle.accel.mean())/mcycle.accel.std()\n", 41 | "\n", 42 | "# Reshape to input format for network\n", 43 | "times = np.expand_dims(mcycle.times.values, 1)\n", 44 | "accel = np.expand_dims(mcycle.accel.values, 1)\n", 45 | "times.shape, accel.shape" 46 | ] 47 | }, 48 | { 49 | "cell_type": "code", 50 | "execution_count": 3, 51 | "metadata": {}, 52 | "outputs": [], 53 | "source": [ 54 | "# Create network\n", 55 | "class q_model:\n", 56 | " def __init__(self, \n", 57 | " sess, \n", 58 | " quantiles, \n", 59 | " in_shape=1, \n", 60 | " out_shape=1, \n", 61 | " batch_size=32):\n", 62 | " \n", 63 | " self.sess = sess\n", 64 | " \n", 65 | " self.quantiles = quantiles\n", 66 | " self.num_quantiles = len(quantiles)\n", 67 | " \n", 68 | " self.in_shape = in_shape\n", 69 | " self.out_shape = out_shape\n", 70 | " self.batch_size = batch_size\n", 71 | " \n", 72 | " self.outputs = []\n", 73 | " self.losses = []\n", 74 | " self.loss_history = []\n", 75 | " self.optim = tf.train.AdamOptimizer()\n", 76 | " \n", 77 | " self.build_model()\n", 78 | " \n", 79 | " def build_model(self, scope='q_model', reuse=tf.AUTO_REUSE): \n", 80 | " with tf.variable_scope(scope, reuse=reuse) as scope:\n", 81 | " self.x = tf.placeholder(tf.float32, shape=(None, self.in_shape))\n", 82 | " self.y = tf.placeholder(tf.float32, shape=(None, self.out_shape))\n", 83 | "\n", 84 | " self.layer0 = tf.layers.dense(self.x, \n", 85 | " units=32, \n", 86 | " activation=tf.nn.relu)\n", 87 | " self.layer1 = tf.layers.dense(self.layer0, \n", 88 | " units=32, \n", 89 | " activation=tf.nn.relu)\n", 90 | "\n", 91 | " # Create outputs and losses for all quantiles\n", 92 | " for i in range(self.num_quantiles):\n", 93 | " q = self.quantiles[i]\n", 94 | " \n", 95 | " # Get output layers \n", 96 | " output = tf.layers.dense(self.layer1, 1, name=\"{}_q{}\".format(i, int(q*100)))\n", 97 | " self.outputs.append(output)\n", 98 | " \n", 99 | " # Create losses\n", 100 | " error = tf.subtract(self.y, output)\n", 101 | " loss = tf.reduce_mean(tf.maximum(q*error, (q-1)*error), axis=-1)\n", 102 | "\n", 103 | " self.losses.append(loss)\n", 104 | "\n", 105 | " # Create combined loss\n", 106 | " self.combined_loss = tf.reduce_mean(tf.add_n(self.losses))\n", 107 | " self.train_step = self.optim.minimize(self.combined_loss)\n", 108 | "\n", 109 | " def fit(self, x, y, epochs=100): \n", 110 | " for epoch in range(epochs):\n", 111 | " epoch_losses = []\n", 112 | " for idx in range(0, x.shape[0], self.batch_size):\n", 113 | " batch_x = x[idx : min(idx + self.batch_size, x.shape[0]),:]\n", 114 | " batch_y = y[idx : min(idx + self.batch_size, y.shape[0]),:]\n", 115 | "\n", 116 | " feed_dict = {self.x: batch_x,\n", 117 | " self.y: batch_y}\n", 118 | "\n", 119 | " _, c_loss = self.sess.run([self.train_step, self.combined_loss], feed_dict)\n", 120 | " epoch_losses.append(c_loss)\n", 121 | " \n", 122 | " epoch_loss = np.mean(epoch_losses)\n", 123 | " self.loss_history.append(epoch_loss)\n", 124 | " if epoch % 100 == 0:\n", 125 | " print(\"Epoch {}: {}\".format(epoch, epoch_loss))\n", 126 | " \n", 127 | " def predict(self, x): \n", 128 | " # Run model to get outputs\n", 129 | " feed_dict = {self.x: x}\n", 130 | " predictions = sess.run(self.outputs, feed_dict)\n", 131 | " \n", 132 | " return predictions" 133 | ] 134 | }, 135 | { 136 | "cell_type": "code", 137 | "execution_count": 4, 138 | "metadata": {}, 139 | "outputs": [], 140 | "source": [ 141 | "# Instantiate model\n", 142 | "quantiles = [.1, .5, .9]\n", 143 | "model = q_model(sess, quantiles, batch_size=32)\n", 144 | "\n", 145 | "# Initialize all variables\n", 146 | "init_op = tf.global_variables_initializer()\n", 147 | "sess.run(init_op)" 148 | ] 149 | }, 150 | { 151 | "cell_type": "code", 152 | "execution_count": 5, 153 | "metadata": {}, 154 | "outputs": [ 155 | { 156 | "name": "stdout", 157 | "output_type": "stream", 158 | "text": [ 159 | "Epoch 0: 1.1482526063919067\n", 160 | "Epoch 100: 0.5262297987937927\n", 161 | "Epoch 200: 0.374498575925827\n", 162 | "Epoch 300: 0.31381872296333313\n", 163 | "Epoch 400: 0.2866593897342682\n", 164 | "Epoch 500: 0.27239206433296204\n", 165 | "Epoch 600: 0.2649902105331421\n", 166 | "Epoch 700: 0.25793322920799255\n", 167 | "Epoch 800: 0.25121280550956726\n", 168 | "Epoch 900: 0.2494228631258011\n", 169 | "Epoch 1000: 0.2460067719221115\n", 170 | "Epoch 1100: 0.2446104735136032\n", 171 | "Epoch 1200: 0.24384759366512299\n", 172 | "Epoch 1300: 0.24382255971431732\n", 173 | "Epoch 1400: 0.24536514282226562\n", 174 | "Epoch 1500: 0.24287573993206024\n", 175 | "Epoch 1600: 0.24245969951152802\n", 176 | "Epoch 1700: 0.2416047602891922\n", 177 | "Epoch 1800: 0.24192357063293457\n", 178 | "Epoch 1900: 0.239970400929451\n" 179 | ] 180 | } 181 | ], 182 | "source": [ 183 | "# Run training\n", 184 | "epochs = 2000\n", 185 | "model.fit(times, accel, epochs)" 186 | ] 187 | }, 188 | { 189 | "cell_type": "code", 190 | "execution_count": 6, 191 | "metadata": {}, 192 | "outputs": [ 193 | { 194 | "data": { 195 | "image/png": "iVBORw0KGgoAAAANSUhEUgAAAXYAAAD8CAYAAABjAo9vAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAADl0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uIDIuMi4yLCBodHRwOi8vbWF0cGxvdGxpYi5vcmcvhp/UCwAAIABJREFUeJzsnXd4VFX6xz93SjKT3hOSAKGGkpCEKqBYkKKoRGyruy667qrYdnVFsPwsq664uvay62LBXimKINIUKQqBJEASECGU9JCQPsm08/tjMpM2aWTSz+d55pnJvefe806S+73nvuc976sIIZBIJBJJ30HV3QZIJBKJxLVIYZdIJJI+hhR2iUQi6WNIYZdIJJI+hhR2iUQi6WNIYZdIJJI+hhR2iUQi6WNIYZdIJJI+hhR2iUQi6WNouqPToKAgERUV1R1dSyQSSa9l7969p4UQwa216xZhj4qKIikpqTu6lkgkkl6Loign2tJOumIkEomkjyGFXSKRSPoYUtglEomkj9EtPnaJRNK9mEwmsrKyqK6u7m5TJE7Q6XRERkai1WrP6ngp7BJJPyQrKwtvb2+ioqJQFKW7zZHUQwhBUVERWVlZDBky5KzOIV0xEkk/pLq6msDAQCnqPRBFUQgMDOzQ05QUdomknyJFvefS0b+NdMVIJGfJd5nfkVmaia+7L4nDE/HQenS3SRIJ4IIRu6IoAxVF2aooSrqiKGmKovzVFYZJJD2Z9w6+x+Jti3kj9Q2e2f0Ml6++nG+OfoNVWLvbtF7Dn/70J0JCQoiJiWmwvbi4mFmzZjFixAhmzZrFmTNnAPjhhx/YuXOno91NN93El19+2Wo/WVlZzJ8/nxEjRjB06FDuuusuampqXPtlnNj3n//8h/fff79dtroKV7hizMDfhRBjgHOAOxVFGeOC80okPZJvjn7Dv/f+m9mDZ5N8YzLvX/I+wfpgHtr+EDeuu5HUwtTuNrFXcNNNN/Hdd9812b5s2TJmzpzJkSNHmDlzJsuWLQOaCmdbEEKwYMECEhMTOXLkCEeOHMFgMPDAAw+45DvUp7F9t99+O3/84x9d3k+bEEK49AWsAWa11GbChAlCIumN/JT1k4hfES/+9N2fRI25xrHdYrWI1UdWiws/u1DEvBcjlm5bKgoqC7rR0pZJT0/vbhOEEEJkZmaKsWPHNtg2cuRIkZOTI4QQIicnR4wcOVJkZmaK0NBQER4eLuLi4sS2bdvEwoULxd133y2mTp0qhgwZIr744osm59+0aZM477zzGmwrLS0Vfn5+ory8XLz77rvizjvvdOybN2+e2Lp1qxBCiNtvv11MmDBBjBkzRjz66KOONoMHDxaPPvqoSEhIEDExMSIjI8OpfY899ph47rnnhBBCLFy40GFfUlKSmDFjhhg/fryYPXu247s2xtnfCEgSbdBhl/rYFUWJAhKAX5zsuxW4FWDQoEGu7FYi6RIOnj7IfT/cxzC/Ybx04Uu4qd0c+1SKivnD5zNr8CyWH1jOe2nvsfXUVhbFLeKG0TegVZ1dPHJX8MQ3aaTnlLn0nGPCfXjs8rFndWx+fj4DBgwAICwsjPz8fKKiorj99tvx8vLi/vvvB+Dtt98mNzeX7du3c+jQIa644gquvvrqBudKS0tjwoQJDbb5+PgQFRXFb7/91qIdTz/9NAEBAVgsFmbOnMn+/fsZN24cAEFBQezbt4833niD559/nuXLlzexb/PmzU3OaTKZuPvuu1mzZg3BwcF89tlnPPzww7zzzjtn9btqDpcJu6IoXsBXwN+EEE3+S4QQbwFvAUycOFG4ql+JpDNZnZzNcxsOk1t5Cq8hb+Kn9+XNi9/E283baXsPrQf3jL+HxOGJLNu9jOeTnmfVkVU8NOUhJg+Y3OCcOSUGwv30LJ4TTWJCRFd+rV6DoigtRogkJiaiUqkYM2YM+fn5Lu37888/56233sJsNpObm0t6erpD2BcsWADAhAkTWLlyZZvPefjwYQ4ePMisWbMAsFgsjpuYK3GJsCuKosUm6h8JIdr+LSWSHszq5GweXHkAg7kKjyHvYxFQeOSP7DhsJDGh5WMH+Qzi9Zmv82PWjyzbvYxbvr+FOVFziNX9gWVr8zCYLABklxh4cOUBgG4T97MdWXcWoaGh5ObmMmDAAHJzcwkJCWm2rbu7u+OzzVPRkDFjxjSZtCwrKyMvL4/o6GgOHjyI1Vo34W2PHc/MzOT5559nz549+Pv7c9NNNzWIK7f3q1arMZvNbf5uQgjGjh3Lrl272nzM2eCKqBgFeBvIEEK80HGTJJKewXMbDmMwmdENWIXKrZDq7OsxVAXw3IbDbTpeURQuGHgBq+ev5o64O/jh1A88n34LFp/N2GIObBhMljafsz9wxRVXsGLFCgBWrFjB/PnzAfD29qa8vLxd55o5cyZVVVWO6BSLxcLf//537rrrLvR6PVFRUaSkpGC1Wjl16hS7d+8GbOLv6emJr68v+fn5rF+/vtW+2mJfdHQ0hYWFDmE3mUykpaW16zu1BVdExUwHbgQuUhQlpfZ1qQvOK5F0KzklBtQeR9H6pmA8PRNL1XDH9vag0+hYFL+I1fNXY64chnvId3gOfQm1/liDvvob119/PVOnTuXw4cNERkby9ttvA7B06VI2btzIiBEj2LRpE0uXLgXg8ssvZ9WqVcTHx/PTTz+1qQ9FUVi1ahVffvklI0aMIDAwEJVKxcMPPwzA9OnTGTJkCGPGjOGee+5h/PjxAMTFxZGQkMCoUaO44YYbmD59eqt9tcU+Nzc3vvzyS5YsWUJcXBzx8fHtjvRpC4qzx5fOZuLEiUIW2pD0dKYt20yJ74so2hIqj94PwjYBGuGnZ8fSi87qnNOXbSHPlIwu7BsUTSmGk7dgMQzp0DnPhoyMDEaPHt1l/fUUdu7cyfXXX8+qVascIt5TcfY3UhRlrxBiYmvHypQCEkkzXDmtArXHCYynL3SIul6rZvGc6LM+5+I50bgZx1J1fBHC5Id+4ArU7jlklxiYvmwLq5OzXWW+xAnTpk3jxIkTPV7UO4oUdonECUII9pZ9gp82lGDOQ8E2Un9mQWyHJjkTEyJ4ZkEs4d7BVJ38M8Lqjm7QOyjaIsdEqhR3SUeRuWIkEif8mPUjaUVp/GPaP7jyhtkuPXdiQgSJCRFMX7aF3JO3oB/8HzwGvU3VidsxmHx4bsNhGf4o6RByxC6RNMIqrLye8joDvQdy2bDLOq2fnBIDVmMIhlM3o2gq0A98B1RV/XIiVeJapLBLJI3YfHIzh4oPsShuUaeuGA330wNgrR6I4dSNqNwK8Ri4ggF+6k7rU9I/kMIukdTDKqy8kfIGUT5RXDqkc6N2F8+JRq+1ibilagTVOb9DpT9J2MjPMVlNndq3pG8jhV0iqceG4xv4reQ37oi/A7Wqc0fO9onUCD89ChCqnkxi5D0cKd/DI9sf6fMpgKOiooiNjSU+Pp6JE+si+GTa3o4jhV0iqcVitfBGyhsM9xvOnKg5XdJnYkIEO5ZeROayeexYehFPXfxn/jr+r6zLXMfyA8u7xIbuZOvWraSkpFB/XYtM29txpLBLJLWsy1zH8bLj3Bl/Jyql+y6NW2JuYW7UXN5MeZOMooxus6O7WLNmDQsXLgRg4cKFrF69muPHj/Of//yHF198scHKzm3btjFt2jSGDh3qdES8ZcsWdDodN998M2DL7fLiiy/y/vvvU1FRwXvvvcddd93laH/ZZZfxww8/ALBo0SImTpzI2LFjeeyxxxxtoqKieOyxxxg/fjyxsbEcOnTIqX2PP/44zz//fBOb9u7dy/nnn8+ECROYM2cOubm5Lvvd2ZHhjhIJYLKaeDP1TUYFjOKiQV23AtQZiqLw8JSH2Zu/l4e2P8Snl32Ku9q99QPPlvVLIe+Aa88ZFguXLGuxiaIozJ49G0VRuO2227j11lsBmbbXFcgRu0QCrD26llPlp7p9tG7HT+fHE9Oe4LeS33gt+bXuNqdT2L59O/v27WP9+vW8/vrrbNu2rUmb7k7bO378eBISEkhLSyM9Pd2xr37a3uPHj7f5nPXT9sbHx/PUU0+RlZXlUrtBjtglEkwWE/9J/Q8xgTGcH3l+d5vj4LzI87hm5DWsSFvB+ZHnMzGs1RQhZ0crI+vOIiLCtggrJCSEK6+8kt27dzNjxgyZttcFdP/QRCLpZlb9toqcyhzuTLizxdFhd3D/xPuJ9I7kkR2PUGmq7G5zXEZlZaUjxW1lZSXff/+9o6i1TNvbcaSwS/o1NZYa3tr/FvHB8UwPbz01K9gKcExftoUhS7/t9MRdHloPnj73aXIrc/nXnn91Wj9dTX5+Pueeey5xcXFMnjyZefPmMXfuXECm7XUFMm2vpF/zUcZHLNu9jP/N/h/nDDin1faOqkq1FZDAlvGxo8nBWuOlvS/x9sG3efWiV7lg4AUdPp9M2yvT9kokfRKD2cDyA8uZGDqRKWFT2nSMraqSpcG2rqiAdEf8HYz0H8ljOx+juLq4U/vqy8i0vRJJH+fzw59z2nCaO+Pb7ltvLkFXZyfuclO78c9z/0m5sZwndz3pdKJQIrEjhV3SL6kyVfHOwXc4Z8A57Yo2sSfuaut2VxIdEM1dCXex6eQm1h5b2+n9SXovUtgl/ZJPDn1CcXUxdyXc1XrjetRP3GWno1WV2sPCMQtJCEngmV+eIa8yr0v6lPQ+pLBL+h0VxgreTXuX8yLOIy44rl3HNk7c5YqqSu1BrVLz9PSnMQszj+zo+4nCJGeHXKAk6Xd8mPEhpTWl3Bl/51kdb6+A1F0M9BnI4kmL+ceuf/DJoU/4/ejfd5stkp6JHLFL+g2rk7OZ9uxaXtv7NprqWI6c8ju7E1mtkPIxvBgLr06Ab+9n3c/7uyy2HUBTMRVN9Rie+fl5znnuk15ZJ/Xll18mJiaGsWPH8tJLLzm2y7S9HUcKu6RfYI8/L1R+QlFXU5pzwdkVjs7eC2/PgtWLwCsYAkdg2fseU9dfyqSyjQiEoyj1I6sPdIrYr07O5qFVByk5lQhWN8q83+fBlSm9StwPHjzI//73P3bv3k1qaipr1651JOWSaXs7jhR2Sb/AFn9uws1/F+bKIVhrItoXf15ZBGvugv/NhJKTkPgm3LIJbviUP2r+zTERxktub/Cu9l+EcxqDycJHP58ku8SAAIfYu0J87bH0wuxDdV4ian0WFt9NnR5L70oyMjKYMmUKHh4eaDQazj//fFauXAnItL2uQPrYJf2CnBIDGq8MVG5nqCm4tMH2Vik4BB9fA2U5MO0umPEA6Hwcu3eWBbGLx/ij+nse0HzG9+4P8Kz5d3xouZj6Yyf7jaSj/vn6NpvLx2EqTcctaAt5x0cB7U85/OzuZzlUfKhDNjVmVMAolkxe0uz+mJgYHn74YYqKitDr9axbt85RRUmm7e04Utgl/YJwPz3F3j9jNfliLh/TYHuLlOXCO7NBo4M/fQ+RE5o0CffTk11i4D3LXDZZJ/BPzXKe1L7H5epdLDHdSqYY4GjrioVM9v7sVOddgafHMbwiv6DavBCdRtfhPjqb0aNHs2TJEmbPno2npyfx8fGo1U1LEXZ32t633noLs9lMbm4u6enpDmGvn7bX/qTRFuqn7QVbUjL7TcyVSGGX9AvumhnO02lHMRbNAGwC0qb4861Pg7EK/rwZgkY4bbJ4TrQjf0yWCOaPpqVcbd3G/2k+YL3bUv5l/h3vWuYgULlkIVP9/gCweiAKrsUasZxXkl/hgUnt8x+3NLLuTG655RZuueUWAB566CEiIyMBZNpeFyB97JJ+gVfAERTFij8JbY8/zzsIyR/C5FubFXVwFtvugW7SjVxu/TfbrTE8qv2Az9yeJFpb6JKFTM5i6f95ydX8Lvp3fJD+Abtzd3e4j66goKAAgJMnT7Jy5UpuuOEGQKbtdQVyxC7pF2w9tZVgfTCb7rup7RWSNj4KOl+YcX+rTZ3Ftq8eHMBj34Wyvvx7Hnd7n2/VS9DUVIP1VlB1bEzlrL/ZpnvZlbuLR3Y8wldXfIW3m3eH+uhsrrrqKoqKitBqtbz++uv4+dnCT5cuXcq1117L22+/zeDBg/n8888BW1rcq6++mjVr1vDqq6+2qQ972t4777yTJ598ksLCQq677jqnaXtHjx7tNG3vwIED25y2tzX77Gl777nnHkpLSzGbzfztb39j7Nixbfo+bUYI0eWvCRMmCImkq6g2V4tJH04ST+x8ou0HHdkoxGM+Qux8zTVGlGYL8eHVtnO+c4kQRUddc95GpBSkiHErxomHf3q4xXbp6emd0n9PZ8eOHWLQoEFi79693W1Kqzj7GwFJog0aK10xkj7PL7m/YDAbWi1SbS+gMWzpNxz96F4qPQbCpD+7xgifcLjhc5j/uq1w9JvTbYucXExccBy3xNzCmqNr2HyyaVRGf0em7ZVI+ghbTm7BU+vJ5LDJzbaxL2DKLjFwlfpHhomTPFx+FasPnHadIYoCCX+AO36GyImw+g5IW+2689eyKG4RowNG88TOJ0g77Xr/raTnI4Vd0qexCis/nPqBcyPOxU3t1mw7+6IfD6r5u+YL9lmHs9o0qXMW/fhG2EbvAyfDylv5aeMal65Q1aq1PDvjWXQaHTeuv5G39r9FmbGsSTshc7r3WDr6t5HCLunT7C/cT1F1ERcOvLDFdvb48r+ovyVUKeEp0x8ApfMKaGj1cP2nlOsjiNt+O56lv7p0heoQ3yF8dtlnnBtxLq8mv8qsL2bxrz3/IqciBwCdTkdRUZEU9x6IEIKioiJ0urNfjyCjYiR9mi2ntqBRNJwXeV6L7cL99BhLcrhNs5a1linsEyMd2zsNjwAWmpbwJkt4z+1Zrqp5glwCXbZC1V/nzysXvcKh4kOsSFvBJxmf8HHGx8yJmsPCUQuhHAoLC130ZSSuRKfTOeL6zwYp7JI+zdaTW5kYNhEfN58W2y2eE41p1atoMPMv8++ArimgkVzqzU3KEj5z+wfvuT3LNcbHKMPTpU8KowJG8cx5z/DX8X/lg/QP+PLXL1mXuY4pA6Zw89ibmRY+jTUpOTy34TA5JQbC/fQsnhPdramJJR3DJa4YRVHeURSlQFGUg644n0TiCo6VHuN42fFWo2EAEsNLuFr1Ays1l3JKhHZZAY1wPz0ZYjC3me5jiJLLW24v4I6xU54UwjzDWDxpMRuv2ci9E+4lsyST2zfdzqzPr+Chje+SXVLu8oRlku5BcYWPTVGUGUAF8L4QIqa19hMnThRJSUkd7lciaYm3D7zNS/teYuPVGwnzDGu58QcLIDsJ7kkBj4CuMZC6aByDycLlqp286vYa663nUJO4nMTxAzu1b5PFxLrMdTz6w+tYtblYTT4Yi6djKpkCVh1qRcEqhBzB9yAURdkrhGi1SK9LXDFCiG2KokS54lwSiavYcmoLYwLHtC7qv22Co5th9tNdKuqAQyyf23CYtSXTGKGp4B7ze5D/GohlthDJTkKr1jJ/+Hz+tlyNyvNX3AK3oQtdj3vQFkwlkzEWT0eY/Rwj+Pr2Sno20scu6ZMUVhVyoPBA6+XvrBb4/lHwj4LJf+kS2xrTMD3APPjOE35+HYJGwqRbOr3/cD8PskuiMVRGo9Jl4xawDW3ADrQBOzCXxWEsOg9DTbhLJnQlXUOXhTsqinKroihJiqIkyZl4SWezI2cHAsEFAy9ouWHKx1CQBhc/Dhr3ltt2FbOfguGz4LsHbYnIOpnFc6LRa20ZL63VEVTnXE/l0fsxFU9F452G59BX0A98m3zTfhke2UvoMmEXQrwlhJgohJgYHBzcVd1K+inJBcn4uPkwwr/5rIwYK2HLUxA5CcYkdp1xraFS2So06f3hy5ttdnYijbNFqhUFYQqgpuByKo4spaZgDipdHvpBb3Pt2mtZe2wtJqupU22SdAy5QEnSJ0kuSCY+JL7lTI47X4OKPJtvvRN92WeFVzAseAtOH4H1rq/P2ZjEhAh2LL2IzGXz+Pe1cY4RPFYPjEUXYj3+EImR92K0GHnwpwe5dOWlrEhbQYWxotNtk7QfV4U7fgLsAqIVRclSFKXzHYMSSTOcqT5DZmkmCSEJzTcqz4MdL8OY+TBoStcZ1x6Gnm9LGZz8Iez/osu6dZbv/ZkFCTw580+smr+K12e+TqRXJM8nPc/sL2fzwt4XyK90bfUiScdwVVTM9a44j0TiClIKUgBaFvatT4PFaPOt92TOXwqZP8Hav0HEeAgc1iXdOsv3DqBSVMyInMGMyBkcPH2QFWkrWJG2gg/SP+DSIZdy09ibWnZ/SboE6YqR9DmSC5PRqDSMDWymeEF+Wl1lpIChXWtce1Fr4KrloNLY/O3mmu62yEFMUAzPnf8c3175LdeOvJaNJzay4OsF3L7pdn7J/UVOtHYjUtglfY6UghTGBI5pvqjzxkfB3btNlZF6BH4DIfENyE2FTU90tzVNiPSO5MEpD7Lx6o3cnXA3h4oO8efv/8x1a69j3bF1mK1trwkqcQ1S2CV9CpPFRNrpNOKD4503OL7dtiBpxgNdvhipQ4yaB5Nvs8W3H/6uu61xiq+7L7eOu5UNV2/g8amPYzAbWPLTEuatnMeH6R9SZarqbhP7DVLYJX2KjOIMjFYjccFxzhvseAU8g11XGakrmfUPCIuF1YugtOfmcXFXu3PVyKtYk7iGVy96lTDPMJ7d8ywXf3kxL+97mcIquY6ls5HCLulTpBamAjgX9sLDcGQDTPoLaM8+13Vr2EvsuapwhgOtDq5+1+ZnX/kX26rZHoxKUXHBwAtYcckKPrr0I84ZcA7vHHyHOV/N4dEdj3Ks5Fh3m9hnkcIu6VOkFqYS5hlGqGdo0527XgeNrlNH6/VL7HVKpsSgETDv33BiB2x7zjXnbAMdvVmNCx7HCxe8wNrEtVw14irWZ65n/pr53LX5Lvbk7ZETrS5GCrukT5FamOp8tG422uqLxlwFnoGd1r+9xF597IUzXEb89RB3Pfz4rG3OoJNx5c1qoM9AHj7nYb6/+nvuiL+DA6cP8KcNf+KGb2/gu+PfyYlWFyGFXdJnyK/MJ68yr4Gw20eaCx99HmpK2eU+vVNtaK5AhstL7F36PPgPga/+DJVFrj13IzrjZuWv82dR3CI2XLWB/zvn/yg3lbP4x8VctuoyPs74WE60dhAp7JI+Q2P/ev2R5mxVEhVCx+07vTu1gERzBTJcXjjD3QuueReqimyTqZ3oyujMm5VOo+Pa6Gv5OvFrXrrwJYL1wTyz+xlmfTmLx3c+zo+nfnRaiFvSMjJtr6TPkFqYipvKjdEBo4G6kaaClVnqvfxgjaPUpO7U9LOL50Q7CmfY6bQSewPibJkg1z8AP78JU+9wfR/YbkrZTkS8uZvV6uTsFsvsNbd/5qCZzBw0k5SCFD4+9DHrM9fz1ZGvUCkqov2jmRA6gQmhEwjUB+KmdsNN5Ya72h03tRsalQaNokGr1jo+q1XqdtnVmXR131LYJX2G1MJUxgSOQavWAnUjygTlN0KUEr63TGqwvTOoXzijSy7iybfCsR9ti64GT4XwFtIonCXtuVnVrwgFNCnS0dp+gPiQeOJD4qmx1JBakEpSfhJJ+Ul88esXfJjxYZvtVikqh9hbrSqqagQiSI1HoJozQs0je9S8ecSLIC8PWzuVFo1ag1ax3Ry0qtp3tRaNommwrcH+ZrbZP+/OLOHd7aeoMSmg9Sa7JLDTC5dIYZf0CYwWI+lF6dww6gbHNvtIc456D0ahZqs13rG9PbR3tNVcnpVOQVFg/mvwn/Pgi5vhtm2ga7lwd3tpz82qJX98YkJEq/vr4652Z/KAyUweMBmw/Y0PFx+m3FSO0WKkxlKD0WLEaDFitpoxCzMmi8n2bjU5PputZj7dcxyTsQZFsYBiAcWKVTFTcAai/L0wWU0YrUaqzFWYrCbMVnOr7+1BHQEegPHMFGryrmz2O7sKKeySPkFGcQYmq4m4kLqJU9tIcz9zlCR2WmMox6PdbpG2jDC7HY8AWz6Z9y6FtffaPrs4DXFbb1at+eM74q93U7sRGxzbajtnvPnVtzibhagB/nvbvHafTwjhuGnUF3tnN4D5r28DxQyKFWGqu+l25pOjFHZJnyC1oOnCpMSECHzKfiVqaz5vmS4j4izcIu0ZYXYrg6fCBQ/B1qfYp4nn7kNju8WX3Jo/vr3++q6yq70oioJWsblbWiPMvaDLv7OMipH0CVILUxngOYAQj5AG2y8SuwGFfz64lB1LL2q3wHVZ+KIrOO8+CoOmMCr5SXSlv3XOAqlWqF9mz079p6TW9neXXX2tbynskj5B/YVJ9VdJ/vrjJxQFJIBXSCtncE6XhS+6ApWaP5ffShXuvKZ9BXeMQCcskGoB50U6Yh031Nb2d5ddfa1vpTuW8k6cOFEkJSV1eb+SvkleZR6zvpzFkklL8Kq50OETj1QK2O7+N561/oHoKx86qwupsY8dbKOtrhKF9jJk6becr0rhPbd/8ZF5Jg+b/wQoKEDmsvb7kiU9C0VR9gohJrbWTvrYJb2e/YX7AZt//fa363zic1S2wcNa0wS+PkufuCvDF7siljncT88PJfH81zyP2zTfMkjJ513LXKq8osBiAnXrPmFJ76dXCXtzF0Z3LjxoL2dj6+rkbP614SD5NUcI8lGYnxBC/CBPaiw1jpfRYmRS2CTiQ5rJQ96HSS1MxV3tzqiAUeSUnHRsn61OIsM6iFMiFKUDPnFXhC92VXSNPeb8WdP1ZIlgFms+5x3182AEnvor+A2yldcLGAaBwyFwqO2z3yBotKCnvu1ns+Coq+kpdvQEeo2wO7sw7v0shS+STrLvZKlLLpj2/GOcrUC39eKuMlVRUFXAyv3pLP/5Z5SgrXhoS6gCPjlhezVGQeHGMTdy34T7mqy668vUX5hkj34IopRJymFesVwJdL9PvKuia+znevzrND4wzGaV5Vwm6vO4O17FBK9iKD4KRb/BiV1gqqw7UKUF/6h6om97bcj15KHvCqky2Vy29f9n7f2UGEyOn7srHLRXhKV2Ib1G2O0XhsbX9ngtLB5g8WDXqXyE1QMUPQjb1zmbC6Y9/xhn+09k+w4mUBtQacpRNKWYtaU8uWMzyQYP8qvyya/MJ78qn3JjueM4dQi7ukJIAAAgAElEQVRYqsMx5M/DavYBq4YQL29WLjofd7U77mp3rFh5ae9LvJ/+PqMDR3PZ0Mva/N17M40XJtlHrOdZ9qNSBBstE7ss+qElujq6psZsBaACD34wDOWXJDXPLEgkcWbt/6cQUJEPRUdrxb7e+7EfwFwNwBxgn0rLCbdQjoswMsUAMq1hfLMmgyOWUEpMPkDDmPnuCAft8WGpFhMYzoCiAs+gTu+u1wi7/QLwDPkWq8b5xSCsahAahNBSIjTEvf00wV6ehHh54aauyythF0N3tTtZxSb2Hi+n3AD4aNFaNbXn0GASWh7blMYzP+goKrfgqdWjoKXUAKCg0lWjqKpR1AbMqmqWbPqBJZsNuLsbQWXAJKrQaGtAZcCqVKGEVOM9oGkx4hqhsC0rkFCPUAZ6D2Ri6EROl+rYfshMcakOq9kXYQxEAURtIFN+Dew+InhuQ6rjqeHvs28mxT+Ft/a/xSVRl/SLUXvjhUn2i9i69l3KzXrKfEbyzNwx3X5xd2X8dptETlHAO8z2imqU8dJqhfIcKDrKQ2+vIUrJY4iSx1AllwtUKbgrtasu1VCpcueECCNThJIpBtjE3xrGiZIw283DxQulmqOrbpyrk7N5+bsDVJUWEqiqwJdyhnnWcPVoD+IDrWAohqpiW3I2Q+171RmoKbWdYOItcNkLLrXJGb1G2O0XxspTBeg0ZRQpWo6rfDmh8iFb5UG5SoVBBUZFoUZRMClgVASW6gowm1Dp1FQIK0ZhoUZYMAozZUYjlaYarB4W3L2cV6OxAlWAPsD2GcCrBTuFULBadQiLHsWqw2zRIUz+YAlHWHQIq972bvaxCbbJlwFeIWxdOstxjtXJ2Ty4fT9upjKilFIGK6e4ze1Nhio5XFzzPKV44avXNnlqeHhVGr+/6Bo+O/lPvj/xPZcMucQlv/ueTHMLk9idC7rJ/LRwVnOHdpiD2aUUVRqJ8NMzPKSl/4quTQ7WYZFTqcA3Enwj+dHbwsf1jlNhJVw5zRAlzyH4UUoeo5WTzFbtRavUu46WLYGAobVuneF1Lp6AoS6vN3tWN04hwFRVK77F9US5vjDXfa4qKWBWVTGJSg3UL8BlAvbXfnbzsn03fYDtPWBo7edA289h41z5tZul1wj74jnR3PtZCo8ab2egqZBIxfaaqpwmTMnGHSPumGwvpVEehzaUWLQCJgVqFAWjolCFikpFS4X9hYYqlYYqRU2VoqYGDVqLBsXqjrDosVg9MFk9MVo8qMSTCqGnHD0VQk8FdZ/VWAlSSm0vTjFAk86CSDdYtRIqC6CigOl5WexXlaLV1V0khcKHYKWM36s38aYlEUXB6ahs3S/BDB89nP+m/pc5UXP4OiW3T00oNZ7bGDjq56YLk0wGyE+D6X/tFBsyT1fy9LcZbMrIB2yD0iVzR3HbjKEozYxQuzI5mCufDhrfkKyoKNIMoFIbwU9VpgZtNZiJVAqJ1hRyZ5zCOP1pm3snKwnSVoGw1jXW+9ebwB1WdwMIGHZWuW4Wzx7BM6t+QWcuxZ8K/JRyQjVV/GG4N2ze0Uikz9R9tjR9gnag87UJsj4AvEL5Md+fbIueM8KbM3hzRnjVvQtvPHyD+eHBOe22vTPoNcKemBBB0oliPvy59bYKVtww446p9t2Iu2Kq/dku/vU+t+FnN8WEJyYC6u13pwYPyvBSDHhRjZdiQKU9i3UBmW7gGQJeweAdxtYsf07jy2lhexXiS7J1OP/VvsjNmg0st8yjpJk6BLklNTw67jYWb1vMP3/4jI+2+PeZCSVncxulp/cTG9QoEig3FaxmiGg13LddlBpMvLr5CCt2HcdNreKBudFMGRLAOzuOs2z9IY4WVPD0lbG4aZyv++uq5GCufDpo7oYENOnDjIZS/SAuufwSxjX+nuYaOHO8qU//+HbY/2nDtp7BNqHX+YLWA9w8bWGaKq0tcqe6zCbOdrE2FJNoOEOi2gqNvY8Hsfm19f51I2e/QRAeXzeqtou3fVStD7C1VzeUxzuWOs83Y0cp7TnVn3qNsAM8lRjLxMEBPLfhsNMRiR2BihrcqMENgHBfHd/+/QIEAiHAKgQCmPPCNnLLql1mn4IVPUa8MOCtVOGFoVb0DXgrBjypxorCaeFLkfBB7R3KJ/debvsHrjfSe3nZFqff7y3LPD5SP8NNXrv5VnNxs6OyWYPPZ4jvEL44+i4G093UX2DcoyaU2klj37GiKUXRlnLkZKPH+qw9tvdI1wn7quQs/vFNOiUGE9dNHMh9s0cS4m17Hh8/yJ/hwV68vPkIJ4qr+O8fJuDv6eayvtuLq58OWrohtbkPjTsER9tejTEZoDizLmKn6KjtJlBRYHOVGKvAarJNQFottiIj+gDw8IfQsQ1dH03e/UHnZ3MvdZDmnoTq7+8p9Cphh7p/MmcrArUqBRQwWeruq3qtmgfmjkLv1nQiccklo5qco/5xV02I4Ku92U7329s8s8CWbc52HqhCRxU6CoS/rVFLt/gyuHt1JkMCPRgc6ElUkCdRgR7cP3skD6062KTfHdYYMsRg7tKtZ8xFi3hwVZrTUZlapea2cbex9KelaLzTMJc3zIjXI/OctIHGdqv1tpjPM8XhDRtmJdlGZWeZRqA+ZouVJV8d4Kt9WUwY7M8/5o9lbLhvgzaKonDvrJEMDfZk8Zf7WfDmTt69aRJRQZ4d7v9s6YqnA5f1odVD6Bjbqwfj7EnITk+IvKpPrxN2Oy09IrZ1FFH/HNklBtSKgkWIBlkA7U8IOSUGfPVaFAVKqkxOz21vh9K2SmXuGhWpp0r4dn8O1nrtvXUagrzcOF1hrK0AZLs/RPh5UDlmET77lpLolQELYpv9rnOj5vLQ1hdwC9qMuXws9UftPWlk0R4aj5jU+pMIq4ZQ3dCGDbP3QuQkl/T59LoMvtqXxT0XDeevF49ErWo+ymN+fAQRfnr+8n4SV76xg+ULJzJhsGsnCSXdR1v0oqcgc8V0As6eJhpTP9+I0Wzl1JkqThRVknna/l7JyeIqss4YsFgFigKXxg7glWtiUL8SZ5toumlti3Y8vmUFX516HsOpGzFXjG3Sb2+j8e/VY/AbqBQVT035T933Kc+Df0fDnH/C1Ds71N/nSad44Mv93Dw9iscuH9vm446fruTm9/aQXWLg39fEcXlceOsHSSRtQOaK6UacPU1cOCqYrYcKnY6u3TQqhgV7MSy4acicyWLl+OlKVuw6zoc/n+T6SYM495xFsPH/ICe5xVJoj1zwe7Z+9hGlIbsorRjb66NiGvxeS8tR67OZHpzY8Ptk1Q4YOjhi33viDI+sOsi5w4N4+NLR7To2KsiTlYumcesHSdz9STKnzlSx6PxhzUbMSCSuRo7YewnVJguTnt7ExaNDeXH+EEzPj2ardTy3VS1qUbBf3vcy7x18j22/24a3m3c3WN45pBSkcOP6G3nxghe5ePDFdTs2PQ47X4MHs0Cra/b4lsgtNXD5qzvwdFez5s7p+Hmc3URotcnCA1/u5+vUHH43aSBPJsagVXdfpuzenEulN9vuSuSIvY+h06q5bFw4q5OzmTIkgCrjhfxRWccAriG7JKjZMMZzI85l+YHl/JL7S0MB7OWkFjZdmATYRuxhMWct6tUmC7e+vxeD0czHf5ly1qIOtr/ZS9fFMyjAg9e2/kZ2iYHXfz8eH13XZ1jszblUeqPtQgjKqs0UlFWTX1ZDQbntfWSoFzNHh3Z6/1LYexFXT4jgk90nWbb+EHrjHG50X8+fNOt5ynxjs2GM44LH4aX1Ynv29j4n7OGe4QR7BNdttFps7qm468/qnEIIlny1n4M5pbx140RGhnb8CUelUrh/TjSDAjx4aNUBrnlzF+/cPImILp7A7vG5VFqgK2xvzxNBRY2Z/LJq8suqKSirqf1sE++Cshryy237qk3WJsdeP3mQFHZJQ8YP8icq0IPjRVWUEMi31ilcp/6Bl81XUY6H0zBGrUrL1PCpbM/ejhCiz/h5UwtTmRAywfHz6uRsvlr/PR8YK3gixYO48Ox2X/T/3XaMNSk53D97JLPGuPbiu3bSQML99Cz6cC+Jr+/gnYWTOFpY0WXuhV5V4q8RnW27syeCxV+msvVwASHe7uTXindBeQ0FZdVUGpsGRXi4qQn10RHi7U5cpB+hPu62n310hHq7E1K7z9O9ayRXCnsvQlEUFoyP5IWNvwKw3Hwpie47uU69leWWec2GMU4Pn87GExv5reQ3RviP6EqTO4W8yjwKqgocib/sF+YV1jTQwtaKQXzahkf1+qO0AE83iiuNzBs3gDsvHN4pdp87Ioiv7pjGze/u4ao3dyIQjjUXne1e6K4i0q6go7ZXmywNRtINRtdl1ezOLMZsbTjXaLII1qTk4K5REearI9Rbx9hwHy6MDqkn2rb3UB8dXl0k2G2lZ1kjaZUrEyJ4YeOvaFQKB61D+dk6mps13/Gpal6zCyQmD5gMQHJBcp8Q9pTCFKDOv25/VE/QHOGM8OK4CINWHtUbj9KKKo0owIwRQZ36VDMy1JtVd05j+rItDRbSQee6RroyCZmrac72+2aNJKfEQF5ZNQVl1eSVVpNfXuNwkdj92qX18sXbcVOrHMLcWNTrc+jJub3yKVcKey9jYIAHU4YEcKywEq1aYXn5pSx3+zfvTs5hUoLzmpaRXpEE6ALYX7ifa6Ov7WKLXc/+wv24q92J9reJkv2RPF51lFTrMOz5wVt6VHfmtxXAK5t/47pJgzrFbjsh3romom6ns1wjXZmEzNXMjQnjaGEFK3Yep6zajFatoFEr3P9lapOFgBqVQqiPjmBvd4YEeXLO0ECHi8Q+ug7xdsfPQ+sQ7OnNpPCI8NP3SlEHFwm7oihzgZexpeBZLoRY5orzSpxz1YRIHvhyP18tmsaEgRfC62uYlPsRiD87zX+tKAqxQbHsP73fydl6H6mFqYwNHIu2tn5nuJ+ekpJiRipZrLdMdrRr6VG9u33OEc24F/w93DptLqSrkpC1FSEElUYL5dUmygxm23vt5+JKI4fyyjiYXcav+eWOUfWQ2rQbdpEO9dER5utOiLeOMF8dAR5uqFpYHeyM3vw00xwdFnZFUdTA68AsIAvYoyjK10KI9I6eW+KcS2MH8Oiag6yszV/COXfAt/fByV2sLh7sNAVCYKQXNd6ZlNaU4uvu23onPRSjxUhGUQZ/GP0Hx7bFc6JZtfITVIogRdj8440vzMZRD34eWs5UNX1E7yqfszMxUYDiKiML393DE1eMZYiLcs10Vgy42WKlvNpMebXZJsi1olxWbbJtM5gafK7fzn6cpQU3SKCnGzERvlwQHcz4Qf6MH+xPQCckV+vNTzPN4YoR+2TgNyHEMQBFUT4F5gNS2DsJL3cNc8eG8U1qDv932Rh0cddTs/EfbHv3Cf5WfY+jXf1alGeKB+DhDf/75Ufun3FFd5jtEtKL0m0VkxoV1hjxazlkQKp1WJPcHc6iHtROBnVdOUpzJib3zRpJqcHEixt/ZdYLPzIo0IMwHx1htdEVYfbJOl/btmBv91YXPDUXAy6E4JLYAbXi27wYOxXm2tG1s+iQxni5a/DRafDWafHRawjz0TEixAsfvRZvnQYfnbbBZ2+dBh+9Fj+9lgBPty5zhfS0p5mO4gphjwBO1fs5C5jigvNKWuCqCZGsTslhc0YBJouV0upp/E75Hl8qKHVS48lSHYkQCp/u396rhd2+MGlccMNKNGOtRyBgGCn3XNfkGGf+dIsAtUoh1Nud3NLqbhmlNScml8UN4N0dxzlRVEl+WQ2/ZBZTUF7dxC+vKBDo6U6ojzthtYIf6l3rmvDRoVIUHlndNEuowWTh3s9Tuffz1Bbt06gUh9DahXdokJdjW30hrhPp2nedFi+dpsWkaZLOo8smTxVFuRW4FWDQoM6dnOoPTBsWRJiPjq/2ZXE4rxxf07ksdF/H5epdfGhxUg7OqsNaE0KlcqzrjXUhThcmCQHZSTD0QqfHNOc3t1gFOx+c2RlmdogQbx1L5o5qsM1qFZypMpJXVheyl1daTUG5LRokp7SalFMlFFUa29zPA3OjbSNph1DbR802gdZr1b128rC/4wphzwYG1vs5snZbA4QQbwFvgS1XjAv67deoVQqJCRH876djWKyCbKLIsA7iavU258IOWAyDcPNN69ULlRovTAKg9BRU5DdbWKO5OOiuXv3ZEVQqhUAvdwK93Jvkg69PjdniCPWrNlm5/4tUckubFpOJ8NNzxwWdE68v6X5ckZFoDzBCUZQhiqK4Ab8DvnbBeSWtsGB8BBarwFdviw750nIe8aqjDFbynLZXmwaDqooTZSe60kyXsDo5m6n/+oqCqgK2pOpZnVxv7GDP6Bgxwemxi+dEo9c2LLTS26MemsNdo2ZggAcTBgcwfXgQS+aO6lXffXVyNtOXbWHI0m+ZvmxLw7+zpM10WNiFEGbgLmADkAF8LoRI6+h5Ja0zMtSb2AhffHS2x+bN1vEAnKs6CICHVoW/hxYF2wjtr+facsUcOH2gu0w+K+wTgIUm24rb4uIBPLjyQN1Fn70XNDoIjXF6fGJCBM8siEVbO2Ma7qvrtTnp24v9u0f46R3/Bz31u9v/ztklBgR1E71S3NuPS3zsQoh1wDpXnEvSPhaMj+CJb9JZMjeaD3dpyakO4CL3w0y6/P4mF6/FauHdTE9SC1O5fNjl3WRx+7FPfrr7n0BYNVirB2Cg3irNrCQYEAea5kPhhod4YbIInpw/lhunRnWd8T2A3hLx0ZsTlfU0ui85tMQlXB4XjlqlUGows+PBmYTHz2Gm7jCJcQOatFWr1MQExbC/sHctVLJPfqo9TmKpjsQ+HskpMdgKHOemQETLKao/3XMSd42KK+KlQPRUunvRWF9CCnsvJ8jLnQtGBrM6Odu22GPIDKg6DYUZTtuPCxrHr2d+xWDuPRdLuJ8eFBMqXQ5Ww6CG2/MPgrkaIp371wGqjGbWJOcwL3aAYz5C0vNobnFYb0hU1tOQwt4HWDA+kryyanYdLYKo82wbM7c5bRsXHIdFWEgv6j3rxxbPiUbvlYeiWLDUCrtjArANpfDWHcijvMbMdZMGNttG0v30p0nuzkYKex9g5ugQvHUaViVng99A8B8CmT85bRsbHAvAgcLeM4GamBDBZZNs8dnWqsENJwCzksAzBHybF+3P9pxkaJAnk4cEdJXJkrOgN0309nRkdsc+gE6r5uLRoWw+lI/ZYkUzZAakrbZVFFI1HAEF6AKI9IrsdQnBLG7HifCK4Lunf9dwR3aSLX69mbj83wrK2XP8DA9eMqrXxu73J3rLRG9PR47Y+wizxoRSUmUi6cQZm5+9phRynS8ZHxc8zrE0vzcghCC1ILVJGgEMZ6Dot2bj1wE+23MKjcpWoEQi6S/IEXsfYcbIYNzUKjam53PO+fX87BHjm7QdFzyOdZnrmPqvleQVu/f4bHZ5lXkUGAqaFq7O3mt7b8a/bjRb+WpfNrPGhBLs7d7JVrqGzsrEKOlfyBF7H8HLXcO04YFsyshHeIVA8Cg47tzPfqbYFgpZaPq1VywEsT9dxAfHN9yRlQQoEJ7g9LiN6fkUVxp7zaSpXKAjcRVS2PsQF48O5URRFUcLK2zumBO7wNw0KdTHP5kRVg1qfV1STvtCkJ5IamEqOrWOkQEjG+7ISrLdwHQ+To/7dM9JIvz0nDci2On+nkZLC3QkkvYghb0PUV0rChe/sI0Hk/3BVAk5+5q0yy0xYa0OR6U/2WB7T10Isr9wP2MCx6BV1YtBt2d0bCbx16niKrb/dpprJkb2mtSxcoGOxFVIYe8jrE7O5vl6I7t15cOwCoWMnWubtA3302MxDEKtywIsDbb3NGosNaQXpxMX0si/XnzMNnnajLB/dzAPIeCqXjRpKhfoSFyFFPY+wnMbDlNttjp+LsWLdDGYkrTNTbLkLZ4TjdoUhaIyo9LlAj13IUhGUQZmq7npxKkjo6NzYd92pJDhIV4MDPDoZAtdh1ygI3EVUtj7CM4e13daxzJedYTTJaUNJuESEyJ44ILZAGj0p3r0QhD7xGnTiJgk0HpCyOgmxxiMFn7JLGZGL/Gt25ELdCSuQoY79hGcFZPYZR3DrZpvGa86wi7T2AZZ8hZOjuf9E8GcM83KP8+7qDtMbhOphalEeEUQpA9quCMryRbK2WgBFsAvmUUYzVZmjAxqsq+nIxfoSFyBHLH3EZw9xu+xRmMWKqapbOnx64/qFUUhNii2R69AbXZhkqka8g40WZhkL9Jw07t7ACgoq+kqUyWSHoUU9j5C/cd4OxV4cEAMZYrKlumx8STcuOBxnCg7QUl1SZfa2laaXZiUtx+spgYTp/VjwO089nWajAGX9EuksPchEhMi2LH0Iv4wpS61bbJ1ODHKcby0NJmEs4+Ee+qoveWFSTSYOJUx4BJJHVLY+yB3zxwBgI9Ow37rUDyUGl6ZqW/iux0bOBaVouLg6YPdYWarNL8waQ/4RIJPXTERGQMukdQhhb0PEuqjY2iQJxOjAnjp738G4CLvU03aeWg9GOo7tMfWQE0tTG26MAlqFyY19K/LGHCJpA4p7H2Uc4YFsjuzGLNvFOh8na5ABYgNiiXtdBpCiK41sBVqLDVkFGc0XZhUUQglJ5vEry+eE41O0/DfWcaAS/orUtj7KFOHBlJRY+ZgbjmEj6/LhNiImKAYztScIbuiZ00yphelO1+YlO28YlJiQgS3nz/M8bOMAZf0Z2Qcex/lnKGBAOw6WkR8xHjY/hIYq8Ct4UrM2CBbRaWDpw8S6d0zlt+vTs7mqe1fgQ888mk55bOy6wQ6aw8oahgQ1/RAxVZvY98js/D3dOtaoyWSHoQcsfdRgr3dGRHixa5jRbZ4b2GxxX43Yrj/cNzV7j3Gz24PW6zgN6zGAHKLtQ1T12YlQejYJjcogG2/FjIuwleKuqTfI4W9DzN1WCBJx4sxhdhG5eQ1DWvUqrSMChjVYyJjbGGLZtT6k47C1Y6wRasFsvc5TfxVWmUi5VQJM0b2rjQCEklnIIW9DzN1aCBVRgv7yzxB7+90xA42d0xGsS3ZVneTU2JA0ZSg0pY7hN2+ndO/grHcacWkHUdPYxVIYZdIkMLep5li97MfK4aw2GaFfWzQWAxmA0dLjnaleU4J99Oj9rDlia8v7OF++hYzOv50pBBvdw3xA/26xE6JpCcjhb0PE+Dpxqgwb75OzeGTk35UZx9gxjMbmyyzt0+gphWldYeZDVg8Jxp3z1MIqxZrtW0BkiNsMTsJ3H0hcHiDY4QQbPv1NNOGB6JVy39piUReBX2cQE83fs2vYLchAp1iwq3sWJM6moO8B+Ht5t0jJlATEyIYOKAAjWkQCuqGYYtZe2szOjb8tz1aWEl2iaHXlMCTSDobGe7Yx0nPLbO9i8EAjFFO8JspskEKX0VRiAmM6RETqNXmavKrj7Fwwh+599Z5dTtqKqAgDc67v8kx234tBOB86V+XSAA5Yu/znKkyAXBUhFMjNIxRnQCa5lCJCYrhyJkjGMzdm1slozgDs3CyMCk3BYTVaUTMtiOFDAny7FXVkiSSzkQKex8n3FcHgBkNv4pIxig2YW+cQyU2KBaLsHC4uHuzIaYWNFMxyTFx2jBHTLXJws/HipgxovcV1ZBIOgsp7H2cB+aOQqXYPqdboxijOoFeq2qSQyUmKAag2/3sqYWpRHpFEqgPdBTOGLL0W37cso4Kz4Hg2VDAk46fodpklWGOEkk9pLD3cRITIpgbEwbY/OxBShkvXhLWJIdKsEcwoR6h3SrsQghSC1OJC4lrUDhDANGWX9laPqjBpO/q5Gxu/9CWA+eR1QdlUQ2JpBYp7P2A30+xTZxeeclcAOYGFTptZ8/02F3kVuZSaCgkLjiuQeGMMIoIU86w1zLMUTjDkXqgxraoKre0ukm0j0TSX5HC3g+IjfQFYI+htjCFk9QCYFuodLL8JKU1pV1lWgPsFZPGBY9rMLkbr7ItnEqxDndslxWTJJLmkcLeD/DRaRka5MmeXAv4DW4xtQDQbWGPjopJ/iMbTO4mqI5QIzSki8GO7bJikkTSPFLY+wmxkb4cyC5tMbXAmMAxKCjd5mdPLUhlbNBYtCoti+dEo9eqAduIPV1EodbqHJO+smKSRNI8HRJ2RVGuURQlTVEUq6IoTQOMJT0GBZsf+oUDOqzFx/hmz69N2ni7eRPlG9UtfvZqczWHig85whwTEyJ4ZkEsg3zdGKcc44g2ukHhjL/PHtnkHLJikkRio6Mj9oPAAmCbC2yRdBKrk7NZdyAPsEXGqBB8/PV3TicaY4Ni2X96f5eXyksvSm+yMCkxIYJtN4WgV4ykq0Zy72cpTF+2hdXJ2QwP8QLA30OLgqyYJJHUp0MpBYQQGWBbki7puTy34TBGixWAdKstQmaY9ViDtAJ2JoZO5OujX3Ok5Agj/ZuOijsL+8Rp44VJKbs2EQ9srhiEALJLDDy48gAXjQoBYON95xPk5d5ldkokvQHpY+8H1J9QzCGQEuHJGOWE04nGcwacA8Avub90mX3QcGFSfbLTtlMkvDklQhzbDCYLG9PzGRvuI0VdInFCq8KuKMomRVEOOnnNb09HiqLcqihKkqIoSYWFzuOoJZ1DwwlFhXTrYMaoTjidaBzgNYDBPoP5OffnLrOv/sKkxow0HSbFOhzbLEEdRotcbSqRNEerwi6EuFgIEePktaY9HQkh3hJCTBRCTAwOlhdkV1I/wgRsfvZRykkemDXMaftzBpxDUl4SJqupS+zLqczhtOF00/wwhhJGqLJJsTq3c4ZM0yuROEW6YvoB9giT4Fq3xQntMHSKifmDqp22nzJgClXmqi6LZ2828VfOPgDSVQ0jXdQqBTeNigmD/bvEPomkt9HRcMcrFUXJAqYC3yqKssE1ZklcTWJCBD8tuRCNSmFojM2P3lw8++SwyagVNT+e+rFLbEstTEWv0TedrJdxggwAABOKSURBVM2y5YFZcPnlRPjpHdEvfnot5w0Pwk0jxyUSiTM6dGUIIVYJISKFEO5CiFAhxBxXGSZxPTqtmugwb3484w9qt2ZTC/i6+zI1fCrrMtdhFdZOtyu1MJWxgWPRqBoFaWXtgaCRzJs8mh1LLyJz2Tw++vMUiiqN0r8ukbSAHPL0M8ZF+pGcXYUIHtXsiB1g3tB55FbmklyQ3Kn2VJmqOFR8iISQhIY7hLDVOI2c1GDztiO2iXcp7BJJ80hh72eMi/Sl1GCi0n805O63CagTLhp4EXqNnm+Pfdup9qQVpWERFuJD4hvuOHMcqoqaFNbY9utpBgboiQqU1ZIkkuaQwt7PiI2wZXrM1A6DqtNQke+0nYfWgwsHXsiG4xs6tVyeI6Nj0LiGO7Jt/vX6pfCMZiu7jp5mxohguShOImkBKez9jOgwb9w0KpKNkbYNLbhjrhl5DWXGsk4dtacUpDDEdwh+Or+GO7L2gEYPIWMdm/adPEOl0SLdMBJJK0hh72do1SrGDPBhy5lQ24ZmJlABJoROYIBuGE9u+y9Dlq515GlxFUIIUgpTiA+Ob7ozKwnCE0BdN6G67ddC1CqFacMCm7aXSCQOpLD3Q+IifdmTa0a0kJsdYE1KDlknJmHV5qHyPOLI0+IqcT9edpzSmtKm/nVzje2GE9nIv36kkPGD/PDWaV3Sv0TSV5HC3g+JjfSj0mih0r/lyJjnNhym6kwMVpMP7kGbAeHSKkUpBSkATUfseQfAYoSIOv/66YoaDmaXydWmEkkbkMLeDxlXWyrvpNtwKDoKxkqn7XJKDCA0GE/PRO1xArXX4brtLiC1MBUfNx+ifKMa7shKsr3XC3XcfuQ0IMMcJZK2IIW9HzIs2AsPNzWppoGAgPx0p+3sScJMJROxGgNxD/4OFJPLqhSlFKQQFxyHSmn0b5i1B7zDwbcupfC2I4X4e2iJqY3qkUgkzSOFvR+iVinEhPuytTTMtiHH+SKkuuRhaqrz56Fyz8dz4CfcN9t5Uq72UFpTytHSo03961C7MKnOvy6E4Kcjpzl3RDBqlQxzlEhaQwp7PyU20pcf890RniGOZFuNsScPi/DTY60Yg77sKlSe6eyt/G+HUw3sL7RF4zTxr1eeti1OqueGycgtp7C8hhkjgjrUp0TSX+hQBSVJ72VcpC81ZkFF4Di87YuBnJCYEFGvytI8/psaxmspr+Hj7sOSSUvOeqFQSmEKakVNTFBMwx12/3q9idOfatMInCcnTiWSNiGFvZ8yLtK2IOi4bhSxJzdDdSnoWvdf3zruVkqNpXyQ/gG+7r4silvU7r6/y/yOD9M/JDYoFg9to9QAWXtAUUN43Uh+25FCokO9CfPVtbsviaQ/Il0x/ZTBAR546zTsMQ8FBOSktOk4RVG4f+L9zB82nzdS3uCjjI/adJxVWNlycgs3rruRxdsWM8J/BM+d/1yDNquTs9mz43sOWgYy/YVfWJ2cTZXRzJ7MM8wYKd0wEklbkSP2fopKpTAu0peNZ6z8CWy5WYae37ZjFRWPT3uccmM5y/6/vTsPjrJOEzj+fbpzQ0IgSIAQjnAEkEMcBpV7BARxRnEdd6dqDynHUsuZLZ2tYRdknapxZ0YGqrDKcizXGrXGkWEZFaOII4ZDQZggkRACEQggRyJHEOUMObp/+8f7pukc3Ul3pw+6n09VF2/Sv7zvkx8vD2//zs+XkZWSxY+G/qjdsg2uBj44+gGv73udYxePkdc9jyWTlvBg4YMkO65PNCoqq2Hp2nJKHFW8557smQxVeeoiDS63NsMoFQBN7AlsbF42r352HnefAhx+2tnbk+RIYvmM5Tyx8Qme2f4MmSmZzMyf6Xn/UsMl3jr0Fm9WvkltXS2jeo1i+fTlzBk0p+2661iTofo3nSQztY4y93DA2rR6VclxUpMcTBrSK6TfValEok0xCexao4tGl+H9c305d2A7RburA/r5VGcqL9z5AqN6jeKXn/6SdUfWUX2pmpWlK5nz9hye/+J5hmUP45U5r7Dmh2u4e8jd7SZ1sCY93eI4DMAec3045ZUGF7cV5JDmtWerUso/fWJPUEVlNaz+/AQApe5CFjh38OK7m0FmeY2C6Vi35G68NPslHvn4EZ7+7GnAaqqZO2guC8csZHTO6E6dp392OhMuV3HBZHDU9Gvxng5zVCowmtgT1IoNB6lvssai73Jbm0WPdVWyYsOAgBI7QM+0nvz1h39l5+mdVH5TybzB8xiQOSCgcyyaW8jIoiPscQ/D2B8kk51Co8voMgJKBUibYhKU93ovh8wALpoMvu84GPQ6ME6Hk7NnBvH6+gKm/bY84CV+F4zuQaFUcyRlpGfT6tH9suiblcbwPt2DikmpRKVP7Amqf3Y6NXYSNzgodY9gouNg0OvAFJXVsGRtBXWNLgDPqBagc58Avi5DcPPwT/6Rh4fPocnl5tb/KWbemL66W5JSAdIn9gR1fR0YS6m7kBGOGp7+QW5Q51ux4aAnqTcLaInf6l3Wn/Yep3trLnDxWpMOc1QqCJrYE1TzOjD97dmcFc5RANyTfSKo8/lqwul00051KfQaChnWsMath2oRganDtONUqUBpYk9gCybksWPJLGaPyuV099HgTIETfw/qXL6acDrVtGOMvaLj9fVhth6qZdyAbHp2SwkqHqUSmSZ2xR1Dc6g630R9n/FwoiSoc7Ru2gFIT3ayaG5hxz984SRcPuNZ0fFCXSN7Tn7nc5hjUVkNU5ZtZsji9V2+D6tS8UATu/JsDv1Vxlio2Q2NgY+M8V7iF8Ap4mlj7zDxenZMsp7Ydxw+h9u0v1tScydtzXd1GOjyfViVigea2BWFuZnkdEvhs/ph4G70ufFGRxZMyPM8ubuMATqZeKtLISkNcq0lfLdW1ZKZmsQt+dltiobcSatUAtDErnA4hDuG5rDmTH/rG0G2s0OQibemFPrdAs5kjDFsPXSOycNySHa2vT1D7qRVKgFoYlcATB7am6pLKZzPKGDHlvVBt18HnHibGnDVlLH66z4MWbye2363iZrv6nwOcwypk1apBKGJXQEwZZjVzr7h0hDGuipJpjGo9utAE+8nW7fgdDewrW4wBjh7qR6A+lZP/c1C6qRVKkFoYlcADOyVgVOEj1wTyZQ6pjmsPUkDbb8ONPHu3lEM4Fmqt9lr24+1W967k7Z56YHn/mFswOvbKBXPdEkBBVg7I7mMYYe5mQsmg/nOnWxyW7NAA2m/bk6wKzZY6870z05n0dxCn4m3oL6SM45sTtFyvXV/12y5D6tSqjVN7MqjZ0Yy316Fj10TmevcRQqNNJAccPt1IIn3e0lHKWsaDrRcD0bbzJUKnjbFKI+nZo8AYL37NrKkjumOvT6bUbpkktCVb8g3p9gvLZth0pIc2mauVAg0sSuPhyYPJjcrlVLHeE6ZXvwidR3P3T+mzdN3l00SOmnNcp00fZ5nYlNKkoNlD4zTphalQqCJXbVw95h+NJJEz/m/4mZziAVpu9uU6bJJQse2Q1Ia02bOo+hnUwB4ctZwTepKhUgTu2rh3lv6U9/kZh0zofcI2PRraLjaokyXTRI6ts1aHyYplW1VtQDM0N2SlApZSIldRFaIyAER2Ssi74pI2zng6oYyIT+bIb278U75abh7OXxzGD76rxZlumSSUN13cLoCBk8FYFvVOXK6pTC6X1bQsSulLKE+sRcDY4wx44BDwJLQQ1LRJCLcPyGPkqPnee3UYN5wPgC73+D3v1nsaUP/wcibaL2nUetOVn+dq0VlNSxa+b+A4efbM1i7u5ptVbVMHd4bh0N3S1IqVCENdzTGfOz1ZQnw49DCUbHg/gl5PF98iN99+CXGvYCByYdZZF7mt+/Ws+erf+adsnMYr/ICPPC960Mc/W2TB/Ds2l38N1updyRTfCmf4rUV1De5ma67JSnVJbpyHPvDwJouPJ+KkvxeGaSnOLna4AKcPNb4C15JXskzzteoK3+T6YzmE+d4PnWP57jpiwG2HKj1/Lx356oDNwXyNeNcR2n44A1Guo+w3XGUdGmgyDWZelKgyQ3AtBG6W5JSXaHDxC4iG4G+7by11Bjznl1mKdAErPJznkeBRwEGDhwYVLAqcqykbqknhYcbFzHdtZfpjr3McJTzbPIeAI65c/nUPY6tF8fDuYHgamDSxWLGJh1ljOMrbpZjdBNr/ZerTansN4P4i3sWH7m+T6kZ0eKafTLTIvcLKhXHxBjTcSl/JxBZCDwGzDLGXO2gOAATJ040paWlIV1XhdeUZZup8TPKZaCcYYajnBmOciY7Ksmwk3ezOpPCfjOYCvcQ62UKuJZVgFuc7Z63e2oS+349t8t/D6XiiYh8YYyZ2FG5kJpiRGQe8J/AjM4mdXVjWDS3kMXv7OWa3UzS2gmTy59dd/Fn111kJbt5aVojU/s2AcKmb2/iyY1XuNx4vXx6spPn5o0GaNH+3uy2gpZrxSilghdqG/uLQCpQLCIAJcaYx0OOSkVdc0fof6zZQ3up3SmC2xjPIl9TvSYVzQJ+k1XjcyGw0uPnWVVyokUH7PaqcxSV1ejkJKW6QMhNMcHQppgbx5DF62nvDhHgq2X3BHVOX808ednpbF98Z1DnVCoRdLYpRmeeKr98TTrq2yP4jk7d3k6p8NLErvxqb+MMsEbNrCv/mmA+8en2dkqFlyZ25Vd7OxY9NXs4+b3S+ffVZfzLqzs5fPZyQMv46vZ2SoWXtrGroLjchr98foIVHx3gSoMLAZrc1++l9GSn3y3rVpUcZ2nRPsD6z8LfLktKKUtEhjuqxOV0CP96+yDmj+nLtOVbWkxoAmsZ32fXVZLXM52eGSnkdEuhR3qyZy2Y9BTrif29n01hfL6uHadUV9LErkKS0z2VulZJvdn5qw08+PLfPV87BLIzUuiemsTpi9fIzUplbF6PSIWqVMLQxK5C1j87vd3hizd1T2XlP43n/JUGzl9p4NsrDZy/2sCla03kZqUxa2QfXc1RqTDQxK5CtmhuYZvZpOnJTpbeM4ppumKjUhGniV2FrLnT09dMU6VUZGliV11iwYQ8TeRKxQgdx66UUnFGE7tSSsUZTexKKRVnNLErpVSc0cSulFJxRhO7UkrFmagsAiYitcDxiF8YegPnonDdjsRqXBC7sWlcgYnVuCB2Y4vFuAYZYzqc9ReVxB4tIlLamZXRIi1W44LYjU3jCkysxgWxG1usxtUZ2hSjlFJxRhO7UkrFmURL7K9EOwAfYjUuiN3YNK7AxGpcELuxxWpcHUqoNnallEoEifbErpRScS+uE7uIPCgi+0XELSI+e7dF5JiIVIjIHhEJ+2asAcQ1T0QOishhEVkc7rjsa/YSkWIRqbL/7OmjnMuurz0i8n4Y4/FbByKSKiJr7Pd3isjgcMUSYFwLRaTWq44eiVBcr4nIWRHZ5+N9EZEX7Lj3isitMRLXTBG54FVfv4pQXPkiskVEKu1/k0+2UyYqdRYSY0zcvoBRQCHwCTDRT7ljQO9YigtwAkeAAiAFKAdGRyC25cBi+3gx8Hsf5S5HIJYO6wB4AnjZPv4JsCZG4loIvBipe8rrutOBW4F9Pt6fD/wNEOB2YGeMxDUT+CAK9dUPuNU+zgQOtfN3GZU6C+UV10/sxpgvjTEHox1Ha52MaxJw2Bhz1BjTAPwfcF/4o+M+4E/28Z+ABRG4pi+dqQPveN8GZolIuPfbi9bfTYeMMVuB836K3Ae8YSwlQLaI9IuBuKLCGHPKGLPbPr4EfAm03lggKnUWirhO7AEwwMci8oWIPBrtYGx5wEmvr6tpe8OFQ64x5pR9fBrI9VEuTURKRaRERMKV/DtTB54yxpgm4AKQE6Z4AokL4AH7o/vbIpIf5pg6K1r3VWfcISLlIvI3Ebk50he3m/EmADtbvRXLddauG34HJRHZCPRt562lxpj3OnmaqcaYGhHpAxSLyAH7CSPacYWFv9i8vzDGGBHxNWxqkF1nBcBmEakwxhzp6lhvYOuA1caYehF5DOtTxZ1RjimW7ca6py6LyHygCBgeqYuLSHfgHeApY8zFSF03XG74xG6Mmd0F56ix/zwrIu9ifdQOKbF3QVw1gPdT3gD7eyHzF5uInBGRfsaYU/bHzbM+ztFcZ0dF5BOsJ52uTuydqYPmMtUikgT0AL7p4jgCjssY4x3DH7H6LmJB2O6rUHgnU2PMhyLykoj0NsaEfa0WEUnGSuqrjDFr2ykSk3XmT8I3xYhINxHJbD4G7gLa7bmPsF3AcBEZIiIpWB2DYRt94uV94CH7+CGgzacLEekpIqn2cW9gClAZhlg6Uwfe8f4Y2GzsHq8w6jCuVm2w92K13caC94F/s0d63A5c8Gp6ixoR6dvcNyIik7ByU7j/g8a+5qvAl8aYlT6KxWSd+RXt3ttwvoD7sdrD6oEzwAb7+/2BD+3jAqxRDeXAfqymkqjHZa73xh/CehIOe1z2NXOATUAVsBHoZX9/IvBH+3gyUGHXWQXw0zDG06YOgGeBe+3jNOAt4DDwOVAQoXrqKK7n7PupHNgCjIxQXKuBU0CjfY/9FHgceNx+X4A/2HFX4Ge0WITj+rlXfZUAkyMU11SsPra9wB77NT8W6iyUl848VUqpOJPwTTFKKRVvNLErpVSc0cSulFJxRhO7UkrFGU3sSikVZzSxK6VUnNHErpRScUYTu1JKxZn/B3CAJ5+wk1dnAAAAAElFTkSuQmCC\n", 196 | "text/plain": [ 197 | "
" 198 | ] 199 | }, 200 | "metadata": {}, 201 | "output_type": "display_data" 202 | } 203 | ], 204 | "source": [ 205 | "# Generate the range of data we'd like to predict\n", 206 | "test_times = np.expand_dims(np.linspace(times.min(),times.max(),200), 1)\n", 207 | "predictions = model.predict(test_times)\n", 208 | "\n", 209 | "plt.scatter(times, accel)\n", 210 | "for i, prediction in enumerate(predictions):\n", 211 | " plt.plot(test_times, prediction, label='{}th Quantile'.format(int(model.quantiles[i]*100)))\n", 212 | " \n", 213 | "plt.legend()\n", 214 | "plt.show()" 215 | ] 216 | }, 217 | { 218 | "cell_type": "code", 219 | "execution_count": 7, 220 | "metadata": {}, 221 | "outputs": [ 222 | { 223 | "data": { 224 | "text/plain": [ 225 | "(-0.39452338, -0.003931583, 0.45669487)" 226 | ] 227 | }, 228 | "execution_count": 7, 229 | "metadata": {}, 230 | "output_type": "execute_result" 231 | } 232 | ], 233 | "source": [ 234 | "predictions = model.predict(times)\n", 235 | "np.mean(predictions[0]), np.mean(predictions[1]), np.mean(predictions[2])" 236 | ] 237 | }, 238 | { 239 | "cell_type": "code", 240 | "execution_count": 8, 241 | "metadata": {}, 242 | "outputs": [ 243 | { 244 | "name": "stdout", 245 | "output_type": "stream", 246 | "text": [ 247 | "Percentage in the range (expecting 80%): 0.8297872340425532\n" 248 | ] 249 | } 250 | ], 251 | "source": [ 252 | "in_the_range = np.sum((accel >= predictions[0]) & (accel <= predictions[2]))\n", 253 | "print(\"Percentage in the range (expecting 80%):\", in_the_range / len(times))" 254 | ] 255 | }, 256 | { 257 | "cell_type": "code", 258 | "execution_count": 9, 259 | "metadata": {}, 260 | "outputs": [ 261 | { 262 | "name": "stdout", 263 | "output_type": "stream", 264 | "text": [ 265 | "Percentage out of the range (expecting 20%): 0.1702127659574468\n" 266 | ] 267 | } 268 | ], 269 | "source": [ 270 | "out_of_the_range = np.sum((accel < predictions[0]) | (accel > predictions[2]))\n", 271 | "print(\"Percentage out of the range (expecting 20%):\", out_of_the_range / len(times))" 272 | ] 273 | }, 274 | { 275 | "cell_type": "code", 276 | "execution_count": null, 277 | "metadata": {}, 278 | "outputs": [], 279 | "source": [] 280 | } 281 | ], 282 | "metadata": { 283 | "kernelspec": { 284 | "display_name": "Python 3", 285 | "language": "python", 286 | "name": "python3" 287 | }, 288 | "language_info": { 289 | "codemirror_mode": { 290 | "name": "ipython", 291 | "version": 3 292 | }, 293 | "file_extension": ".py", 294 | "mimetype": "text/x-python", 295 | "name": "python", 296 | "nbconvert_exporter": "python", 297 | "pygments_lexer": "ipython3", 298 | "version": "3.5.2" 299 | } 300 | }, 301 | "nbformat": 4, 302 | "nbformat_minor": 2 303 | } 304 | -------------------------------------------------------------------------------- /notebooks/00-original-dataset.py: -------------------------------------------------------------------------------- 1 | 2 | # coding: utf-8 3 | 4 | # In[1]: 5 | 6 | 7 | import tensorflow as tf 8 | import pandas as pd 9 | import numpy as np 10 | import matplotlib.pyplot as plt 11 | 12 | get_ipython().run_line_magic('matplotlib', 'inline') 13 | 14 | # Initialize session 15 | sess = tf.Session() 16 | 17 | 18 | # In[2]: 19 | 20 | 21 | # Load data 22 | mcycle = pd.read_csv('mcycle',delimiter='\t') 23 | mcycle.times = (mcycle.times - mcycle.times.mean())/mcycle.times.std() 24 | mcycle.accel = (mcycle.accel - mcycle.accel.mean())/mcycle.accel.std() 25 | 26 | # Reshape to input format for network 27 | times = np.expand_dims(mcycle.times.values, 1) 28 | accel = np.expand_dims(mcycle.accel.values, 1) 29 | times.shape, accel.shape 30 | 31 | 32 | # In[3]: 33 | 34 | 35 | # Create network 36 | class q_model: 37 | def __init__(self, 38 | sess, 39 | quantiles, 40 | in_shape=1, 41 | out_shape=1, 42 | batch_size=32): 43 | 44 | self.sess = sess 45 | 46 | self.quantiles = quantiles 47 | self.num_quantiles = len(quantiles) 48 | 49 | self.in_shape = in_shape 50 | self.out_shape = out_shape 51 | self.batch_size = batch_size 52 | 53 | self.outputs = [] 54 | self.losses = [] 55 | self.loss_history = [] 56 | self.optim = tf.train.AdamOptimizer() 57 | 58 | self.build_model() 59 | 60 | def build_model(self, scope='q_model', reuse=tf.AUTO_REUSE): 61 | with tf.variable_scope(scope, reuse=reuse) as scope: 62 | self.x = tf.placeholder(tf.float32, shape=(None, self.in_shape)) 63 | self.y = tf.placeholder(tf.float32, shape=(None, self.out_shape)) 64 | 65 | self.layer0 = tf.layers.dense(self.x, 66 | units=32, 67 | activation=tf.nn.relu) 68 | self.layer1 = tf.layers.dense(self.layer0, 69 | units=32, 70 | activation=tf.nn.relu) 71 | 72 | # Create outputs and losses for all quantiles 73 | for i in range(self.num_quantiles): 74 | q = self.quantiles[i] 75 | 76 | # Get output layers 77 | output = tf.layers.dense(self.layer1, 1, name="{}_q{}".format(i, int(q*100))) 78 | self.outputs.append(output) 79 | 80 | # Create losses 81 | error = tf.subtract(self.y, output) 82 | loss = tf.reduce_mean(tf.maximum(q*error, (q-1)*error), axis=-1) 83 | 84 | self.losses.append(loss) 85 | 86 | # Create combined loss 87 | self.combined_loss = tf.reduce_mean(tf.add_n(self.losses)) 88 | self.train_step = self.optim.minimize(self.combined_loss) 89 | 90 | def fit(self, x, y, epochs=100): 91 | for epoch in range(epochs): 92 | epoch_losses = [] 93 | for idx in range(0, x.shape[0], self.batch_size): 94 | batch_x = x[idx : min(idx + self.batch_size, x.shape[0]),:] 95 | batch_y = y[idx : min(idx + self.batch_size, y.shape[0]),:] 96 | 97 | feed_dict = {self.x: batch_x, 98 | self.y: batch_y} 99 | 100 | _, c_loss = self.sess.run([self.train_step, self.combined_loss], feed_dict) 101 | epoch_losses.append(c_loss) 102 | 103 | epoch_loss = np.mean(epoch_losses) 104 | self.loss_history.append(epoch_loss) 105 | if epoch % 100 == 0: 106 | print("Epoch {}: {}".format(epoch, epoch_loss)) 107 | 108 | def predict(self, x): 109 | # Run model to get outputs 110 | feed_dict = {self.x: x} 111 | predictions = sess.run(self.outputs, feed_dict) 112 | 113 | return predictions 114 | 115 | 116 | # In[4]: 117 | 118 | 119 | # Instantiate model 120 | quantiles = [.1, .5, .9] 121 | model = q_model(sess, quantiles, batch_size=32) 122 | 123 | # Initialize all variables 124 | init_op = tf.global_variables_initializer() 125 | sess.run(init_op) 126 | 127 | 128 | # In[5]: 129 | 130 | 131 | # Run training 132 | epochs = 2000 133 | model.fit(times, accel, epochs) 134 | 135 | 136 | # In[6]: 137 | 138 | 139 | # Generate the range of data we'd like to predict 140 | test_times = np.expand_dims(np.linspace(times.min(),times.max(),200), 1) 141 | predictions = model.predict(test_times) 142 | 143 | plt.scatter(times, accel) 144 | for i, prediction in enumerate(predictions): 145 | plt.plot(test_times, prediction, label='{}th Quantile'.format(int(model.quantiles[i]*100))) 146 | 147 | plt.legend() 148 | plt.show() 149 | 150 | 151 | # In[7]: 152 | 153 | 154 | predictions = model.predict(times) 155 | np.mean(predictions[0]), np.mean(predictions[1]), np.mean(predictions[2]) 156 | 157 | 158 | # In[8]: 159 | 160 | 161 | in_the_range = np.sum((accel >= predictions[0]) & (accel <= predictions[2])) 162 | print("Percentage in the range (expecting 80%):", in_the_range / len(times)) 163 | 164 | 165 | # In[9]: 166 | 167 | 168 | out_of_the_range = np.sum((accel < predictions[0]) | (accel > predictions[2])) 169 | print("Percentage out of the range (expecting 20%):", out_of_the_range / len(times)) 170 | 171 | -------------------------------------------------------------------------------- /notebooks/01-sklearn-example.py: -------------------------------------------------------------------------------- 1 | 2 | # coding: utf-8 3 | 4 | # Using the dataset and ploting code from [Prediction Intervals for Gradient Boosting Regression](http://scikit-learn.org/stable/auto_examples/ensemble/plot_gradient_boosting_quantile.html) 5 | 6 | # In[1]: 7 | 8 | 9 | import tensorflow as tf 10 | import pandas as pd 11 | import numpy as np 12 | import matplotlib.pyplot as plt 13 | 14 | get_ipython().run_line_magic('matplotlib', 'inline') 15 | np.random.seed(1) 16 | 17 | 18 | # ## Prepare Dataset 19 | 20 | # In[2]: 21 | 22 | 23 | def f(x): 24 | """The function to predict.""" 25 | return x * np.sin(x) 26 | 27 | #---------------------------------------------------------------------- 28 | # First the noiseless case 29 | X = np.atleast_2d(np.random.uniform(0, 10.0, size=100)).T 30 | X = X.astype(np.float32) 31 | 32 | # Observations 33 | y = f(X).ravel() 34 | 35 | dy = 1.5 + 1.0 * np.random.random(y.shape) 36 | noise = np.random.normal(0, dy) 37 | y += noise 38 | y = y.astype(np.float32) 39 | 40 | # Mesh the input space for evaluations of the real function, the prediction and 41 | # its MSE 42 | xx = np.atleast_2d(np.linspace(0, 10, 1000)).T 43 | xx = xx.astype(np.float32) 44 | 45 | X.shape, y.shape, xx.shape 46 | 47 | 48 | # ## Build the Model 49 | 50 | # In[3]: 51 | 52 | 53 | # Create network 54 | class q_model: 55 | def __init__(self, 56 | sess, 57 | quantiles, 58 | in_shape=1, 59 | out_shape=1, 60 | batch_size=32, 61 | dropout=0.5): 62 | 63 | self.sess = sess 64 | 65 | self.quantiles = quantiles 66 | self.num_quantiles = len(quantiles) 67 | 68 | self.in_shape = in_shape 69 | self.out_shape = out_shape 70 | self.batch_size = batch_size 71 | self.dropout = dropout 72 | 73 | self.outputs = [] 74 | self.losses = [] 75 | self.loss_history = [] 76 | self.optim = tf.train.AdamOptimizer() 77 | 78 | self.build_model() 79 | 80 | def build_model(self, scope='q_model', reuse=tf.AUTO_REUSE): 81 | with tf.variable_scope(scope, reuse=reuse) as scope: 82 | self.x = tf.placeholder(tf.float32, shape=(None, self.in_shape)) 83 | self.y = tf.placeholder(tf.float32, shape=(None, self.out_shape)) 84 | self.is_training = tf.placeholder(tf.bool) 85 | 86 | self.layer0 = tf.layers.dense(self.x, 87 | units=64, 88 | activation=tf.nn.relu) 89 | # self.layer0_dropped = tf.layers.dropout(tf.layers.batch_normalization( 90 | # self.layer0), self.dropout, training=self.is_training) 91 | self.layer0_dropped = tf.layers.dropout( 92 | self.layer0, self.dropout, training=self.is_training) 93 | self.layer1 = tf.layers.dense(self.layer0, 94 | units=64, 95 | activation=tf.nn.relu) 96 | # self.layer1_dropped = tf.layers.dropout(tf.layers.batch_normalization( 97 | # self.layer1), self.dropout, training=self.is_training) 98 | self.layer1_dropped = tf.layers.dropout( 99 | self.layer1, self.dropout, training=self.is_training) 100 | # Create outputs and losses for all quantiles 101 | for i in range(self.num_quantiles): 102 | q = self.quantiles[i] 103 | 104 | # Get output layers 105 | output = tf.layers.dense(self.layer1_dropped, 1, name="{}_q{}".format(i, int(q*100))) 106 | self.outputs.append(output) 107 | 108 | # Create losses 109 | error = tf.subtract(self.y, output) 110 | loss = tf.reduce_mean(tf.maximum(q*error, (q-1)*error), axis=-1) 111 | self.losses.append(loss) 112 | 113 | # Create combined loss with weight loss 114 | # self.combined_loss = tf.add( 115 | # tf.reduce_mean(tf.add_n(self.losses)), 116 | # 1e-4 * tf.reduce_sum(tf.stack( 117 | # [tf.nn.l2_loss(i) for i in tf.trainable_variables()] 118 | # )) 119 | # ) 120 | # Create combined loss 121 | self.combined_loss = tf.reduce_mean(tf.add_n(self.losses)) 122 | self.train_step = self.optim.minimize(self.combined_loss) 123 | 124 | def fit(self, x, y, epochs=100): 125 | for epoch in range(epochs): 126 | epoch_losses = [] 127 | shuffle_idx = np.arange(x.shape[0]) 128 | np.random.shuffle(shuffle_idx) 129 | x = x[shuffle_idx] 130 | y = y[shuffle_idx] 131 | for idx in range(0, x.shape[0], self.batch_size): 132 | batch_x = x[idx : min(idx + self.batch_size, x.shape[0]),:] 133 | batch_y = y[idx : min(idx + self.batch_size, y.shape[0]),:] 134 | 135 | feed_dict = { 136 | self.x: batch_x, 137 | self.y: batch_y, 138 | self.is_training: True 139 | } 140 | 141 | _, c_loss = self.sess.run([self.train_step, self.combined_loss], feed_dict) 142 | epoch_losses.append(c_loss) 143 | 144 | epoch_loss = np.mean(epoch_losses) 145 | self.loss_history.append(epoch_loss) 146 | if epoch % 500 == 0: 147 | print("Epoch {}: {}".format(epoch, epoch_loss)) 148 | 149 | def predict(self, x, is_training=False): 150 | # Run model to get outputs 151 | feed_dict = {self.x: x, self.is_training: is_training} 152 | predictions = sess.run(self.outputs, feed_dict) 153 | 154 | return [x[:, 0] for x in predictions] 155 | 156 | 157 | # In[4]: 158 | 159 | 160 | # Initialize session 161 | sess = tf.Session() 162 | 163 | # Instantiate model 164 | quantiles = [.05, .5, .95] 165 | model = q_model(sess, quantiles, batch_size=10, dropout=0.25) 166 | 167 | # Initialize all variables 168 | init_op = tf.global_variables_initializer() 169 | sess.run(init_op) 170 | 171 | 172 | # ## Train the Model 173 | 174 | # In[5]: 175 | 176 | 177 | # Run training 178 | epochs = 10000 179 | model.fit(X, y[:, np.newaxis], epochs) 180 | 181 | 182 | # In[6]: 183 | 184 | 185 | # Make the prediction on the meshed x-axis 186 | y_lower, y_pred, y_upper = model.predict(xx) 187 | 188 | # Plot the function, the prediction and the 90% confidence interval based on 189 | # the MSE 190 | fig = plt.figure() 191 | plt.plot(xx, f(xx), 'g:', label=u'$f(x) = x\,\sin(x)$') 192 | plt.plot(X, y, 'b.', markersize=10, label=u'Observations') 193 | plt.plot(xx, y_pred, 'r-', label=u'Prediction') 194 | plt.plot(xx, y_upper, 'k-') 195 | plt.plot(xx, y_lower, 'k-') 196 | plt.fill(np.concatenate([xx, xx[::-1]]), 197 | np.concatenate([y_upper, y_lower[::-1]]), 198 | alpha=.5, fc='b', ec='None', label='90% prediction interval') 199 | plt.xlabel('$x$') 200 | plt.ylabel('$f(x)$') 201 | plt.ylim(-10, 20) 202 | plt.legend(loc='upper left') 203 | plt.show() 204 | 205 | 206 | # In[7]: 207 | 208 | 209 | predictions = model.predict(X) 210 | np.mean(predictions[0]), np.mean(predictions[1]), np.mean(predictions[2]) 211 | 212 | 213 | # In[8]: 214 | 215 | 216 | in_the_range = np.sum((y >= predictions[0]) & (y <= predictions[2])) 217 | print("Percentage in the range (expecting 90%):", in_the_range / len(y) * 100) 218 | 219 | 220 | # In[9]: 221 | 222 | 223 | out_of_the_range = np.sum((y < predictions[0]) | (y > predictions[2])) 224 | print("Percentage out of the range (expecting 10%):", out_of_the_range / len(y) * 100) 225 | 226 | 227 | # In[10]: 228 | 229 | 230 | predictions[0].shape 231 | 232 | 233 | # ## MC Prediction 234 | 235 | # In[11]: 236 | 237 | 238 | K = 5000 239 | tmp = np.zeros((K, xx.shape[0])).astype("float32") 240 | for k in range(K): 241 | _, preds, _ = model.predict(xx, is_training=True) 242 | tmp[k] = preds 243 | y_lower, y_pred, y_upper = np.percentile(tmp, (5, 50, 95), axis=0) 244 | 245 | 246 | # In[12]: 247 | 248 | 249 | y_lower[1], y_pred[1], y_upper[1] 250 | 251 | 252 | # In[18]: 253 | 254 | 255 | # Plot the function, the prediction and the 90% confidence interval based on 256 | # the MSE 257 | fig = plt.figure() 258 | plt.plot(xx, f(xx), 'g:', label=u'$f(x) = x\,\sin(x)$') 259 | plt.plot(X, y, 'b.', markersize=10, label=u'Observations') 260 | plt.plot(xx, y_pred, 'r-', label=u'Prediction') 261 | plt.plot(xx, y_upper, 'k-') 262 | plt.plot(xx, y_lower, 'k-') 263 | plt.fill(np.concatenate([xx, xx[::-1]]), 264 | np.concatenate([y_upper, y_lower[::-1]]), 265 | alpha=.5, fc='b', ec='None', label='90% credible interval') 266 | plt.xlabel('$x$') 267 | plt.ylabel('$f(x)$') 268 | plt.ylim(-10, 20) 269 | plt.legend(loc='upper left') 270 | plt.show() 271 | 272 | 273 | # In[19]: 274 | 275 | 276 | tmp = np.zeros((K, X.shape[0])).astype("float32") 277 | for k in range(K): 278 | _, preds, _ = model.predict(X, is_training=True) 279 | tmp[k] = preds 280 | predictions = np.percentile(tmp, (5, 50, 95), axis=0) 281 | np.mean(predictions[0]), np.mean(predictions[1]), np.mean(predictions[2]) 282 | 283 | 284 | # In[23]: 285 | 286 | 287 | in_the_range = np.sum((y >= predictions[0]) & (y <= predictions[2])) 288 | print("Percentage in the range:", in_the_range / len(y) * 100) 289 | 290 | 291 | # In[24]: 292 | 293 | 294 | out_of_the_range = np.sum((y < predictions[0]) | (y > predictions[2])) 295 | print("Percentage out of the range:", out_of_the_range / len(y) * 100) 296 | 297 | -------------------------------------------------------------------------------- /notebooks/02-sklearn-example-lightgbm.py: -------------------------------------------------------------------------------- 1 | 2 | # coding: utf-8 3 | 4 | # Based on the code from this [Github issue](https://github.com/Microsoft/LightGBM/issues/1182). 5 | 6 | # In[1]: 7 | 8 | 9 | import numpy as np 10 | import matplotlib.pyplot as plt 11 | import lightgbm as lgb 12 | from sklearn.ensemble import GradientBoostingRegressor 13 | 14 | np.random.seed(1) 15 | 16 | 17 | # ## Prepare Dataset 18 | 19 | # In[2]: 20 | 21 | 22 | def f(x): 23 | """The function to predict.""" 24 | return x * np.sin(x) 25 | 26 | #---------------------------------------------------------------------- 27 | # First the noiseless case 28 | X = np.atleast_2d(np.random.uniform(0, 10.0, size=100)).T 29 | X = X.astype(np.float32) 30 | 31 | # Observations 32 | y = f(X).ravel() 33 | 34 | dy = 1.5 + 1.0 * np.random.random(y.shape) 35 | noise = np.random.normal(0, dy) 36 | y += noise 37 | y = y.astype(np.float32) 38 | 39 | # Mesh the input space for evaluations of the real function, the prediction and 40 | # its MSE 41 | xx = np.atleast_2d(np.linspace(0, 10, 1000)).T 42 | xx = xx.astype(np.float32) 43 | 44 | X.shape, y.shape, xx.shape 45 | 46 | 47 | # ## Shared Hyper-Parameters 48 | 49 | # In[3]: 50 | 51 | 52 | # model parameters 53 | LEARNING_RATE = 0.1 54 | N_ESTIMATORS = 500 55 | MAX_DEPTH = 6 56 | NUM_LEAVES = 128 # lgbm only 57 | OBJECTIVE = 'quantile' # lgbm only, 'quantile' or 'quantile_l2' 58 | REG_SQRT = True # lgbm only 59 | 60 | ALPHA = 0.95 61 | 62 | 63 | # ## Plot Function 64 | 65 | # In[4]: 66 | 67 | 68 | def plot(y_pred, y_upper, y_lower, frac_above_upper, frac_below_lower): 69 | plt.figure(figsize=(10, 7), dpi=100) 70 | plt.plot(xx, f(xx), 'g:', label=u'$f(x) = x\,\sin(x)$') 71 | plt.plot(X, y, 'b.', markersize=5, label=u'Observations') 72 | plt.plot(xx, y_pred, 'r-', label=u'Mean Prediction') 73 | plt.plot(xx, y_upper, 'k-') 74 | plt.plot(xx, y_lower, 'k-') 75 | plt.fill(np.concatenate([xx, xx[::-1]]), 76 | np.concatenate([y_upper, y_lower[::-1]]), 77 | alpha=0.5, fc='b', ec='None', label=(str(round(100*(ALPHA-0.5)*2))+'% prediction interval')) 78 | plt.scatter(x=X[y_autohigh < y], y=y[y_autohigh < y], s=50, marker='x', c = 'red', 79 | label = str(round(100*frac_above_upper,1))+'% of training data above upper (expect '+str(round(100*(1-ALPHA),1))+'%)') 80 | plt.scatter(x=X[y_autolow > y], y=y[y_autolow > y], s=50, marker='x', c = 'orange', 81 | label = str(round(100*frac_below_lower,1))+ '% of training data below lower (expect '+str(round(100*(1-ALPHA),1))+'%)') 82 | plt.xlabel('$x$') 83 | plt.ylabel('$f(x)$') 84 | plt.ylim(-10, 20) 85 | plt.legend(loc='upper left') 86 | plt.title( ' Alpha: '+str(ALPHA) + 87 | ' N_est: '+str(N_ESTIMATORS) + 88 | ' L_rate: '+str(LEARNING_RATE) + 89 | ' N_Leaf: '+str(NUM_LEAVES) + 90 | ' Obj: '+str(OBJECTIVE) + 91 | ' R_sqrt: '+str(int(REG_SQRT)) 92 | ) 93 | plt.show() 94 | 95 | 96 | # ## Scikit-learn Model 97 | 98 | # In[5]: 99 | 100 | 101 | if MAX_DEPTH < 0: # sklearn grows differently than lgbm. 102 | print('Max Depth specified is incompatible with sklearn. Changing to 3.') 103 | SK_MAX_DEPTH = 5 104 | else: 105 | SK_MAX_DEPTH = MAX_DEPTH - 1 106 | 107 | 108 | # ### Upper Bound 109 | 110 | # In[6]: 111 | 112 | 113 | clfh = GradientBoostingRegressor(loss='quantile', alpha=ALPHA, 114 | n_estimators=N_ESTIMATORS, max_depth=SK_MAX_DEPTH, 115 | learning_rate=LEARNING_RATE, min_samples_leaf=9, 116 | min_samples_split=9) 117 | 118 | clfh.fit(X, y) 119 | 120 | 121 | # ### Lower Bound 122 | 123 | # In[7]: 124 | 125 | 126 | clfl = GradientBoostingRegressor(loss='quantile', alpha=1.0-ALPHA, 127 | n_estimators=N_ESTIMATORS, max_depth=SK_MAX_DEPTH, 128 | learning_rate=LEARNING_RATE, min_samples_leaf=9, 129 | min_samples_split=9) 130 | clfl.fit(X, y) 131 | 132 | 133 | # ### Median 134 | 135 | # In[8]: 136 | 137 | 138 | clf = GradientBoostingRegressor(loss='lad', 139 | n_estimators=N_ESTIMATORS, max_depth=SK_MAX_DEPTH, 140 | learning_rate=LEARNING_RATE, min_samples_leaf=9, 141 | min_samples_split=9) 142 | clf.fit(X, y) 143 | 144 | 145 | # ### Prediction 146 | 147 | # In[9]: 148 | 149 | 150 | # Make the prediction on the meshed x-axis 151 | y_pred = clf.predict(xx) 152 | y_lower = clfl.predict(xx) 153 | y_upper = clfh.predict(xx) 154 | 155 | # Check calibration by predicting the training data. 156 | y_autopred = clf.predict(X) 157 | y_autolow = clfl.predict(X) 158 | y_autohigh = clfh.predict(X) 159 | frac_below_upper = round(np.count_nonzero(y_autohigh > y) / len(y),3) 160 | frac_above_upper = round(np.count_nonzero(y_autohigh < y) / len(y),3) 161 | frac_above_lower = round(np.count_nonzero(y_autolow < y) / len(y),3) 162 | frac_below_lower = round(np.count_nonzero(y_autolow > y) / len(y),3) 163 | 164 | 165 | # ### Plot 166 | 167 | # In[10]: 168 | 169 | 170 | plot(y_pred, y_upper, y_lower, frac_above_upper, frac_below_lower) 171 | 172 | 173 | # ## LightGBM Model 174 | 175 | # ### Upper Bound 176 | 177 | # In[11]: 178 | 179 | 180 | clfh = lgb.LGBMRegressor(objective = OBJECTIVE, 181 | alpha = ALPHA, 182 | num_leaves = NUM_LEAVES, 183 | learning_rate = LEARNING_RATE, 184 | n_estimators = N_ESTIMATORS, 185 | min_data_in_leaf=5, 186 | reg_sqrt = REG_SQRT, 187 | max_depth = MAX_DEPTH) 188 | clfh.fit(X, y) 189 | 190 | 191 | # ### Lower Bound 192 | 193 | # In[12]: 194 | 195 | 196 | clfl = lgb.LGBMRegressor(objective = OBJECTIVE, 197 | alpha = 1 - ALPHA, 198 | num_leaves = NUM_LEAVES, 199 | learning_rate = LEARNING_RATE, 200 | n_estimators = N_ESTIMATORS, 201 | min_data_in_leaf=5, 202 | reg_sqrt = REG_SQRT, 203 | max_depth = MAX_DEPTH) 204 | clfl.fit(X, y) 205 | 206 | 207 | # ### Median 208 | 209 | # In[13]: 210 | 211 | 212 | clf = lgb.LGBMRegressor(objective = "regression_l1", 213 | num_leaves = NUM_LEAVES, 214 | learning_rate = LEARNING_RATE, 215 | n_estimators = N_ESTIMATORS, 216 | min_data_in_leaf=5, 217 | reg_sqrt = REG_SQRT, 218 | max_depth = MAX_DEPTH) 219 | clf.fit(X, y) 220 | 221 | 222 | # ### Prediction 223 | 224 | # In[14]: 225 | 226 | 227 | # Make the prediction on the meshed x-axis 228 | y_pred = clf.predict(xx) 229 | y_lower = clfl.predict(xx) 230 | y_upper = clfh.predict(xx) 231 | 232 | # Check calibration by predicting the training data. 233 | y_autopred = clf.predict(X) 234 | y_autolow = clfl.predict(X) 235 | y_autohigh = clfh.predict(X) 236 | frac_below_upper = round(np.count_nonzero(y_autohigh > y) / len(y),3) 237 | frac_above_upper = round(np.count_nonzero(y_autohigh < y) / len(y),3) 238 | frac_above_lower = round(np.count_nonzero(y_autolow < y) / len(y),3) 239 | frac_below_lower = round(np.count_nonzero(y_autolow > y) / len(y),3) 240 | 241 | 242 | # ### Plot 243 | 244 | # In[15]: 245 | 246 | 247 | plot(y_pred, y_upper, y_lower, frac_above_upper, frac_below_lower) 248 | 249 | -------------------------------------------------------------------------------- /notebooks/03-sklearn-example-pytorch.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "Using the dataset and ploting code from [Prediction Intervals for Gradient Boosting Regression](http://scikit-learn.org/stable/auto_examples/ensemble/plot_gradient_boosting_quantile.html)" 8 | ] 9 | }, 10 | { 11 | "cell_type": "code", 12 | "execution_count": 1, 13 | "metadata": {}, 14 | "outputs": [], 15 | "source": [ 16 | "from functools import partial\n", 17 | "from itertools import chain\n", 18 | "\n", 19 | "import pandas as pd\n", 20 | "import numpy as np\n", 21 | "import matplotlib.pyplot as plt\n", 22 | "import torch\n", 23 | "import torch.nn as nn\n", 24 | "\n", 25 | "%matplotlib inline\n", 26 | "np.random.seed(1)" 27 | ] 28 | }, 29 | { 30 | "cell_type": "markdown", 31 | "metadata": {}, 32 | "source": [ 33 | "## Prepare Dataset" 34 | ] 35 | }, 36 | { 37 | "cell_type": "code", 38 | "execution_count": 2, 39 | "metadata": {}, 40 | "outputs": [ 41 | { 42 | "data": { 43 | "text/plain": [ 44 | "((100, 1), (100,), (1000, 1))" 45 | ] 46 | }, 47 | "execution_count": 2, 48 | "metadata": {}, 49 | "output_type": "execute_result" 50 | } 51 | ], 52 | "source": [ 53 | "def f(x):\n", 54 | " \"\"\"The function to predict.\"\"\"\n", 55 | " return x * np.sin(x)\n", 56 | "\n", 57 | "#----------------------------------------------------------------------\n", 58 | "# First the noiseless case\n", 59 | "X = np.atleast_2d(np.random.uniform(0, 10.0, size=100)).T\n", 60 | "X = X.astype(np.float32)\n", 61 | "\n", 62 | "# Observations\n", 63 | "y = f(X).ravel()\n", 64 | "\n", 65 | "dy = 1.5 + 1.0 * np.random.random(y.shape)\n", 66 | "noise = np.random.normal(0, dy)\n", 67 | "y += noise\n", 68 | "y = y.astype(np.float32)\n", 69 | "\n", 70 | "# Mesh the input space for evaluations of the real function, the prediction and\n", 71 | "# its MSE\n", 72 | "xx = np.atleast_2d(np.linspace(0, 10, 1000)).T\n", 73 | "xx = xx.astype(np.float32)\n", 74 | "\n", 75 | "X.shape, y.shape, xx.shape" 76 | ] 77 | }, 78 | { 79 | "cell_type": "markdown", 80 | "metadata": {}, 81 | "source": [ 82 | "## Build the Model" 83 | ] 84 | }, 85 | { 86 | "cell_type": "code", 87 | "execution_count": 3, 88 | "metadata": {}, 89 | "outputs": [], 90 | "source": [ 91 | "class q_model(nn.Module):\n", 92 | " def __init__(self, \n", 93 | " quantiles, \n", 94 | " in_shape=1, \n", 95 | " dropout=0.5): \n", 96 | " super().__init__()\n", 97 | " self.quantiles = quantiles\n", 98 | " self.num_quantiles = len(quantiles)\n", 99 | " \n", 100 | " self.in_shape = in_shape\n", 101 | " self.out_shape = len(quantiles)\n", 102 | " self.dropout = dropout\n", 103 | " self.build_model()\n", 104 | " self.init_weights()\n", 105 | " \n", 106 | " def build_model(self): \n", 107 | " self.base_model = nn.Sequential(\n", 108 | " nn.Linear(self.in_shape, 64),\n", 109 | " nn.ReLU(),\n", 110 | " # nn.BatchNorm1d(64),\n", 111 | " nn.Dropout(self.dropout),\n", 112 | " nn.Linear(64, 64),\n", 113 | " nn.ReLU(),\n", 114 | " # nn.BatchNorm1d(64),\n", 115 | " nn.Dropout(self.dropout),\n", 116 | " )\n", 117 | " final_layers = [\n", 118 | " nn.Linear(64, 1) for _ in range(len(self.quantiles))\n", 119 | " ]\n", 120 | " self.final_layers = nn.ModuleList(final_layers)\n", 121 | " \n", 122 | " def init_weights(self):\n", 123 | " for m in chain(self.base_model, self.final_layers):\n", 124 | " if isinstance(m, nn.Linear):\n", 125 | " nn.init.orthogonal_(m.weight)\n", 126 | " nn.init.constant_(m.bias, 0) \n", 127 | " \n", 128 | " def forward(self, x):\n", 129 | " tmp_ = self.base_model(x)\n", 130 | " return torch.cat([layer(tmp_) for layer in self.final_layers], dim=1)" 131 | ] 132 | }, 133 | { 134 | "cell_type": "code", 135 | "execution_count": 4, 136 | "metadata": {}, 137 | "outputs": [], 138 | "source": [ 139 | "class q_model_simplified(nn.Module):\n", 140 | " def __init__(self, \n", 141 | " quantiles, \n", 142 | " in_shape=1, \n", 143 | " dropout=0.5): \n", 144 | " super().__init__()\n", 145 | " self.quantiles = quantiles\n", 146 | " self.num_quantiles = len(quantiles)\n", 147 | " \n", 148 | " self.in_shape = in_shape\n", 149 | " self.out_shape = len(quantiles)\n", 150 | " self.dropout = dropout\n", 151 | " self.build_model()\n", 152 | " self.init_weights()\n", 153 | " \n", 154 | " def build_model(self): \n", 155 | " self.model = nn.Sequential(\n", 156 | " nn.Linear(self.in_shape, 64),\n", 157 | " nn.ReLU(),\n", 158 | " # nn.BatchNorm1d(64),\n", 159 | " nn.Dropout(self.dropout),\n", 160 | " nn.Linear(64, 128),\n", 161 | " nn.ReLU(),\n", 162 | " # nn.BatchNorm1d(128),\n", 163 | " nn.Dropout(self.dropout),\n", 164 | " nn.Linear(128, self.out_shape)\n", 165 | " )\n", 166 | " \n", 167 | " def init_weights(self):\n", 168 | " for m in self.model:\n", 169 | " if isinstance(m, nn.Linear):\n", 170 | " nn.init.orthogonal_(m.weight)\n", 171 | " nn.init.constant_(m.bias, 0) \n", 172 | " \n", 173 | " def forward(self, x):\n", 174 | " return self.model(x)" 175 | ] 176 | }, 177 | { 178 | "cell_type": "code", 179 | "execution_count": 5, 180 | "metadata": {}, 181 | "outputs": [], 182 | "source": [ 183 | "class QuantileLoss(nn.Module):\n", 184 | " def __init__(self, quantiles):\n", 185 | " super().__init__()\n", 186 | " self.quantiles = quantiles\n", 187 | " \n", 188 | " def forward(self, preds, target):\n", 189 | " assert not target.requires_grad\n", 190 | " assert preds.size(0) == target.size(0)\n", 191 | " losses = []\n", 192 | " for i, q in enumerate(quantiles):\n", 193 | " errors = target - preds[:, i]\n", 194 | " losses.append(torch.max((q-1) * errors, q * errors).unsqueeze(1))\n", 195 | " loss = torch.mean(torch.sum(torch.cat(losses, dim=1), dim=1))\n", 196 | " return loss" 197 | ] 198 | }, 199 | { 200 | "cell_type": "code", 201 | "execution_count": 6, 202 | "metadata": {}, 203 | "outputs": [], 204 | "source": [ 205 | "class Learner:\n", 206 | " def __init__(self, model, optimizer_class, loss_func, device='cpu'):\n", 207 | " self.model = model.to(device)\n", 208 | " self.optimizer = optimizer_class(self.model.parameters())\n", 209 | " self.loss_func = loss_func.to(device)\n", 210 | " self.device = device\n", 211 | " self.loss_history = []\n", 212 | " \n", 213 | " def fit(self, x, y, epochs, batch_size):\n", 214 | " self.model.train()\n", 215 | " for e in range(epochs):\n", 216 | " shuffle_idx = np.arange(x.shape[0])\n", 217 | " np.random.shuffle(shuffle_idx)\n", 218 | " x = x[shuffle_idx]\n", 219 | " y = y[shuffle_idx]\n", 220 | " epoch_losses = []\n", 221 | " for idx in range(0, x.shape[0], batch_size):\n", 222 | " self.optimizer.zero_grad()\n", 223 | " batch_x = torch.from_numpy(\n", 224 | " x[idx : min(idx + batch_size, x.shape[0]),:]\n", 225 | " ).float().to(self.device).requires_grad_(False)\n", 226 | " batch_y = torch.from_numpy(\n", 227 | " y[idx : min(idx + batch_size, y.shape[0])]\n", 228 | " ).float().to(self.device).requires_grad_(False)\n", 229 | " preds = self.model(batch_x)\n", 230 | " loss = loss_func(preds, batch_y)\n", 231 | " loss.backward()\n", 232 | " self.optimizer.step()\n", 233 | " epoch_losses.append(loss.cpu().detach().numpy()) \n", 234 | " epoch_loss = np.mean(epoch_losses)\n", 235 | " self.loss_history.append(epoch_loss)\n", 236 | " if (e+1) % 500 == 0:\n", 237 | " print(\"Epoch {}: {}\".format(e+1, epoch_loss))\n", 238 | " \n", 239 | " def predict(self, x, mc=False):\n", 240 | " if mc:\n", 241 | " self.model.train()\n", 242 | " else:\n", 243 | " self.model.eval()\n", 244 | " return self.model(torch.from_numpy(x).to(self.device).requires_grad_(False)).cpu().detach().numpy()" 245 | ] 246 | }, 247 | { 248 | "cell_type": "code", 249 | "execution_count": 7, 250 | "metadata": {}, 251 | "outputs": [], 252 | "source": [ 253 | "# Instantiate model\n", 254 | "quantiles = [.05, .5, .95]\n", 255 | "model = q_model(quantiles, dropout=0.1)\n", 256 | "loss_func = QuantileLoss(quantiles)\n", 257 | "learner = Learner(model, partial(torch.optim.Adam, weight_decay=1e-6), loss_func, device=\"cuda:0\")" 258 | ] 259 | }, 260 | { 261 | "cell_type": "markdown", 262 | "metadata": {}, 263 | "source": [ 264 | "## Train the Model" 265 | ] 266 | }, 267 | { 268 | "cell_type": "code", 269 | "execution_count": 8, 270 | "metadata": {}, 271 | "outputs": [ 272 | { 273 | "name": "stdout", 274 | "output_type": "stream", 275 | "text": [ 276 | "Epoch 500: 1.6863571405410767\n", 277 | "Epoch 1000: 1.5092973709106445\n", 278 | "Epoch 1500: 1.3402037620544434\n", 279 | "Epoch 2000: 1.2709928750991821\n", 280 | "Epoch 2500: 1.2140167951583862\n", 281 | "Epoch 3000: 1.1602728366851807\n", 282 | "Epoch 3500: 1.165322184562683\n", 283 | "Epoch 4000: 1.173967719078064\n", 284 | "Epoch 4500: 1.190496802330017\n", 285 | "Epoch 5000: 1.2514715194702148\n", 286 | "Epoch 5500: 1.078052043914795\n", 287 | "Epoch 6000: 1.1251020431518555\n", 288 | "Epoch 6500: 1.2558255195617676\n", 289 | "Epoch 7000: 1.1765114068984985\n", 290 | "Epoch 7500: 1.1623271703720093\n", 291 | "Epoch 8000: 1.1238428354263306\n", 292 | "Epoch 8500: 1.104461431503296\n", 293 | "Epoch 9000: 1.14342200756073\n", 294 | "Epoch 9500: 1.145739197731018\n", 295 | "Epoch 10000: 1.1996923685073853\n" 296 | ] 297 | } 298 | ], 299 | "source": [ 300 | "# Run training\n", 301 | "epochs = 10000\n", 302 | "learner.fit(X, y, epochs, batch_size=10)" 303 | ] 304 | }, 305 | { 306 | "cell_type": "code", 307 | "execution_count": 9, 308 | "metadata": {}, 309 | "outputs": [ 310 | { 311 | "data": { 312 | "image/png": "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\n", 313 | "text/plain": [ 314 | "
" 315 | ] 316 | }, 317 | "metadata": {}, 318 | "output_type": "display_data" 319 | } 320 | ], 321 | "source": [ 322 | "# Make the prediction on the meshed x-axis\n", 323 | "tmp = learner.predict(xx)\n", 324 | "y_lower, y_pred, y_upper = tmp[:, 0], tmp[:, 1], tmp[:, 2]\n", 325 | "\n", 326 | "# Plot the function, the prediction and the 90% confidence interval based on\n", 327 | "# the MSE\n", 328 | "fig = plt.figure()\n", 329 | "plt.plot(xx, f(xx), 'g:', label=u'$f(x) = x\\,\\sin(x)$')\n", 330 | "plt.plot(X, y, 'b.', markersize=10, label=u'Observations')\n", 331 | "plt.plot(xx, y_pred, 'r-', label=u'Prediction')\n", 332 | "plt.plot(xx, y_upper, 'k-')\n", 333 | "plt.plot(xx, y_lower, 'k-')\n", 334 | "plt.fill(np.concatenate([xx, xx[::-1]]),\n", 335 | " np.concatenate([y_upper, y_lower[::-1]]),\n", 336 | " alpha=.5, fc='b', ec='None', label='90% prediction interval')\n", 337 | "plt.xlabel('$x$')\n", 338 | "plt.ylabel('$f(x)$')\n", 339 | "plt.ylim(-10, 20)\n", 340 | "plt.legend(loc='upper left')\n", 341 | "plt.show()" 342 | ] 343 | }, 344 | { 345 | "cell_type": "code", 346 | "execution_count": 10, 347 | "metadata": {}, 348 | "outputs": [ 349 | { 350 | "data": { 351 | "text/plain": [ 352 | "(-1.7790313, 0.6894081, 3.9784248)" 353 | ] 354 | }, 355 | "execution_count": 10, 356 | "metadata": {}, 357 | "output_type": "execute_result" 358 | } 359 | ], 360 | "source": [ 361 | "predictions = learner.predict(X)\n", 362 | "np.mean(predictions[:, 0]), np.mean(predictions[:, 1]), np.mean(predictions[:, 2])" 363 | ] 364 | }, 365 | { 366 | "cell_type": "code", 367 | "execution_count": 11, 368 | "metadata": {}, 369 | "outputs": [ 370 | { 371 | "name": "stdout", 372 | "output_type": "stream", 373 | "text": [ 374 | "Percentage in the range (expecting 90%): 93.0\n" 375 | ] 376 | } 377 | ], 378 | "source": [ 379 | "in_the_range = np.sum((y >= predictions[:, 0]) & (y <= predictions[:, 2]))\n", 380 | "print(\"Percentage in the range (expecting 90%):\", in_the_range / len(y) * 100)" 381 | ] 382 | }, 383 | { 384 | "cell_type": "code", 385 | "execution_count": 12, 386 | "metadata": {}, 387 | "outputs": [ 388 | { 389 | "name": "stdout", 390 | "output_type": "stream", 391 | "text": [ 392 | "Percentage out of the range (expecting 10%): 7.000000000000001\n" 393 | ] 394 | } 395 | ], 396 | "source": [ 397 | "out_of_the_range = np.sum((y < predictions[:, 0]) | (y > predictions[:, 2]))\n", 398 | "print(\"Percentage out of the range (expecting 10%):\", out_of_the_range / len(y) * 100)" 399 | ] 400 | }, 401 | { 402 | "cell_type": "markdown", 403 | "metadata": {}, 404 | "source": [ 405 | "## MC Prediction" 406 | ] 407 | }, 408 | { 409 | "cell_type": "code", 410 | "execution_count": 13, 411 | "metadata": {}, 412 | "outputs": [], 413 | "source": [ 414 | "K = 5000\n", 415 | "tmp = np.zeros((K, xx.shape[0])).astype(\"float32\")\n", 416 | "for k in range(K):\n", 417 | " preds = learner.predict(xx, mc=True)\n", 418 | " tmp[k] = preds[:, 1]\n", 419 | "y_lower, y_pred, y_upper = np.percentile(tmp, (5, 50, 95), axis=0) " 420 | ] 421 | }, 422 | { 423 | "cell_type": "code", 424 | "execution_count": 14, 425 | "metadata": {}, 426 | "outputs": [ 427 | { 428 | "data": { 429 | "text/plain": [ 430 | "(-0.5182905226945876, 0.18976250290870667, 0.3624141395092011)" 431 | ] 432 | }, 433 | "execution_count": 14, 434 | "metadata": {}, 435 | "output_type": "execute_result" 436 | } 437 | ], 438 | "source": [ 439 | "y_lower[1], y_pred[1], y_upper[1]" 440 | ] 441 | }, 442 | { 443 | "cell_type": "code", 444 | "execution_count": 15, 445 | "metadata": {}, 446 | "outputs": [ 447 | { 448 | "data": { 449 | "image/png": "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\n", 450 | "text/plain": [ 451 | "
" 452 | ] 453 | }, 454 | "metadata": {}, 455 | "output_type": "display_data" 456 | } 457 | ], 458 | "source": [ 459 | "# Plot the function, the prediction and the 90% confidence interval based on\n", 460 | "# the MSE\n", 461 | "fig = plt.figure()\n", 462 | "plt.plot(xx, f(xx), 'g:', label=u'$f(x) = x\\,\\sin(x)$')\n", 463 | "plt.plot(X, y, 'b.', markersize=10, label=u'Observations')\n", 464 | "plt.plot(xx, y_pred, 'r-', label=u'Prediction')\n", 465 | "plt.plot(xx, y_upper, 'k-')\n", 466 | "plt.plot(xx, y_lower, 'k-')\n", 467 | "plt.fill(np.concatenate([xx, xx[::-1]]),\n", 468 | " np.concatenate([y_upper, y_lower[::-1]]),\n", 469 | " alpha=.5, fc='b', ec='None', label='90% credible interval')\n", 470 | "plt.xlabel('$x$')\n", 471 | "plt.ylabel('$f(x)$')\n", 472 | "plt.ylim(-10, 20)\n", 473 | "plt.legend(loc='upper left')\n", 474 | "plt.show()" 475 | ] 476 | }, 477 | { 478 | "cell_type": "code", 479 | "execution_count": null, 480 | "metadata": {}, 481 | "outputs": [], 482 | "source": [] 483 | } 484 | ], 485 | "metadata": { 486 | "kernelspec": { 487 | "display_name": "Python 3", 488 | "language": "python", 489 | "name": "python3" 490 | }, 491 | "language_info": { 492 | "codemirror_mode": { 493 | "name": "ipython", 494 | "version": 3 495 | }, 496 | "file_extension": ".py", 497 | "mimetype": "text/x-python", 498 | "name": "python", 499 | "nbconvert_exporter": "python", 500 | "pygments_lexer": "ipython3", 501 | "version": "3.5.2" 502 | } 503 | }, 504 | "nbformat": 4, 505 | "nbformat_minor": 2 506 | } 507 | -------------------------------------------------------------------------------- /notebooks/03-sklearn-example-pytorch.py: -------------------------------------------------------------------------------- 1 | 2 | # coding: utf-8 3 | 4 | # Using the dataset and ploting code from [Prediction Intervals for Gradient Boosting Regression](http://scikit-learn.org/stable/auto_examples/ensemble/plot_gradient_boosting_quantile.html) 5 | 6 | # In[1]: 7 | 8 | 9 | from functools import partial 10 | from itertools import chain 11 | 12 | import pandas as pd 13 | import numpy as np 14 | import matplotlib.pyplot as plt 15 | import torch 16 | import torch.nn as nn 17 | 18 | get_ipython().run_line_magic('matplotlib', 'inline') 19 | np.random.seed(1) 20 | 21 | 22 | # ## Prepare Dataset 23 | 24 | # In[2]: 25 | 26 | 27 | def f(x): 28 | """The function to predict.""" 29 | return x * np.sin(x) 30 | 31 | #---------------------------------------------------------------------- 32 | # First the noiseless case 33 | X = np.atleast_2d(np.random.uniform(0, 10.0, size=100)).T 34 | X = X.astype(np.float32) 35 | 36 | # Observations 37 | y = f(X).ravel() 38 | 39 | dy = 1.5 + 1.0 * np.random.random(y.shape) 40 | noise = np.random.normal(0, dy) 41 | y += noise 42 | y = y.astype(np.float32) 43 | 44 | # Mesh the input space for evaluations of the real function, the prediction and 45 | # its MSE 46 | xx = np.atleast_2d(np.linspace(0, 10, 1000)).T 47 | xx = xx.astype(np.float32) 48 | 49 | X.shape, y.shape, xx.shape 50 | 51 | 52 | # ## Build the Model 53 | 54 | # In[3]: 55 | 56 | 57 | class q_model(nn.Module): 58 | def __init__(self, 59 | quantiles, 60 | in_shape=1, 61 | dropout=0.5): 62 | super().__init__() 63 | self.quantiles = quantiles 64 | self.num_quantiles = len(quantiles) 65 | 66 | self.in_shape = in_shape 67 | self.out_shape = len(quantiles) 68 | self.dropout = dropout 69 | self.build_model() 70 | self.init_weights() 71 | 72 | def build_model(self): 73 | self.base_model = nn.Sequential( 74 | nn.Linear(self.in_shape, 64), 75 | nn.ReLU(), 76 | # nn.BatchNorm1d(64), 77 | nn.Dropout(self.dropout), 78 | nn.Linear(64, 64), 79 | nn.ReLU(), 80 | # nn.BatchNorm1d(64), 81 | nn.Dropout(self.dropout), 82 | ) 83 | final_layers = [ 84 | nn.Linear(64, 1) for _ in range(len(self.quantiles)) 85 | ] 86 | self.final_layers = nn.ModuleList(final_layers) 87 | 88 | def init_weights(self): 89 | for m in chain(self.base_model, self.final_layers): 90 | if isinstance(m, nn.Linear): 91 | nn.init.orthogonal_(m.weight) 92 | nn.init.constant_(m.bias, 0) 93 | 94 | def forward(self, x): 95 | tmp_ = self.base_model(x) 96 | return torch.cat([layer(tmp_) for layer in self.final_layers], dim=1) 97 | 98 | 99 | # In[4]: 100 | 101 | 102 | class q_model_simplified(nn.Module): 103 | def __init__(self, 104 | quantiles, 105 | in_shape=1, 106 | dropout=0.5): 107 | super().__init__() 108 | self.quantiles = quantiles 109 | self.num_quantiles = len(quantiles) 110 | 111 | self.in_shape = in_shape 112 | self.out_shape = len(quantiles) 113 | self.dropout = dropout 114 | self.build_model() 115 | self.init_weights() 116 | 117 | def build_model(self): 118 | self.model = nn.Sequential( 119 | nn.Linear(self.in_shape, 64), 120 | nn.ReLU(), 121 | # nn.BatchNorm1d(64), 122 | nn.Dropout(self.dropout), 123 | nn.Linear(64, 128), 124 | nn.ReLU(), 125 | # nn.BatchNorm1d(128), 126 | nn.Dropout(self.dropout), 127 | nn.Linear(128, self.out_shape) 128 | ) 129 | 130 | def init_weights(self): 131 | for m in self.model: 132 | if isinstance(m, nn.Linear): 133 | nn.init.orthogonal_(m.weight) 134 | nn.init.constant_(m.bias, 0) 135 | 136 | def forward(self, x): 137 | return self.model(x) 138 | 139 | 140 | # In[5]: 141 | 142 | 143 | class QuantileLoss(nn.Module): 144 | def __init__(self, quantiles): 145 | super().__init__() 146 | self.quantiles = quantiles 147 | 148 | def forward(self, preds, target): 149 | assert not target.requires_grad 150 | assert preds.size(0) == target.size(0) 151 | losses = [] 152 | for i, q in enumerate(quantiles): 153 | errors = target - preds[:, i] 154 | losses.append(torch.max((q-1) * errors, q * errors).unsqueeze(1)) 155 | loss = torch.mean(torch.sum(torch.cat(losses, dim=1), dim=1)) 156 | return loss 157 | 158 | 159 | # In[6]: 160 | 161 | 162 | class Learner: 163 | def __init__(self, model, optimizer_class, loss_func, device='cpu'): 164 | self.model = model.to(device) 165 | self.optimizer = optimizer_class(self.model.parameters()) 166 | self.loss_func = loss_func.to(device) 167 | self.device = device 168 | self.loss_history = [] 169 | 170 | def fit(self, x, y, epochs, batch_size): 171 | self.model.train() 172 | for e in range(epochs): 173 | shuffle_idx = np.arange(x.shape[0]) 174 | np.random.shuffle(shuffle_idx) 175 | x = x[shuffle_idx] 176 | y = y[shuffle_idx] 177 | epoch_losses = [] 178 | for idx in range(0, x.shape[0], batch_size): 179 | self.optimizer.zero_grad() 180 | batch_x = torch.from_numpy( 181 | x[idx : min(idx + batch_size, x.shape[0]),:] 182 | ).float().to(self.device).requires_grad_(False) 183 | batch_y = torch.from_numpy( 184 | y[idx : min(idx + batch_size, y.shape[0])] 185 | ).float().to(self.device).requires_grad_(False) 186 | preds = self.model(batch_x) 187 | loss = loss_func(preds, batch_y) 188 | loss.backward() 189 | self.optimizer.step() 190 | epoch_losses.append(loss.cpu().detach().numpy()) 191 | epoch_loss = np.mean(epoch_losses) 192 | self.loss_history.append(epoch_loss) 193 | if (e+1) % 500 == 0: 194 | print("Epoch {}: {}".format(e+1, epoch_loss)) 195 | 196 | def predict(self, x, mc=False): 197 | if mc: 198 | self.model.train() 199 | else: 200 | self.model.eval() 201 | return self.model(torch.from_numpy(x).to(self.device).requires_grad_(False)).cpu().detach().numpy() 202 | 203 | 204 | # In[7]: 205 | 206 | 207 | # Instantiate model 208 | quantiles = [.05, .5, .95] 209 | model = q_model(quantiles, dropout=0.1) 210 | loss_func = QuantileLoss(quantiles) 211 | learner = Learner(model, partial(torch.optim.Adam, weight_decay=1e-6), loss_func, device="cuda:0") 212 | 213 | 214 | # ## Train the Model 215 | 216 | # In[8]: 217 | 218 | 219 | # Run training 220 | epochs = 10000 221 | learner.fit(X, y, epochs, batch_size=10) 222 | 223 | 224 | # In[9]: 225 | 226 | 227 | # Make the prediction on the meshed x-axis 228 | tmp = learner.predict(xx) 229 | y_lower, y_pred, y_upper = tmp[:, 0], tmp[:, 1], tmp[:, 2] 230 | 231 | # Plot the function, the prediction and the 90% confidence interval based on 232 | # the MSE 233 | fig = plt.figure() 234 | plt.plot(xx, f(xx), 'g:', label=u'$f(x) = x\,\sin(x)$') 235 | plt.plot(X, y, 'b.', markersize=10, label=u'Observations') 236 | plt.plot(xx, y_pred, 'r-', label=u'Prediction') 237 | plt.plot(xx, y_upper, 'k-') 238 | plt.plot(xx, y_lower, 'k-') 239 | plt.fill(np.concatenate([xx, xx[::-1]]), 240 | np.concatenate([y_upper, y_lower[::-1]]), 241 | alpha=.5, fc='b', ec='None', label='90% prediction interval') 242 | plt.xlabel('$x$') 243 | plt.ylabel('$f(x)$') 244 | plt.ylim(-10, 20) 245 | plt.legend(loc='upper left') 246 | plt.show() 247 | 248 | 249 | # In[10]: 250 | 251 | 252 | predictions = learner.predict(X) 253 | np.mean(predictions[:, 0]), np.mean(predictions[:, 1]), np.mean(predictions[:, 2]) 254 | 255 | 256 | # In[11]: 257 | 258 | 259 | in_the_range = np.sum((y >= predictions[:, 0]) & (y <= predictions[:, 2])) 260 | print("Percentage in the range (expecting 90%):", in_the_range / len(y) * 100) 261 | 262 | 263 | # In[12]: 264 | 265 | 266 | out_of_the_range = np.sum((y < predictions[:, 0]) | (y > predictions[:, 2])) 267 | print("Percentage out of the range (expecting 10%):", out_of_the_range / len(y) * 100) 268 | 269 | 270 | # ## MC Prediction 271 | 272 | # In[13]: 273 | 274 | 275 | K = 5000 276 | tmp = np.zeros((K, xx.shape[0])).astype("float32") 277 | for k in range(K): 278 | preds = learner.predict(xx, mc=True) 279 | tmp[k] = preds[:, 1] 280 | y_lower, y_pred, y_upper = np.percentile(tmp, (5, 50, 95), axis=0) 281 | 282 | 283 | # In[14]: 284 | 285 | 286 | y_lower[1], y_pred[1], y_upper[1] 287 | 288 | 289 | # In[15]: 290 | 291 | 292 | # Plot the function, the prediction and the 90% confidence interval based on 293 | # the MSE 294 | fig = plt.figure() 295 | plt.plot(xx, f(xx), 'g:', label=u'$f(x) = x\,\sin(x)$') 296 | plt.plot(X, y, 'b.', markersize=10, label=u'Observations') 297 | plt.plot(xx, y_pred, 'r-', label=u'Prediction') 298 | plt.plot(xx, y_upper, 'k-') 299 | plt.plot(xx, y_lower, 'k-') 300 | plt.fill(np.concatenate([xx, xx[::-1]]), 301 | np.concatenate([y_upper, y_lower[::-1]]), 302 | alpha=.5, fc='b', ec='None', label='90% credible interval') 303 | plt.xlabel('$x$') 304 | plt.ylabel('$f(x)$') 305 | plt.ylim(-10, 20) 306 | plt.legend(loc='upper left') 307 | plt.show() 308 | 309 | -------------------------------------------------------------------------------- /notebooks/mcycle: -------------------------------------------------------------------------------- 1 | times accel strata v 2 | 2.4 0 1 3.7 3 | 2.6 -1.3 1 3.7 4 | 3.2 -2.7 1 3.7 5 | 3.6 0 1 3.7 6 | 4 -2.7 1 3.7 7 | 6.2 -2.7 1 3.7 8 | 6.6 -2.7 1 3.7 9 | 6.8 -1.3 1 3.7 10 | 7.8 -2.7 1 3.7 11 | 8.2 -2.7 1 3.7 12 | 8.8 -1.3 1 3.7 13 | 9.6 -2.7 1 3.7 14 | 10 -2.7 1 3.7 15 | 10.2 -5.4 1 3.7 16 | 10.6 -2.7 1 3.7 17 | 11 -5.4 1 3.7 18 | 11.4 0 1 3.7 19 | 13.2 -2.7 2 607 20 | 13.6 -2.7 2 607 21 | 13.8 0 2 607 22 | 14.6 -13.3 2 607 23 | 14.8 -2.7 2 607 24 | 15.4 -22.8 2 607 25 | 15.6 -40.2 2 607 26 | 15.8 -21.5 2 607 27 | 16 -42.9 2 607 28 | 16.2 -21.5 2 607 29 | 16.4 -5.4 2 607 30 | 16.6 -59 2 607 31 | 16.8 -71 2 607 32 | 17.6 -37.5 2 607 33 | 17.8 -99.1 2 607 34 | 18.6 -112.5 2 607 35 | 19.2 -123.1 2 607 36 | 19.4 -85.6 2 607 37 | 19.6 -127.2 2 607 38 | 20.2 -123.1 2 607 39 | 20.4 -117.9 2 607 40 | 21.2 -134 2 607 41 | 21.4 -101.9 2 607 42 | 21.8 -108.4 2 607 43 | 22 -123.1 2 607 44 | 23.2 -123.1 2 607 45 | 23.4 -128.5 2 607 46 | 24 -112.5 2 607 47 | 24.2 -95.1 2 607 48 | 24.6 -53.5 2 607 49 | 25 -64.4 2 607 50 | 25.4 -72.3 2 607 51 | 25.6 -26.8 2 607 52 | 26 -5.4 2 607 53 | 26.2 -107.1 2 607 54 | 26.4 -65.6 2 607 55 | 27 -16 2 607 56 | 27.2 -45.6 2 607 57 | 27.6 4 2 607 58 | 28.2 12 2 607 59 | 28.4 -21.5 2 607 60 | 28.6 46.9 2 607 61 | 29.4 -17.4 2 607 62 | 30.2 36.2 2 607 63 | 31 75 2 607 64 | 31.2 8.1 2 607 65 | 32 54.9 2 607 66 | 32.8 46.9 2 607 67 | 33.4 16 2 607 68 | 33.8 45.6 2 607 69 | 34.4 1.3 2 607 70 | 34.8 75 2 607 71 | 35.2 -16 2 607 72 | 35.4 69.6 2 607 73 | 35.6 34.8 2 607 74 | 36.2 -37.5 2 607 75 | 38 46.9 2 607 76 | 39.2 5.4 2 607 77 | 39.4 -1.3 2 607 78 | 40 -21.5 2 607 79 | 40.4 -13.3 2 607 80 | 41.6 30.8 3 138 81 | 42.4 29.4 3 138 82 | 42.8 0 3 138 83 | 43 14.7 3 138 84 | 44 -1.3 3 138 85 | 44.4 0 3 138 86 | 45 10.7 3 138 87 | 46.6 10.7 3 138 88 | 47.8 -26.8 3 138 89 | 48.8 -13.3 3 138 90 | 50.6 0 3 138 91 | 52 10.7 3 138 92 | 53.2 -14.7 3 138 93 | 55 -2.7 3 138 94 | 55.4 -2.7 3 138 95 | 57.6 10.7 3 138 96 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # Deep Quantile Regression Implementations 2 | 3 | Based on [strongio/quantile-regression-tensorflow](https://github.com/strongio/quantile-regression-tensorflow), and with the following expansions: 4 | 5 | 1. Use the example dataset from [the scikit-learn example](http://scikit-learn.org/stable/auto_examples/ensemble/plot_gradient_boosting_quantile.html). 6 | 2. The TensorFlow implementation is mostly the same as in [strongio/quantile-regression-tensorflow](https://github.com/strongio/quantile-regression-tensorflow). 7 | 3. Add a LightGBM "quantile" objective example (and a scikit-learn GBM example for comparison) based on this [Github issue](https://github.com/Microsoft/LightGBM/issues/1182). 8 | 4. Add a Pytorch implementation. 9 | 10 | ## Dockerfile 11 | The accompanied *Dockerfile* contains all the dependencies needed to reproduce the results. 12 | --------------------------------------------------------------------------------