| { |
| "cells": [ |
| { |
| "cell_type": "markdown", |
| "metadata": {}, |
| "source": [ |
| "# Hyperband diagonal using MNIST\n", |
| "\n", |
| "Implemention of Hyperband https://arxiv.org/pdf/1603.06560.pdf for MPP with a synchronous barrier. Uses the Hyperband schedule but runs it on a diagonal across brackets, instead of one bracket at a time, to be more efficient with cluster resources. \n", |
| "\n", |
| "Model architecture based on https://keras.io/examples/mnist_transfer_cnn/ \n", |
| "\n", |
| "To load images into tables we use the script called <em>madlib_image_loader.py</em> located at https://github.com/apache/madlib-site/tree/asf-site/community-artifacts/Deep-learning which uses the Python Imaging Library so supports multiple formats http://www.pythonware.com/products/pil/\n", |
| "\n", |
| "## Table of contents\n", |
| "<a href=\"#import_libraries\">1. Import libraries</a>\n", |
| "\n", |
| "<a href=\"#load_and_prepare_data\">2. Load and prepare data</a>\n", |
| "\n", |
| "<a href=\"#image_preproc\">3. Call image preprocessor</a>\n", |
| "\n", |
| "<a href=\"#define_and_load_model\">4. Define and load model architecture</a>\n", |
| "\n", |
| "<a href=\"#hyperband\">5. Hyperband diagonal</a>\n", |
| "\n", |
| "<a href=\"#plot\">6. Plot results</a>\n", |
| "\n", |
| "<a href=\"#print\">7. Print run schedules (display only)</a>" |
| ] |
| }, |
| { |
| "cell_type": "code", |
| "execution_count": 2, |
| "metadata": { |
| "scrolled": true |
| }, |
| "outputs": [ |
| { |
| "name": "stderr", |
| "output_type": "stream", |
| "text": [ |
| "/Users/fmcquillan/anaconda/lib/python2.7/site-packages/IPython/config.py:13: ShimWarning: The `IPython.config` package has been deprecated since IPython 4.0. You should import from traitlets.config instead.\n", |
| " \"You should import from traitlets.config instead.\", ShimWarning)\n", |
| "/Users/fmcquillan/anaconda/lib/python2.7/site-packages/IPython/utils/traitlets.py:5: UserWarning: IPython.utils.traitlets has moved to a top-level traitlets package.\n", |
| " warn(\"IPython.utils.traitlets has moved to a top-level traitlets package.\")\n" |
| ] |
| } |
| ], |
| "source": [ |
| "%load_ext sql" |
| ] |
| }, |
| { |
| "cell_type": "code", |
| "execution_count": 4, |
| "metadata": {}, |
| "outputs": [], |
| "source": [ |
| "# Greenplum Database 5.x on GCP (PM demo machine) - direct external IP access\n", |
| "#%sql postgresql://gpadmin@34.67.65.96:5432/madlib\n", |
| "\n", |
| "# Greenplum Database 5.x on GCP - via tunnel\n", |
| "%sql postgresql://gpadmin@localhost:8000/madlib\n", |
| " \n", |
| "# PostgreSQL local\n", |
| "#%sql postgresql://fmcquillan@localhost:5432/madlib\n", |
| "\n", |
| "# psycopg2 connection\n", |
| "import psycopg2 as p2\n", |
| "#conn = p2.connect('postgresql://fmcquillan@localhost:5432/madlib')\n", |
| "conn = p2.connect('postgresql://gpadmin@localhost:8000/madlib')\n", |
| "cur = conn.cursor()" |
| ] |
| }, |
| { |
| "cell_type": "code", |
| "execution_count": 3, |
| "metadata": {}, |
| "outputs": [ |
| { |
| "name": "stdout", |
| "output_type": "stream", |
| "text": [ |
| "1 rows affected.\n" |
| ] |
| }, |
| { |
| "data": { |
| "text/html": [ |
| "<table>\n", |
| " <tr>\n", |
| " <th>version</th>\n", |
| " </tr>\n", |
| " <tr>\n", |
| " <td>MADlib version: 1.17-dev, git revision: rel/v1.16-50-g5abfb79, cmake configuration time: Tue Nov 26 01:00:01 UTC 2019, build type: release, build system: Linux-3.10.0-1062.4.3.el7.x86_64, C compiler: gcc 4.8.5, C++ compiler: g++ 4.8.5</td>\n", |
| " </tr>\n", |
| "</table>" |
| ], |
| "text/plain": [ |
| "[(u'MADlib version: 1.17-dev, git revision: rel/v1.16-50-g5abfb79, cmake configuration time: Tue Nov 26 01:00:01 UTC 2019, build type: release, build system: Linux-3.10.0-1062.4.3.el7.x86_64, C compiler: gcc 4.8.5, C++ compiler: g++ 4.8.5',)]" |
| ] |
| }, |
| "execution_count": 3, |
| "metadata": {}, |
| "output_type": "execute_result" |
| } |
| ], |
| "source": [ |
| "%sql select madlib.version();\n", |
| "#%sql select version();" |
| ] |
| }, |
| { |
| "cell_type": "markdown", |
| "metadata": {}, |
| "source": [ |
| "<a id=\"import_libraries\"></a>\n", |
| "# 1. Import libraries\n", |
| "From https://keras.io/examples/mnist_transfer_cnn/ import libraries and define some params" |
| ] |
| }, |
| { |
| "cell_type": "code", |
| "execution_count": 5, |
| "metadata": {}, |
| "outputs": [ |
| { |
| "name": "stderr", |
| "output_type": "stream", |
| "text": [ |
| "Using TensorFlow backend.\n" |
| ] |
| }, |
| { |
| "name": "stdout", |
| "output_type": "stream", |
| "text": [ |
| "Couldn't import dot_parser, loading of dot files will not be possible.\n" |
| ] |
| } |
| ], |
| "source": [ |
| "from __future__ import print_function\n", |
| "\n", |
| "import datetime\n", |
| "import keras\n", |
| "from keras.datasets import mnist\n", |
| "from keras.models import Sequential\n", |
| "from keras.layers import Dense, Dropout, Activation, Flatten\n", |
| "from keras.layers import Conv2D, MaxPooling2D\n", |
| "from keras import backend as K\n", |
| "\n", |
| "now = datetime.datetime.now\n", |
| "\n", |
| "#batch_size = 128\n", |
| "num_classes = 10\n", |
| "#epochs = 5\n", |
| "\n", |
| "# input image dimensions\n", |
| "img_rows, img_cols = 28, 28\n", |
| "# number of convolutional filters to use\n", |
| "filters = 32\n", |
| "# size of pooling area for max pooling\n", |
| "pool_size = 2\n", |
| "# convolution kernel size\n", |
| "kernel_size = 3\n", |
| "\n", |
| "if K.image_data_format() == 'channels_first':\n", |
| " input_shape = (1, img_rows, img_cols)\n", |
| "else:\n", |
| " input_shape = (img_rows, img_cols, 1)" |
| ] |
| }, |
| { |
| "cell_type": "markdown", |
| "metadata": {}, |
| "source": [ |
| "Others needed in this workbook" |
| ] |
| }, |
| { |
| "cell_type": "code", |
| "execution_count": 6, |
| "metadata": {}, |
| "outputs": [], |
| "source": [ |
| "import pandas as pd\n", |
| "import numpy as np" |
| ] |
| }, |
| { |
| "cell_type": "markdown", |
| "metadata": {}, |
| "source": [ |
| "<a id=\"load_and_prepare_data\"></a>\n", |
| "# 2. Load and prepare data\n", |
| "\n", |
| "First load MNIST data from Keras, consisting of 60,000 28x28 grayscale images of the 10 digits, along with a test set of 10,000 images." |
| ] |
| }, |
| { |
| "cell_type": "code", |
| "execution_count": 7, |
| "metadata": {}, |
| "outputs": [ |
| { |
| "name": "stdout", |
| "output_type": "stream", |
| "text": [ |
| "(10000, 28, 28)\n", |
| "(10000, 28, 28, 1)\n" |
| ] |
| } |
| ], |
| "source": [ |
| "# the data, split between train and test sets\n", |
| "(x_train, y_train), (x_test, y_test) = mnist.load_data()\n", |
| "\n", |
| "# reshape to match model architecture\n", |
| "print(x_test.shape)\n", |
| "x_train = x_train.reshape(len(x_train), *input_shape)\n", |
| "x_test = x_test.reshape(len(x_test), *input_shape)\n", |
| "print(x_test.shape)" |
| ] |
| }, |
| { |
| "cell_type": "markdown", |
| "metadata": {}, |
| "source": [ |
| "Load datasets into tables using image loader scripts called <em>madlib_image_loader.py</em> located at https://github.com/apache/madlib-site/tree/asf-site/community-artifacts/Deep-learning" |
| ] |
| }, |
| { |
| "cell_type": "code", |
| "execution_count": 8, |
| "metadata": {}, |
| "outputs": [], |
| "source": [ |
| "# MADlib tools directory\n", |
| "import sys\n", |
| "import os\n", |
| "madlib_site_dir = '/Users/fmcquillan/Documents/Product/MADlib/Demos/data'\n", |
| "sys.path.append(madlib_site_dir)\n", |
| "\n", |
| "# Import image loader module\n", |
| "from madlib_image_loader import ImageLoader, DbCredentials" |
| ] |
| }, |
| { |
| "cell_type": "code", |
| "execution_count": 34, |
| "metadata": {}, |
| "outputs": [], |
| "source": [ |
| "# Specify database credentials, for connecting to db\n", |
| "db_creds = DbCredentials(user='gpadmin',\n", |
| " host='localhost',\n", |
| " port='8000',\n", |
| " password='')" |
| ] |
| }, |
| { |
| "cell_type": "code", |
| "execution_count": 35, |
| "metadata": {}, |
| "outputs": [], |
| "source": [ |
| "# Initialize ImageLoader (increase num_workers to run faster)\n", |
| "iloader = ImageLoader(num_workers=5, db_creds=db_creds)" |
| ] |
| }, |
| { |
| "cell_type": "code", |
| "execution_count": 36, |
| "metadata": {}, |
| "outputs": [ |
| { |
| "name": "stdout", |
| "output_type": "stream", |
| "text": [ |
| "Done.\n", |
| "MainProcess: Connected to madlib db.\n", |
| "Executing: CREATE TABLE train_mnist (id SERIAL, x REAL[], y TEXT)\n", |
| "CREATE TABLE\n", |
| "Created table train_mnist in madlib db\n", |
| "Spawning 5 workers...\n", |
| "Initializing PoolWorker-11 [pid 34068]\n", |
| "PoolWorker-11: Created temporary directory /tmp/madlib_RbuQlbqxI5\n", |
| "Initializing PoolWorker-12 [pid 34069]\n", |
| "PoolWorker-12: Created temporary directory /tmp/madlib_tEyH9GMFGV\n", |
| "Initializing PoolWorker-13 [pid 34070]\n", |
| "PoolWorker-13: Created temporary directory /tmp/madlib_TyYs4viAVD\n", |
| "Initializing PoolWorker-14 [pid 34071]\n", |
| "Initializing PoolWorker-15 [pid 34072]\n", |
| "PoolWorker-14: Created temporary directory /tmp/madlib_KTwnncRsaq\n", |
| "PoolWorker-15: Created temporary directory /tmp/madlib_jtG9zAC8HU\n", |
| "PoolWorker-11: Connected to madlib db.\n", |
| "PoolWorker-13: Connected to madlib db.\n", |
| "PoolWorker-14: Connected to madlib db.\n", |
| "PoolWorker-12: Connected to madlib db.\n", |
| "PoolWorker-15: Connected to madlib db.\n", |
| "PoolWorker-11: Wrote 1000 images to /tmp/madlib_RbuQlbqxI5/train_mnist0000.tmp\n", |
| "PoolWorker-14: Wrote 1000 images to /tmp/madlib_KTwnncRsaq/train_mnist0000.tmp\n", |
| "PoolWorker-13: Wrote 1000 images to /tmp/madlib_TyYs4viAVD/train_mnist0000.tmp\n", |
| "PoolWorker-12: Wrote 1000 images to /tmp/madlib_tEyH9GMFGV/train_mnist0000.tmp\n", |
| "PoolWorker-15: Wrote 1000 images to /tmp/madlib_jtG9zAC8HU/train_mnist0000.tmp\n", |
| "PoolWorker-11: Loaded 1000 images into train_mnist\n", |
| "PoolWorker-14: Loaded 1000 images into train_mnist\n", |
| "PoolWorker-13: Loaded 1000 images into train_mnist\n", |
| "PoolWorker-12: Loaded 1000 images into train_mnist\n", |
| "PoolWorker-15: Loaded 1000 images into train_mnist\n", |
| "PoolWorker-11: Wrote 1000 images to /tmp/madlib_RbuQlbqxI5/train_mnist0001.tmp\n", |
| "PoolWorker-14: Wrote 1000 images to /tmp/madlib_KTwnncRsaq/train_mnist0001.tmp\n", |
| "PoolWorker-11: Loaded 1000 images into train_mnist\n", |
| "PoolWorker-13: Wrote 1000 images to /tmp/madlib_TyYs4viAVD/train_mnist0001.tmp\n", |
| "PoolWorker-12: Wrote 1000 images to /tmp/madlib_tEyH9GMFGV/train_mnist0001.tmp\n", |
| "PoolWorker-15: Wrote 1000 images to /tmp/madlib_jtG9zAC8HU/train_mnist0001.tmp\n", |
| "PoolWorker-11: Wrote 1000 images to /tmp/madlib_RbuQlbqxI5/train_mnist0002.tmp\n", |
| "PoolWorker-14: Loaded 1000 images into train_mnist\n", |
| "PoolWorker-14: Wrote 1000 images to /tmp/madlib_KTwnncRsaq/train_mnist0002.tmp\n", |
| "PoolWorker-13: Loaded 1000 images into train_mnist\n", |
| "PoolWorker-15: Loaded 1000 images into train_mnist\n", |
| "PoolWorker-12: Loaded 1000 images into train_mnist\n", |
| "PoolWorker-13: Wrote 1000 images to /tmp/madlib_TyYs4viAVD/train_mnist0002.tmp\n", |
| "PoolWorker-11: Loaded 1000 images into train_mnist\n", |
| "PoolWorker-14: Loaded 1000 images into train_mnist\n", |
| "PoolWorker-15: Wrote 1000 images to /tmp/madlib_jtG9zAC8HU/train_mnist0002.tmp\n", |
| "PoolWorker-12: Wrote 1000 images to /tmp/madlib_tEyH9GMFGV/train_mnist0002.tmp\n", |
| "PoolWorker-13: Loaded 1000 images into train_mnist\n", |
| "PoolWorker-11: Wrote 1000 images to /tmp/madlib_RbuQlbqxI5/train_mnist0003.tmp\n", |
| "PoolWorker-14: Wrote 1000 images to /tmp/madlib_KTwnncRsaq/train_mnist0003.tmp\n", |
| "PoolWorker-13: Wrote 1000 images to /tmp/madlib_TyYs4viAVD/train_mnist0003.tmp\n", |
| "PoolWorker-12: Loaded 1000 images into train_mnist\n", |
| "PoolWorker-15: Loaded 1000 images into train_mnist\n", |
| "PoolWorker-15: Wrote 1000 images to /tmp/madlib_jtG9zAC8HU/train_mnist0003.tmp\n", |
| "PoolWorker-12: Wrote 1000 images to /tmp/madlib_tEyH9GMFGV/train_mnist0003.tmp\n", |
| "PoolWorker-11: Loaded 1000 images into train_mnist\n", |
| "PoolWorker-14: Loaded 1000 images into train_mnist\n", |
| "PoolWorker-11: Wrote 1000 images to /tmp/madlib_RbuQlbqxI5/train_mnist0004.tmp\n", |
| "PoolWorker-13: Loaded 1000 images into train_mnist\n", |
| "PoolWorker-14: Wrote 1000 images to /tmp/madlib_KTwnncRsaq/train_mnist0004.tmp\n", |
| "PoolWorker-13: Wrote 1000 images to /tmp/madlib_TyYs4viAVD/train_mnist0004.tmp\n", |
| "PoolWorker-12: Loaded 1000 images into train_mnist\n", |
| "PoolWorker-15: Loaded 1000 images into train_mnist\n", |
| "PoolWorker-11: Loaded 1000 images into train_mnist\n", |
| "PoolWorker-12: Wrote 1000 images to /tmp/madlib_tEyH9GMFGV/train_mnist0004.tmp\n", |
| "PoolWorker-15: Wrote 1000 images to /tmp/madlib_jtG9zAC8HU/train_mnist0004.tmp\n", |
| "PoolWorker-14: Loaded 1000 images into train_mnist\n", |
| "PoolWorker-13: Loaded 1000 images into train_mnist\n", |
| "PoolWorker-11: Wrote 1000 images to /tmp/madlib_RbuQlbqxI5/train_mnist0005.tmp\n", |
| "PoolWorker-14: Wrote 1000 images to /tmp/madlib_KTwnncRsaq/train_mnist0005.tmp\n", |
| "PoolWorker-13: Wrote 1000 images to /tmp/madlib_TyYs4viAVD/train_mnist0005.tmp\n", |
| "PoolWorker-12: Loaded 1000 images into train_mnist\n", |
| "PoolWorker-15: Loaded 1000 images into train_mnist\n", |
| "PoolWorker-12: Wrote 1000 images to /tmp/madlib_tEyH9GMFGV/train_mnist0005.tmp\n", |
| "PoolWorker-15: Wrote 1000 images to /tmp/madlib_jtG9zAC8HU/train_mnist0005.tmp\n", |
| "PoolWorker-11: Loaded 1000 images into train_mnist\n", |
| "PoolWorker-14: Loaded 1000 images into train_mnist\n", |
| "PoolWorker-13: Loaded 1000 images into train_mnist\n", |
| "PoolWorker-11: Wrote 1000 images to /tmp/madlib_RbuQlbqxI5/train_mnist0006.tmp\n", |
| "PoolWorker-13: Wrote 1000 images to /tmp/madlib_TyYs4viAVD/train_mnist0006.tmp\n", |
| "PoolWorker-14: Wrote 1000 images to /tmp/madlib_KTwnncRsaq/train_mnist0006.tmp\n", |
| "PoolWorker-12: Loaded 1000 images into train_mnist\n", |
| "PoolWorker-15: Loaded 1000 images into train_mnist\n", |
| "PoolWorker-12: Wrote 1000 images to /tmp/madlib_tEyH9GMFGV/train_mnist0006.tmp\n", |
| "PoolWorker-15: Wrote 1000 images to /tmp/madlib_jtG9zAC8HU/train_mnist0006.tmp\n", |
| "PoolWorker-11: Loaded 1000 images into train_mnist\n", |
| "PoolWorker-13: Loaded 1000 images into train_mnist\n", |
| "PoolWorker-14: Loaded 1000 images into train_mnist\n", |
| "PoolWorker-11: Wrote 1000 images to /tmp/madlib_RbuQlbqxI5/train_mnist0007.tmp\n", |
| "PoolWorker-15: Loaded 1000 images into train_mnist\n", |
| "PoolWorker-12: Loaded 1000 images into train_mnist\n", |
| "PoolWorker-13: Wrote 1000 images to /tmp/madlib_TyYs4viAVD/train_mnist0007.tmp\n", |
| "PoolWorker-14: Wrote 1000 images to /tmp/madlib_KTwnncRsaq/train_mnist0007.tmp\n", |
| "PoolWorker-11: Loaded 1000 images into train_mnist\n", |
| "PoolWorker-15: Wrote 1000 images to /tmp/madlib_jtG9zAC8HU/train_mnist0007.tmp\n", |
| "PoolWorker-12: Wrote 1000 images to /tmp/madlib_tEyH9GMFGV/train_mnist0007.tmp\n", |
| "PoolWorker-11: Wrote 1000 images to /tmp/madlib_RbuQlbqxI5/train_mnist0008.tmp\n", |
| "PoolWorker-13: Loaded 1000 images into train_mnist\n", |
| "PoolWorker-13: Wrote 1000 images to /tmp/madlib_TyYs4viAVD/train_mnist0008.tmp\n", |
| "PoolWorker-14: Loaded 1000 images into train_mnist\n", |
| "PoolWorker-14: Wrote 1000 images to /tmp/madlib_KTwnncRsaq/train_mnist0008.tmp\n", |
| "PoolWorker-12: Loaded 1000 images into train_mnist\n", |
| "PoolWorker-15: Loaded 1000 images into train_mnist\n", |
| "PoolWorker-11: Loaded 1000 images into train_mnist\n", |
| "PoolWorker-13: Loaded 1000 images into train_mnist\n", |
| "PoolWorker-14: Loaded 1000 images into train_mnist\n", |
| "PoolWorker-12: Wrote 1000 images to /tmp/madlib_tEyH9GMFGV/train_mnist0008.tmp\n", |
| "PoolWorker-15: Wrote 1000 images to /tmp/madlib_jtG9zAC8HU/train_mnist0008.tmp\n", |
| "PoolWorker-13: Wrote 1000 images to /tmp/madlib_TyYs4viAVD/train_mnist0009.tmp\n", |
| "PoolWorker-11: Wrote 1000 images to /tmp/madlib_RbuQlbqxI5/train_mnist0009.tmp\n", |
| "PoolWorker-14: Wrote 1000 images to /tmp/madlib_KTwnncRsaq/train_mnist0009.tmp\n", |
| "PoolWorker-12: Loaded 1000 images into train_mnist\n", |
| "PoolWorker-12: Wrote 1000 images to /tmp/madlib_tEyH9GMFGV/train_mnist0009.tmp\n", |
| "PoolWorker-15: Loaded 1000 images into train_mnist\n", |
| "PoolWorker-11: Loaded 1000 images into train_mnist\n", |
| "PoolWorker-13: Loaded 1000 images into train_mnist\n", |
| "PoolWorker-15: Wrote 1000 images to /tmp/madlib_jtG9zAC8HU/train_mnist0009.tmp\n", |
| "PoolWorker-13: Wrote 1000 images to /tmp/madlib_TyYs4viAVD/train_mnist0010.tmp\n", |
| "PoolWorker-11: Wrote 1000 images to /tmp/madlib_RbuQlbqxI5/train_mnist0010.tmp\n", |
| "PoolWorker-14: Loaded 1000 images into train_mnist\n", |
| "PoolWorker-14: Wrote 1000 images to /tmp/madlib_KTwnncRsaq/train_mnist0010.tmp\n", |
| "PoolWorker-12: Loaded 1000 images into train_mnist\n", |
| "PoolWorker-12: Wrote 1000 images to /tmp/madlib_tEyH9GMFGV/train_mnist0010.tmp\n", |
| "PoolWorker-11: Loaded 1000 images into train_mnist\n", |
| "PoolWorker-13: Loaded 1000 images into train_mnist\n", |
| "PoolWorker-15: Loaded 1000 images into train_mnist\n", |
| "PoolWorker-14: Loaded 1000 images into train_mnist\n", |
| "PoolWorker-12: Loaded 1000 images into train_mnist\n", |
| "PoolWorker-15: Wrote 1000 images to /tmp/madlib_jtG9zAC8HU/train_mnist0010.tmp\n", |
| "PoolWorker-13: Wrote 1000 images to /tmp/madlib_TyYs4viAVD/train_mnist0011.tmp\n", |
| "PoolWorker-11: Wrote 1000 images to /tmp/madlib_RbuQlbqxI5/train_mnist0011.tmp\n", |
| "PoolWorker-14: Wrote 1000 images to /tmp/madlib_KTwnncRsaq/train_mnist0011.tmp\n" |
| ] |
| }, |
| { |
| "name": "stdout", |
| "output_type": "stream", |
| "text": [ |
| "PoolWorker-12: Wrote 1000 images to /tmp/madlib_tEyH9GMFGV/train_mnist0011.tmp\n", |
| "PoolWorker-15: Loaded 1000 images into train_mnist\n", |
| "PoolWorker-13: Loaded 1000 images into train_mnist\n", |
| "PoolWorker-11: Loaded 1000 images into train_mnist\n", |
| "PoolWorker-14: Loaded 1000 images into train_mnist\n", |
| "PoolWorker-15: Wrote 1000 images to /tmp/madlib_jtG9zAC8HU/train_mnist0011.tmp\n", |
| "PoolWorker-12: Loaded 1000 images into train_mnist\n", |
| "PoolWorker-15: Loaded 1000 images into train_mnist\n", |
| "PoolWorker-11: Removed temporary directory /tmp/madlib_RbuQlbqxI5\n", |
| "PoolWorker-15: Removed temporary directory /tmp/madlib_jtG9zAC8HU\n", |
| "PoolWorker-12: Removed temporary directory /tmp/madlib_tEyH9GMFGV\n", |
| "PoolWorker-13: Removed temporary directory /tmp/madlib_TyYs4viAVD\n", |
| "PoolWorker-14: Removed temporary directory /tmp/madlib_KTwnncRsaq\n", |
| "Done! Loaded 60000 images in 45.7068669796s\n", |
| "5 workers terminated.\n", |
| "MainProcess: Connected to madlib db.\n", |
| "Executing: CREATE TABLE test_mnist (id SERIAL, x REAL[], y TEXT)\n", |
| "CREATE TABLE\n", |
| "Created table test_mnist in madlib db\n", |
| "Spawning 5 workers...\n", |
| "Initializing PoolWorker-16 [pid 34074]\n", |
| "PoolWorker-16: Created temporary directory /tmp/madlib_MjwU1yRoMW\n", |
| "Initializing PoolWorker-17 [pid 34075]\n", |
| "PoolWorker-17: Created temporary directory /tmp/madlib_kTezv88uWu\n", |
| "Initializing PoolWorker-18 [pid 34076]\n", |
| "PoolWorker-18: Created temporary directory /tmp/madlib_TFIofbewK1\n", |
| "Initializing PoolWorker-19 [pid 34077]\n", |
| "PoolWorker-19: Created temporary directory /tmp/madlib_QUIRxlckvj\n", |
| "PoolWorker-20: Created temporary directory /tmp/madlib_Eii5YFUzCZ\n", |
| "Initializing PoolWorker-20 [pid 34078]\n", |
| "PoolWorker-17: Connected to madlib db.\n", |
| "PoolWorker-18: Connected to madlib db.\n", |
| "PoolWorker-19: Connected to madlib db.\n", |
| "PoolWorker-16: Connected to madlib db.\n", |
| "PoolWorker-20: Connected to madlib db.\n", |
| "PoolWorker-18: Wrote 1000 images to /tmp/madlib_TFIofbewK1/test_mnist0000.tmp\n", |
| "PoolWorker-19: Wrote 1000 images to /tmp/madlib_QUIRxlckvj/test_mnist0000.tmp\n", |
| "PoolWorker-17: Wrote 1000 images to /tmp/madlib_kTezv88uWu/test_mnist0000.tmp\n", |
| "PoolWorker-16: Wrote 1000 images to /tmp/madlib_MjwU1yRoMW/test_mnist0000.tmp\n", |
| "PoolWorker-20: Wrote 1000 images to /tmp/madlib_Eii5YFUzCZ/test_mnist0000.tmp\n", |
| "PoolWorker-18: Loaded 1000 images into test_mnist\n", |
| "PoolWorker-17: Loaded 1000 images into test_mnist\n", |
| "PoolWorker-19: Loaded 1000 images into test_mnist\n", |
| "PoolWorker-18: Wrote 1000 images to /tmp/madlib_TFIofbewK1/test_mnist0001.tmp\n", |
| "PoolWorker-16: Loaded 1000 images into test_mnist\n", |
| "PoolWorker-20: Loaded 1000 images into test_mnist\n", |
| "PoolWorker-18: Loaded 1000 images into test_mnist\n", |
| "PoolWorker-19: Wrote 1000 images to /tmp/madlib_QUIRxlckvj/test_mnist0001.tmp\n", |
| "PoolWorker-17: Wrote 1000 images to /tmp/madlib_kTezv88uWu/test_mnist0001.tmp\n", |
| "PoolWorker-16: Wrote 1000 images to /tmp/madlib_MjwU1yRoMW/test_mnist0001.tmp\n", |
| "PoolWorker-20: Wrote 1000 images to /tmp/madlib_Eii5YFUzCZ/test_mnist0001.tmp\n", |
| "PoolWorker-19: Loaded 1000 images into test_mnist\n", |
| "PoolWorker-17: Loaded 1000 images into test_mnist\n", |
| "PoolWorker-16: Loaded 1000 images into test_mnist\n", |
| "PoolWorker-20: Loaded 1000 images into test_mnist\n", |
| "PoolWorker-16: Removed temporary directory /tmp/madlib_MjwU1yRoMW\n", |
| "PoolWorker-19: Removed temporary directory /tmp/madlib_QUIRxlckvj\n", |
| "PoolWorker-17: Removed temporary directory /tmp/madlib_kTezv88uWu\n", |
| "PoolWorker-20: Removed temporary directory /tmp/madlib_Eii5YFUzCZ\n", |
| "PoolWorker-18: Removed temporary directory /tmp/madlib_TFIofbewK1\n", |
| "Done! Loaded 10000 images in 6.80017995834s\n", |
| "5 workers terminated.\n" |
| ] |
| } |
| ], |
| "source": [ |
| "# Drop tables\n", |
| "%sql DROP TABLE IF EXISTS train_mnist, test_mnist\n", |
| "\n", |
| "# Save images to temporary directories and load into database\n", |
| "iloader.load_dataset_from_np(x_train, y_train, 'train_mnist', append=False)\n", |
| "iloader.load_dataset_from_np(x_test, y_test, 'test_mnist', append=False)" |
| ] |
| }, |
| { |
| "cell_type": "markdown", |
| "metadata": {}, |
| "source": [ |
| "<a id=\"image_preproc\"></a>\n", |
| "# 3. Call image preprocessor\n", |
| "\n", |
| "Transforms from one image per row to multiple images per row for batch optimization. Also normalizes and one-hot encodes.\n", |
| "\n", |
| "Training dataset" |
| ] |
| }, |
| { |
| "cell_type": "code", |
| "execution_count": 37, |
| "metadata": { |
| "scrolled": true |
| }, |
| "outputs": [ |
| { |
| "name": "stdout", |
| "output_type": "stream", |
| "text": [ |
| "Done.\n", |
| "1 rows affected.\n", |
| "1 rows affected.\n" |
| ] |
| }, |
| { |
| "data": { |
| "text/html": [ |
| "<table>\n", |
| " <tr>\n", |
| " <th>source_table</th>\n", |
| " <th>output_table</th>\n", |
| " <th>dependent_varname</th>\n", |
| " <th>independent_varname</th>\n", |
| " <th>dependent_vartype</th>\n", |
| " <th>class_values</th>\n", |
| " <th>buffer_size</th>\n", |
| " <th>normalizing_const</th>\n", |
| " <th>num_classes</th>\n", |
| " </tr>\n", |
| " <tr>\n", |
| " <td>train_mnist</td>\n", |
| " <td>train_mnist_packed</td>\n", |
| " <td>y</td>\n", |
| " <td>x</td>\n", |
| " <td>text</td>\n", |
| " <td>[u'0', u'1', u'2', u'3', u'4', u'5', u'6', u'7', u'8', u'9']</td>\n", |
| " <td>1000</td>\n", |
| " <td>255.0</td>\n", |
| " <td>10</td>\n", |
| " </tr>\n", |
| "</table>" |
| ], |
| "text/plain": [ |
| "[(u'train_mnist', u'train_mnist_packed', u'y', u'x', u'text', [u'0', u'1', u'2', u'3', u'4', u'5', u'6', u'7', u'8', u'9'], 1000, 255.0, 10)]" |
| ] |
| }, |
| "execution_count": 37, |
| "metadata": {}, |
| "output_type": "execute_result" |
| } |
| ], |
| "source": [ |
| "%%sql\n", |
| "DROP TABLE IF EXISTS train_mnist_packed, train_mnist_packed_summary;\n", |
| "\n", |
| "SELECT madlib.training_preprocessor_dl('train_mnist', -- Source table\n", |
| " 'train_mnist_packed', -- Output table\n", |
| " 'y', -- Dependent variable\n", |
| " 'x', -- Independent variable\n", |
| " 1000, -- Buffer size\n", |
| " 255 -- Normalizing constant\n", |
| " );\n", |
| "\n", |
| "SELECT * FROM train_mnist_packed_summary;" |
| ] |
| }, |
| { |
| "cell_type": "markdown", |
| "metadata": {}, |
| "source": [ |
| "Test dataset" |
| ] |
| }, |
| { |
| "cell_type": "code", |
| "execution_count": 39, |
| "metadata": {}, |
| "outputs": [ |
| { |
| "name": "stdout", |
| "output_type": "stream", |
| "text": [ |
| "Done.\n", |
| "1 rows affected.\n", |
| "1 rows affected.\n" |
| ] |
| }, |
| { |
| "data": { |
| "text/html": [ |
| "<table>\n", |
| " <tr>\n", |
| " <th>source_table</th>\n", |
| " <th>output_table</th>\n", |
| " <th>dependent_varname</th>\n", |
| " <th>independent_varname</th>\n", |
| " <th>dependent_vartype</th>\n", |
| " <th>class_values</th>\n", |
| " <th>buffer_size</th>\n", |
| " <th>normalizing_const</th>\n", |
| " <th>num_classes</th>\n", |
| " </tr>\n", |
| " <tr>\n", |
| " <td>test_mnist</td>\n", |
| " <td>test_mnist_packed</td>\n", |
| " <td>y</td>\n", |
| " <td>x</td>\n", |
| " <td>text</td>\n", |
| " <td>[u'0', u'1', u'2', u'3', u'4', u'5', u'6', u'7', u'8', u'9']</td>\n", |
| " <td>5000</td>\n", |
| " <td>255.0</td>\n", |
| " <td>10</td>\n", |
| " </tr>\n", |
| "</table>" |
| ], |
| "text/plain": [ |
| "[(u'test_mnist', u'test_mnist_packed', u'y', u'x', u'text', [u'0', u'1', u'2', u'3', u'4', u'5', u'6', u'7', u'8', u'9'], 5000, 255.0, 10)]" |
| ] |
| }, |
| "execution_count": 39, |
| "metadata": {}, |
| "output_type": "execute_result" |
| } |
| ], |
| "source": [ |
| "%%sql\n", |
| "DROP TABLE IF EXISTS test_mnist_packed, test_mnist_packed_summary;\n", |
| "\n", |
| "SELECT madlib.validation_preprocessor_dl('test_mnist', -- Source table\n", |
| " 'test_mnist_packed', -- Output table\n", |
| " 'y', -- Dependent variable\n", |
| " 'x', -- Independent variable\n", |
| " 'train_mnist_packed' -- Training preproc table\n", |
| " );\n", |
| "\n", |
| "SELECT * FROM test_mnist_packed_summary;" |
| ] |
| }, |
| { |
| "cell_type": "markdown", |
| "metadata": {}, |
| "source": [ |
| "<a id=\"define_and_load_model\"></a>\n", |
| "# 4. Define and load model architecture\n", |
| "\n", |
| "Model with feature and classification layers trainable" |
| ] |
| }, |
| { |
| "cell_type": "code", |
| "execution_count": 8, |
| "metadata": {}, |
| "outputs": [ |
| { |
| "name": "stdout", |
| "output_type": "stream", |
| "text": [ |
| "_________________________________________________________________\n", |
| "Layer (type) Output Shape Param # \n", |
| "=================================================================\n", |
| "conv2d_1 (Conv2D) (None, 26, 26, 32) 320 \n", |
| "_________________________________________________________________\n", |
| "activation_1 (Activation) (None, 26, 26, 32) 0 \n", |
| "_________________________________________________________________\n", |
| "conv2d_2 (Conv2D) (None, 24, 24, 32) 9248 \n", |
| "_________________________________________________________________\n", |
| "activation_2 (Activation) (None, 24, 24, 32) 0 \n", |
| "_________________________________________________________________\n", |
| "max_pooling2d_1 (MaxPooling2 (None, 12, 12, 32) 0 \n", |
| "_________________________________________________________________\n", |
| "dropout_1 (Dropout) (None, 12, 12, 32) 0 \n", |
| "_________________________________________________________________\n", |
| "flatten_1 (Flatten) (None, 4608) 0 \n", |
| "_________________________________________________________________\n", |
| "dense_1 (Dense) (None, 128) 589952 \n", |
| "_________________________________________________________________\n", |
| "activation_3 (Activation) (None, 128) 0 \n", |
| "_________________________________________________________________\n", |
| "dropout_2 (Dropout) (None, 128) 0 \n", |
| "_________________________________________________________________\n", |
| "dense_2 (Dense) (None, 10) 1290 \n", |
| "_________________________________________________________________\n", |
| "activation_4 (Activation) (None, 10) 0 \n", |
| "=================================================================\n", |
| "Total params: 600,810\n", |
| "Trainable params: 600,810\n", |
| "Non-trainable params: 0\n", |
| "_________________________________________________________________\n" |
| ] |
| } |
| ], |
| "source": [ |
| "# define two groups of layers: feature (convolutions) and classification (dense)\n", |
| "feature_layers = [\n", |
| " Conv2D(filters, kernel_size,\n", |
| " padding='valid',\n", |
| " input_shape=input_shape),\n", |
| " Activation('relu'),\n", |
| " Conv2D(filters, kernel_size),\n", |
| " Activation('relu'),\n", |
| " MaxPooling2D(pool_size=pool_size),\n", |
| " Dropout(0.25),\n", |
| " Flatten(),\n", |
| "]\n", |
| "\n", |
| "classification_layers = [\n", |
| " Dense(128),\n", |
| " Activation('relu'),\n", |
| " Dropout(0.5),\n", |
| " Dense(num_classes),\n", |
| " Activation('softmax')\n", |
| "]\n", |
| "\n", |
| "# create complete model\n", |
| "model = Sequential(feature_layers + classification_layers)\n", |
| "\n", |
| "model.summary()" |
| ] |
| }, |
| { |
| "cell_type": "markdown", |
| "metadata": {}, |
| "source": [ |
| "Load into model architecture table using psycopg2" |
| ] |
| }, |
| { |
| "cell_type": "code", |
| "execution_count": 9, |
| "metadata": {}, |
| "outputs": [ |
| { |
| "name": "stdout", |
| "output_type": "stream", |
| "text": [ |
| "Done.\n", |
| "1 rows affected.\n" |
| ] |
| }, |
| { |
| "data": { |
| "text/html": [ |
| "<table>\n", |
| " <tr>\n", |
| " <th>model_id</th>\n", |
| " <th>name</th>\n", |
| " </tr>\n", |
| " <tr>\n", |
| " <td>1</td>\n", |
| " <td>feature + classification layers trainable</td>\n", |
| " </tr>\n", |
| "</table>" |
| ], |
| "text/plain": [ |
| "[(1, u'feature + classification layers trainable')]" |
| ] |
| }, |
| "execution_count": 9, |
| "metadata": {}, |
| "output_type": "execute_result" |
| } |
| ], |
| "source": [ |
| "%sql DROP TABLE IF EXISTS model_arch_table_mnist;\n", |
| "query = \"SELECT madlib.load_keras_model('model_arch_table_mnist', %s, NULL, %s)\"\n", |
| "cur.execute(query,[model.to_json(), \"feature + classification layers trainable\"])\n", |
| "conn.commit()\n", |
| "\n", |
| "# check model loaded OK\n", |
| "%sql SELECT model_id, name FROM model_arch_table_mnist;" |
| ] |
| }, |
| { |
| "cell_type": "markdown", |
| "metadata": {}, |
| "source": [ |
| "<a id=\"hyperband\"></a>\n", |
| "# 5. Hyperband diagonal" |
| ] |
| }, |
| { |
| "cell_type": "markdown", |
| "metadata": {}, |
| "source": [ |
| "Create tables" |
| ] |
| }, |
| { |
| "cell_type": "code", |
| "execution_count": 34, |
| "metadata": {}, |
| "outputs": [ |
| { |
| "name": "stdout", |
| "output_type": "stream", |
| "text": [ |
| "Done.\n", |
| "Done.\n", |
| "Done.\n", |
| "Done.\n", |
| "Done.\n", |
| "Done.\n", |
| "1 rows affected.\n", |
| "Done.\n", |
| "Done.\n", |
| "Done.\n", |
| "Done.\n", |
| "1 rows affected.\n" |
| ] |
| }, |
| { |
| "data": { |
| "text/plain": [ |
| "[]" |
| ] |
| }, |
| "execution_count": 34, |
| "metadata": {}, |
| "output_type": "execute_result" |
| } |
| ], |
| "source": [ |
| "%%sql\n", |
| "-- overall results table\n", |
| "DROP TABLE IF EXISTS results_mnist;\n", |
| "CREATE TABLE results_mnist ( \n", |
| " mst_key INTEGER, -- note not SERIAL\n", |
| " model_id INTEGER, \n", |
| " compile_params TEXT,\n", |
| " fit_params TEXT, \n", |
| " model_type TEXT, \n", |
| " model_size DOUBLE PRECISION, \n", |
| " metrics_elapsed_time DOUBLE PRECISION[], \n", |
| " metrics_type TEXT[], \n", |
| " training_metrics_final DOUBLE PRECISION, \n", |
| " training_loss_final DOUBLE PRECISION, \n", |
| " training_metrics DOUBLE PRECISION[], \n", |
| " training_loss DOUBLE PRECISION[], \n", |
| " validation_metrics_final DOUBLE PRECISION, \n", |
| " validation_loss_final DOUBLE PRECISION, \n", |
| " validation_metrics DOUBLE PRECISION[], \n", |
| " validation_loss DOUBLE PRECISION[], \n", |
| " model_arch_table TEXT, \n", |
| " num_iterations INTEGER, \n", |
| " start_training_time TIMESTAMP, \n", |
| " end_training_time TIMESTAMP,\n", |
| " s INTEGER, \n", |
| " i INTEGER,\n", |
| " run_id SERIAL\n", |
| " );\n", |
| "\n", |
| "-- model selection table\n", |
| "DROP TABLE IF EXISTS mst_table_hb_mnist;\n", |
| "CREATE TABLE mst_table_hb_mnist (\n", |
| " mst_key SERIAL, \n", |
| " s INTEGER, -- bracket\n", |
| " model_id INTEGER, \n", |
| " compile_params VARCHAR, \n", |
| " fit_params VARCHAR\n", |
| " );\n", |
| "\n", |
| "-- model selection summary table\n", |
| "DROP TABLE IF EXISTS mst_table_hb_mnist_summary;\n", |
| "CREATE TABLE mst_table_hb_mnist_summary (model_arch_table VARCHAR);\n", |
| "INSERT INTO mst_table_hb_mnist_summary VALUES ('model_arch_table_mnist');\n", |
| "\n", |
| "-- model selection table for diagonal\n", |
| "DROP TABLE IF EXISTS mst_diag_table_hb_mnist;\n", |
| "CREATE TABLE mst_diag_table_hb_mnist (\n", |
| " mst_key INTEGER, -- note not SERIAL since this table derived from main model selection table\n", |
| " s INTEGER, -- bracket\n", |
| " model_id INTEGER, \n", |
| " compile_params VARCHAR, \n", |
| " fit_params VARCHAR\n", |
| " );\n", |
| "\n", |
| "-- model selection summary table for diagonal\n", |
| "DROP TABLE IF EXISTS mst_diag_table_hb_mnist_summary;\n", |
| "CREATE TABLE mst_diag_table_hb_mnist_summary (model_arch_table VARCHAR);\n", |
| "INSERT INTO mst_diag_table_hb_mnist_summary VALUES ('model_arch_table_mnist');" |
| ] |
| }, |
| { |
| "cell_type": "markdown", |
| "metadata": {}, |
| "source": [ |
| "Generalize table names" |
| ] |
| }, |
| { |
| "cell_type": "code", |
| "execution_count": 35, |
| "metadata": {}, |
| "outputs": [], |
| "source": [ |
| "results_table = 'results_mnist'\n", |
| "\n", |
| "output_table = 'mnist_multi_model'\n", |
| "output_table_info = '_'.join([output_table, 'info'])\n", |
| "output_table_summary = '_'.join([output_table, 'summary'])\n", |
| "\n", |
| "mst_table = 'mst_table_hb_mnist'\n", |
| "mst_table_summary = '_'.join([mst_table, 'summary'])\n", |
| "\n", |
| "mst_diag_table = 'mst_diag_table_hb_mnist'\n", |
| "mst_diag_table_summary = '_'.join([mst_diag_table, 'summary'])\n", |
| "\n", |
| "model_arch_table = 'model_arch_library_mnist'" |
| ] |
| }, |
| { |
| "cell_type": "markdown", |
| "metadata": {}, |
| "source": [ |
| "Hyperband diagonal logic" |
| ] |
| }, |
| { |
| "cell_type": "code", |
| "execution_count": 36, |
| "metadata": {}, |
| "outputs": [], |
| "source": [ |
| "import numpy as np\n", |
| "from random import random\n", |
| "from math import log, ceil\n", |
| "from time import time, ctime\n", |
| "\n", |
| "class Hyperband_diagonal:\n", |
| " \n", |
| " def __init__( self, get_params_function, try_params_function ):\n", |
| " self.get_params = get_params_function\n", |
| " self.try_params = try_params_function\n", |
| "\n", |
| " self.max_iter = 9 # maximum iterations per configuration\n", |
| " self.eta = 3 # defines downsampling rate (default = 3)\n", |
| "\n", |
| " self.logeta = lambda x: log( x ) / log( self.eta )\n", |
| " self.s_max = int( self.logeta( self.max_iter ))\n", |
| " self.B = ( self.s_max + 1 ) * self.max_iter\n", |
| " self.setup_full_schedule()\n", |
| " self.create_mst_superset()\n", |
| " \n", |
| " self.best_loss = np.inf\n", |
| " self.best_accuracy = 0.0\n", |
| "\n", |
| " \n", |
| " # create full Hyperband schedule for all brackets ahead of time\n", |
| " def setup_full_schedule(self):\n", |
| " self.n_vals = np.zeros((self.s_max+1, self.s_max+1), dtype=int)\n", |
| " self.r_vals = np.zeros((self.s_max+1, self.s_max+1), dtype=int)\n", |
| " sum_leaf_n_i = 0 # count configurations at leaf nodes across all s\n", |
| " \n", |
| " print (\" \")\n", |
| " print (\"Hyperband brackets\")\n", |
| "\n", |
| " # loop through each bracket in reverse order\n", |
| " for s in reversed(range(self.s_max+1)):\n", |
| " \n", |
| " print (\" \")\n", |
| " print (\"s=\" + str(s))\n", |
| " print (\"n_i r_i\")\n", |
| " print (\"------------\")\n", |
| " counter = 0\n", |
| "\n", |
| " n = int(ceil(int(self.B/self.max_iter/(s+1))*self.eta**s)) # initial number of configurations\n", |
| " r = self.max_iter*self.eta**(-s) # initial number of iterations to run configurations for\n", |
| "\n", |
| " #### Begin Finite Horizon Successive Halving with (n,r)\n", |
| " for i in range(s+1):\n", |
| " # n_i configs for r_i iterations\n", |
| " n_i = n*self.eta**(-i)\n", |
| " r_i = r*self.eta**(i)\n", |
| "\n", |
| " self.n_vals[s][i] = n_i\n", |
| " self.r_vals[s][i] = r_i\n", |
| "\n", |
| " print (str(n_i) + \" \" + str (r_i))\n", |
| "\n", |
| " # check if leaf node for this s\n", |
| " if counter == s:\n", |
| " sum_leaf_n_i += n_i\n", |
| " counter += 1\n", |
| " \n", |
| " #### End Finite Horizon Successive Halving with (n,r)\n", |
| "\n", |
| " #print (\" \")\n", |
| " #print (\"sum of configurations at leaf nodes across all s = \" + str(sum_leaf_n_i))\n", |
| " #print (\"(if have more workers than this, they may not be 100% busy)\")\n", |
| " \n", |
| " \n", |
| " # generate model selection tuples for all brackets\n", |
| " def create_mst_superset(self):\n", |
| " # get hyper parameter configs for each bracket s\n", |
| " for s in reversed(range(self.s_max+1)):\n", |
| " n = int(ceil(int(self.B/self.max_iter/(s+1))*self.eta**s)) # initial number of configurations\n", |
| " r = self.max_iter*self.eta**(-s) # initial number of iterations to run configurations for\n", |
| "\n", |
| " print (\" \")\n", |
| " print (\"Create superset of MSTs, i.e., i=0 for for each bracket s\")\n", |
| " print (\" \")\n", |
| " print (\"s=\" + str(s))\n", |
| " print (\"n=\" + str(n))\n", |
| " print (\"r=\" + str(r))\n", |
| " print (\" \")\n", |
| " \n", |
| " # n random configurations for each bracket s\n", |
| " self.get_params(n, s)\n", |
| " \n", |
| " \n", |
| " # Hyperband diagonal logic\n", |
| " def run( self, skip_last = 0, dry_run = False ): \n", |
| " \n", |
| " print (\" \")\n", |
| " print (\"Hyperband diagonal\")\n", |
| " print (\"outer loop on diagonal:\")\n", |
| " \n", |
| " # outer loop on diagonal\n", |
| " for i in range(self.s_max+1):\n", |
| " print (\" \")\n", |
| " print (\"i=\" + str(i))\n", |
| " \n", |
| " # zero out diagonal table\n", |
| " %sql TRUNCATE TABLE $mst_diag_table\n", |
| " \n", |
| " # loop on brackets s desc to create diagonal table\n", |
| " print (\"loop on s desc to create diagonal table:\")\n", |
| " for s in range(self.s_max, self.s_max-i-1, -1):\n", |
| "\n", |
| " # build up mst table for diagonal\n", |
| " %sql INSERT INTO $mst_diag_table (SELECT * FROM $mst_table WHERE s=$s);\n", |
| " \n", |
| " # first pass\n", |
| " if i == 0:\n", |
| " first_pass = True\n", |
| " else:\n", |
| " first_pass = False\n", |
| " \n", |
| " # multi-model training\n", |
| " print (\" \")\n", |
| " print (\"try params for i = \" + str(i))\n", |
| " U = self.try_params(i, self.r_vals[self.s_max][i], first_pass) # r_i is the same for all diagonal elements\n", |
| " \n", |
| " # loop on brackets s desc to prune model selection table\n", |
| " # don't need to prune if finished last diagonal\n", |
| " if i < self.s_max:\n", |
| " print (\"loop on s desc to prune mst table:\")\n", |
| " for s in range(self.s_max, self.s_max-i-1, -1):\n", |
| " \n", |
| " # compute number of configs to keep\n", |
| " # remember i value is different for each bracket s on the diagonal\n", |
| " k = int( self.n_vals[s][s-self.s_max+i] / self.eta)\n", |
| " print (\"pruning s = {} with k = {}\".format(s, k))\n", |
| "\n", |
| " # temporarily re-define table names due to weird Python scope issues\n", |
| " results_table = 'results_mnist'\n", |
| "\n", |
| " output_table = 'mnist_multi_model'\n", |
| " output_table_info = '_'.join([output_table, 'info'])\n", |
| " output_table_summary = '_'.join([output_table, 'summary'])\n", |
| "\n", |
| " mst_table = 'mst_table_hb_mnist'\n", |
| " mst_table_summary = '_'.join([mst_table, 'summary'])\n", |
| "\n", |
| " mst_diag_table = 'mst_diag_table_hb_mnist'\n", |
| " mst_diag_table_summary = '_'.join([mst_diag_table, 'summary'])\n", |
| "\n", |
| " model_arch_table = 'model_arch_library_mnist'\n", |
| " \n", |
| " query = \"\"\"\n", |
| " DELETE FROM {mst_table} WHERE s={s} AND mst_key NOT IN (SELECT {output_table_info}.mst_key FROM {output_table_info} JOIN {mst_table} ON {output_table_info}.mst_key={mst_table}.mst_key WHERE s={s} ORDER BY validation_loss_final ASC LIMIT {k}::INT);\n", |
| " \"\"\".format(**locals())\n", |
| " cur.execute(query)\n", |
| " conn.commit()\n", |
| " \n", |
| " # these were not working so used cursor instead\n", |
| " #%sql DELETE FROM $mst_table WHERE s=$s AND mst_key NOT IN (SELECT $output_table_info.mst_key FROM $output_table_info JOIN $mst_table ON $output_table_info.mst_key=$mst_table.mst_key WHERE s=$s ORDER BY validation_loss_final ASC LIMIT $k::INT);\n", |
| " #%sql DELETE FROM mst_table_hb_mnist WHERE s=1 AND mst_key NOT IN (SELECT mnist_multi_model_info.mst_key FROM mnist_multi_model_info JOIN mst_table_hb_mnist ON mnist_multi_model_info.mst_key=mst_table_hb_mnist.mst_key WHERE s=1 ORDER BY validation_loss_final ASC LIMIT 1);\n", |
| " \n", |
| " # keep track of best loss so far (for display purposes only)\n", |
| " loss = %sql SELECT validation_loss_final FROM $output_table_info ORDER BY validation_loss_final ASC LIMIT 1;\n", |
| " accuracy = %sql SELECT validation_metrics_final FROM $output_table_info ORDER BY validation_loss_final ASC LIMIT 1;\n", |
| " \n", |
| " if loss < self.best_loss:\n", |
| " self.best_loss = loss\n", |
| " self.best_accuracy = accuracy\n", |
| " \n", |
| " print (\" \")\n", |
| " print (\"best validation loss so far = \" + str(loss))\n", |
| " print (\"best validation accuracy so far = \" + str(accuracy))\n", |
| " \n", |
| " return" |
| ] |
| }, |
| { |
| "cell_type": "markdown", |
| "metadata": {}, |
| "source": [ |
| "Generate params and insert into MST table" |
| ] |
| }, |
| { |
| "cell_type": "code", |
| "execution_count": 37, |
| "metadata": {}, |
| "outputs": [], |
| "source": [ |
| "def get_params(n, s):\n", |
| " \n", |
| " from sklearn.model_selection import ParameterSampler\n", |
| " from scipy.stats.distributions import uniform\n", |
| " import numpy as np\n", |
| " \n", |
| " # model architecture\n", |
| " model_id = [1]\n", |
| "\n", |
| " # compile params\n", |
| " # loss function\n", |
| " loss = ['categorical_crossentropy']\n", |
| " # optimizer\n", |
| " optimizer = ['Adam', 'SGD']\n", |
| " # learning rate (sample on log scale here not in ParameterSampler)\n", |
| " lr_range = [0.001, 0.1]\n", |
| " lr = 10**np.random.uniform(np.log10(lr_range[0]), np.log10(lr_range[1]), n)\n", |
| " # metrics\n", |
| " metrics = ['accuracy']\n", |
| "\n", |
| " # fit params\n", |
| " # batch size\n", |
| " batch_size = [32, 64, 128]\n", |
| " # epochs\n", |
| " epochs = [1]\n", |
| "\n", |
| " # create random param list\n", |
| " param_grid = {\n", |
| " 'model_id': model_id,\n", |
| " 'loss': loss,\n", |
| " 'optimizer': optimizer,\n", |
| " 'lr': lr,\n", |
| " 'metrics': metrics,\n", |
| " 'batch_size': batch_size,\n", |
| " 'epochs': epochs\n", |
| " }\n", |
| " param_list = list(ParameterSampler(param_grid, n_iter=n))\n", |
| " \n", |
| " for params in param_list:\n", |
| "\n", |
| " model_id = str(params.get(\"model_id\"))\n", |
| " compile_params = \"$$loss='\" + str(params.get(\"loss\")) + \"',optimizer='\" + str(params.get(\"optimizer\")) + \"(lr=\" + str(params.get(\"lr\")) + \")',metrics=['\" + str(params.get(\"metrics\")) + \"']$$\" \n", |
| " fit_params = \"$$batch_size=\" + str(params.get(\"batch_size\")) + \",epochs=\" + str(params.get(\"epochs\")) + \"$$\" \n", |
| " row_content = \"(\" + str(s) + \", \" + model_id + \", \" + compile_params + \", \" + fit_params + \");\"\n", |
| " \n", |
| " %sql INSERT INTO $mst_table (s, model_id, compile_params, fit_params) VALUES $row_content\n", |
| " \n", |
| " return" |
| ] |
| }, |
| { |
| "cell_type": "markdown", |
| "metadata": {}, |
| "source": [ |
| "Run model hopper for candidates in MST table" |
| ] |
| }, |
| { |
| "cell_type": "code", |
| "execution_count": 38, |
| "metadata": {}, |
| "outputs": [], |
| "source": [ |
| "def try_params(i, r, first_pass):\n", |
| " \n", |
| " # multi-model fit\n", |
| " if first_pass:\n", |
| " # cold start\n", |
| " %sql DROP TABLE IF EXISTS $output_table, $output_table_summary, $output_table_info;\n", |
| " # passing vars as madlib args does not seem to work\n", |
| " #%sql SELECT madlib.madlib_keras_fit_multiple_model('train_mnist_packed', $output_table, $mst_diag_table, $r_i::INT, 0);\n", |
| " %sql SELECT madlib.madlib_keras_fit_multiple_model('train_mnist_packed', 'mnist_multi_model', 'mst_diag_table_hb_mnist', $r::INT, FALSE, 'test_mnist_packed');\n", |
| "\n", |
| " else:\n", |
| " # warm start to continue from previous run\n", |
| " %sql SELECT madlib.madlib_keras_fit_multiple_model('train_mnist_packed', 'mnist_multi_model', 'mst_diag_table_hb_mnist', $r::INT, FALSE, 'test_mnist_packed', NULL, True);\n", |
| "\n", |
| " # save results via temp table\n", |
| " # add everything from info table\n", |
| " %sql DROP TABLE IF EXISTS temp_results;\n", |
| " %sql CREATE TABLE temp_results AS (SELECT * FROM $output_table_info);\n", |
| " \n", |
| " # add summary table info and i value (same for each row)\n", |
| " %sql ALTER TABLE temp_results ADD COLUMN model_arch_table TEXT, ADD COLUMN num_iterations INTEGER, ADD COLUMN start_training_time TIMESTAMP, ADD COLUMN end_training_time TIMESTAMP, ADD COLUMN s INTEGER, ADD COLUMN i INTEGER;\n", |
| " %sql UPDATE temp_results SET model_arch_table = (SELECT model_arch_table FROM $output_table_summary), num_iterations = (SELECT num_iterations FROM $output_table_summary), start_training_time = (SELECT start_training_time FROM $output_table_summary), end_training_time = (SELECT end_training_time FROM $output_table_summary), i = $i;\n", |
| " \n", |
| " # get the s value for each run (not the same for each row since diagonal table crosses multiple brackets)\n", |
| " %sql UPDATE temp_results SET s = m.s FROM mst_diag_table_hb_mnist AS m WHERE m.mst_key = temp_results.mst_key;\n", |
| " \n", |
| " # copy temp table into results table\n", |
| " %sql INSERT INTO $results_table (SELECT * FROM temp_results);\n", |
| "\n", |
| " return" |
| ] |
| }, |
| { |
| "cell_type": "markdown", |
| "metadata": {}, |
| "source": [ |
| "Call Hyperband diagonal" |
| ] |
| }, |
| { |
| "cell_type": "code", |
| "execution_count": null, |
| "metadata": { |
| "scrolled": false |
| }, |
| "outputs": [ |
| { |
| "name": "stdout", |
| "output_type": "stream", |
| "text": [ |
| " \n", |
| "Hyperband brackets\n", |
| " \n", |
| "s=2\n", |
| "n_i r_i\n", |
| "------------\n", |
| "9 1.0\n", |
| "3.0 3.0\n", |
| "1.0 9.0\n", |
| " \n", |
| "s=1\n", |
| "n_i r_i\n", |
| "------------\n", |
| "3 3.0\n", |
| "1.0 9.0\n", |
| " \n", |
| "s=0\n", |
| "n_i r_i\n", |
| "------------\n", |
| "3 9\n", |
| " \n", |
| "Create superset of MSTs, i.e., i=0 for for each bracket s\n", |
| " \n", |
| "s=2\n", |
| "n=9\n", |
| "r=1.0\n", |
| " \n", |
| "1 rows affected.\n", |
| "1 rows affected.\n", |
| "1 rows affected.\n", |
| "1 rows affected.\n", |
| "1 rows affected.\n", |
| "1 rows affected.\n", |
| "1 rows affected.\n", |
| "1 rows affected.\n", |
| "1 rows affected.\n", |
| " \n", |
| "Create superset of MSTs, i.e., i=0 for for each bracket s\n", |
| " \n", |
| "s=1\n", |
| "n=3\n", |
| "r=3.0\n", |
| " \n", |
| "1 rows affected.\n", |
| "1 rows affected.\n", |
| "1 rows affected.\n", |
| " \n", |
| "Create superset of MSTs, i.e., i=0 for for each bracket s\n", |
| " \n", |
| "s=0\n", |
| "n=3\n", |
| "r=9\n", |
| " \n", |
| "1 rows affected.\n", |
| "1 rows affected.\n", |
| "1 rows affected.\n", |
| " \n", |
| "Hyperband diagonal\n", |
| "outer loop on diagonal:\n", |
| " \n", |
| "i=0\n", |
| "Done.\n", |
| "loop on s desc to create diagonal table:\n", |
| "9 rows affected.\n", |
| " \n", |
| "try params for i = 0\n", |
| "Done.\n" |
| ] |
| } |
| ], |
| "source": [ |
| "hp = Hyperband_diagonal(get_params, try_params )\n", |
| "results = hp.run()\n", |
| "#hp.n_vals[1]" |
| ] |
| }, |
| { |
| "cell_type": "markdown", |
| "metadata": {}, |
| "source": [ |
| "<a id=\"plot\"></a>\n", |
| "# 6. Plot results" |
| ] |
| }, |
| { |
| "cell_type": "code", |
| "execution_count": 22, |
| "metadata": {}, |
| "outputs": [], |
| "source": [ |
| "%matplotlib notebook\n", |
| "import matplotlib.pyplot as plt\n", |
| "from matplotlib.ticker import MaxNLocator\n", |
| "from collections import defaultdict\n", |
| "import pandas as pd\n", |
| "import seaborn as sns\n", |
| "sns.set_palette(sns.color_palette(\"hls\", 20))\n", |
| "plt.rcParams.update({'font.size': 12})\n", |
| "pd.set_option('display.max_colwidth', -1)" |
| ] |
| }, |
| { |
| "cell_type": "markdown", |
| "metadata": {}, |
| "source": [ |
| "Training dataset" |
| ] |
| }, |
| { |
| "cell_type": "code", |
| "execution_count": 30, |
| "metadata": {}, |
| "outputs": [ |
| { |
| "name": "stdout", |
| "output_type": "stream", |
| "text": [ |
| "8 rows affected.\n" |
| ] |
| }, |
| { |
| "data": { |
| "application/javascript": [ |
| "/* Put everything inside the global mpl namespace */\n", |
| "window.mpl = {};\n", |
| "\n", |
| "\n", |
| "mpl.get_websocket_type = function() {\n", |
| " if (typeof(WebSocket) !== 'undefined') {\n", |
| " return WebSocket;\n", |
| " } else if (typeof(MozWebSocket) !== 'undefined') {\n", |
| " return MozWebSocket;\n", |
| " } else {\n", |
| " alert('Your browser does not have WebSocket support.' +\n", |
| " 'Please try Chrome, Safari or Firefox ≥ 6. ' +\n", |
| " 'Firefox 4 and 5 are also supported but you ' +\n", |
| " 'have to enable WebSockets in about:config.');\n", |
| " };\n", |
| "}\n", |
| "\n", |
| "mpl.figure = function(figure_id, websocket, ondownload, parent_element) {\n", |
| " this.id = figure_id;\n", |
| "\n", |
| " this.ws = websocket;\n", |
| "\n", |
| " this.supports_binary = (this.ws.binaryType != undefined);\n", |
| "\n", |
| " if (!this.supports_binary) {\n", |
| " var warnings = document.getElementById(\"mpl-warnings\");\n", |
| " if (warnings) {\n", |
| " warnings.style.display = 'block';\n", |
| " warnings.textContent = (\n", |
| " \"This browser does not support binary websocket messages. \" +\n", |
| " \"Performance may be slow.\");\n", |
| " }\n", |
| " }\n", |
| "\n", |
| " this.imageObj = new Image();\n", |
| "\n", |
| " this.context = undefined;\n", |
| " this.message = undefined;\n", |
| " this.canvas = undefined;\n", |
| " this.rubberband_canvas = undefined;\n", |
| " this.rubberband_context = undefined;\n", |
| " this.format_dropdown = undefined;\n", |
| "\n", |
| " this.image_mode = 'full';\n", |
| "\n", |
| " this.root = $('<div/>');\n", |
| " this._root_extra_style(this.root)\n", |
| " this.root.attr('style', 'display: inline-block');\n", |
| "\n", |
| " $(parent_element).append(this.root);\n", |
| "\n", |
| " this._init_header(this);\n", |
| " this._init_canvas(this);\n", |
| " this._init_toolbar(this);\n", |
| "\n", |
| " var fig = this;\n", |
| "\n", |
| " this.waiting = false;\n", |
| "\n", |
| " this.ws.onopen = function () {\n", |
| " fig.send_message(\"supports_binary\", {value: fig.supports_binary});\n", |
| " fig.send_message(\"send_image_mode\", {});\n", |
| " if (mpl.ratio != 1) {\n", |
| " fig.send_message(\"set_dpi_ratio\", {'dpi_ratio': mpl.ratio});\n", |
| " }\n", |
| " fig.send_message(\"refresh\", {});\n", |
| " }\n", |
| "\n", |
| " this.imageObj.onload = function() {\n", |
| " if (fig.image_mode == 'full') {\n", |
| " // Full images could contain transparency (where diff images\n", |
| " // almost always do), so we need to clear the canvas so that\n", |
| " // there is no ghosting.\n", |
| " fig.context.clearRect(0, 0, fig.canvas.width, fig.canvas.height);\n", |
| " }\n", |
| " fig.context.drawImage(fig.imageObj, 0, 0);\n", |
| " };\n", |
| "\n", |
| " this.imageObj.onunload = function() {\n", |
| " fig.ws.close();\n", |
| " }\n", |
| "\n", |
| " this.ws.onmessage = this._make_on_message_function(this);\n", |
| "\n", |
| " this.ondownload = ondownload;\n", |
| "}\n", |
| "\n", |
| "mpl.figure.prototype._init_header = function() {\n", |
| " var titlebar = $(\n", |
| " '<div class=\"ui-dialog-titlebar ui-widget-header ui-corner-all ' +\n", |
| " 'ui-helper-clearfix\"/>');\n", |
| " var titletext = $(\n", |
| " '<div class=\"ui-dialog-title\" style=\"width: 100%; ' +\n", |
| " 'text-align: center; padding: 3px;\"/>');\n", |
| " titlebar.append(titletext)\n", |
| " this.root.append(titlebar);\n", |
| " this.header = titletext[0];\n", |
| "}\n", |
| "\n", |
| "\n", |
| "\n", |
| "mpl.figure.prototype._canvas_extra_style = function(canvas_div) {\n", |
| "\n", |
| "}\n", |
| "\n", |
| "\n", |
| "mpl.figure.prototype._root_extra_style = function(canvas_div) {\n", |
| "\n", |
| "}\n", |
| "\n", |
| "mpl.figure.prototype._init_canvas = function() {\n", |
| " var fig = this;\n", |
| "\n", |
| " var canvas_div = $('<div/>');\n", |
| "\n", |
| " canvas_div.attr('style', 'position: relative; clear: both; outline: 0');\n", |
| "\n", |
| " function canvas_keyboard_event(event) {\n", |
| " return fig.key_event(event, event['data']);\n", |
| " }\n", |
| "\n", |
| " canvas_div.keydown('key_press', canvas_keyboard_event);\n", |
| " canvas_div.keyup('key_release', canvas_keyboard_event);\n", |
| " this.canvas_div = canvas_div\n", |
| " this._canvas_extra_style(canvas_div)\n", |
| " this.root.append(canvas_div);\n", |
| "\n", |
| " var canvas = $('<canvas/>');\n", |
| " canvas.addClass('mpl-canvas');\n", |
| " canvas.attr('style', \"left: 0; top: 0; z-index: 0; outline: 0\")\n", |
| "\n", |
| " this.canvas = canvas[0];\n", |
| " this.context = canvas[0].getContext(\"2d\");\n", |
| "\n", |
| " var backingStore = this.context.backingStorePixelRatio ||\n", |
| "\tthis.context.webkitBackingStorePixelRatio ||\n", |
| "\tthis.context.mozBackingStorePixelRatio ||\n", |
| "\tthis.context.msBackingStorePixelRatio ||\n", |
| "\tthis.context.oBackingStorePixelRatio ||\n", |
| "\tthis.context.backingStorePixelRatio || 1;\n", |
| "\n", |
| " mpl.ratio = (window.devicePixelRatio || 1) / backingStore;\n", |
| "\n", |
| " var rubberband = $('<canvas/>');\n", |
| " rubberband.attr('style', \"position: absolute; left: 0; top: 0; z-index: 1;\")\n", |
| "\n", |
| " var pass_mouse_events = true;\n", |
| "\n", |
| " canvas_div.resizable({\n", |
| " start: function(event, ui) {\n", |
| " pass_mouse_events = false;\n", |
| " },\n", |
| " resize: function(event, ui) {\n", |
| " fig.request_resize(ui.size.width, ui.size.height);\n", |
| " },\n", |
| " stop: function(event, ui) {\n", |
| " pass_mouse_events = true;\n", |
| " fig.request_resize(ui.size.width, ui.size.height);\n", |
| " },\n", |
| " });\n", |
| "\n", |
| " function mouse_event_fn(event) {\n", |
| " if (pass_mouse_events)\n", |
| " return fig.mouse_event(event, event['data']);\n", |
| " }\n", |
| "\n", |
| " rubberband.mousedown('button_press', mouse_event_fn);\n", |
| " rubberband.mouseup('button_release', mouse_event_fn);\n", |
| " // Throttle sequential mouse events to 1 every 20ms.\n", |
| " rubberband.mousemove('motion_notify', mouse_event_fn);\n", |
| "\n", |
| " rubberband.mouseenter('figure_enter', mouse_event_fn);\n", |
| " rubberband.mouseleave('figure_leave', mouse_event_fn);\n", |
| "\n", |
| " canvas_div.on(\"wheel\", function (event) {\n", |
| " event = event.originalEvent;\n", |
| " event['data'] = 'scroll'\n", |
| " if (event.deltaY < 0) {\n", |
| " event.step = 1;\n", |
| " } else {\n", |
| " event.step = -1;\n", |
| " }\n", |
| " mouse_event_fn(event);\n", |
| " });\n", |
| "\n", |
| " canvas_div.append(canvas);\n", |
| " canvas_div.append(rubberband);\n", |
| "\n", |
| " this.rubberband = rubberband;\n", |
| " this.rubberband_canvas = rubberband[0];\n", |
| " this.rubberband_context = rubberband[0].getContext(\"2d\");\n", |
| " this.rubberband_context.strokeStyle = \"#000000\";\n", |
| "\n", |
| " this._resize_canvas = function(width, height) {\n", |
| " // Keep the size of the canvas, canvas container, and rubber band\n", |
| " // canvas in synch.\n", |
| " canvas_div.css('width', width)\n", |
| " canvas_div.css('height', height)\n", |
| "\n", |
| " canvas.attr('width', width * mpl.ratio);\n", |
| " canvas.attr('height', height * mpl.ratio);\n", |
| " canvas.attr('style', 'width: ' + width + 'px; height: ' + height + 'px;');\n", |
| "\n", |
| " rubberband.attr('width', width);\n", |
| " rubberband.attr('height', height);\n", |
| " }\n", |
| "\n", |
| " // Set the figure to an initial 600x600px, this will subsequently be updated\n", |
| " // upon first draw.\n", |
| " this._resize_canvas(600, 600);\n", |
| "\n", |
| " // Disable right mouse context menu.\n", |
| " $(this.rubberband_canvas).bind(\"contextmenu\",function(e){\n", |
| " return false;\n", |
| " });\n", |
| "\n", |
| " function set_focus () {\n", |
| " canvas.focus();\n", |
| " canvas_div.focus();\n", |
| " }\n", |
| "\n", |
| " window.setTimeout(set_focus, 100);\n", |
| "}\n", |
| "\n", |
| "mpl.figure.prototype._init_toolbar = function() {\n", |
| " var fig = this;\n", |
| "\n", |
| " var nav_element = $('<div/>')\n", |
| " nav_element.attr('style', 'width: 100%');\n", |
| " this.root.append(nav_element);\n", |
| "\n", |
| " // Define a callback function for later on.\n", |
| " function toolbar_event(event) {\n", |
| " return fig.toolbar_button_onclick(event['data']);\n", |
| " }\n", |
| " function toolbar_mouse_event(event) {\n", |
| " return fig.toolbar_button_onmouseover(event['data']);\n", |
| " }\n", |
| "\n", |
| " for(var toolbar_ind in mpl.toolbar_items) {\n", |
| " var name = mpl.toolbar_items[toolbar_ind][0];\n", |
| " var tooltip = mpl.toolbar_items[toolbar_ind][1];\n", |
| " var image = mpl.toolbar_items[toolbar_ind][2];\n", |
| " var method_name = mpl.toolbar_items[toolbar_ind][3];\n", |
| "\n", |
| " if (!name) {\n", |
| " // put a spacer in here.\n", |
| " continue;\n", |
| " }\n", |
| " var button = $('<button/>');\n", |
| " button.addClass('ui-button ui-widget ui-state-default ui-corner-all ' +\n", |
| " 'ui-button-icon-only');\n", |
| " button.attr('role', 'button');\n", |
| " button.attr('aria-disabled', 'false');\n", |
| " button.click(method_name, toolbar_event);\n", |
| " button.mouseover(tooltip, toolbar_mouse_event);\n", |
| "\n", |
| " var icon_img = $('<span/>');\n", |
| " icon_img.addClass('ui-button-icon-primary ui-icon');\n", |
| " icon_img.addClass(image);\n", |
| " icon_img.addClass('ui-corner-all');\n", |
| "\n", |
| " var tooltip_span = $('<span/>');\n", |
| " tooltip_span.addClass('ui-button-text');\n", |
| " tooltip_span.html(tooltip);\n", |
| "\n", |
| " button.append(icon_img);\n", |
| " button.append(tooltip_span);\n", |
| "\n", |
| " nav_element.append(button);\n", |
| " }\n", |
| "\n", |
| " var fmt_picker_span = $('<span/>');\n", |
| "\n", |
| " var fmt_picker = $('<select/>');\n", |
| " fmt_picker.addClass('mpl-toolbar-option ui-widget ui-widget-content');\n", |
| " fmt_picker_span.append(fmt_picker);\n", |
| " nav_element.append(fmt_picker_span);\n", |
| " this.format_dropdown = fmt_picker[0];\n", |
| "\n", |
| " for (var ind in mpl.extensions) {\n", |
| " var fmt = mpl.extensions[ind];\n", |
| " var option = $(\n", |
| " '<option/>', {selected: fmt === mpl.default_extension}).html(fmt);\n", |
| " fmt_picker.append(option)\n", |
| " }\n", |
| "\n", |
| " // Add hover states to the ui-buttons\n", |
| " $( \".ui-button\" ).hover(\n", |
| " function() { $(this).addClass(\"ui-state-hover\");},\n", |
| " function() { $(this).removeClass(\"ui-state-hover\");}\n", |
| " );\n", |
| "\n", |
| " var status_bar = $('<span class=\"mpl-message\"/>');\n", |
| " nav_element.append(status_bar);\n", |
| " this.message = status_bar[0];\n", |
| "}\n", |
| "\n", |
| "mpl.figure.prototype.request_resize = function(x_pixels, y_pixels) {\n", |
| " // Request matplotlib to resize the figure. Matplotlib will then trigger a resize in the client,\n", |
| " // which will in turn request a refresh of the image.\n", |
| " this.send_message('resize', {'width': x_pixels, 'height': y_pixels});\n", |
| "}\n", |
| "\n", |
| "mpl.figure.prototype.send_message = function(type, properties) {\n", |
| " properties['type'] = type;\n", |
| " properties['figure_id'] = this.id;\n", |
| " this.ws.send(JSON.stringify(properties));\n", |
| "}\n", |
| "\n", |
| "mpl.figure.prototype.send_draw_message = function() {\n", |
| " if (!this.waiting) {\n", |
| " this.waiting = true;\n", |
| " this.ws.send(JSON.stringify({type: \"draw\", figure_id: this.id}));\n", |
| " }\n", |
| "}\n", |
| "\n", |
| "\n", |
| "mpl.figure.prototype.handle_save = function(fig, msg) {\n", |
| " var format_dropdown = fig.format_dropdown;\n", |
| " var format = format_dropdown.options[format_dropdown.selectedIndex].value;\n", |
| " fig.ondownload(fig, format);\n", |
| "}\n", |
| "\n", |
| "\n", |
| "mpl.figure.prototype.handle_resize = function(fig, msg) {\n", |
| " var size = msg['size'];\n", |
| " if (size[0] != fig.canvas.width || size[1] != fig.canvas.height) {\n", |
| " fig._resize_canvas(size[0], size[1]);\n", |
| " fig.send_message(\"refresh\", {});\n", |
| " };\n", |
| "}\n", |
| "\n", |
| "mpl.figure.prototype.handle_rubberband = function(fig, msg) {\n", |
| " var x0 = msg['x0'] / mpl.ratio;\n", |
| " var y0 = (fig.canvas.height - msg['y0']) / mpl.ratio;\n", |
| " var x1 = msg['x1'] / mpl.ratio;\n", |
| " var y1 = (fig.canvas.height - msg['y1']) / mpl.ratio;\n", |
| " x0 = Math.floor(x0) + 0.5;\n", |
| " y0 = Math.floor(y0) + 0.5;\n", |
| " x1 = Math.floor(x1) + 0.5;\n", |
| " y1 = Math.floor(y1) + 0.5;\n", |
| " var min_x = Math.min(x0, x1);\n", |
| " var min_y = Math.min(y0, y1);\n", |
| " var width = Math.abs(x1 - x0);\n", |
| " var height = Math.abs(y1 - y0);\n", |
| "\n", |
| " fig.rubberband_context.clearRect(\n", |
| " 0, 0, fig.canvas.width, fig.canvas.height);\n", |
| "\n", |
| " fig.rubberband_context.strokeRect(min_x, min_y, width, height);\n", |
| "}\n", |
| "\n", |
| "mpl.figure.prototype.handle_figure_label = function(fig, msg) {\n", |
| " // Updates the figure title.\n", |
| " fig.header.textContent = msg['label'];\n", |
| "}\n", |
| "\n", |
| "mpl.figure.prototype.handle_cursor = function(fig, msg) {\n", |
| " var cursor = msg['cursor'];\n", |
| " switch(cursor)\n", |
| " {\n", |
| " case 0:\n", |
| " cursor = 'pointer';\n", |
| " break;\n", |
| " case 1:\n", |
| " cursor = 'default';\n", |
| " break;\n", |
| " case 2:\n", |
| " cursor = 'crosshair';\n", |
| " break;\n", |
| " case 3:\n", |
| " cursor = 'move';\n", |
| " break;\n", |
| " }\n", |
| " fig.rubberband_canvas.style.cursor = cursor;\n", |
| "}\n", |
| "\n", |
| "mpl.figure.prototype.handle_message = function(fig, msg) {\n", |
| " fig.message.textContent = msg['message'];\n", |
| "}\n", |
| "\n", |
| "mpl.figure.prototype.handle_draw = function(fig, msg) {\n", |
| " // Request the server to send over a new figure.\n", |
| " fig.send_draw_message();\n", |
| "}\n", |
| "\n", |
| "mpl.figure.prototype.handle_image_mode = function(fig, msg) {\n", |
| " fig.image_mode = msg['mode'];\n", |
| "}\n", |
| "\n", |
| "mpl.figure.prototype.updated_canvas_event = function() {\n", |
| " // Called whenever the canvas gets updated.\n", |
| " this.send_message(\"ack\", {});\n", |
| "}\n", |
| "\n", |
| "// A function to construct a web socket function for onmessage handling.\n", |
| "// Called in the figure constructor.\n", |
| "mpl.figure.prototype._make_on_message_function = function(fig) {\n", |
| " return function socket_on_message(evt) {\n", |
| " if (evt.data instanceof Blob) {\n", |
| " /* FIXME: We get \"Resource interpreted as Image but\n", |
| " * transferred with MIME type text/plain:\" errors on\n", |
| " * Chrome. But how to set the MIME type? It doesn't seem\n", |
| " * to be part of the websocket stream */\n", |
| " evt.data.type = \"image/png\";\n", |
| "\n", |
| " /* Free the memory for the previous frames */\n", |
| " if (fig.imageObj.src) {\n", |
| " (window.URL || window.webkitURL).revokeObjectURL(\n", |
| " fig.imageObj.src);\n", |
| " }\n", |
| "\n", |
| " fig.imageObj.src = (window.URL || window.webkitURL).createObjectURL(\n", |
| " evt.data);\n", |
| " fig.updated_canvas_event();\n", |
| " fig.waiting = false;\n", |
| " return;\n", |
| " }\n", |
| " else if (typeof evt.data === 'string' && evt.data.slice(0, 21) == \"data:image/png;base64\") {\n", |
| " fig.imageObj.src = evt.data;\n", |
| " fig.updated_canvas_event();\n", |
| " fig.waiting = false;\n", |
| " return;\n", |
| " }\n", |
| "\n", |
| " var msg = JSON.parse(evt.data);\n", |
| " var msg_type = msg['type'];\n", |
| "\n", |
| " // Call the \"handle_{type}\" callback, which takes\n", |
| " // the figure and JSON message as its only arguments.\n", |
| " try {\n", |
| " var callback = fig[\"handle_\" + msg_type];\n", |
| " } catch (e) {\n", |
| " console.log(\"No handler for the '\" + msg_type + \"' message type: \", msg);\n", |
| " return;\n", |
| " }\n", |
| "\n", |
| " if (callback) {\n", |
| " try {\n", |
| " // console.log(\"Handling '\" + msg_type + \"' message: \", msg);\n", |
| " callback(fig, msg);\n", |
| " } catch (e) {\n", |
| " console.log(\"Exception inside the 'handler_\" + msg_type + \"' callback:\", e, e.stack, msg);\n", |
| " }\n", |
| " }\n", |
| " };\n", |
| "}\n", |
| "\n", |
| "// from http://stackoverflow.com/questions/1114465/getting-mouse-location-in-canvas\n", |
| "mpl.findpos = function(e) {\n", |
| " //this section is from http://www.quirksmode.org/js/events_properties.html\n", |
| " var targ;\n", |
| " if (!e)\n", |
| " e = window.event;\n", |
| " if (e.target)\n", |
| " targ = e.target;\n", |
| " else if (e.srcElement)\n", |
| " targ = e.srcElement;\n", |
| " if (targ.nodeType == 3) // defeat Safari bug\n", |
| " targ = targ.parentNode;\n", |
| "\n", |
| " // jQuery normalizes the pageX and pageY\n", |
| " // pageX,Y are the mouse positions relative to the document\n", |
| " // offset() returns the position of the element relative to the document\n", |
| " var x = e.pageX - $(targ).offset().left;\n", |
| " var y = e.pageY - $(targ).offset().top;\n", |
| "\n", |
| " return {\"x\": x, \"y\": y};\n", |
| "};\n", |
| "\n", |
| "/*\n", |
| " * return a copy of an object with only non-object keys\n", |
| " * we need this to avoid circular references\n", |
| " * http://stackoverflow.com/a/24161582/3208463\n", |
| " */\n", |
| "function simpleKeys (original) {\n", |
| " return Object.keys(original).reduce(function (obj, key) {\n", |
| " if (typeof original[key] !== 'object')\n", |
| " obj[key] = original[key]\n", |
| " return obj;\n", |
| " }, {});\n", |
| "}\n", |
| "\n", |
| "mpl.figure.prototype.mouse_event = function(event, name) {\n", |
| " var canvas_pos = mpl.findpos(event)\n", |
| "\n", |
| " if (name === 'button_press')\n", |
| " {\n", |
| " this.canvas.focus();\n", |
| " this.canvas_div.focus();\n", |
| " }\n", |
| "\n", |
| " var x = canvas_pos.x * mpl.ratio;\n", |
| " var y = canvas_pos.y * mpl.ratio;\n", |
| "\n", |
| " this.send_message(name, {x: x, y: y, button: event.button,\n", |
| " step: event.step,\n", |
| " guiEvent: simpleKeys(event)});\n", |
| "\n", |
| " /* This prevents the web browser from automatically changing to\n", |
| " * the text insertion cursor when the button is pressed. We want\n", |
| " * to control all of the cursor setting manually through the\n", |
| " * 'cursor' event from matplotlib */\n", |
| " event.preventDefault();\n", |
| " return false;\n", |
| "}\n", |
| "\n", |
| "mpl.figure.prototype._key_event_extra = function(event, name) {\n", |
| " // Handle any extra behaviour associated with a key event\n", |
| "}\n", |
| "\n", |
| "mpl.figure.prototype.key_event = function(event, name) {\n", |
| "\n", |
| " // Prevent repeat events\n", |
| " if (name == 'key_press')\n", |
| " {\n", |
| " if (event.which === this._key)\n", |
| " return;\n", |
| " else\n", |
| " this._key = event.which;\n", |
| " }\n", |
| " if (name == 'key_release')\n", |
| " this._key = null;\n", |
| "\n", |
| " var value = '';\n", |
| " if (event.ctrlKey && event.which != 17)\n", |
| " value += \"ctrl+\";\n", |
| " if (event.altKey && event.which != 18)\n", |
| " value += \"alt+\";\n", |
| " if (event.shiftKey && event.which != 16)\n", |
| " value += \"shift+\";\n", |
| "\n", |
| " value += 'k';\n", |
| " value += event.which.toString();\n", |
| "\n", |
| " this._key_event_extra(event, name);\n", |
| "\n", |
| " this.send_message(name, {key: value,\n", |
| " guiEvent: simpleKeys(event)});\n", |
| " return false;\n", |
| "}\n", |
| "\n", |
| "mpl.figure.prototype.toolbar_button_onclick = function(name) {\n", |
| " if (name == 'download') {\n", |
| " this.handle_save(this, null);\n", |
| " } else {\n", |
| " this.send_message(\"toolbar_button\", {name: name});\n", |
| " }\n", |
| "};\n", |
| "\n", |
| "mpl.figure.prototype.toolbar_button_onmouseover = function(tooltip) {\n", |
| " this.message.textContent = tooltip;\n", |
| "};\n", |
| "mpl.toolbar_items = [[\"Home\", \"Reset original view\", \"fa fa-home icon-home\", \"home\"], [\"Back\", \"Back to previous view\", \"fa fa-arrow-left icon-arrow-left\", \"back\"], [\"Forward\", \"Forward to next view\", \"fa fa-arrow-right icon-arrow-right\", \"forward\"], [\"\", \"\", \"\", \"\"], [\"Pan\", \"Pan axes with left mouse, zoom with right\", \"fa fa-arrows icon-move\", \"pan\"], [\"Zoom\", \"Zoom to rectangle\", \"fa fa-square-o icon-check-empty\", \"zoom\"], [\"\", \"\", \"\", \"\"], [\"Download\", \"Download plot\", \"fa fa-floppy-o icon-save\", \"download\"]];\n", |
| "\n", |
| "mpl.extensions = [\"eps\", \"jpeg\", \"pdf\", \"png\", \"ps\", \"raw\", \"svg\", \"tif\"];\n", |
| "\n", |
| "mpl.default_extension = \"png\";var comm_websocket_adapter = function(comm) {\n", |
| " // Create a \"websocket\"-like object which calls the given IPython comm\n", |
| " // object with the appropriate methods. Currently this is a non binary\n", |
| " // socket, so there is still some room for performance tuning.\n", |
| " var ws = {};\n", |
| "\n", |
| " ws.close = function() {\n", |
| " comm.close()\n", |
| " };\n", |
| " ws.send = function(m) {\n", |
| " //console.log('sending', m);\n", |
| " comm.send(m);\n", |
| " };\n", |
| " // Register the callback with on_msg.\n", |
| " comm.on_msg(function(msg) {\n", |
| " //console.log('receiving', msg['content']['data'], msg);\n", |
| " // Pass the mpl event to the overridden (by mpl) onmessage function.\n", |
| " ws.onmessage(msg['content']['data'])\n", |
| " });\n", |
| " return ws;\n", |
| "}\n", |
| "\n", |
| "mpl.mpl_figure_comm = function(comm, msg) {\n", |
| " // This is the function which gets called when the mpl process\n", |
| " // starts-up an IPython Comm through the \"matplotlib\" channel.\n", |
| "\n", |
| " var id = msg.content.data.id;\n", |
| " // Get hold of the div created by the display call when the Comm\n", |
| " // socket was opened in Python.\n", |
| " var element = $(\"#\" + id);\n", |
| " var ws_proxy = comm_websocket_adapter(comm)\n", |
| "\n", |
| " function ondownload(figure, format) {\n", |
| " window.open(figure.imageObj.src);\n", |
| " }\n", |
| "\n", |
| " var fig = new mpl.figure(id, ws_proxy,\n", |
| " ondownload,\n", |
| " element.get(0));\n", |
| "\n", |
| " // Call onopen now - mpl needs it, as it is assuming we've passed it a real\n", |
| " // web socket which is closed, not our websocket->open comm proxy.\n", |
| " ws_proxy.onopen();\n", |
| "\n", |
| " fig.parent_element = element.get(0);\n", |
| " fig.cell_info = mpl.find_output_cell(\"<div id='\" + id + \"'></div>\");\n", |
| " if (!fig.cell_info) {\n", |
| " console.error(\"Failed to find cell for figure\", id, fig);\n", |
| " return;\n", |
| " }\n", |
| "\n", |
| " var output_index = fig.cell_info[2]\n", |
| " var cell = fig.cell_info[0];\n", |
| "\n", |
| "};\n", |
| "\n", |
| "mpl.figure.prototype.handle_close = function(fig, msg) {\n", |
| " var width = fig.canvas.width/mpl.ratio\n", |
| " fig.root.unbind('remove')\n", |
| "\n", |
| " // Update the output cell to use the data from the current canvas.\n", |
| " fig.push_to_output();\n", |
| " var dataURL = fig.canvas.toDataURL();\n", |
| " // Re-enable the keyboard manager in IPython - without this line, in FF,\n", |
| " // the notebook keyboard shortcuts fail.\n", |
| " IPython.keyboard_manager.enable()\n", |
| " $(fig.parent_element).html('<img src=\"' + dataURL + '\" width=\"' + width + '\">');\n", |
| " fig.close_ws(fig, msg);\n", |
| "}\n", |
| "\n", |
| "mpl.figure.prototype.close_ws = function(fig, msg){\n", |
| " fig.send_message('closing', msg);\n", |
| " // fig.ws.close()\n", |
| "}\n", |
| "\n", |
| "mpl.figure.prototype.push_to_output = function(remove_interactive) {\n", |
| " // Turn the data on the canvas into data in the output cell.\n", |
| " var width = this.canvas.width/mpl.ratio\n", |
| " var dataURL = this.canvas.toDataURL();\n", |
| " this.cell_info[1]['text/html'] = '<img src=\"' + dataURL + '\" width=\"' + width + '\">';\n", |
| "}\n", |
| "\n", |
| "mpl.figure.prototype.updated_canvas_event = function() {\n", |
| " // Tell IPython that the notebook contents must change.\n", |
| " IPython.notebook.set_dirty(true);\n", |
| " this.send_message(\"ack\", {});\n", |
| " var fig = this;\n", |
| " // Wait a second, then push the new image to the DOM so\n", |
| " // that it is saved nicely (might be nice to debounce this).\n", |
| " setTimeout(function () { fig.push_to_output() }, 1000);\n", |
| "}\n", |
| "\n", |
| "mpl.figure.prototype._init_toolbar = function() {\n", |
| " var fig = this;\n", |
| "\n", |
| " var nav_element = $('<div/>')\n", |
| " nav_element.attr('style', 'width: 100%');\n", |
| " this.root.append(nav_element);\n", |
| "\n", |
| " // Define a callback function for later on.\n", |
| " function toolbar_event(event) {\n", |
| " return fig.toolbar_button_onclick(event['data']);\n", |
| " }\n", |
| " function toolbar_mouse_event(event) {\n", |
| " return fig.toolbar_button_onmouseover(event['data']);\n", |
| " }\n", |
| "\n", |
| " for(var toolbar_ind in mpl.toolbar_items){\n", |
| " var name = mpl.toolbar_items[toolbar_ind][0];\n", |
| " var tooltip = mpl.toolbar_items[toolbar_ind][1];\n", |
| " var image = mpl.toolbar_items[toolbar_ind][2];\n", |
| " var method_name = mpl.toolbar_items[toolbar_ind][3];\n", |
| "\n", |
| " if (!name) { continue; };\n", |
| "\n", |
| " var button = $('<button class=\"btn btn-default\" href=\"#\" title=\"' + name + '\"><i class=\"fa ' + image + ' fa-lg\"></i></button>');\n", |
| " button.click(method_name, toolbar_event);\n", |
| " button.mouseover(tooltip, toolbar_mouse_event);\n", |
| " nav_element.append(button);\n", |
| " }\n", |
| "\n", |
| " // Add the status bar.\n", |
| " var status_bar = $('<span class=\"mpl-message\" style=\"text-align:right; float: right;\"/>');\n", |
| " nav_element.append(status_bar);\n", |
| " this.message = status_bar[0];\n", |
| "\n", |
| " // Add the close button to the window.\n", |
| " var buttongrp = $('<div class=\"btn-group inline pull-right\"></div>');\n", |
| " var button = $('<button class=\"btn btn-mini btn-primary\" href=\"#\" title=\"Stop Interaction\"><i class=\"fa fa-power-off icon-remove icon-large\"></i></button>');\n", |
| " button.click(function (evt) { fig.handle_close(fig, {}); } );\n", |
| " button.mouseover('Stop Interaction', toolbar_mouse_event);\n", |
| " buttongrp.append(button);\n", |
| " var titlebar = this.root.find($('.ui-dialog-titlebar'));\n", |
| " titlebar.prepend(buttongrp);\n", |
| "}\n", |
| "\n", |
| "mpl.figure.prototype._root_extra_style = function(el){\n", |
| " var fig = this\n", |
| " el.on(\"remove\", function(){\n", |
| "\tfig.close_ws(fig, {});\n", |
| " });\n", |
| "}\n", |
| "\n", |
| "mpl.figure.prototype._canvas_extra_style = function(el){\n", |
| " // this is important to make the div 'focusable\n", |
| " el.attr('tabindex', 0)\n", |
| " // reach out to IPython and tell the keyboard manager to turn it's self\n", |
| " // off when our div gets focus\n", |
| "\n", |
| " // location in version 3\n", |
| " if (IPython.notebook.keyboard_manager) {\n", |
| " IPython.notebook.keyboard_manager.register_events(el);\n", |
| " }\n", |
| " else {\n", |
| " // location in version 2\n", |
| " IPython.keyboard_manager.register_events(el);\n", |
| " }\n", |
| "\n", |
| "}\n", |
| "\n", |
| "mpl.figure.prototype._key_event_extra = function(event, name) {\n", |
| " var manager = IPython.notebook.keyboard_manager;\n", |
| " if (!manager)\n", |
| " manager = IPython.keyboard_manager;\n", |
| "\n", |
| " // Check for shift+enter\n", |
| " if (event.shiftKey && event.which == 13) {\n", |
| " this.canvas_div.blur();\n", |
| " event.shiftKey = false;\n", |
| " // Send a \"J\" for go to next cell\n", |
| " event.which = 74;\n", |
| " event.keyCode = 74;\n", |
| " manager.command_mode();\n", |
| " manager.handle_keydown(event);\n", |
| " }\n", |
| "}\n", |
| "\n", |
| "mpl.figure.prototype.handle_save = function(fig, msg) {\n", |
| " fig.ondownload(fig, null);\n", |
| "}\n", |
| "\n", |
| "\n", |
| "mpl.find_output_cell = function(html_output) {\n", |
| " // Return the cell and output element which can be found *uniquely* in the notebook.\n", |
| " // Note - this is a bit hacky, but it is done because the \"notebook_saving.Notebook\"\n", |
| " // IPython event is triggered only after the cells have been serialised, which for\n", |
| " // our purposes (turning an active figure into a static one), is too late.\n", |
| " var cells = IPython.notebook.get_cells();\n", |
| " var ncells = cells.length;\n", |
| " for (var i=0; i<ncells; i++) {\n", |
| " var cell = cells[i];\n", |
| " if (cell.cell_type === 'code'){\n", |
| " for (var j=0; j<cell.output_area.outputs.length; j++) {\n", |
| " var data = cell.output_area.outputs[j];\n", |
| " if (data.data) {\n", |
| " // IPython >= 3 moved mimebundle to data attribute of output\n", |
| " data = data.data;\n", |
| " }\n", |
| " if (data['text/html'] == html_output) {\n", |
| " return [cell, data, j];\n", |
| " }\n", |
| " }\n", |
| " }\n", |
| " }\n", |
| "}\n", |
| "\n", |
| "// Register the function which deals with the matplotlib target/channel.\n", |
| "// The kernel may be null if the page has been refreshed.\n", |
| "if (IPython.notebook.kernel != null) {\n", |
| " IPython.notebook.kernel.comm_manager.register_target('matplotlib', mpl.mpl_figure_comm);\n", |
| "}\n" |
| ], |
| "text/plain": [ |
| "<IPython.core.display.Javascript object>" |
| ] |
| }, |
| "metadata": {}, |
| "output_type": "display_data" |
| }, |
| { |
| "data": { |
| "text/html": [ |
| "<img src=\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAABEwAAAImCAYAAABJvh+8AAAgAElEQVR4XuzdC5xVVfn/8WcGHEEDVEQFQchLKgKapqWCifzwbirmL/9mCt7wlkl44ScJeAEKvBGpQCKYJlpA5j3xggUqppkiWCZeQMUyQC4iyGX+r++yM80MM3PWOWcO59nnfPbrxQuZWfvstd5rjeuZZ6+9dlllZWWlcSCAAAIIIIAAAggggAACCCCAAAIIVAmUkTBhNCCAAAIIIIAAAggggAACCCCAAAI1BUiYMCISKVBWVpZxvc866yybPHlyxudlcsJ3v/tdmzZtmv32t781/Xeuxy9+8Qv74Q9/aBdffLHpvzk2j8Dll19uN910k40ePdr03xwIIIAAAghkIkCckolWfsu+8cYb1rVrV9tnn31M/82BAAIIZCJAwiQTLcq6Eejbt+8mdfn444/tD3/4g2299dZ1Jiu6d+9u5557bl7bQMIkr7xpP/yRRx6xE044wY477jjTf2d7kDDJVo7zEEAAAQQkQJziZxyQMPHTF9QEgSQKkDBJYq9R5zoFZs6caT179rSOHTvae++9VxCljz76yFasWGE777yztWjRIuc6fPrpp6ZE0Lbbbms77rhjzp9X7B/QWAmTTz75xJYsWWI77LCDbbfddsXORvsQQAABBDaDAHHKZkCu4xIkTArjzlURKBYBEibF0pO0wzwEInRDYQUaK2FS2FZwdQQQQACBYhQgTilMr5IwKYw7V0WgWARImBRLT9KO6IRJ9X1BrrnmGhs2bJg99thjtnjxYvvf//1fu/fee00vj/rNb34Tvv7SSy+ZVo588cUXtssuu9jRRx9tgwYNsrZt226iXt8jOdUf8fh//+//ma77+OOP29KlS61Dhw52+umn209+8hOrqKio8Zn17WFSPTEwdepUGzlypN133322cOHCsBpFddTX6qqjLvDrX//afv7zn4dnebfccks7+OCDbciQIaaVFZk+0lK9Lg888IBdd911YQ8XmbVv39769+8f9gHR89zvvPNO8J4xY4YtW7bM9tprr9Du+vZ7Wbt2rU2YMMGmTJli8+fPtzVr1oQVRCeddFLoA7U1dXzjG9+wV155pc6fhOqP6KTK/fnPfw51uPHGG+3ll18OfaF6/c///E+ob0N7mLz++ut26623hjGndm611VZVY+PSSy+1du3aRf9Evvvuu3bzzTeHa6v/tthii+DWq1evsHfNnnvuGT4rXTKovoCw+tf/8pe/hPZqrKgvtHrmwQcftAMPPDDUXyuz6nruXu4aS8uXL7cFCxbYV7/61ar2rVy5Mowl7d3zj3/8wzZs2GC77767nXbaaTZgwABr3rx5tAUFEUAAgWIWiE2YEKc0bpySLmGiee2nP/1pmIcVC+rR7v33398uuOCCOuOT9evX2+233x7ixb/97W8hNtF8qnjuiCOOsP/7v/+zbbbZpmoo6/qKyf74xz/aP//5T2vWrJltv/324RraX09xVybHn/70J7vtttts9uzZ9q9//ctatWoV5uXjjz/eFIPo3zrSxTL1xZjpxt9+++1nV1xxhZ1xxhl2zz331Fn1F198McSWqpd8q8cWinsUi+gx+g8++CDEofvuu2/wVjzMgYA3ARIm3nqE+mQtkGkgcvLJJ9ucOXNCIqRHjx5WXl4eJrtbbrklTH76RU+P1XTu3Dn8MvnZZ5/Zq6++GibTnXbaKZyrr1c/0iVMlDyYPn16mCy/9a1vhV/YZ82aFa6nXzCVGKh+pEuY6Jfqzz//PCQ+vv3tb4c2PP/88yHxoV9aVd+vfOUrNT7z6quvDhO3ympfF/1yrwSAftm96KKLbMyYMRntAZL6RV5Bgn6hfv/990Nd9Iv0c889Z0p6aNLW89yHHXZYCCoOOOCAUE4TqibR3/3ud3biiSfWqKceiTnmmGNMiQ0lRhRYqD+UFFm0aJHttttuIfhIJSeuvfZaUxDx9NNPVyUcUh+oyf2yyy4L/0wlTJSMUMCjSXrvvfcOjz4paXT44Yc3GGSMGzcubMSrgOlrX/ua6bPVB/JT4JTJhr+///3vQ3CwevXqMPYOOugg27hxY0hmqE9GjRpVtelsrgkTJV50DY039Y+COY0TeWkzPI2hp556KiRqah9KhGl86jz9nKUO1fOoo46yt99+O/xMyEIJH/1sKIhTIuaZZ57ZZAxm/UPOiQgggECCBYhTChOnNJQwUZ8oYbFq1SrbY489Qnyi+Uvxheb5Cy+8MMQKdcV6SqwojlJco/lUcYBim7lz51qXLl3CKYphNHcqTtCms7pRpJtyH374of31r38NSQ7d+Io9dJNp+PDhoXgqflHspfhDiQhdT3GOjlwTJvXFyVdddVWIJ3STT7FTXY+gy03x0tChQ8ONstShm4WnnnpqiKkVlyjG1uPnihsUC9XlHWtDOQTyJUDCJF+yfO5mF8g0EFEFtVJBqy20QqD6oUlSd941kSm5kTqUXNHKBiVVlBzRL8d1TaK1f2lOTVoqq+y/Vi80bdo0nKoJU8kTJRb035oAU0e6hInKHXroofbQQw9V7bWhRIMSE1qRoVUQP/rRj6o+TwkFfU/JIE1amsRTh5IoSqboyGTT1NQv8jqvd+/eISGUStJoAjzkkEOsSZMm4S6DzG644YaqOw0/+9nPgme3bt3stddeq2Ep+0cffdR+8IMfhDcEtWzZMnx/3bp19uMf/zh8rXY90yUVdH71lSi6O/T9739/k7FaX5ChBJASQ+q7u+66a5NzFSTJVsmqdMdbb71VlWzRnRatxlASK3UoGaGkU2o8pGtbuhUm+lzVS0kRrdKpfmg8qs2y/tWvfrVJ1Y899tgwXiZNmlS1kaFWkiiwVL9deeWVYWWR7hLpUOCpBJlWnfCGp3Qjge8jgECpCBCnfLkn2OaOU+qbHzXHal5UgkTxz/XXX181D+vmjGIa3diqHisotlLiQ+cpxqm9z5lWrOqGTmoFrFYuKybUSkzdbKl+pBId3/zmN6N+BFQPzdNaQaLPVP2qH7phpmun9rzLNWGiz64vTlaSSXHJxIkT7eyzz65RD8WzWpWqREj1ValaxaobNPq+VqZ873vfqzpPyR7FdG+++WZGN56i4ChUEgJKSmofSY0v3Xys71CsrXhVv1fEroImYVISQ6g0GplpIKIfEv1SqjvjmRz6IWzdunW4W6AfzOqP0aRbYaK7F/PmzQt34asfZ555Zpg89IurkgGpI13CRL+4a3Kp/Qu6fpk/55xzwl0TJVNSR2riVhJFyZTqh+54KHGhwCKbhInapCRAp06danyuHnHRKgbdVdFnK3mSOrSyRpa6q6AAKhV46A6JVlvoHP1CXvtRJSVNdFdCKxvUh6lHRNIlFXTdVMJEd06U3KnrqC/ISLVFQZXu8uRy6I1NCjTUT3feeWfaj0rXtpiEiVa0fOc739nkWloirMeANIHoblH1VUlaUaU7SUocVv+egjWNJyWQ1L+1DwVKGguauPS4U+yklBaCAggggEBCBYhT/ttxmzNOqW9+vOOOO8LKWq0yVZnqNy1UU93M0KMnWj2pFbs6nn322TDv6WaLEhjpDt2Y0moVrT6JuZnS0OfpfCUgFC/qcZh0R64Jk4biZN0QUcyrFdpqX/VDj7QrGVJ7VWpqZa9usOjR9NpH6udDN/Z0g4oDgVgB/Q6hxKcOxasNvdZdv+/o9w8dermDfg9Jd5AwSSfE9xMjkGkgokcPdLe9oUN3Ep588skwQWn5YCpj+fDDD4dfApUgUBIkdaRLmNR3t33EiBE2ePBgGzhwYJigU0e6hInucmiSr33oLoNWnmgFgO52pA7dddD/UHRXRAmJ2oeWeSoRkE3CRHcN9BhJ7UPPpI4fPz7cWdEdltqHkiJ///vfayxh1eM1WsKpZZ96rriuQ8/9ajWEVgilnnlNl1TQ56QSJg0FHHUFGUqQ6a6OkjWpx7Jy+eFQgkJLcl944YWwwijdka5t6RImSjqpDbUDwtR167tbNHr06LCCRN6TJ0+uqma/fv3CvxVwqo/rOlKBop6z1kojDgQQQKCUBYhT/tv7mzNOqW9+1KOmeuRUMZj2Hal9KM7TL1P65Uv/rUdZ//3vf4fHsbXKUucpMaD5vL5DCRfFdUosKLbRIzy1b5rF/EzoBpHiTd3Q0KqX1Crlhs7NNWHSUJysFdd6JFouqtuuu+5aVRXFkNoDsPqqVH1TN7e0ykQ3+hT71T4UX2nFt26sKV5p6JfeGDPKlIaAxorGlX42NCar35itT0A/v9qDUCuidXMv3U09EialMZZKopWZBiJaQqg7/HUdmgh05z/d3QMlI5SUSB3pEiZ6BEW/fNY+YjbeUpnUkfrlWXt8aFKqfdQVHOgxo9QkrZUCdb2mOLXcM5uESX11SU3Y9bW9+iasqeduU8mQmIFb/bGjdEkFfV7qerojogCmrqOuIENJM93dUdJEqydyOfQ/avWFstwKfKpvDlff56ZrW7qEiYIZtaG+Q6ttTjnllPDIVvU7O3oOW6uidFdN+7ukjlQyJMZBj7fV3qMm5jzKIIAAAsUkQJzy397cnHFKffOjYgDt66X945Q8qevQ/KxHZzQPamWrDt1w0Y0CrY7VoVWYuimgGw/an6P6qlidq5hKNw506O7317/+devZs2dYJaLVLTGHVnJqlase09Xj2zFHrgmThuJkXV+PmI8dOzbs/6ZkkA7Fl0og1V6VqhuOSvIo7ok5FGelNq+NKU+Z0hXQKmkl7rSvYEyyJCWl34u08kur2+v6nai6KAmT0h1fRdfyTAORhvZWSK1w0F4PekxGz5dq2VZqEtSjK9qvovrmWgJNlzDR3XpNYLWPbBMm9SU20gUi+p+L2lP70NtTtMw0m4RJfeekm7DrSpgoiNDKEbnXdReier0V5OitQDrSJRVUpq7r1Xaoq8569EfPBntNmGh1jwKp2quO0r0dINX2uu4W6RluedW1y73ukikA1B2ohu6u6fMvueSSqk3oiu5/PDQIAQQQiBQgTvkv1OaMU9IlTO6///4a+2lU7866Eib6vh4B0GOuuvmiuVCrLHRoFYj2i6v9C5jK6K0w+lsb3ivZohUUigu1ujjdoQ3UNd82ZsJEq371iHbteLi+mLR2HfXmPd001B16xUhqT2pPtNqrUqvftFOcmW6FjOpQ+6UF6Yz4fmkK6A2TuhFZ/Q2OsRIatxqLtV/iUft8EiaxopRzL9CYgUh9d9WFoOy4/ieuyS5JCRPVXUkS7eSuVyXrDSa1j9SjQYVOmGhprB7F0WM52mE99shnwiRfj+QocIrZ8E2PhumNNLWfCU7Z6DEx7U+SbcJEn1P7bpEeo1LQUnuXe5XV67EVZOqxHAVGHAgggAACDQsQpzScMMlXnNKYj+TU18N6tFiPquox2/POO88mTJhQ72DQ3l56XEWJCiUZtOGpVqk0dGTzSE7qrYh1zeG6lhI1N998c9YJE31G6gaiEjpaNZP6d+1VqSqrjWC1AkWPT9TefJ7/dyCQrYDGk47aeyjGfF7suSRMYjQpkwiBxgxE9Ayc9qnQBKVVBdUPvQK3T58+4UtJS5ikVsDojSyaJKsfSgRpYzOtVCh0wkR3bJQY0L4oWnpa374btQdmKqmgxMITTzxR57jNdoWJPkx3dxQU6E0/2nMml0OPfGnTO23++stf/jLtR2k5sBJ5Cjg++OCDTUxSr/DLJWGiTe30+mZNOnpF4c477xyWOVbf5T5VUe0fo0RJJmMlbSMpgAACCBSxAHHKfzu3viRGPuKUdJu+6lEblam9Z0ZqtUT1TV8bGp5acaK3ymiPOO0Vl+7QozmKcfRotR5rTnekNn2t7w1/tc+/7bbbwgpP7bOiGxzVD92RV7vU7mxXmOjz9NZIvaxA8YBuumjFSV2rUlVWj/goUVTfaut07ef7CNQlEJv0yOVcEiaMvaIRaMxARDugKzuuSUCbdaUmUW1UdeSRR4ZfWHUkLWGivSm0D4U21VJCofoeHqNGjQqbrOrI5JfgdKs6snkkR3XQ6/K0Ka9WMii5U/ttRloOq83atMN96kglFfQLv37JryvRkkvCRGNMY0OPZmllRe1nnjN5rbDuRilY0Z0mBRxazVG9vrrjpLcwpV4rrOd/dQdKm1TVfj2hnr/WqwYVAOWSMJGjrqekmQIfXae+FS3anE3118bIl112WVgNVPt5Y21qqyXItV85WDT/06EhCCCAQAYCxCnpEyb5iFNiXiusfTg0j6XiPd1A0J4humlQPUGhVaGahxUn6c1yqUM3nc4///zw1jvtY6I3xejQPKqytW++6aaE4hG9UKD6/igNDafUjQq9snjq1KkhHql+aHWL9itLPQ6ka2iPFMUs2mQ3teee5m9tRjtmzJhwei4JE71IQDdXZKHEjG4E1beiRftFKG5QPKO9T7Qip/aeE3oz4vvvv1/nG/0y+FGjaAkJkDApoc6mqbkLNGYgolUESozoF1DdedBKB+2MrpUPmkC1+kR3BZKWMJGykiJKjuiXcyVMtJpGvyDrF3itUtAk1tArd2v3VL4SJkqIHH/88eFZX+1erUlWSzi114aSIUpOKLDRc7HVAxYFB2qLVmPo7o0CBSUBlJDQkUvCROfrERUlCDQ29txzz/DZej2ZAgEl1PS6Xd0hizm00ar2a9HjPnp+UnelFEiofeoT9VP1PW+UpFGAoUNldY6uqT/q15EjR+acMEndLUrVv/Yu99XbpWc/jz322OCtZImW4mo/Ez2upq8pWFOQmHq2O8aEMggggECxChCn/LdnG9pfq7HjlIaupT7RZq16W4Y2jVRSQUkAJW4UX2hzV70NLnWkNsfXo9lakalkgW58aM8v/aKvPU+0h4liEB2pVSHa20Tx5NZbbx0SLtrLRImL2FWmqeunbPRvxUXa5003VxQH6EZL7bi0b9++dvfdd4eEhmI+XV91VaJGN6Ruv/32nBImqoc2dX/ooYdCFRWX1bUqNVX/xx9/PNxsUp1lp5s8bdq0CXvCKO6RjVbgKvHEgUCMAAmTGCXKIPAfgcYMRPSRmnT0nnhtaqWJRVl7bVSlZz4PPvjgMOEkMWGitmmHd9310F0NTaLaQ0N3VzRZKWmS7vnb6oMuXwkTXUPBhOqqzWiVoNIEqzsrSvJo4ldiR8/MVj+UuFBAoZ3vNQErAVF9xUyuCRNdS32vt/NozGkD3RYtWoRkjjafVWJGj83EHqqvlv3OmDEjvGZYO8sr6aDEnO76VH9ttT5TCRklUhQAKhmkvWj0eJACDiX2cl1hoj1uFMTIXoGVnjduaOM1JUf0rLbudmk86WdFddFn6O6X3rxT1345sT6UQwABBIpFgDjlvz2ZbkPyxoxT0l1LSX29yU+P9eqGmOY+JUOULNFqkeqHVhgrAaEbaLoxoBhAN3W0AlQ3EPQIjOa/1KEbI0oSaO84zfErV64MMYKSJ/379w8rKTJ9fa5u6ulxGyVdtAJGcZEeg1HiRzFIy5Ytq66vpI9upmh1ijbHVEJH8cXw4cPDRvkqn8sKE11Ib8JTPKajvlWptQ0Vg2qls2686AaUVhGrDTKUOXucFMv/9fLfDhIm+TfmCgggUE1Ayym1jHTcuHFhIudAAAEEEEAAAQS8CBCneOkJ6oGADwESJj76gVogUFQCqc08tSoidWgVhpac6s6I7qxoWWnr1q2Lqt00BgEEEEAAAQT8CxCn+O8jaoiAFwESJl56gnogUEQC2n9DK0i03FSPfqQ2HFOSRJtvaampHj3iQAABBBBAAAEENrcAccrmFud6CCRXQI+a6bEuPdKV6aFHwpo2bRr2BGzo4C05mcpSHoGEC+jtP1pNoudptZGtNlHVjurdu3cPbwViv4mEdzDVRwABBBBAIMECxCkJ7jyqjsBmFtA+QtrLR5s2137rUkNV0f4+2kdwu+22q3qzVH3lSZhs5k7lcggggAACCCCAAAIIIIAAAgggkJuA3jSpx3L0ggK9FCImaaIVKXojk96O1alTp7Bxc0MHCZPc+oizEUAAAQQQQAABBBBAAAEEEECgAAJ6K6ZeB65Db/8sLy+vtxbat1GvAtexww47RO3ZSMKkAJ3KJRFAAAEEEEAAAQQQQAABBBBAIHcBrTRZsWJFSIYoKVLfoWSKkip6/Xa6lSWpzyBhknv/8AkIIIAAAggggAACCCCAAAIIIFBkAiWVMFmzZo3NnTvX2rRpE3bE5UAAAQQQQACBeAFtkvbJJ59Y165drVmzZvEnllhJ4o0S63CaiwACCCDQqAKe4o2SSpj8+c9/toMOOqhRO5MPQwABBBBAoNQE9JYt3qhVf68Tb5TaTwTtRQABBBDIh4CHeKOkEibaQVfvaBZ827Zt89GnfCYCCCCAAAJFK7B48eJw4+Hdd98NO8tz1C1AvMHIQAABBBBAIHsBT/FGSSVMPvjgA+vQoYMtWrTI2rdvn30PciYCCCCAAAIlKMA8GtfpOMU5UQoBBBBAAIG6BDzNoyRMGKMIIIAAAgggECXgKYCJqnCBCuFUIHguiwACCCBQFAKe5lESJkUxpGgEAggggAAC+RfwFMDkv7XZXwGn7O04EwEEEEAAAU/zKAkTxiMCCCCAAAIIRAl4CmCiKlygQjgVCJ7LIoAAAggUhYCneZSESVEMKRqBAAIIIIBA/gU8BTD5b232V8ApezvORAABBBBAwNM8SsKE8YgAAggggAACUQKeApioCheoEE4FgueyCCCAAAJFIeBpHiVhUhRDikYggAACCCCQfwFPAUz+W5v9FXDK3o4zEUAAAQQQ8DSPkjBhPCKAAAIIIIBAlICnACaqwgUqhFOB4LksAggggEBRCHiaR0mYFMWQohEIIIAAAgjkX8BTAJP/1mZ/BZyyt+NMBBBAAAEEPM2jJEwYjwgggAACCCAQJeApgImqcIEK4VQgeC6LAAIIIFAUAp7mURImRTGkaAQCCCCAAAL5F/AUwOS/tdlfAafs7TgTAQQQQAABT/MoCRPGIwIIIIAAAghECXgKYKIqXKBCOBUInssigAACCBSFgKd5lIRJUQwpGoEAAggggED+BTwFMPlvbfZXwCl7O85EAAEEEEDA0zxKwoTxiAACCCCAAAJRAp4CmKgKF6gQTgWC57IIIIAAAkUh4GkeJWFSFEOKRiCAAAIIIJB/AU8BTP5bm/0VcMrejjMRQAABBBDwNI+SMGE8IoBAwQU2rl1jK1+YZSufn2UbVqywJi1bWotDuluLg7tb+ZbNCl4/KoAAAl8KeApgPPcJTp57h7qVssCK9Svsvn//xl75bJ6trVxvW5Y1tQO23sdO3/5/rWXTlqVMQ9sRcCXgaR4lYeJqaFAZBEpP4IvFH9mHo4fbhqVLzMrKzCorq/5usl1r2/mKwVbRtl3pwdBiBBwKeApgHPJUVQknz71D3UpV4PXP5trPFo+3DVZmZpVm1f5uYpV2Vdv+1m3rrqXKQ7sRcCXgaR4lYeJqaFAZBEpLQCtL3v+/gbZh2dIvEyW1j7Iya7LtdtZx5E2sNCmtoUFrnQp4CmCcEoVq4eS5d6hbKQpoZcmF711tG0LjlTCpfVRaEzO7o9MIVpqU4gChze4EPM2jJEzcDQ8qhEDpCCyf+ZR9MvnOtA1u0/c8a3V4r7TlKIAAAvkV8BTA5LeluX06Trn5cTYCjS0w7uM7beaq19J+bM+v7Gf9dzonbTkKIIBAfgU8zaMkTPLb13w6Agg0IPDBiGG25h9/r3t1Seq8sjJr9rW9rP3/DcUSAQQKLOApgCkwRYOXx8lz71C3UhQ4b8EAW1m5rp7VJSmRSmtRtoX9crdbSpGINiPgSsDTPErCxNXQoDIIlJbA+4N+bOs+/ihto7fYqZ11/OnNactRAAEE8ivgKYDJb0tz+3SccvPjbAQaW+DMt39oX0R8aIWZ/Wr3sRElKYIAAvkU8DSPkjDJZ0/z2Qgg0KAAK0wYIAgkS8BTAONZDifPvUPdSlGAFSal2Ou0OckCnuZREiZJHknUHYGEC1TtYaL91/Q2P/3RrmvalW3Ff/5UmrGHScI7muoXjYCnAMYzKk6ee4e6laIAe5iUYq/T5iQLeJpHSZgkeSRRdwQSLqC35Lz3k8tsY4tPzbbY5C1/ZuvMylduY51uuJW35CS8r6l+cQh4CmA8i+LkuXeoWykK8JacUux12pxkAU/zKAmTJI8k6o5AwgU2frHG3r/jMtvw2af1veXPmmy9jXW88FYrr2iW8NZSfQSSL+ApgPGsiZPn3qFupSrw7JIH7ZfLnrSNYSlr5X82gP3y73LbYOdte6T1bH1SqfLQbgRcCXiaR0mYuBoaVAaB0hJY/upT9skTEa8VPuY8a7UfrxUurdFBaz0KeApgPPqk6oST596hbqUo8Pn65Tb5vQtttW20D6yZLbEtbYOVWxPbaK1trbW3NbaVlVvfTndY86atSpGINiPgSsDTPErCxNXQoDIIlJbAB/cOszUfRLxWuP1e1v4MXitcWqOD1noU8BTAePQhYeK5V6hbKQv86eNf2KurZqUl+PpXeliPnS5OW44CCCCQXwFP8QYJk/z2NZ+OAAINCLw//se2bmnEa4W3a2cd+/NaYQYTAoUW8BTAFNqioevj5Ll3qFspCkxa0M9WVn6etuktyppbv90mpS1HAQQQyK+Ap3mUhEl++5pPRwCBBgRYYcLwQCBZAp4CGM9yOHnuHepWigLj3z7D1tr6tE3f0ppa/93vTVuOAgggkF8BT/MoCZP89jWfjgACDQiwhwnDA4FkCXgKYDzL4eS5d6hbKQqwwqQUe502J1nA0zxKwiTJI4m6I5BwgfCWnAkDbcOqpWaV2qm+1lFWZk2+sp11PP8m3pKT8L6m+sUh4CmA8SyKk+feoW6lKMAeJqXY67Q5yQKe5lESJkkeSdQdgSIQ+GLJR/bhlOG2YeUSs7KyLxMn//m7SYvWtvP/G2wVrdsVQUtpAgLJF/AUwHjWxMlz71C3UhRIvSVnnW2st/lb8JacUhwatNmpgKd5lJCIATAAACAASURBVISJ00FCtRAoJQGtNFk5f7atfGOWbfhsuTXZupW16NLdWnQ+lJUlpTQQaKt7AU8BjGcsnDz3DnUrVYEPP/urPbR4lNWVNFGy5Dttr7Sdt96vVHloNwKuBDzNoyRMXA0NKoMAAggggIBfAU8BjF8lM5w89w51K2UBrTR5+d/32tufvWxfVK6zirItbPetv2Hf2P4Ma960VSnT0HYEXAl4mkdJmLgaGlQGAQQQQAABvwKeAhi/SiRMPPcNdUMAAQQQ8C/gKd4gYeJ/vFBDBBBAAAEEXAh4CmBcgNRTCZw89w51QwABBBDwLuBpHiVh4n20UD8EEEAAAQScCHgKYJyQ1FkNnDz3DnVDAAEEEPAu4GkeJWHifbRQPwQQQAABBJwIeApgnJCQMPHcEdQNAQQQQCCRAp7iDRImiRxCVBoBBBBAAIHNL+ApgNn8rY+/Ik7xVpREAAEEEECgtoCneZSECeMTAQQQQAABBKIEPAUwURUuUCGcCgTPZRFAAAEEikLA0zxKwqQohhSNQAABBBBAIP8CngKY/Lc2+yvglL0dZyKAAAIIIOBpHiVhwnhEAAEEEEAAgSgBTwFMVIULVAinAsFzWQQQQACBohDwNI+SMCmKIUUjEEAAAQQQyL+ApwAm/63N/go4ZW/HmQgggAACCHiaR0mYMB4RQAABBBBAIErAUwATVeECFcKpQPBcFgEEEECgKAQ8zaMkTIpiSNEIBBBAAAEE8i/gKYDJf2uzvwJO2dtxJgIIIIAAAp7mURImjEcEEEAAAQQQiBLwFMBEVbhAhXAqEDyXRQABBBAoCgFP8ygJk6IYUjQCAQQQQACB/At4CmDy39rsr4BT9naciQACCCCAgKd5lIQJ4xEBBBBAAAEEogQ8BTBRFS5QIZwKBM9lEUAAAQSKQsDTPErCpCiGFI1AAAEEEEAg/wKeApj8tzb7K+CUvR1nIoAAAggg4GkeJWHCeEQAAQQQQACBKAFPAUxUhQtUCKcCwXNZBBBAAIGiEPA0j5IwKYohRSMQQAABBBDIv4CnACb/rc3+Cjhlb8eZCCCAAAIIeJpHSZgwHhFAAAEEEEAgSsBTABNV4QIVwqlA8FwWAQQQQKAoBDzNoyRMimJI0QgEEEAAAQTyL+ApgMl/a7O/Ak7Z23EmAggggAACnuZREiaMRwQQQAABBBCIEvAUwERVuECFcCoQPJdFAAEEECgKAU/zKAmTohhSNAIBBBBAAIH8C3gKYPLf2uyvgFP2dpyJAAIIIICAp3k0o4TJ9OnTbdSoUTZ37lyrqKiwHj162IgRI6xLly5Rvfrcc8+F8nPmzLG1a9faHnvsYeeff75ddNFFVl5eXuMzNm7caPfee6+NGzfO3nrrLVuzZo116NDBTjrpJPvxj39sbdq0ibpm9UKe4DOuPCcggAACCCBQYAHm0bgOwCnOiVIIIIAAAgjUJeBpHo1OmEycONHOPffckBzp379/SGCMHTvWli1bZrNnz7auXbs22NsPPPCAnX766bb99tuHBIkSHjNmzLAHH3ww/Pu2226rcf6AAQPs1ltvtZ49e4YkyZZbbmnPP/+83XPPPbb77rvba6+9Zs2bN89ohHmCz6jiFEYAAQQQQMCBAPNoXCfgFOdEKQQQQAABBIoiYaKkSKdOnaxly5Y2b9688LeOhQsXWufOne2ggw6yZ555pt7eXr9+vbVr185WrVplb7zxhu26665VZZV8mTBhQki6HHLIIeHrq1evtm222cb222+/sBqlrKysqvyll14aEjWPPPKIHXfccRmNMAKYjLgojAACCCCAQA0B5tG4AYFTnBOlEEAAAQQQKIqEyeTJk61fv342bNgwGzp0aI029e3b1+6+++6QPNEjM3Udr776qu2///521FFH2RNPPFGjyAsvvBASJeecc47deeed4XtLliwJK1FOOOEEe+ihh2qU/9nPfmaDBg0KCRqtPsnkIIDJRIuyCCCAAAII1BRgHo0bETjFOVEKAQQQQACBokiYXHjhhWEvkSeffNJ69+5do01aHaJVItOmTbM+ffrU2eMvvviiHXzwwXbyySeb9kGpfujRGq0k2WeffcLqk9TxjW98w5Ro0Z4n+lztmaJHci655BLT9x5//PFN9j1JN9wIYNIJ8X0EEEAAAQTqF2AejRsdOMU5UQoBBBBAAIGiSJhopYcegZk/f77tvffeNdr02GOPhUdjxowZY3pcpq7j008/DStGdthhB1uwYEGNvUe0T4n2K2nRooWtWLGi6vT33nsvrGqZOXNmjY+8+OKLw94mTZs2TTu69HnVP3Px4sXh8aFFixZZ+/bt055PAQQQQAABBBD4rwCJgLjRgFOcE6UQQAABBBAoioRJr169wiMwSnZU339EjdPX9f2RI0eGR2XqOy644AIbP368HX300XbdddeFBMpTTz1ll19+edizpLKy0rTXSer417/+ZVdffbX985//tFNPPdW22mor+8Mf/mB33XVXSKSkHt9paIjpEaJrr712kyIkTPjBRAABBBBAIHMBEgFxZjjFOVEKAQQQQACBokiY5LrCRAh6jfDAgQPDBq/r1q0LLto89pZbbgmJFiVLli5dGr7+2WefWbdu3WzHHXcMm8FW3/RVZbWPyaOPPmrHHntsgyOMFSb8ACKAAAIIINB4AiQC4ixxinOiFAIIIIAAAkWRMMl1D5PqCEpi6E07SoLsu+++tmHDhpA40R4nSo7o+NWvfmVnnXWW3XjjjSHJUv145ZVXwh4mWpkyevTojEYYAUxGXBRGAAEEEECghgDzaNyAwCnOiVIIIIAAAggURcJk0qRJdvbZZ4fHW4YMGVKjTXo8Rm/RaegtOQ0Ng6lTp4ZHbm644QYbPHhwKKrHe/Q4jlaSXHnllTVO12uGv/Wtb9lll10WVqdkchDAZKJFWQQQQAABBGoKMI/GjQic4pwohQACCCCAQFEkTJYtW2YdO3a0Vq1ahdUhWhGiQ0mSzp0724EHHmjPPvts+Jr2I9HXVbZt27YNjgC9PlgrS7Qp7JtvvmmtW7cO5fUq4RNPPDE8lvPyyy/bFltsUfU5qdUuU6ZMsdNOOy2jEUYAkxEXhRFAAAEEEKghwDwaNyBwinOiFAIIIIAAAkWRMFEjtGGrNm7t0qVLeI2w9iQZO3asKekxa9as8HiNDr3VpmfPnuGRGq08SR1KcOjfhx9+eNib5N1337WJEyeGt9g8/PDD4ZzUocd0unfvbnodsZImZ5xxRtWmryr7zW9+M1wz5k051TuAAIYfSAQQQAABBLIXYB6Ns8MpzolSCCCAAAIIFE3CRA3R4zPaN2Tu3LlWUVFhPXr0sOHDh4ekRuqoL2GivUe0Yevrr79uWrHSpk0bO/LII8NjOLvvvvsmTtr4VY/kTJs2LbydR2/R+epXv2qnnHJKeFxn6623znh0EcBkTMYJCCCAAAIIVAkwj8YNBpzinCiFAAIIIIBAUSVMkt6dBDBJ70HqjwACCCBQSAHm0Th9nOKcKIUAAggggAAJE0djgADGUWdQFQQQQACBxAkwj8Z1GU5xTpRCAAEEEECAhImjMUAA46gzqAoCCCCAQOIEmEfjugynOCdKIYAAAgggQMLE0RgggHHUGVQFAQQQQCBxAsyjcV2GU5wTpRBAAAEEECBh4mgMEMA46gyqggACCCCQOAHm0bguwynOiVIIIIAAAgiQMHE0BghgHHUGVUEAAQQQSJwA82hcl+EU50QpBBBAAAEESJg4GgMEMI46g6oggAACCCROgHk0rstwinOiFAIIIIAAAiRMHI0BAhhHnUFVEEAAAQQSJ8A8GtdlOMU5UQoBBBBAAAESJo7GAAGMo86gKggggAACiRNgHo3rMpzinCiFAAIIIIAACRNHY4AAxlFnUBUEEEAAgcQJMI/GdRlOcU6UQgABBBBAgISJozFAAOOoM6gKAggggEDiBJhH47oMpzgnSiGAAAIIIEDCxNEYIIBx1BlUBQEEEEAgcQLMo3FdhlOcE6UQQAABBBAgYeJoDBDAOOoMqoIAAgggkDgB5tG4LsMpzolSCCCAAAIIkDBxNAYIYBx1BlVBAAEEEEicAPNoXJfhFOdEKQQQQAABBEiYOBoDBDCOOoOqIIAAAggkToB5NK7LcIpzohQCCCCAAAIkTByNAQIYR51BVRBAAAEEEifAPBrXZTjFOVEKAQQQQAABEiaOxgABjKPOoCoIIIAAAokT8DqPTp8+3UaNGmVz5861iooK69Gjh40YMcK6dOmSsfFf//pXO/DAA239+vV2zz332BlnnJHxZ3h1yrghnIAAAggggEABBDzNo2WVlZWVBTAoyCU9wRcEgIsigAACCCCQg4DHeXTixIl27rnnhuRI//79bc2aNTZ27FhbtmyZzZ4927p27RrdYiVJvvnNb9pbb71lq1atImESLUdBBBBAAAEEGk/AU7xBwqTx+pVPQgABBBBAoKgFPAUwglZSpFOnTtayZUubN29e+FvHwoULrXPnznbQQQfZM888E90nP/3pT8PKlCuvvNKuueYaEibRchREAAEEEECg8QQ8xRskTBqvX/kkBBBAAAEEilrAUwAj6MmTJ1u/fv1s2LBhNnTo0Br2ffv2tbvvvjskTzp06JC2X7SqZN999w2P9rRo0SJ8Lo/kpGWjAAIIIIAAAo0u4CneIGHS6N3LByKAAAIIIFCcAp4CGAlfeOGFNm7cOHvyySetd+/eNdAnTJgQHtGZNm2a9enTp8EO0dPJhx12mK1bt86ef/55+9WvfkXCpDiHMK1CAAEEEEiAgKd4g4RJAgYMVUQAAQQQQMCDgKcARh4nnHCCPfLIIzZ//nzbe++9axA99thjdtxxx9mYMWPs0ksvbZDvtttuswEDBtgrr7wS9jxJrVyJXWGyYsUK05/UsXjx4vA40KJFi6x9+/Yeuo46IIAAAgggkBgBT/EGCZPEDBsqigACCCCAQGEFPAUwkujVq1fYo2TBggW266671sDR1/X9kSNH2qBBg+qF0yM72jD2kksuCfuX6Mg0YaJHgq699tpNrkHCpLDjlasjgAACCCRTwFO8QcIkmWOIWiNQVAIb16+xlYtmhT8b1q6wJlu2tBYduoc/5U2bFVVbaQwCSRbwFMDIsTFWmBxzzDH29ttvh1cSN2v25f9vMk2YsMIkyaOauiOAAAIIeBPwFG+QMPE2OqgPAiUm8MXKj+zD2cNtw+dLzKzMzPSm8y//btK8te186GCraNGuxFRoLgI+BTwFMBLKdQ+T3/3ud2F/k/Hjx9sRRxxRhT59+nS76qqr7MYbb7QTTzzR2rVrZ1tttVV0p3hziq44BRFAAAEEEHAg4GkeJWHiYEBQBQRKVUArS95/aqBt+HzpfxIltSXKrEnz7azj/9zESpNSHSS025WApwBGMJMmTbKzzz47PA4zZMiQGlZ6y41WijT0lpxbb7017F2S7nj88cft6KOPTles6vvenKIrTkEEEEAAAQQcCHiaR0mYOBgQVAGBUhVY/u5T9slf70zb/Db7nWetvtorbTkKIIBAfgU8BTBq6bJly6xjx47WqlUrmzdvnrVs2TIAKEnSuXNnO/DAA+3ZZ58NX1u9enX4usq2bds2fE2P4vz1r3/dBG3mzJmmjWC1WWyPHj3Cnx133DEa15tTdMUpiAACCCCAgAMBT/MoCRMHA4IqIFCqAh/8cZitWfL3elaXpFTKrFnrvaz9YUNLlYl2I+BGwFMAk0LR4zQXXHBB2LhVrxFeu3atjR071pYsWWKzZs2yfffdNxRVEqRnz5521llnhZUnDR2Z7mFS+7M8OrkZRFQEAQQQQACBNAKe5lESJgxXBBAomMD7M35s61Z9lPb6W3ylnXXsfXPachRAAIH8CngKYKq3dOrUqTZ69OiwcWtFRUVYETJ8+HDr1q1bVTESJvkdG3w6AggggAACjSXgKd4gYdJYvcrnIIBAxgKsMMmYjBMQKKiApwCmoBAJujPm2Ym6IYAAAgggUJeAp3iDhAljFAEECibAHiYFo+fCCGQl4CmAyaoBm+kknDYTNJdBAAEEEChKAU/zKAmTohxiNAqBZAjwlpxk9BO1RCAl4CmA8dwrOHnuHeqGAAIIIOBdwNM8SsLE+2ihfggUucAXKz+yD2cPtw2fLzGzsv9sAPvl302at7adDx1sFS3aFbkCzUMgGQKeAhjPYjh57h3qhgACCCDgXcDTPErCxPtooX4IlICAVpqsXDTbVi6aZRvWLrcmW7ayFh26W4sOh1p502YlIEATEUiGgKcAxrMYTp57h7ohgAACCHgX8DSPkjDxPlqoHwIIIIAAAk4EPAUwTkjqrAZOnnuHuiGAAAIIeBfwNI+SMPE+WqgfAggggAACTgQ8BTBOSEiYeO4I6oYAAgggkEgBT/EGCZNEDiEqjQACCCCAwOYX8BTAbP7Wx18Rp3grSiKAAAIIIFBbwNM8SsKE8YkAAggggAACUQKeApioCheoEE4FgueyCCCAAAJFIeBpHiVhUhRDikYggAACCCCQfwFPAUz+W5v9FXDK3o4zEUAAAQQQ8DSPkjBhPCKAAAIIIIBAlICnACaqwgUqhFOB4LksAggggEBRCHiaR0mYFMWQohEIIIAAAgjkX8BTAJP/1mZ/BZyyt+NMBBBAAAEEPM2jJEwYjwgggAACCCAQJeApgImqcIEK4VQgeC6LAAIIIFAUAp7mURImRTGkaAQCCCCAAAL5F/AUwOS/tdlfAafs7TgTAQQQQAABT/MoCRPGIwIIIIAAAghECXgKYKIqXKBCOBUInssigAACCBSFgKd5lIRJUQwpGoEAAggggED+BTwFMPlvbfZXwCl7O85EAAEEEEDA0zxKwoTxiAACCCCAAAJRAp4CmKgKF6gQTgWC57IIIIAAAkUh4GkeJWFSFEOKRiCAAAIIIJB/AU8BTP5bm/0VcMrejjMRQAABBBDwNI+SMGE8IoAAAggggECUgKcAJqrCBSqEU4HguSwCCCCAQFEIeJpHSZgUxZCiEQgggAACCORfwFMAk//WZn8FnLK340wEEEAAAQQ8zaMkTBiPCCCAAAIIIBAl4CmAiapwgQrhVCB4LosAAgggUBQCnuZREiZFMaRoBAIIIIAAAvkX8BTA5L+12V8Bp+ztOBMBBBBAAAFP8ygJE8YjAggggAACCEQJeApgoipcoEI4FQieyyKAAAIIFIWAp3mUhElRDCkagQACCCCAQP4FPAUw+W9t9lfAKXs7zkQAAQQQQMDTPErChPGIAAIIIIAAAlECngKYqAoXqBBOBYLnsggggAACRSHgaR4lYVIUQ4pGIIAAAgggkH8BTwFM/lub/RVwyt6OMxFAAAEEEPA0j5IwYTwigAACCCCAQJSApwAmqsIFKoRTgeC5LAIIIIBAUQh4mkdJmBTFkKIRCCCAAAII5F/AUwCT/9ZmfwWcsrfjTAQQQAABBDzNoyRMGI8IIIAAAgggECXgKYCJqnCBCuFUIHguiwACCCBQFAKe5lESJkUxpGgEAggggAAC+RfwFMDkv7XZXwGn7O04EwEEEEAAAU/zKAkTxiMCCCCAAAIIRAl4CmCiKlygQjgVCJ7LIoAAAggUhYCneTSjhMn06dNt1KhRNnfuXKuoqLAePXrYiBEjrEuXLlEd89xzz4Xyc+bMsbVr19oee+xh559/vl100UVWXl6+yWds3LjR7rzzTrvrrrts3rx5VllZabvssot95zvfsZ/+9KdR16xeyBN8xpXnBAQQQAABBAoswDwa1wE4xTlRCgEEEEAAgboEPM2j0QmTiRMn2rnnnhuSI/3797c1a9bY2LFjbdmyZTZ79mzr2rVrg739wAMP2Omnn27bb799SJC0adPGZsyYYQ8++GD492233Vbj/PXr19spp5xijz32mH3ve9+z7t27W1lZmb333nvhz5QpUzIeXZ7gM648JyCAAAIIIFBgAebRuA7AKc6JUggggAACCBRFwkRJkU6dOlnLli3DSg/9rWPhwoXWuXNnO+igg+yZZ56pt7eV/GjXrp2tWrXK3njjDdt1112ryir5MmHChJB0OeSQQ6q+fv3119uwYcNCwuSoo45qlJFEANMojHwIAggggECJCjCPxnU8TnFOlEIAAQQQQKAoEiaTJ0+2fv36hQTG0KFDa7Spb9++dvfdd4fkSYcOHers8VdffdX233//kPh44oknapR54YUXQqLknHPOCY/f6Fi9enVIsBx++OFhBYoexVm5cqW1aNEirDLJ9iCAyVaO8xBAAAEEEDBjHo0bBTjFOVEKAQQQQACBokiYXHjhhTZu3Dh78sknrXfv3jXapNUhWiUybdo069OnT509/uKLL9rBBx9sJ598smkflOrHa6+9Zvvtt5/ts88+YfWJDl1HyZXhw4eHR35++ctf2vLly0PCRI/p3Hjjjda6deuMRxcBTMZknIAAAggggECVAPNo3GDAKc6JUggggAACCBRFwuSEE06wRx55xObPn2977713jTbpkZnjjjvOxowZY5deemmdPf7pp5+GvUt22GEHW7BggTVv3ryq3K233moDBgwIyZAVK1aEr+uzLrvssrDPSZMmTezqq68OK0602uTee+8N+6W89NJL1qxZswZHmD4v9ZkquHjx4vD40KJFi6x9+/aMTgQQQAABBBDIQIBEQBwWTnFOlEIAAQQQQKAoEia9evUKe5Qo2VF9/xE1Tl/X90eOHGmDBg2qt8cvuOACGz9+vB199NF23XXXhQTKU089ZZdffnl4BEeP3WivEx033HCDXXPNNSFZ8vrrr4d9UlLHD37wg5A00WfpDTsNHXqE6Nprr92kCAkTfjARQAABBBDIXIBEQJwZTnFOlEIAAQQQQKAoEia5rjARgl4jPHDgwLDB67p164KLNo+95ZZbQqJFyZKlS5eGr990000hkaK9TbQZbPXj2WeftSOOOCK8Oef+++9vcISxwoQfQAQQQAABBBpPgERAnCVOcU6UQgABBBBAoCgSJrnuYVIdQUkMvWlHm7fuu+++tmHDhpA40R4nqeTIb37zm5AQ0X4lU6dOrWH4t7/9LTwWpL1UtNdJJgcBTCZalEUAAQQQQKCmAPNo3IjAKc6JUggggAACCBRFwmTSpEl29tlnh8dbhgwZUqNNenuO3qLT0FtyGhoGSoiceuqp4TGcwYMHh6Lvv/9+eI2x9huZM2dOjdNnzJhhRx55pJ1xxhl2zz33ZDTCCGAy4qIwAggggAACNQSYR+MGBE5xTpRCAAEEEECgKBImelNNx44drVWrVmF1iFaE6FCSRPuLHHjggaZHZXRoPxJ9XWXbtm3b4AhYsmRJWFmiTWHffPPNGm++6dmzpz333HMhYaLPTx160442f9XjOFqFkslBAJOJFmURQAABBBCoKcA8GjcicIpzohQCCCCAAAJFkTBRI7TJqjZu7dKlS3iNsPYkGTt2rCnpMWvWrPB4jY6ZM2eakh1nnXVWWHmSOqZMmRL+ffjhh9uOO+5o7777rk2cODG8xebhhx8O51Q/9Irh7t27h81gf/jDH4a35Dz00EP2hz/8IWwc++ijj1p5eXlGI4wAJiMuCiOAAAIIIFBDgHk0bkDgFOdEKQQQQAABBIomYaKG6PGZ0aNH29y5c62iosJ69Ohhw4cPt27dulW1s76EySuvvBI2d9Vbb7RiRa8M1qM1egxn9913r3OkaL8SvS1Hq1eUWNFjOnpLzlVXXRWun+lBAJOpGOURQAABBBD4rwDzaNxowCnOiVIIIIAAAggUVcIk6d1JAJP0HqT+CCCAAAKFFGAejdPHKc6JUggggAACCJAwcTQGCGAcdQZVQQABBBBInADzaFyX4RTnRCkEEEAAAQRImDgaAwQwjjqDqiCAAAIIJE6AeTSuy3CKc6IUAggggAACJEwcjQECGEedQVUQQAABBBInwDwa12U4xTlRCgEEEEAAARImjsYAAYyjzqAqCCCAAAKJE2AejesynOKcKIUAAggggAAJE0djgADGUWdQFQQQQACBxAkwj8Z1GU5xTpRCAAEEEECAhImjMUAA46gzqAoCCCCAQOIEmEfjugynOCdKIYAAAgggQMLE0RgggHHUGVQFAQQQQCBxAsyjcV2GU5wTpRBAAAEEECBh4mgMEMA46gyqggACCCCQOAHm0bguwynOiVIIIIAAAgiQMHE0BghgHHUGVUEAAQQQSJwA82hcl+EU50QpBBBAAAEESJg4GgMEMI46g6oggAACCCROgHk0rstwinOiFAIIIIAAAiRMHI0BAhhHnUFVEEAAAQQSJ8A8GtdlOMU5UQoBBBBAAAESJo7GAAGMo86gKggggAACiRNgHo3rMpzinCiFAAIIIIAACRNHY4AAxlFnUBUEEEAAgcQJMI/GdRlOcU6UQgABBBBAgISJozFAAOOoM6gKAggggEDiBJhH47oMpzgnSiGAAAIIIEDCxNEYIIBx1BlUBQEEEEAgcQLMo3FdhlOcE6UQQAABBBAgYeJoDBDAOOoMqoIAAgggkDgB5tG4LsMpzolSCCCAAAIIkDBxNAYIYBx1BlVBAAEEEEicAPNoXJfhFOdEKQQQQAABBEiYOBoDBDCOOoOqIIAAAggkToB5NK7LcIpzohQCCCCAAAIkTByNAQIYR51BVRBAAAEEEifAPBrXZTjFOVEKAQQQQAABEiaOxgABjKPOoCoIIIAAAokTYB6N6zKc4pwohQACCCCAAAkTR2OAAMZRZ1AVBBBAAIHECTCPxnUZTnFOlEIAAQQQQICEiaMxQADjqDOoCgIIIIBA4gSYR+O6DKc4J0ohgAACCCBAwsTRGCCAcdQZVAUBBBBAIHECzKNxXYZTnBOlEEAAAQQQIGHiaAwQwDjqDKqCAAIIIJA4AebRuC7DKc6JUggggAACCJAwcTQGCGAcdQZVQQABBBBInADzaFyX4RTnRCkEEEAAAQRImDgaAwQwEOqkuQAAIABJREFUjjqDqiCAAAIIJE6AeTSuy3CKc6IUAggggAACJEwcjQECGEedQVUQQAABBBInwDwa12U4xTlRCgEEEEAAARImjsYAAYyjzqAqCCCAAAKJE2AejesynOKcKIUAAggggAAJE0djgADGUWdQFQQQQACBxAkwj8Z1GU5xTpRCAAEEEECAhImjMUAA46gzqAoCCCCAQOIEmEfjugynOCdKIYAAAgggQMLE0RgggHHUGVQFAQQQQCBxAsyjcV2GU5wTpRBAAAEEECBh4mgMEMA46gyqggACCCCQOAHm0bguwynOiVIIIIAAAgiQMHE0BghgHHUGVUEAAQQQSJwA82hcl+EU50QpBBBAAAEESJg4GgMEMI46g6oggAACCCROgHk0rstwinOiFAIIIIAAAiRMHI0BAhhHnUFVEEAAAQQSJ8A8GtdlOMU5UQoBBBBAAAESJo7GAAGMo86gKggggAACiRNgHo3rMpzinCiFAAIIIIAACRNHY4AAxlFnUBUEEEAAgcQJMI/GdRlOcU6UQgABBBBAgISJozFAAOOoM6gKAggggEDiBJhH47oMpzgnSiGAAAIIIEDCxNEYIIBx1BlUBQEEEEAgcQLMo3FdhlOcE6UQQAABBBAgYeJoDBDAOOoMqoIAAgggkDgBr/Po9OnTbdSoUTZ37lyrqKiwHj162IgRI6xLly5pjR9//HEbN26cvf766/bJJ59YeXm5dezY0b773e/aj370I9tmm23SfkbtAl6dMm4IJyCAAAIIIFAAAU/zaFllZWVlAQwKcklP8AUB4KIIIIAAAgjkIOBxHp04caKde+65ITnSv39/W7NmjY0dO9aWLVtms2fPtq5duzbY4tGjR9usWbPsgAMOsLZt29q6devspZdesl//+te2++6728svv2xbb711RmoenTJqAIURQAABBBAooICneZSESQEHApdGAIEvBTZuXGMrl88KfzZsWGFNmrS0Fq26hz/l5c1gQgABJwKeAhiRKCnSqVMna9mypc2bNy/8rWPhwoXWuXNnO+igg+yZZ57JSk8rVq666iq7++677cwzz8zoM7w5ZVR5CiOAAAIIIFBgAU/zKAmTAg8GLo9AqQt8sfYj+3DhcNuwfomZlZmZFr19+XeTpq1t510GW8WW7UqdifYj4ELAUwAjkMmTJ1u/fv1s2LBhNnTo0BpGffv2DckOJU86dOiQsd9vfvMb+973vmdjxoyxSy+9NKPzvTllVHkKI4AAAgggUGABT/MoCZMCDwYuj0ApC2hlyfsLBtqG9Uv/kyiprVFmTZpuZx13u4mVJqU8UGi7GwFPAYxQLrzwwrD/yJNPPmm9e/eu4TRhwoTwiM60adOsT58+aQ1XrVoVHufR33/5y19s4MCB9uGHH4a9Tfbaa6+051cv4M0po8pTGAEEEEAAgQILeJpHSZgUeDBweQRKWWD5sqfsk4/vTEvQZqfzrNW2vdKWowACCORXwFMAo5aecMIJ9sgjj9j8+fNt7733rtH4xx57zI477rjoFSKpFSmpD9lnn33sxhtvtKOPPjot6ooVK0x/UsfixYvD40CLFi2y9u3bpz2fAggggAACCCDwXwFP8QYJE0YmAggUTOCD94bZms//Xs/qklS1yqxZ872sfaeay+0LVmkujEAJC3gKYNQNvXr1CnuULFiwwHbdddcaPaOv6/sjR460QYMGpe01JV0++ugjW7JkSdgs9k9/+pNdfvnl9v3vfz/tuXok6Nprr92kHAmTtHQUQAABBBBAYBMBT/EGCRMGKAIIFEzg/QU/tnVffJT2+ltUtLOOu92cthwFEEAgvwKeAhi1tDFXmNSW06M8erXwlClT7LTTTmsQlhUm+R13fDoCCCCAQGkJeIo3SJiU1tijtQi4EmCFiavuoDIIpBXwFMCoso25h0ntxldWVlqrVq1s//33t5kzZ6a1qV7Am1NGlacwAggggAACBRbwNI+SMCnwYODyCJSyAHuYlHLv0/YkCngKYOQ3adIkO/vss8PjMEOGDKlBqrfn6C062b4lZ/369faVr3zF9txzT3vttdcy6i5vThlVnsIIIIAA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GlaSDB48OBTVJrDaDLahY8cdd7SPP/44oxHmCT6jilMYAQQQQAABBwLMo3GdgFOcE6UQQAABBBAoioTJsmXLrGPHjtaqVauwOkQrQnQoSdK5c2c78MAD7dlnnw1f034k+rrKtm3btsERsGTJkrCyRJvCvvnmm9a6detQXitL9LXah15prNUlDzzwQFh5cvzxx2c0wghgMuKiMAIIIIAAAjUEmEfjBgROcU6UQgABBBBAoCgSJmqENmzVxq1dunQJrxHWniRjx441JT30Fhs9XqNj5syZ1rNnTzvrrLPCypPUMWXKlPBv7UOi1SHvvvuuTZw4MbzF5uGHHw7npDsy3cOk9ucRwKQT5vsIIIAAAgjUL8A8Gjc6cIpzohQCCCCAAAJFkzBRQ/T4zOjRo23u3LlWUVFhPXr0sOHDh1u3bt2q2llfwkR7j2hz19dff920YqVNmzZ25JFHhsdwdt9996iRQsIkiolCCCCAAAII5EWAREAcK05xTpRCAAEEEECgqBImSe9OApik9yD1RwABBBAopADzaJw+TnFOlEIAAQQQQICEiaMxQADjqDOoCgIIIIBA4gSYR+O6DKc4J0ohgAACCCBAwsTRGCCAcdQZVAUBBBBAIHECzKNxXYZTnBOlEEAAAQQQIGHiaAwQwDjqDKqCAAIIIJA4AebRuC7DKc6JUggggAACCJAwcTQGCGAcdQZVQQABBBBInEBjz6NffPGF3X///eGNe3369LGOHTsmzsR7oFcUoDQCAQQQQKCkBBo73sgFr6yysrIylw9I0rme4JPkRl0RQAABBBCQQC7z6IABA+yZZ56x1157LWBu3LjRDj30UHvppZdMoUirVq1szpw59rWvfS3x2Lk4Jb7xNAABBBBAAIEcBTzNoyRMcuxMTkcAAQQQQKBUBHIJYLp162a9e/e2m266KXD97ne/s1NOOcUGDRpkX//61+2iiy6yE0880e68887Ec+bilPjG0wAEEEAAAQRyFPA0j5IwybEzOR0BBBBAAIFSEcglgNluu+3shhtuCIkRHeedd549/fTT9s4774R//+QnP7H77ruv6t9JNs3FKcntpu4IIIAAAgg0hoCneZSESWP0KJ+BAAIIIIBACQjkEsBstdVW9vOf/9zOPffcILXXXntZ9+7dq1aUTJo0yS6++GJbvXp14iVzcUp842kAAggggAACOQp4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FAKe5lESJkUxpGgEAggggAAC+RfwFMDkv7XZXwGn7O04EwEEEEAAAU/zaEYJk+nTp9uoUaNs7ty5VlFRYT169LARI0ZYly5donr1ueeeC+XnzJlja9eutT322MPOP/98u+iii6y8vLzqM5YtW2b33nuvPf744zZv3jz75z//aW3btrUDDjjABg8ebF//+tejrle7kCf4rBrASQgggAACCBRQgHk0Dh+nOCdKIYAAAgggUJeAp3k0OmEyceJEO/fcc0NypH///rZmzRobO3asKbkxe/Zs69q1a4O9/cADD9jpp59u22+/fUiQtGnTxmbMmGEPPvhg+Pdtt91Wdf4TTzxhxx13nB1xxBHhzw477GD/+Mc/bNy4cbZixQq777777LTTTst4dHmCz7jynIAAAggggECBBZhH4zoApzgnSiGAAAIIIFAUCRMlRTp16mQtW7YMKz70t46FCxda586d7aCDDrJnnnmm3t5ev369tWvXzlatWmVvvPGG7brrrlVllXyZMGFCSLoccsgh4evvvfee6Zzdd9+9xmfq3P3339+22247++ijj2qsSokZagQwMUqUQQABBBBAoG4B5tG4kYFTnBOlEEAAAQQQKIqEyeTJk61fv342bNgwGzp0aI029e3b1+6+++6QPOnQoUOdPf7qq6+GRMdRRx1lWj1S/XjhhRdCouScc86xO++8M+2I0efo8xYvXmw77bRT2vLVCxDAZMRFYQQQQAABBGoIMI/GDQic4pwohQACCCCAQFEkTC688MLwOMyTTz5pvXv3rtEmrQ7RKpFp06ZZnz596uzxF1980Q4++GA7+eSTTfugVD9ee+0122+//WyfffYJq08aOjZu3Gjt27e3JUuW2PLly61Zs2YZjTACmIy4KIwAAggggEAiEia57LH28MMP2+9//3vTDRzd/FFsoRWu5513np155pnWtGnTjEcB8UbGZJyAAAIIIIBAlYCneTRqD5MTTjjBHnnkEZs/f77tvffeNbryscceC/uNjBkzxi699NI6u/nTTz8Ne5doL5IFCxZY8+bNq8rdeuutNmDAAGvRokXYn6ShQ3um6BpnnXWWadVLukOfV/0ztSpFjw8tWrQoJF44EEAAAQQQQCBewFMAk6p1rnusabXq1ltvbSeddFKIcXRD5v7777eXX37Zjj322BD/lJWVxSOZmUenjBpAYQQQQAABBAoo4GkejUqY9OrVK+xRomRH9f1HZKiv6/sjR460QYMG1ct6wQUX2Pjx4+3oo4+26667LiRQnnrqKbv88stt9erVVllZGfYtqe+YOXNmeKRn5513DkGM9jFJd+gRomuvvXaTYiRM0snxfQQQQAABBDYV8BTAqHa57rGmz3j66afDBvPVkyIbNmywww8/3GbNmmWPPvpoSJxkcnhzyqTulEUAAQQQQKDQAp7m0aiESa4rTASu1wgPHDgwbPC6bt260AfaPPaWW24JiRYlS5YuXVpn32hDWCVaWrVqZUqc1N4Mtr4OZYVJoYc610cAAQQQKCYBTwGMXHPdY62hvvn5z39uP/rRj9LeEKrrM7w5FdMYpC0IIIAAAsUv4GkejUqY5LqHSfUuVRJDb9rRnZx9993XdBdHiRPtcaLESO3jj3/8Y3jkZ9tttw2rWWKTJQQwxf92icGPAAAgAElEQVSDRAsRQAABBDavgKcARi1vzPiktuRVV11lo0aNsrvuuitsfJ/J4c0pk7pTFgEEEEAAgUILeJpHoxImkyZNsrPPPjs83jJkyJAafgoidIenobfkNAQ+depUO/XUU+2GG26wwYMH1yj67LPP2vHHHx/2PlGy5Ktf/WpOfecJPqeGcDICCCCAAAIFEPA2jzbGCtj6brB06dLFysvL7e233077GDArWgswGLkkAggggEDRCniKN6ISJnpGuGPHjuGRGK0O0YoQHUqSdO7c2Q488EBTckOH9iPR11W2bdu2DXai3najlSXaFPbNN9+01q1bV5XXM8UKhNq1axeSJbvsskvOA8ITfM6N4QMQQAABBBDYzALe5tHG2GOtNuGqVavs29/+tr366qvhDYB6w1+6gz3T0gnxfQQQQAABBOIFPMUbUQkTNU0btmrjVt1x0WuEtSeJ3lqjpIc2RdPjNTq0x0jPnj03eZPNlClTwkoUbaK244472rvvvmva2V53ZfRKP52TOrSp62GHHRb2OtHKE230WvtQAKNd7TM5PMFnUm/KIoAAAggg4EHA2zza2CtMlCzRBq+Ka37xi1/YRRddFMXOCpMoJgohgAACCCAQJeAp3ohOmKhlenxm9OjRNnfuXKuoqLAePXrY8OHDrVu3blUNry9h8sorr4TNXV9//fWwq32bNm3syCOPDI/h1N6XJLWJW0OaSrh06tQpCjxVyBN8RhWnMAIIIIAAAg4EvM2jjbmHycqVK+2YY46x559/3u64445wcyjbw5tTtu3gPAQQQAABBAoh4GkezShhUgisxrymJ/jGbBefhQACCCCAwOYQ8DaPNtYea8uXLw9v43vppZfsl7/8Zdi3LZfDm1MubeFcBBBAAAEENreAp3mUhMnm7n2uhwACCCCAQEIFPAUwImyMPdaULNGKV62EVQLmBz/4Qc69480p5wbxAQgggAACCGxGAU/zKAmTzdjxXAoBBBBAAIEkC3gKYFKOue6xpo3rtXfaiSeeaN/97nc36R49dlz90eOY/vPoFFNvyiCAAAIIIOBBwNM8SsLEw4igDggggAACCCRAwFMAU50rlz3WysrKGpQfOnSo6S04mRxenTJpA2URQAABBBAolICneZSESaFGAddFAAEEEEAgYQKeAhjPdDh57h3qhgACCCDgXcDTPErCxPtooX4IIIAAAgg4EfAUwDghqbMaOHnuHepWygIbN66xlctnhT8bNqywJk1aWotW3cOf8vJmpUxD2xFwJeBpHiVh4mpoUBkEEEAAAQT8CngKYPwqmeHkuXeoW6kKfLH2I/tw4XDbsH6JmelRvMqqv5s0bW077zLYKrZsV6o8tBsBVwKe5lESJq6GBpVBAAEEEEDAr4CnAMavEgkTz31D3UpTQCtL3l8w0DasX/qfRElthzJr0nQ767jbTaw0Kc0hQqudCXiKN0iYOBscVAcBBBBAAAGvAp4CGK9GqhdOnnuHupWiwPJlT9knH9+ZtultdjrPWm3bK205CiCAQH4FPM2jJEzy29d8OgIIIIAAAkUj4CmA8YyKk+feoW6lKPDBe8Nszed/r2d1SUqkzJo138vadxpaikS0GQFXAp7mURImroYGlUEAAQQQQMCvgKcAxq8SK0w89w11K02B9xf82NZ98VHaxm9R0c467nZz2nIUQACB/Ap4ijdImOS3r/l0BBBAAAEEikbAUwDjGRUnz71D3UpRgBUmpdjrtDnJAp7mURImSR5J1B0BBBBAAIHNKOApgNmMzc74UjhlTMYJCORVgD1M8srLhyPQ6AKe5lESJo3evXwgAggggAACxSngKYDxLIyT596hbqUowFtySrHXaXOSBTzNoyRMkjySqDsCCCCAAAKbUcBTALMZm53xpXDKmIwTEMi7wBdrP7IPFw63DeuXmFnZfzaA/fLvJk1b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Lg1grDIcUiKcQoLntggggAACkRBwqR8lYBKJJkUhEEAAAQQQSL+ASwOY9Jc2+B1wCm7HmQgggAACCLjUjxIwoT0igAACCCCAgJWASwMYqwyHlAinkOC5LQIIIIBAJARc6kcJmESiSVEIBBBAAAEE0i/g0gAm/aUNfgecgttxJgIIIIAAAi71o74CJrNmzZI777xTli1bJvn5+dK5c2cZPXq0tGrVyqpWly5datK/8847sn79ejnwwAOlXbt2cv3118uJJ54Yd419+/bJ448/LhMmTJBPPvlEduzYIU2aNJFevXrJoEGDpEGDBlb3jE3kErzvzHMCAggggAACIQvQj9pVAE52TqRCAAEEEEAgkYBL/ah1wGTy5Mly6aWXmuDIgAEDTABj/PjxsnHjRlmwYIG0bt26zNpeuHChdOnSRerVqyeXXXaZCX6sXr1aHn74Yfn666/lxRdflFNPPbX4Gtddd53cc8890rVrVxMkqVatmrz11lvy2GOPyaGHHipLliyRGjVq+GphLsH7yjiJEUAAAQQQcECAftSuEnCycyIVAggggAACkQiYaFCkWbNmUlBQIMuXLzd/6/H555CTC8AAACAASURBVJ9Ly5YtpUOHDjJnzpwya7tPnz7yxBNPmNkpsTNSFi1aZGaZnHPOOfL000+ba2zfvl32339/OfbYY+Xdd9+VvLy84mtfc801JlAze/Zs6dGjh68WxgDGFxeJEUAAAQQQiBOgH7VrEDjZOZEKAQQQQACBSARMpk6dKv3795cRI0bI8OHD48rUr18/mTZtmgme6KyRZEfPnj1NkKOwsFDq1q1bnOyrr76Sgw8+WC666CJzHT00Tf369UXPefbZZ+Mueccdd8jQoUNNgEZnn/g5GMD40SItAggggAAC8QL0o3YtAic7J1IhgAACCCAQiYDJwIEDzV4ir7zyinTv3j2uTLqkRpfozJw5U3r37p20xu+//365+uqr5bTTTpORI0cWL8n505/+JDrL5PXXX49b1nPcccfJ4sWLzZ4nel3dM0WX5Fx11VWi/6ZLeCpVquSrhTGA8cVFYgQQQAABBOIE6EftGgROdk6kQgABBBBAIBIBE292yIoVK+Soo46KK9MLL7xglsbce++9ostlkh179+6VW265RTRwsnXr1uJkujxHN5M97LDD4k5dtWqVmdUyb968uJ9feeWVZm+T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\" width=\"999.9999783255842\">" |
| ], |
| "text/plain": [ |
| "<IPython.core.display.HTML object>" |
| ] |
| }, |
| "metadata": {}, |
| "output_type": "display_data" |
| }, |
| { |
| "name": "stdout", |
| "output_type": "stream", |
| "text": [ |
| "1 rows affected.\n", |
| "1 rows affected.\n", |
| "1 rows affected.\n", |
| "1 rows affected.\n", |
| "1 rows affected.\n", |
| "1 rows affected.\n", |
| "1 rows affected.\n", |
| "1 rows affected.\n" |
| ] |
| } |
| ], |
| "source": [ |
| "#df_results = %sql SELECT * FROM $results_table ORDER BY run_id;\n", |
| "df_results = %sql SELECT * FROM $results_table ORDER BY training_loss ASC LIMIT 12;\n", |
| "df_results = df_results.DataFrame()\n", |
| "\n", |
| "#set up plots\n", |
| "fig, axs = plt.subplots(nrows=1, ncols=2, figsize=(10,5))\n", |
| "fig.legend(ncol=4)\n", |
| "fig.tight_layout()\n", |
| "\n", |
| "ax_metric = axs[0]\n", |
| "ax_loss = axs[1]\n", |
| "\n", |
| "ax_metric.xaxis.set_major_locator(MaxNLocator(integer=True))\n", |
| "ax_metric.set_xlabel('Iteration')\n", |
| "ax_metric.set_ylabel('Metric')\n", |
| "ax_metric.set_title('Training metric curve')\n", |
| "\n", |
| "ax_loss.xaxis.set_major_locator(MaxNLocator(integer=True))\n", |
| "ax_loss.set_xlabel('Iteration')\n", |
| "ax_loss.set_ylabel('Loss')\n", |
| "ax_loss.set_title('Training loss curve')\n", |
| "\n", |
| "for run_id in df_results['run_id']:\n", |
| " df_output_info = %sql SELECT training_metrics,training_loss FROM $results_table WHERE run_id = $run_id\n", |
| " df_output_info = df_output_info.DataFrame()\n", |
| " training_metrics = df_output_info['training_metrics'][0]\n", |
| " training_loss = df_output_info['training_loss'][0]\n", |
| " X = range(len(training_metrics))\n", |
| " \n", |
| " ax_metric.plot(X, training_metrics, label=run_id, marker='o')\n", |
| " ax_loss.plot(X, training_loss, label=run_id, marker='o')\n", |
| "\n", |
| "# fig.savefig('./lc_keras_fit.png', dpi = 300)" |
| ] |
| }, |
| { |
| "cell_type": "markdown", |
| "metadata": {}, |
| "source": [ |
| "Validation dataset" |
| ] |
| }, |
| { |
| "cell_type": "code", |
| "execution_count": 31, |
| "metadata": {}, |
| "outputs": [ |
| { |
| "name": "stdout", |
| "output_type": "stream", |
| "text": [ |
| "8 rows affected.\n" |
| ] |
| }, |
| { |
| "data": { |
| "application/javascript": [ |
| "/* Put everything inside the global mpl namespace */\n", |
| "window.mpl = {};\n", |
| "\n", |
| "\n", |
| "mpl.get_websocket_type = function() {\n", |
| " if (typeof(WebSocket) !== 'undefined') {\n", |
| " return WebSocket;\n", |
| " } else if (typeof(MozWebSocket) !== 'undefined') {\n", |
| " return MozWebSocket;\n", |
| " } else {\n", |
| " alert('Your browser does not have WebSocket support.' +\n", |
| " 'Please try Chrome, Safari or Firefox ≥ 6. ' +\n", |
| " 'Firefox 4 and 5 are also supported but you ' +\n", |
| " 'have to enable WebSockets in about:config.');\n", |
| " };\n", |
| "}\n", |
| "\n", |
| "mpl.figure = function(figure_id, websocket, ondownload, parent_element) {\n", |
| " this.id = figure_id;\n", |
| "\n", |
| " this.ws = websocket;\n", |
| "\n", |
| " this.supports_binary = (this.ws.binaryType != undefined);\n", |
| "\n", |
| " if (!this.supports_binary) {\n", |
| " var warnings = document.getElementById(\"mpl-warnings\");\n", |
| " if (warnings) {\n", |
| " warnings.style.display = 'block';\n", |
| " warnings.textContent = (\n", |
| " \"This browser does not support binary websocket messages. \" +\n", |
| " \"Performance may be slow.\");\n", |
| " }\n", |
| " }\n", |
| "\n", |
| " this.imageObj = new Image();\n", |
| "\n", |
| " this.context = undefined;\n", |
| " this.message = undefined;\n", |
| " this.canvas = undefined;\n", |
| " this.rubberband_canvas = undefined;\n", |
| " this.rubberband_context = undefined;\n", |
| " this.format_dropdown = undefined;\n", |
| "\n", |
| " this.image_mode = 'full';\n", |
| "\n", |
| " this.root = $('<div/>');\n", |
| " this._root_extra_style(this.root)\n", |
| " this.root.attr('style', 'display: inline-block');\n", |
| "\n", |
| " $(parent_element).append(this.root);\n", |
| "\n", |
| " this._init_header(this);\n", |
| " this._init_canvas(this);\n", |
| " this._init_toolbar(this);\n", |
| "\n", |
| " var fig = this;\n", |
| "\n", |
| " this.waiting = false;\n", |
| "\n", |
| " this.ws.onopen = function () {\n", |
| " fig.send_message(\"supports_binary\", {value: fig.supports_binary});\n", |
| " fig.send_message(\"send_image_mode\", {});\n", |
| " if (mpl.ratio != 1) {\n", |
| " fig.send_message(\"set_dpi_ratio\", {'dpi_ratio': mpl.ratio});\n", |
| " }\n", |
| " fig.send_message(\"refresh\", {});\n", |
| " }\n", |
| "\n", |
| " this.imageObj.onload = function() {\n", |
| " if (fig.image_mode == 'full') {\n", |
| " // Full images could contain transparency (where diff images\n", |
| " // almost always do), so we need to clear the canvas so that\n", |
| " // there is no ghosting.\n", |
| " fig.context.clearRect(0, 0, fig.canvas.width, fig.canvas.height);\n", |
| " }\n", |
| " fig.context.drawImage(fig.imageObj, 0, 0);\n", |
| " };\n", |
| "\n", |
| " this.imageObj.onunload = function() {\n", |
| " fig.ws.close();\n", |
| " }\n", |
| "\n", |
| " this.ws.onmessage = this._make_on_message_function(this);\n", |
| "\n", |
| " this.ondownload = ondownload;\n", |
| "}\n", |
| "\n", |
| "mpl.figure.prototype._init_header = function() {\n", |
| " var titlebar = $(\n", |
| " '<div class=\"ui-dialog-titlebar ui-widget-header ui-corner-all ' +\n", |
| " 'ui-helper-clearfix\"/>');\n", |
| " var titletext = $(\n", |
| " '<div class=\"ui-dialog-title\" style=\"width: 100%; ' +\n", |
| " 'text-align: center; padding: 3px;\"/>');\n", |
| " titlebar.append(titletext)\n", |
| " this.root.append(titlebar);\n", |
| " this.header = titletext[0];\n", |
| "}\n", |
| "\n", |
| "\n", |
| "\n", |
| "mpl.figure.prototype._canvas_extra_style = function(canvas_div) {\n", |
| "\n", |
| "}\n", |
| "\n", |
| "\n", |
| "mpl.figure.prototype._root_extra_style = function(canvas_div) {\n", |
| "\n", |
| "}\n", |
| "\n", |
| "mpl.figure.prototype._init_canvas = function() {\n", |
| " var fig = this;\n", |
| "\n", |
| " var canvas_div = $('<div/>');\n", |
| "\n", |
| " canvas_div.attr('style', 'position: relative; clear: both; outline: 0');\n", |
| "\n", |
| " function canvas_keyboard_event(event) {\n", |
| " return fig.key_event(event, event['data']);\n", |
| " }\n", |
| "\n", |
| " canvas_div.keydown('key_press', canvas_keyboard_event);\n", |
| " canvas_div.keyup('key_release', canvas_keyboard_event);\n", |
| " this.canvas_div = canvas_div\n", |
| " this._canvas_extra_style(canvas_div)\n", |
| " this.root.append(canvas_div);\n", |
| "\n", |
| " var canvas = $('<canvas/>');\n", |
| " canvas.addClass('mpl-canvas');\n", |
| " canvas.attr('style', \"left: 0; top: 0; z-index: 0; outline: 0\")\n", |
| "\n", |
| " this.canvas = canvas[0];\n", |
| " this.context = canvas[0].getContext(\"2d\");\n", |
| "\n", |
| " var backingStore = this.context.backingStorePixelRatio ||\n", |
| "\tthis.context.webkitBackingStorePixelRatio ||\n", |
| "\tthis.context.mozBackingStorePixelRatio ||\n", |
| "\tthis.context.msBackingStorePixelRatio ||\n", |
| "\tthis.context.oBackingStorePixelRatio ||\n", |
| "\tthis.context.backingStorePixelRatio || 1;\n", |
| "\n", |
| " mpl.ratio = (window.devicePixelRatio || 1) / backingStore;\n", |
| "\n", |
| " var rubberband = $('<canvas/>');\n", |
| " rubberband.attr('style', \"position: absolute; left: 0; top: 0; z-index: 1;\")\n", |
| "\n", |
| " var pass_mouse_events = true;\n", |
| "\n", |
| " canvas_div.resizable({\n", |
| " start: function(event, ui) {\n", |
| " pass_mouse_events = false;\n", |
| " },\n", |
| " resize: function(event, ui) {\n", |
| " fig.request_resize(ui.size.width, ui.size.height);\n", |
| " },\n", |
| " stop: function(event, ui) {\n", |
| " pass_mouse_events = true;\n", |
| " fig.request_resize(ui.size.width, ui.size.height);\n", |
| " },\n", |
| " });\n", |
| "\n", |
| " function mouse_event_fn(event) {\n", |
| " if (pass_mouse_events)\n", |
| " return fig.mouse_event(event, event['data']);\n", |
| " }\n", |
| "\n", |
| " rubberband.mousedown('button_press', mouse_event_fn);\n", |
| " rubberband.mouseup('button_release', mouse_event_fn);\n", |
| " // Throttle sequential mouse events to 1 every 20ms.\n", |
| " rubberband.mousemove('motion_notify', mouse_event_fn);\n", |
| "\n", |
| " rubberband.mouseenter('figure_enter', mouse_event_fn);\n", |
| " rubberband.mouseleave('figure_leave', mouse_event_fn);\n", |
| "\n", |
| " canvas_div.on(\"wheel\", function (event) {\n", |
| " event = event.originalEvent;\n", |
| " event['data'] = 'scroll'\n", |
| " if (event.deltaY < 0) {\n", |
| " event.step = 1;\n", |
| " } else {\n", |
| " event.step = -1;\n", |
| " }\n", |
| " mouse_event_fn(event);\n", |
| " });\n", |
| "\n", |
| " canvas_div.append(canvas);\n", |
| " canvas_div.append(rubberband);\n", |
| "\n", |
| " this.rubberband = rubberband;\n", |
| " this.rubberband_canvas = rubberband[0];\n", |
| " this.rubberband_context = rubberband[0].getContext(\"2d\");\n", |
| " this.rubberband_context.strokeStyle = \"#000000\";\n", |
| "\n", |
| " this._resize_canvas = function(width, height) {\n", |
| " // Keep the size of the canvas, canvas container, and rubber band\n", |
| " // canvas in synch.\n", |
| " canvas_div.css('width', width)\n", |
| " canvas_div.css('height', height)\n", |
| "\n", |
| " canvas.attr('width', width * mpl.ratio);\n", |
| " canvas.attr('height', height * mpl.ratio);\n", |
| " canvas.attr('style', 'width: ' + width + 'px; height: ' + height + 'px;');\n", |
| "\n", |
| " rubberband.attr('width', width);\n", |
| " rubberband.attr('height', height);\n", |
| " }\n", |
| "\n", |
| " // Set the figure to an initial 600x600px, this will subsequently be updated\n", |
| " // upon first draw.\n", |
| " this._resize_canvas(600, 600);\n", |
| "\n", |
| " // Disable right mouse context menu.\n", |
| " $(this.rubberband_canvas).bind(\"contextmenu\",function(e){\n", |
| " return false;\n", |
| " });\n", |
| "\n", |
| " function set_focus () {\n", |
| " canvas.focus();\n", |
| " canvas_div.focus();\n", |
| " }\n", |
| "\n", |
| " window.setTimeout(set_focus, 100);\n", |
| "}\n", |
| "\n", |
| "mpl.figure.prototype._init_toolbar = function() {\n", |
| " var fig = this;\n", |
| "\n", |
| " var nav_element = $('<div/>')\n", |
| " nav_element.attr('style', 'width: 100%');\n", |
| " this.root.append(nav_element);\n", |
| "\n", |
| " // Define a callback function for later on.\n", |
| " function toolbar_event(event) {\n", |
| " return fig.toolbar_button_onclick(event['data']);\n", |
| " }\n", |
| " function toolbar_mouse_event(event) {\n", |
| " return fig.toolbar_button_onmouseover(event['data']);\n", |
| " }\n", |
| "\n", |
| " for(var toolbar_ind in mpl.toolbar_items) {\n", |
| " var name = mpl.toolbar_items[toolbar_ind][0];\n", |
| " var tooltip = mpl.toolbar_items[toolbar_ind][1];\n", |
| " var image = mpl.toolbar_items[toolbar_ind][2];\n", |
| " var method_name = mpl.toolbar_items[toolbar_ind][3];\n", |
| "\n", |
| " if (!name) {\n", |
| " // put a spacer in here.\n", |
| " continue;\n", |
| " }\n", |
| " var button = $('<button/>');\n", |
| " button.addClass('ui-button ui-widget ui-state-default ui-corner-all ' +\n", |
| " 'ui-button-icon-only');\n", |
| " button.attr('role', 'button');\n", |
| " button.attr('aria-disabled', 'false');\n", |
| " button.click(method_name, toolbar_event);\n", |
| " button.mouseover(tooltip, toolbar_mouse_event);\n", |
| "\n", |
| " var icon_img = $('<span/>');\n", |
| " icon_img.addClass('ui-button-icon-primary ui-icon');\n", |
| " icon_img.addClass(image);\n", |
| " icon_img.addClass('ui-corner-all');\n", |
| "\n", |
| " var tooltip_span = $('<span/>');\n", |
| " tooltip_span.addClass('ui-button-text');\n", |
| " tooltip_span.html(tooltip);\n", |
| "\n", |
| " button.append(icon_img);\n", |
| " button.append(tooltip_span);\n", |
| "\n", |
| " nav_element.append(button);\n", |
| " }\n", |
| "\n", |
| " var fmt_picker_span = $('<span/>');\n", |
| "\n", |
| " var fmt_picker = $('<select/>');\n", |
| " fmt_picker.addClass('mpl-toolbar-option ui-widget ui-widget-content');\n", |
| " fmt_picker_span.append(fmt_picker);\n", |
| " nav_element.append(fmt_picker_span);\n", |
| " this.format_dropdown = fmt_picker[0];\n", |
| "\n", |
| " for (var ind in mpl.extensions) {\n", |
| " var fmt = mpl.extensions[ind];\n", |
| " var option = $(\n", |
| " '<option/>', {selected: fmt === mpl.default_extension}).html(fmt);\n", |
| " fmt_picker.append(option)\n", |
| " }\n", |
| "\n", |
| " // Add hover states to the ui-buttons\n", |
| " $( \".ui-button\" ).hover(\n", |
| " function() { $(this).addClass(\"ui-state-hover\");},\n", |
| " function() { $(this).removeClass(\"ui-state-hover\");}\n", |
| " );\n", |
| "\n", |
| " var status_bar = $('<span class=\"mpl-message\"/>');\n", |
| " nav_element.append(status_bar);\n", |
| " this.message = status_bar[0];\n", |
| "}\n", |
| "\n", |
| "mpl.figure.prototype.request_resize = function(x_pixels, y_pixels) {\n", |
| " // Request matplotlib to resize the figure. Matplotlib will then trigger a resize in the client,\n", |
| " // which will in turn request a refresh of the image.\n", |
| " this.send_message('resize', {'width': x_pixels, 'height': y_pixels});\n", |
| "}\n", |
| "\n", |
| "mpl.figure.prototype.send_message = function(type, properties) {\n", |
| " properties['type'] = type;\n", |
| " properties['figure_id'] = this.id;\n", |
| " this.ws.send(JSON.stringify(properties));\n", |
| "}\n", |
| "\n", |
| "mpl.figure.prototype.send_draw_message = function() {\n", |
| " if (!this.waiting) {\n", |
| " this.waiting = true;\n", |
| " this.ws.send(JSON.stringify({type: \"draw\", figure_id: this.id}));\n", |
| " }\n", |
| "}\n", |
| "\n", |
| "\n", |
| "mpl.figure.prototype.handle_save = function(fig, msg) {\n", |
| " var format_dropdown = fig.format_dropdown;\n", |
| " var format = format_dropdown.options[format_dropdown.selectedIndex].value;\n", |
| " fig.ondownload(fig, format);\n", |
| "}\n", |
| "\n", |
| "\n", |
| "mpl.figure.prototype.handle_resize = function(fig, msg) {\n", |
| " var size = msg['size'];\n", |
| " if (size[0] != fig.canvas.width || size[1] != fig.canvas.height) {\n", |
| " fig._resize_canvas(size[0], size[1]);\n", |
| " fig.send_message(\"refresh\", {});\n", |
| " };\n", |
| "}\n", |
| "\n", |
| "mpl.figure.prototype.handle_rubberband = function(fig, msg) {\n", |
| " var x0 = msg['x0'] / mpl.ratio;\n", |
| " var y0 = (fig.canvas.height - msg['y0']) / mpl.ratio;\n", |
| " var x1 = msg['x1'] / mpl.ratio;\n", |
| " var y1 = (fig.canvas.height - msg['y1']) / mpl.ratio;\n", |
| " x0 = Math.floor(x0) + 0.5;\n", |
| " y0 = Math.floor(y0) + 0.5;\n", |
| " x1 = Math.floor(x1) + 0.5;\n", |
| " y1 = Math.floor(y1) + 0.5;\n", |
| " var min_x = Math.min(x0, x1);\n", |
| " var min_y = Math.min(y0, y1);\n", |
| " var width = Math.abs(x1 - x0);\n", |
| " var height = Math.abs(y1 - y0);\n", |
| "\n", |
| " fig.rubberband_context.clearRect(\n", |
| " 0, 0, fig.canvas.width, fig.canvas.height);\n", |
| "\n", |
| " fig.rubberband_context.strokeRect(min_x, min_y, width, height);\n", |
| "}\n", |
| "\n", |
| "mpl.figure.prototype.handle_figure_label = function(fig, msg) {\n", |
| " // Updates the figure title.\n", |
| " fig.header.textContent = msg['label'];\n", |
| "}\n", |
| "\n", |
| "mpl.figure.prototype.handle_cursor = function(fig, msg) {\n", |
| " var cursor = msg['cursor'];\n", |
| " switch(cursor)\n", |
| " {\n", |
| " case 0:\n", |
| " cursor = 'pointer';\n", |
| " break;\n", |
| " case 1:\n", |
| " cursor = 'default';\n", |
| " break;\n", |
| " case 2:\n", |
| " cursor = 'crosshair';\n", |
| " break;\n", |
| " case 3:\n", |
| " cursor = 'move';\n", |
| " break;\n", |
| " }\n", |
| " fig.rubberband_canvas.style.cursor = cursor;\n", |
| "}\n", |
| "\n", |
| "mpl.figure.prototype.handle_message = function(fig, msg) {\n", |
| " fig.message.textContent = msg['message'];\n", |
| "}\n", |
| "\n", |
| "mpl.figure.prototype.handle_draw = function(fig, msg) {\n", |
| " // Request the server to send over a new figure.\n", |
| " fig.send_draw_message();\n", |
| "}\n", |
| "\n", |
| "mpl.figure.prototype.handle_image_mode = function(fig, msg) {\n", |
| " fig.image_mode = msg['mode'];\n", |
| "}\n", |
| "\n", |
| "mpl.figure.prototype.updated_canvas_event = function() {\n", |
| " // Called whenever the canvas gets updated.\n", |
| " this.send_message(\"ack\", {});\n", |
| "}\n", |
| "\n", |
| "// A function to construct a web socket function for onmessage handling.\n", |
| "// Called in the figure constructor.\n", |
| "mpl.figure.prototype._make_on_message_function = function(fig) {\n", |
| " return function socket_on_message(evt) {\n", |
| " if (evt.data instanceof Blob) {\n", |
| " /* FIXME: We get \"Resource interpreted as Image but\n", |
| " * transferred with MIME type text/plain:\" errors on\n", |
| " * Chrome. But how to set the MIME type? It doesn't seem\n", |
| " * to be part of the websocket stream */\n", |
| " evt.data.type = \"image/png\";\n", |
| "\n", |
| " /* Free the memory for the previous frames */\n", |
| " if (fig.imageObj.src) {\n", |
| " (window.URL || window.webkitURL).revokeObjectURL(\n", |
| " fig.imageObj.src);\n", |
| " }\n", |
| "\n", |
| " fig.imageObj.src = (window.URL || window.webkitURL).createObjectURL(\n", |
| " evt.data);\n", |
| " fig.updated_canvas_event();\n", |
| " fig.waiting = false;\n", |
| " return;\n", |
| " }\n", |
| " else if (typeof evt.data === 'string' && evt.data.slice(0, 21) == \"data:image/png;base64\") {\n", |
| " fig.imageObj.src = evt.data;\n", |
| " fig.updated_canvas_event();\n", |
| " fig.waiting = false;\n", |
| " return;\n", |
| " }\n", |
| "\n", |
| " var msg = JSON.parse(evt.data);\n", |
| " var msg_type = msg['type'];\n", |
| "\n", |
| " // Call the \"handle_{type}\" callback, which takes\n", |
| " // the figure and JSON message as its only arguments.\n", |
| " try {\n", |
| " var callback = fig[\"handle_\" + msg_type];\n", |
| " } catch (e) {\n", |
| " console.log(\"No handler for the '\" + msg_type + \"' message type: \", msg);\n", |
| " return;\n", |
| " }\n", |
| "\n", |
| " if (callback) {\n", |
| " try {\n", |
| " // console.log(\"Handling '\" + msg_type + \"' message: \", msg);\n", |
| " callback(fig, msg);\n", |
| " } catch (e) {\n", |
| " console.log(\"Exception inside the 'handler_\" + msg_type + \"' callback:\", e, e.stack, msg);\n", |
| " }\n", |
| " }\n", |
| " };\n", |
| "}\n", |
| "\n", |
| "// from http://stackoverflow.com/questions/1114465/getting-mouse-location-in-canvas\n", |
| "mpl.findpos = function(e) {\n", |
| " //this section is from http://www.quirksmode.org/js/events_properties.html\n", |
| " var targ;\n", |
| " if (!e)\n", |
| " e = window.event;\n", |
| " if (e.target)\n", |
| " targ = e.target;\n", |
| " else if (e.srcElement)\n", |
| " targ = e.srcElement;\n", |
| " if (targ.nodeType == 3) // defeat Safari bug\n", |
| " targ = targ.parentNode;\n", |
| "\n", |
| " // jQuery normalizes the pageX and pageY\n", |
| " // pageX,Y are the mouse positions relative to the document\n", |
| " // offset() returns the position of the element relative to the document\n", |
| " var x = e.pageX - $(targ).offset().left;\n", |
| " var y = e.pageY - $(targ).offset().top;\n", |
| "\n", |
| " return {\"x\": x, \"y\": y};\n", |
| "};\n", |
| "\n", |
| "/*\n", |
| " * return a copy of an object with only non-object keys\n", |
| " * we need this to avoid circular references\n", |
| " * http://stackoverflow.com/a/24161582/3208463\n", |
| " */\n", |
| "function simpleKeys (original) {\n", |
| " return Object.keys(original).reduce(function (obj, key) {\n", |
| " if (typeof original[key] !== 'object')\n", |
| " obj[key] = original[key]\n", |
| " return obj;\n", |
| " }, {});\n", |
| "}\n", |
| "\n", |
| "mpl.figure.prototype.mouse_event = function(event, name) {\n", |
| " var canvas_pos = mpl.findpos(event)\n", |
| "\n", |
| " if (name === 'button_press')\n", |
| " {\n", |
| " this.canvas.focus();\n", |
| " this.canvas_div.focus();\n", |
| " }\n", |
| "\n", |
| " var x = canvas_pos.x * mpl.ratio;\n", |
| " var y = canvas_pos.y * mpl.ratio;\n", |
| "\n", |
| " this.send_message(name, {x: x, y: y, button: event.button,\n", |
| " step: event.step,\n", |
| " guiEvent: simpleKeys(event)});\n", |
| "\n", |
| " /* This prevents the web browser from automatically changing to\n", |
| " * the text insertion cursor when the button is pressed. We want\n", |
| " * to control all of the cursor setting manually through the\n", |
| " * 'cursor' event from matplotlib */\n", |
| " event.preventDefault();\n", |
| " return false;\n", |
| "}\n", |
| "\n", |
| "mpl.figure.prototype._key_event_extra = function(event, name) {\n", |
| " // Handle any extra behaviour associated with a key event\n", |
| "}\n", |
| "\n", |
| "mpl.figure.prototype.key_event = function(event, name) {\n", |
| "\n", |
| " // Prevent repeat events\n", |
| " if (name == 'key_press')\n", |
| " {\n", |
| " if (event.which === this._key)\n", |
| " return;\n", |
| " else\n", |
| " this._key = event.which;\n", |
| " }\n", |
| " if (name == 'key_release')\n", |
| " this._key = null;\n", |
| "\n", |
| " var value = '';\n", |
| " if (event.ctrlKey && event.which != 17)\n", |
| " value += \"ctrl+\";\n", |
| " if (event.altKey && event.which != 18)\n", |
| " value += \"alt+\";\n", |
| " if (event.shiftKey && event.which != 16)\n", |
| " value += \"shift+\";\n", |
| "\n", |
| " value += 'k';\n", |
| " value += event.which.toString();\n", |
| "\n", |
| " this._key_event_extra(event, name);\n", |
| "\n", |
| " this.send_message(name, {key: value,\n", |
| " guiEvent: simpleKeys(event)});\n", |
| " return false;\n", |
| "}\n", |
| "\n", |
| "mpl.figure.prototype.toolbar_button_onclick = function(name) {\n", |
| " if (name == 'download') {\n", |
| " this.handle_save(this, null);\n", |
| " } else {\n", |
| " this.send_message(\"toolbar_button\", {name: name});\n", |
| " }\n", |
| "};\n", |
| "\n", |
| "mpl.figure.prototype.toolbar_button_onmouseover = function(tooltip) {\n", |
| " this.message.textContent = tooltip;\n", |
| "};\n", |
| "mpl.toolbar_items = [[\"Home\", \"Reset original view\", \"fa fa-home icon-home\", \"home\"], [\"Back\", \"Back to previous view\", \"fa fa-arrow-left icon-arrow-left\", \"back\"], [\"Forward\", \"Forward to next view\", \"fa fa-arrow-right icon-arrow-right\", \"forward\"], [\"\", \"\", \"\", \"\"], [\"Pan\", \"Pan axes with left mouse, zoom with right\", \"fa fa-arrows icon-move\", \"pan\"], [\"Zoom\", \"Zoom to rectangle\", \"fa fa-square-o icon-check-empty\", \"zoom\"], [\"\", \"\", \"\", \"\"], [\"Download\", \"Download plot\", \"fa fa-floppy-o icon-save\", \"download\"]];\n", |
| "\n", |
| "mpl.extensions = [\"eps\", \"jpeg\", \"pdf\", \"png\", \"ps\", \"raw\", \"svg\", \"tif\"];\n", |
| "\n", |
| "mpl.default_extension = \"png\";var comm_websocket_adapter = function(comm) {\n", |
| " // Create a \"websocket\"-like object which calls the given IPython comm\n", |
| " // object with the appropriate methods. Currently this is a non binary\n", |
| " // socket, so there is still some room for performance tuning.\n", |
| " var ws = {};\n", |
| "\n", |
| " ws.close = function() {\n", |
| " comm.close()\n", |
| " };\n", |
| " ws.send = function(m) {\n", |
| " //console.log('sending', m);\n", |
| " comm.send(m);\n", |
| " };\n", |
| " // Register the callback with on_msg.\n", |
| " comm.on_msg(function(msg) {\n", |
| " //console.log('receiving', msg['content']['data'], msg);\n", |
| " // Pass the mpl event to the overridden (by mpl) onmessage function.\n", |
| " ws.onmessage(msg['content']['data'])\n", |
| " });\n", |
| " return ws;\n", |
| "}\n", |
| "\n", |
| "mpl.mpl_figure_comm = function(comm, msg) {\n", |
| " // This is the function which gets called when the mpl process\n", |
| " // starts-up an IPython Comm through the \"matplotlib\" channel.\n", |
| "\n", |
| " var id = msg.content.data.id;\n", |
| " // Get hold of the div created by the display call when the Comm\n", |
| " // socket was opened in Python.\n", |
| " var element = $(\"#\" + id);\n", |
| " var ws_proxy = comm_websocket_adapter(comm)\n", |
| "\n", |
| " function ondownload(figure, format) {\n", |
| " window.open(figure.imageObj.src);\n", |
| " }\n", |
| "\n", |
| " var fig = new mpl.figure(id, ws_proxy,\n", |
| " ondownload,\n", |
| " element.get(0));\n", |
| "\n", |
| " // Call onopen now - mpl needs it, as it is assuming we've passed it a real\n", |
| " // web socket which is closed, not our websocket->open comm proxy.\n", |
| " ws_proxy.onopen();\n", |
| "\n", |
| " fig.parent_element = element.get(0);\n", |
| " fig.cell_info = mpl.find_output_cell(\"<div id='\" + id + \"'></div>\");\n", |
| " if (!fig.cell_info) {\n", |
| " console.error(\"Failed to find cell for figure\", id, fig);\n", |
| " return;\n", |
| " }\n", |
| "\n", |
| " var output_index = fig.cell_info[2]\n", |
| " var cell = fig.cell_info[0];\n", |
| "\n", |
| "};\n", |
| "\n", |
| "mpl.figure.prototype.handle_close = function(fig, msg) {\n", |
| " var width = fig.canvas.width/mpl.ratio\n", |
| " fig.root.unbind('remove')\n", |
| "\n", |
| " // Update the output cell to use the data from the current canvas.\n", |
| " fig.push_to_output();\n", |
| " var dataURL = fig.canvas.toDataURL();\n", |
| " // Re-enable the keyboard manager in IPython - without this line, in FF,\n", |
| " // the notebook keyboard shortcuts fail.\n", |
| " IPython.keyboard_manager.enable()\n", |
| " $(fig.parent_element).html('<img src=\"' + dataURL + '\" width=\"' + width + '\">');\n", |
| " fig.close_ws(fig, msg);\n", |
| "}\n", |
| "\n", |
| "mpl.figure.prototype.close_ws = function(fig, msg){\n", |
| " fig.send_message('closing', msg);\n", |
| " // fig.ws.close()\n", |
| "}\n", |
| "\n", |
| "mpl.figure.prototype.push_to_output = function(remove_interactive) {\n", |
| " // Turn the data on the canvas into data in the output cell.\n", |
| " var width = this.canvas.width/mpl.ratio\n", |
| " var dataURL = this.canvas.toDataURL();\n", |
| " this.cell_info[1]['text/html'] = '<img src=\"' + dataURL + '\" width=\"' + width + '\">';\n", |
| "}\n", |
| "\n", |
| "mpl.figure.prototype.updated_canvas_event = function() {\n", |
| " // Tell IPython that the notebook contents must change.\n", |
| " IPython.notebook.set_dirty(true);\n", |
| " this.send_message(\"ack\", {});\n", |
| " var fig = this;\n", |
| " // Wait a second, then push the new image to the DOM so\n", |
| " // that it is saved nicely (might be nice to debounce this).\n", |
| " setTimeout(function () { fig.push_to_output() }, 1000);\n", |
| "}\n", |
| "\n", |
| "mpl.figure.prototype._init_toolbar = function() {\n", |
| " var fig = this;\n", |
| "\n", |
| " var nav_element = $('<div/>')\n", |
| " nav_element.attr('style', 'width: 100%');\n", |
| " this.root.append(nav_element);\n", |
| "\n", |
| " // Define a callback function for later on.\n", |
| " function toolbar_event(event) {\n", |
| " return fig.toolbar_button_onclick(event['data']);\n", |
| " }\n", |
| " function toolbar_mouse_event(event) {\n", |
| " return fig.toolbar_button_onmouseover(event['data']);\n", |
| " }\n", |
| "\n", |
| " for(var toolbar_ind in mpl.toolbar_items){\n", |
| " var name = mpl.toolbar_items[toolbar_ind][0];\n", |
| " var tooltip = mpl.toolbar_items[toolbar_ind][1];\n", |
| " var image = mpl.toolbar_items[toolbar_ind][2];\n", |
| " var method_name = mpl.toolbar_items[toolbar_ind][3];\n", |
| "\n", |
| " if (!name) { continue; };\n", |
| "\n", |
| " var button = $('<button class=\"btn btn-default\" href=\"#\" title=\"' + name + '\"><i class=\"fa ' + image + ' fa-lg\"></i></button>');\n", |
| " button.click(method_name, toolbar_event);\n", |
| " button.mouseover(tooltip, toolbar_mouse_event);\n", |
| " nav_element.append(button);\n", |
| " }\n", |
| "\n", |
| " // Add the status bar.\n", |
| " var status_bar = $('<span class=\"mpl-message\" style=\"text-align:right; float: right;\"/>');\n", |
| " nav_element.append(status_bar);\n", |
| " this.message = status_bar[0];\n", |
| "\n", |
| " // Add the close button to the window.\n", |
| " var buttongrp = $('<div class=\"btn-group inline pull-right\"></div>');\n", |
| " var button = $('<button class=\"btn btn-mini btn-primary\" href=\"#\" title=\"Stop Interaction\"><i class=\"fa fa-power-off icon-remove icon-large\"></i></button>');\n", |
| " button.click(function (evt) { fig.handle_close(fig, {}); } );\n", |
| " button.mouseover('Stop Interaction', toolbar_mouse_event);\n", |
| " buttongrp.append(button);\n", |
| " var titlebar = this.root.find($('.ui-dialog-titlebar'));\n", |
| " titlebar.prepend(buttongrp);\n", |
| "}\n", |
| "\n", |
| "mpl.figure.prototype._root_extra_style = function(el){\n", |
| " var fig = this\n", |
| " el.on(\"remove\", function(){\n", |
| "\tfig.close_ws(fig, {});\n", |
| " });\n", |
| "}\n", |
| "\n", |
| "mpl.figure.prototype._canvas_extra_style = function(el){\n", |
| " // this is important to make the div 'focusable\n", |
| " el.attr('tabindex', 0)\n", |
| " // reach out to IPython and tell the keyboard manager to turn it's self\n", |
| " // off when our div gets focus\n", |
| "\n", |
| " // location in version 3\n", |
| " if (IPython.notebook.keyboard_manager) {\n", |
| " IPython.notebook.keyboard_manager.register_events(el);\n", |
| " }\n", |
| " else {\n", |
| " // location in version 2\n", |
| " IPython.keyboard_manager.register_events(el);\n", |
| " }\n", |
| "\n", |
| "}\n", |
| "\n", |
| "mpl.figure.prototype._key_event_extra = function(event, name) {\n", |
| " var manager = IPython.notebook.keyboard_manager;\n", |
| " if (!manager)\n", |
| " manager = IPython.keyboard_manager;\n", |
| "\n", |
| " // Check for shift+enter\n", |
| " if (event.shiftKey && event.which == 13) {\n", |
| " this.canvas_div.blur();\n", |
| " event.shiftKey = false;\n", |
| " // Send a \"J\" for go to next cell\n", |
| " event.which = 74;\n", |
| " event.keyCode = 74;\n", |
| " manager.command_mode();\n", |
| " manager.handle_keydown(event);\n", |
| " }\n", |
| "}\n", |
| "\n", |
| "mpl.figure.prototype.handle_save = function(fig, msg) {\n", |
| " fig.ondownload(fig, null);\n", |
| "}\n", |
| "\n", |
| "\n", |
| "mpl.find_output_cell = function(html_output) {\n", |
| " // Return the cell and output element which can be found *uniquely* in the notebook.\n", |
| " // Note - this is a bit hacky, but it is done because the \"notebook_saving.Notebook\"\n", |
| " // IPython event is triggered only after the cells have been serialised, which for\n", |
| " // our purposes (turning an active figure into a static one), is too late.\n", |
| " var cells = IPython.notebook.get_cells();\n", |
| " var ncells = cells.length;\n", |
| " for (var i=0; i<ncells; i++) {\n", |
| " var cell = cells[i];\n", |
| " if (cell.cell_type === 'code'){\n", |
| " for (var j=0; j<cell.output_area.outputs.length; j++) {\n", |
| " var data = cell.output_area.outputs[j];\n", |
| " if (data.data) {\n", |
| " // IPython >= 3 moved mimebundle to data attribute of output\n", |
| " data = data.data;\n", |
| " }\n", |
| " if (data['text/html'] == html_output) {\n", |
| " return [cell, data, j];\n", |
| " }\n", |
| " }\n", |
| " }\n", |
| " }\n", |
| "}\n", |
| "\n", |
| "// Register the function which deals with the matplotlib target/channel.\n", |
| "// The kernel may be null if the page has been refreshed.\n", |
| "if (IPython.notebook.kernel != null) {\n", |
| " IPython.notebook.kernel.comm_manager.register_target('matplotlib', mpl.mpl_figure_comm);\n", |
| "}\n" |
| ], |
| "text/plain": [ |
| "<IPython.core.display.Javascript object>" |
| ] |
| }, |
| "metadata": {}, |
| "output_type": "display_data" |
| }, |
| { |
| "data": { |
| "text/html": [ |
| "<img src=\"data:image/png;base64,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zTfbW2+9ZatXr7Z27drZcccdZz/5yU+sVatWUddML+QJPuvKcwICCCCAAAJFFmAejesAnOKcKIUAAggggEBNAp7m0eiEyaRJk+yss84KyZGBAweGBMb48eNt6dKlNnv2bOvSpUudvX3PPffYySefbDvssENIkCjhMX36dLv//vvD72+88cYq5w8ePNhuuOEG69mzZ0iSbLnllvbss8/a7bffbh07drS5c+da06ZNsxphnuCzqjiFEUAAAQQQcCDAPBrXCTjFOVEKAQQQQACBkkiYKCnSoUMHa968uc2fPz/8qmPhwoXWqVMnO+CAA+ypp56qtbfXrVtnbdq0sZUrV9prr71mu+2228aySr5MnDgxJF0OOuig8OerVq2ybbfd1vbZZ5+wGqVBgwYby1944YUhUfPQQw9Znz59shphBDBZcVEYAQQQQACBKgLMo3EDAqc4J0ohgAACCCBQEgmTKVOm2IABA2zEiBE2fPjwKm3q37+/3XbbbSF5okdmajpefvll23fffe3II4+0xx57rEqR5557LiRKzjzzTLvlllvC3y1ZsiSsROnbt6898MADVcr/4he/sKFDh4YEjVafZHMQwGSjRVkEEEAAAQSqCjCPxo0InOKcKIUAAggggEBJJEzOO++8sJfI448/br17967SJq0O0SqRe++91/r161djjz///PN24IEH2vHHH2/aByX90KM1Wkmy9957h9UnqWP//fc3JVq054k+V3um6JGcCy64wPR3jz766Cb7nmQabgQwmYT4ewQQQAABBGoXYB6NGx04xTlRCgEEEEAAgZJImGilhx6Bef31122vvfaq0qZHHnkkPBozbtw40+MyNR2fffZZWDGy44472oIFC6rsPaJ9SrRfSbNmzWz58uUbT3/vvffCqpYZM2ZU+cjzzz8/7G3SuHHjjKNLn5f+mYsXLw6PDy1atMjatm2b8XwKIIAAAggggMB/BUgExI0GnOKcKIUAAggggEBJJEx69eoVHoFRsiN9/xE1Tn+uv7/mmmvCozK1Heeee65NmDDBjjrqKLvyyitDAuWJJ56wiy++OOxZUllZadrrJHX8+9//tssvv9z+9a9/2YknnmhbbbWV/fnPf7Zbb701JFJSj+/UNcT0CNHIkSM3KULChB9MBBBAAAEEshcgERBnhlOcE6UQQAABBBAoiYRJvitMhKDXCA8Z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TrnbcSYCCCCAAAKe5lESJoxHBBBAAAEEEIgS8BTARFW4SIVwKhI8l0UAAQQQKAkBT/MoCZOSGFI0AgEEEEAAgcILeApgCt/a3K+AU+52nIkAAggggICneZSECeMRAQQQQAABBKIEPAUwURUuUiGcigTPZRFAAAEESkLA0zxKwqQkhhSNQAABBBBAoPACngKYwrc29yvglLsdZyKAAAIIIOBpHiVhwnhEAAEEEEAAgSgBTwFMVIWLVAinIsFzWQQQQACBkhDwNI+SMCmJIUUjEEAAAQQQKLyApwCm8K3N/Qo45W7HmQgggAACCHiaR0mYMB4RQAABBBBAIErAUwATVeEiFcKpSPBcFgEEEECgJAQ8zaMkTEpiSNEIBBBAAAEECi/gKYApfGtzvwJOudtxJgIIIIAAAp7mURImjEcEEEAAAQQQiBLwFMBEVbhIhXAqEjyXRQABBBAoCQFP8ygJk5IYUjQCAQQQQACBwgt4CmAK39rcr4BT7naciQACCCCAgKd5NKuEybRp02zMmDE2b948q6iosB49etjo0aOtc+fOUb06c+bMUH7OnDm2Zs0a22OPPeycc86xQYMGWcOGDTf5jA0bNtgtt9xit956q82fP98qKytt1113te9973v285//POqa6YU8wWddeU5AAAEEEECgyALMo3EdgFOcE6UQQAABBBCoScDTPBqdMJk0aZKdddZZITkycOBAW716tY0fP96WLl1qs2fPti5dutTZ2/fcc4+dfPLJtsMOO4QESatWrWz69Ol2//33h9/feOONVc5ft26dnXDCCfbII4/YD37wAzvkkEOsQYMG9t5774X/7rrrrqxHlyf4rCvPCQgggAACCBRZgHk0rgNwinOiFAIIIIAAAiWRMFFSpEOHDta8efOw0kO/6li4cKF16tTJDjjgAHvqqadq7W0lP9q0aWMrV6601157zXbbbbeNZZV8mThxYki6HHTQQRv//KqrrrIRI0aEhMmRRx5ZLyOJAKZeGPkQBBBAAIEyFWAejet4nOKcKIUAAggggEBJJEymTJliAwYMCAmM4cOHV2lT//797bbbbgvJk3bt2tXY4y+//LLtu+++IfHx2GOPVSnz3HPPhUTJmWeeGR6/0bFq1aqQYDnssMPCChQ9irNixQpr1qxZWGWS60EAk6sc5yGAAAIIIGDGPBo3CnCKc6IUAggggAACJZEwOe+88+zmm2+2xx9/3Hr37l2lTVodolUi9957r/Xr16/GHn/++eftwAMPtOOPP960D0r6MXfuXNtnn31s7733DqtPdOg6Sq6MGjUqPPLz29/+1pYtWxYSJnpM59prr7WWLVtmPboIYLIm4wQEEEAAAQQ2CjCPxg0GnOKcKIUAAggggEBJJEz69u1rDz30kL3++uu21157VWmTHpnp06ePjRs3zi688MIae/yzzz4Le5fsuOOOtmDBAmvatOnGcjfccIMNHjw4JEOWL18e/lyfddFFF4V9Tho1amSXX355WHGi1SZ33HFH2C/lhRdesCZNmtQ5wvR5qc9UwcWLF4fHhxYtWmRt27ZldCKAAAIIIIBAFgIkAuKwcIpzohQCCCCAAAIlkTDp1atX2KNEyY70/UfUOP25/v6aa66xoUOH1trj5557rk2YMMGOOuoou/LKK0MC5YknnrCLL744PIKjx26014mOq6++2n72s5+FZMmrr74a9klJHaeeempImuiz9Iadug49QjRy5MhNipAw4QcTAQQQQACB7AVIBMSZ4RTnRCkEEEAAAQRKImGS7woTIeg1wkOGDAkbvK5duza4aPPY66+/PiRalCz59NNPw59fd911IZGivU20GWz68fTTT9vhhx8e3pxz99131znCWGHCDyACCCCAAAL1J0AiIM4SpzgnSiGAAAIIIFASCZN89zBJR1ASQ2/a0eat3bp1s/Xr14fEifY4SSVH/vCHP4SEiPYrmTp1ahXDN998MzwWpL1UtNdJNgcBTDZalEUAAQQQQKCqAPNo3IjAKc6JUggggAACCJREwmTy5Ml2xhlnhMdbhg0bVqVNenuO3qJT11ty6hoGSoiceOKJ4TGcK664IhR9//33w2uMtd/InDlzqpw+ffp0O+KII+yUU06x22+/PasRRgCTFReFEUAAAQQQqCLAPBo3IHCKc6IUAggggAACJZEw0Ztq2rdvby1atAirQ7QiRIeSJNpfpHv37qZHZXRoPxL9ucq2bt26zhGwZMmSsLJEm8K+8cYbVd5807NnT5s5c2ZImOjzU4fetKPNX/U4jlahZHMQwGSjRVkEEEAAAQSqCjCPxo0InOKcKIUAAggggEBJJEzUCG2yqo1bO3fuHF4jrD1Jxo8fb0p6zJo1Kzxeo2PGjBmmZMfpp58eVp6kjrvuuiv8/rDD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krE79Dn6vMWLF9vOO++csXx6AU/wWVWcwggggAACCDgQ8DiP5rsCVrHE1ltvbccdd5zttddetmzZMrv77rvtxRdftGOOOcYeeugha9CgQVb6Hp2yagCFEUAAAQQQKKKAp3k0KmFy3nnnhcdhHn/8cevdu3cVOq0O0SqRe++91/r161cj6/PPP28HHnigHX/88aa7QOnH3LlzbZ999rG99947rD6p69iwYYO1bdvWlixZEgIa3QXK5vAEn029KYsAAggggIAHAW/zaL4rYGX65JNPhsd/05Mi69evt8MOO8xmzZplDz/8cEicZHN4c8qm7pRFAAEEEECg2AKe5tGohEnfvn3DHZbXX3893H1JPx555JGw38i4cePswgsvrNH2s88+C3uXaC+SBQsWWNOmTTeWu+GGG2zw4MHWrFmzsD9JXYf2TNE1Tj/9dNOql0yHPi/9M7XJklv7AAAgAElEQVQqRY8PLVq0KCReOBBAAAEEEEAgXsBTAKNa57sCtq6W/+pXv7If//jHds0119jQoUPjkczMm1NWlacwAggggAACRRbwNI9GJUx69eoV9ihRsiN9/xE56s/195kCinPPPdcmTJhgRx11lF155ZUhgfLEE0/YxRdfbKtWrbLKysqwb0ltx4wZM8IjPbvssktYJqt9TDIdeoRo5MiRmxQjYZJJjr9HAAEEEEBgUwFPAYxql+8K2Lr6+LLLLgtvBrz11lvDY8nZHN6csqk7ZRFAAAEEECi2gKd5NCphku8KE4HrNcJDhgwJG7yuXbs29IE2j73++uvDnRslSz799NMa+0YbwirR0qJFC1PipPpmsLV1KCtMij3UuT4CCCCAQCkJeApg5Fof8UlN/aN26q2ADRs2tLfffjvjTRrijVIa5bQFAQQQQKDYAp7ijaiESX3ewVFQoTft6Fnhbt26mZ4TVuJEe5woMVL9+Mtf/hIe+dluu+3CapbYZEltAZA2pmWFSbF/BLg+AggggEASBTwFMPKrjxWw1ftBb/T7zne+EzaY1/5s2n8t08GK1kxC/D0CCCCAAALxAp7ijaiEyeTJk+2MM84Ij7cMGzasSku1TFXPENf1lpy6aKZOnWonnniiXX311XbFFVdUKfr000/bd7/73bD3iZIlX/va1+KVayjpCT6vhnAyAggggAACRRDwNo/W9woTJUu0was2e/31r39tgwYN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C61Lly42atQoa926dYXGb7/9tk2YMMEWLFjg/qxdu9bOP/9897OoR6hOUdvDeQgggAACCORSIKR5tGDnzp07c9n4OK8VEnycDlwbAQQQQACBKAIhzqMTJ060Cy64wCVHBg4caJs2bbJx48bZunXrbO7cudamTZtymzp58mQbMGCAtWjRwg499FB79tlnSZhEGRycgwACCCCAwG4SCCneIGGymzqVl0EAAQQQQCDpAiEFMLJWUqR58+ZWr149W7Jkiftbx/vvv2+tWrWyjh072syZM8vtljVr1li1atVsn332seXLl7vECStMkj6SaR8CCCCAQMgCIcUbJExCHinUDQEEEEAAgYAEQgpgxJJaHTJ8+HAbNmxYCan+/fvblClTXPKkadOmXookTLyYKIQAAggggEBWBUKKN0iYZLWreXEEEEAAAQSSIxBSACPVQYMG2fjx4+25556zHj16lIC+77773C0606ZNsz59+nh1AgkTLyYKIYAAAgggkFWBkOINEiZZ7WpeHAEEEEAAgeQIhBTASLVXr142Y8YMW7p0qR1xxBEloJ966inr2bOn3X777XbZZZd5dULUhMn69etNf1LHqlWr3O1AK1eutCZNmnhdm0IIIIAAAggg8LVASPEGCRNGJQIIIIAAAgh4CYQUwKjC3bt3d3uULFu2zFq2bFmiDfq5/n306NE2ZMgQr/ZFTZjolqARI0bscg0SJl7sFEIgZwI7tm2yDSvnuD/bN6+36jXrWd2mnd2fajVq5aweXAgBBMoXCCneIGHCaEUAAQQQQAABL4GQAhhVmBUmXt1GIQQQMLMtGz6yD+eOtO1frTGzAjPTg0K//rt67QZ20Hevs8K6B2KFAAIBCIQUb5AwCWBAUAUEEEAAAQSqgkBIAYy82MOkKowa6ohA/AJaWbLihcG2/au13yRKStepwKrX3teafX8sK03i7y5qgAC35MQ1BkIL9OJy4LoIIIAAAghEEQhtHp00aZKdd9557naYoUOHlmjSgAED3FN0eEpOlJ7mHASSJfD5ey/YJ29OqLBRDY++0Oq36F5hOQoggEB2BUKKN1hhkt2+5tURQAABBBBIjEBIAYxQ161bZ82aNbP69evbkiVLrF69es5aSZJWrVpZhw4dbNasWe5nGzdudD9X2caNG5fZJ1H3MCn9YqE5JWYA0hAEIgp88LfhtmnN22lW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\" width=\"999.9999783255842\">" |
| ], |
| "text/plain": [ |
| "<IPython.core.display.HTML object>" |
| ] |
| }, |
| "metadata": {}, |
| "output_type": "display_data" |
| }, |
| { |
| "name": "stdout", |
| "output_type": "stream", |
| "text": [ |
| "1 rows affected.\n", |
| "1 rows affected.\n", |
| "1 rows affected.\n", |
| "1 rows affected.\n", |
| "1 rows affected.\n", |
| "1 rows affected.\n", |
| "1 rows affected.\n", |
| "1 rows affected.\n" |
| ] |
| } |
| ], |
| "source": [ |
| "#df_results = %sql SELECT * FROM $results_table ORDER BY run_id;\n", |
| "df_results = %sql SELECT * FROM $results_table ORDER BY validation_loss ASC LIMIT 12;\n", |
| "df_results = df_results.DataFrame()\n", |
| "\n", |
| "#set up plots\n", |
| "fig, axs = plt.subplots(nrows=1, ncols=2, figsize=(10,5))\n", |
| "fig.legend(ncol=4)\n", |
| "fig.tight_layout()\n", |
| "\n", |
| "ax_metric = axs[0]\n", |
| "ax_loss = axs[1]\n", |
| "\n", |
| "ax_metric.xaxis.set_major_locator(MaxNLocator(integer=True))\n", |
| "ax_metric.set_xlabel('Iteration')\n", |
| "ax_metric.set_ylabel('Metric')\n", |
| "ax_metric.set_title('Validation metric curve')\n", |
| "\n", |
| "ax_loss.xaxis.set_major_locator(MaxNLocator(integer=True))\n", |
| "ax_loss.set_xlabel('Iteration')\n", |
| "ax_loss.set_ylabel('Loss')\n", |
| "ax_loss.set_title('Validation loss curve')\n", |
| "\n", |
| "for run_id in df_results['run_id']:\n", |
| " df_output_info = %sql SELECT validation_metrics,validation_loss FROM $results_table WHERE run_id = $run_id\n", |
| " df_output_info = df_output_info.DataFrame()\n", |
| " validation_metrics = df_output_info['validation_metrics'][0]\n", |
| " validation_loss = df_output_info['validation_loss'][0]\n", |
| " X = range(len(validation_metrics))\n", |
| " \n", |
| " ax_metric.plot(X, validation_metrics, label=run_id, marker='o')\n", |
| " ax_loss.plot(X, validation_loss, label=run_id, marker='o')\n", |
| "\n", |
| "# fig.savefig('./lc_keras_fit.png', dpi = 300)" |
| ] |
| }, |
| { |
| "cell_type": "markdown", |
| "metadata": {}, |
| "source": [ |
| "<a id=\"print\"></a>\n", |
| "# 7. Print run schedules (display only)" |
| ] |
| }, |
| { |
| "cell_type": "markdown", |
| "metadata": {}, |
| "source": [ |
| "Pretty print reg Hyperband run schedule" |
| ] |
| }, |
| { |
| "cell_type": "code", |
| "execution_count": 32, |
| "metadata": { |
| "scrolled": false |
| }, |
| "outputs": [ |
| { |
| "name": "stdout", |
| "output_type": "stream", |
| "text": [ |
| "max_iter = 9\n", |
| "eta = 3\n", |
| "B = 3*max_iter = 27\n", |
| " \n", |
| "s=2\n", |
| "n_i r_i\n", |
| "------------\n", |
| "9 1.0\n", |
| "3.0 3.0\n", |
| "1.0 9.0\n", |
| " \n", |
| "s=1\n", |
| "n_i r_i\n", |
| "------------\n", |
| "3 3.0\n", |
| "1.0 9.0\n", |
| " \n", |
| "s=0\n", |
| "n_i r_i\n", |
| "------------\n", |
| "3 9\n", |
| " \n", |
| "sum of configurations at leaf nodes across all s = 5.0\n", |
| "(if have more workers than this, they may not be 100% busy)\n" |
| ] |
| } |
| ], |
| "source": [ |
| "import numpy as np\n", |
| "from math import log, ceil\n", |
| "\n", |
| "#input\n", |
| "max_iter = 9 # maximum iterations/epochs per configuration\n", |
| "eta = 3 # defines downsampling rate (default=3)\n", |
| "\n", |
| "logeta = lambda x: log(x)/log(eta)\n", |
| "s_max = int(logeta(max_iter)) # number of unique executions of Successive Halving (minus one)\n", |
| "B = (s_max+1)*max_iter # total number of iterations (without reuse) per execution of Succesive Halving (n,r)\n", |
| "\n", |
| "#echo output\n", |
| "print (\"max_iter = \" + str(max_iter))\n", |
| "print (\"eta = \" + str(eta))\n", |
| "print (\"B = \" + str(s_max+1) + \"*max_iter = \" + str(B))\n", |
| "\n", |
| "sum_leaf_n_i = 0 # count configurations at leaf nodes across all s\n", |
| "\n", |
| "#### Begin Finite Horizon Hyperband outlerloop. Repeat indefinitely.\n", |
| "for s in reversed(range(s_max+1)):\n", |
| " \n", |
| " print (\" \")\n", |
| " print (\"s=\" + str(s))\n", |
| " print (\"n_i r_i\")\n", |
| " print (\"------------\")\n", |
| " counter = 0\n", |
| " \n", |
| " n = int(ceil(int(B/max_iter/(s+1))*eta**s)) # initial number of configurations\n", |
| " r = max_iter*eta**(-s) # initial number of iterations to run configurations for\n", |
| "\n", |
| " #### Begin Finite Horizon Successive Halving with (n,r)\n", |
| " #T = [ get_random_hyperparameter_configuration() for i in range(n) ] \n", |
| " for i in range(s+1):\n", |
| " # Run each of the n_i configs for r_i iterations and keep best n_i/eta\n", |
| " n_i = n*eta**(-i)\n", |
| " r_i = r*eta**(i)\n", |
| " \n", |
| " print (str(n_i) + \" \" + str (r_i))\n", |
| " \n", |
| " # check if leaf node for this s\n", |
| " if counter == s:\n", |
| " sum_leaf_n_i += n_i\n", |
| " counter += 1\n", |
| " \n", |
| " #val_losses = [ run_then_return_val_loss(num_iters=r_i,hyperparameters=t) for t in T ]\n", |
| " #T = [ T[i] for i in argsort(val_losses)[0:int( n_i/eta )] ]\n", |
| " #### End Finite Horizon Successive Halving with (n,r)\n", |
| "\n", |
| "print (\" \")\n", |
| "print (\"sum of configurations at leaf nodes across all s = \" + str(sum_leaf_n_i))\n", |
| "print (\"(if have more workers than this, they may not be 100% busy)\")" |
| ] |
| }, |
| { |
| "cell_type": "markdown", |
| "metadata": {}, |
| "source": [ |
| "Pretty print Hyperband diagonal run schedule" |
| ] |
| }, |
| { |
| "cell_type": "code", |
| "execution_count": 33, |
| "metadata": {}, |
| "outputs": [ |
| { |
| "name": "stdout", |
| "output_type": "stream", |
| "text": [ |
| "echo input:\n", |
| "max_iter = 9\n", |
| "eta = 3\n", |
| "s_max = 2\n", |
| "B = 3*max_iter = 27\n", |
| " \n", |
| "initial n, r values for each s:\n", |
| "s=2\n", |
| "n=9\n", |
| "r=1.0\n", |
| " \n", |
| "s=1\n", |
| "n=3\n", |
| "r=3.0\n", |
| " \n", |
| "s=0\n", |
| "n=3\n", |
| "r=9\n", |
| " \n", |
| "outer loop on diagonal:\n", |
| " \n", |
| "i=0\n", |
| "inner loop on s desc:\n", |
| "s=2\n", |
| "n_i=9\n", |
| "r_i=1.0\n", |
| " \n", |
| "i=1\n", |
| "inner loop on s desc:\n", |
| "s=2\n", |
| "n_i=3.0\n", |
| "r_i=3.0\n", |
| "s=1\n", |
| "n_i=3\n", |
| "r_i=3.0\n", |
| " \n", |
| "i=2\n", |
| "inner loop on s desc:\n", |
| "s=2\n", |
| "n_i=1.0\n", |
| "r_i=9.0\n", |
| "s=1\n", |
| "n_i=1.0\n", |
| "r_i=9.0\n", |
| "s=0\n", |
| "n_i=3\n", |
| "r_i=9\n" |
| ] |
| } |
| ], |
| "source": [ |
| "import numpy as np\n", |
| "from math import log, ceil\n", |
| "\n", |
| "#input\n", |
| "max_iter = 9 # maximum iterations/epochs per configuration\n", |
| "eta = 3 # defines downsampling rate (default=3)\n", |
| "\n", |
| "logeta = lambda x: log(x)/log(eta)\n", |
| "s_max = int(logeta(max_iter)) # number of unique executions of Successive Halving (minus one)\n", |
| "B = (s_max+1)*max_iter # total number of iterations (without reuse) per execution of Succesive Halving (n,r)\n", |
| "\n", |
| "#echo output\n", |
| "print (\"echo input:\")\n", |
| "print (\"max_iter = \" + str(max_iter))\n", |
| "print (\"eta = \" + str(eta))\n", |
| "print (\"s_max = \" + str(s_max))\n", |
| "print (\"B = \" + str(s_max+1) + \"*max_iter = \" + str(B))\n", |
| "\n", |
| "print (\" \")\n", |
| "print (\"initial n, r values for each s:\")\n", |
| "initial_n_vals = {}\n", |
| "initial_r_vals = {}\n", |
| "# get hyper parameter configs for each s\n", |
| "for s in reversed(range(s_max+1)):\n", |
| " \n", |
| " n = int(ceil(int(B/max_iter/(s+1))*eta**s)) # initial number of configurations\n", |
| " r = max_iter*eta**(-s) # initial number of iterations to run configurations for\n", |
| " \n", |
| " initial_n_vals[s] = n \n", |
| " initial_r_vals[s] = r \n", |
| " \n", |
| " print (\"s=\" + str(s))\n", |
| " print (\"n=\" + str(n))\n", |
| " print (\"r=\" + str(r))\n", |
| " print (\" \")\n", |
| " \n", |
| "print (\"outer loop on diagonal:\")\n", |
| "# outer loop on diagonal\n", |
| "for i in range(s_max+1):\n", |
| " print (\" \")\n", |
| " print (\"i=\" + str(i))\n", |
| " \n", |
| " print (\"inner loop on s desc:\")\n", |
| " # inner loop on s desc\n", |
| " for s in range(s_max, s_max-i-1, -1):\n", |
| " n_i = initial_n_vals[s]*eta**(-i+s_max-s)\n", |
| " r_i = initial_r_vals[s]*eta**(i-s_max+s)\n", |
| " \n", |
| " print (\"s=\" + str(s))\n", |
| " print (\"n_i=\" + str(n_i))\n", |
| " print (\"r_i=\" + str(r_i))" |
| ] |
| } |
| ], |
| "metadata": { |
| "kernelspec": { |
| "display_name": "Python 2", |
| "language": "python", |
| "name": "python2" |
| }, |
| "language_info": { |
| "codemirror_mode": { |
| "name": "ipython", |
| "version": 2 |
| }, |
| "file_extension": ".py", |
| "mimetype": "text/x-python", |
| "name": "python", |
| "nbconvert_exporter": "python", |
| "pygments_lexer": "ipython2", |
| "version": "2.7.10" |
| } |
| }, |
| "nbformat": 4, |
| "nbformat_minor": 1 |
| } |