diff --git a/demos/gpu/README.ipynb b/demos/gpu/README.ipynb
index 1ea9aa58..36596ff6 100644
--- a/demos/gpu/README.ipynb
+++ b/demos/gpu/README.ipynb
@@ -25,16 +25,11 @@
"- A **horovod** directory with applications that use Uber's [Horovod](https://eng.uber.com/horovod/) distributed deep-learning framework, which can be used to convert a single-GPU TensorFlow, Keras, or PyTorch model-training program to a distributed program that trains the model simultaneously over multiple GPUs.\n",
" The objective is to speed up your model training with minimal changes to your existing single-GPU code and without complicating the execution.\n",
" Horovod code can also run over CPUs with only minor modifications.\n",
- " For more information and examples, see the [Horovod GitHub repository](https://github.com/horovod/horovod).\n",
- " \n",
" The Horovod tutorials include the following:\n",
- "\n",
- " - An image-recognition demo application for execution over GPUs (**image-classification**).\n",
- " - A slightly modified version of the GPU image-classification demo application for execution over CPUs (**cpu/image-classification**).\n",
" - Benchmark tests (**benchmark-tf.ipynb**, which executes **tf_cnn_benchmarks.py**).\n",
+ " - Note that under the demo folder you will find an image classificaiton demo that is also running with Horovod and can be set to run with GPU
\n",
"\n",
"- A **rapids** directory with applications that use NVIDIA's [RAPIDS](https://rapids.ai/) open-source libraries suite for executing end-to-end data science and analytics pipelines entirely on GPUs.\n",
- "\n",
" The RAPIDS tutorials include the following:\n",
"\n",
" - Demo applications that use the [cuDF](https://rapidsai.github.io/projects/cudf/en/latest/index.html) RAPIDS GPU DataFrame library to perform batching and aggregation of data that's read from a Kafaka stream, and then write the results to a Parquet file.
\n",
diff --git a/demos/gpu/horovod/cpu/image-classification/01-load-data-cats-n-dogs.ipynb b/demos/gpu/horovod/cpu/image-classification/01-load-data-cats-n-dogs.ipynb
deleted file mode 100644
index d08237a5..00000000
--- a/demos/gpu/horovod/cpu/image-classification/01-load-data-cats-n-dogs.ipynb
+++ /dev/null
@@ -1,284 +0,0 @@
-{
- "cells": [
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "# Load Cats and Dogs Images"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "_uuid": "fe76d1d1ded592430e7548feacfa38dc42f085d9"
- },
- "source": [
- "## Install Packages"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 1,
- "metadata": {},
- "outputs": [],
- "source": [
- "!pip install --upgrade keras==2.2.4\n",
- "!pip install --upgrade tensorflow==1.13.1 \n",
- "!pip install --upgrade 'numpy<1.15.0'"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "> **Note:** After running the pip command you should restart the Jupyter kernel.
\n",
- "> To restart the kernel, click on the kernel-restart button in the notebook menu toolbar (the refresh icon next to the **Code** button)."
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "## Import Library"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 2,
- "metadata": {
- "_cell_guid": "b1076dfc-b9ad-4769-8c92-a6c4dae69d19",
- "_uuid": "8f2839f25d086af736a60e9eeb907d3b93b6e0e5"
- },
- "outputs": [
- {
- "name": "stderr",
- "output_type": "stream",
- "text": [
- "Using TensorFlow backend.\n"
- ]
- }
- ],
- "source": [
- "# This Python 3 environment comes with many helpful analytics libraries installed.\n",
- "# It is defined by the kaggle/python docker image: https://github.com/kaggle/docker-python.\n",
- "# For example, here are several helpful packages to load:\n",
- "\n",
- "import numpy as np # linear algebra\n",
- "import pandas as pd # data processing, CSV file I/O (e.g. pd.read_csv)\n",
- "from keras.preprocessing.image import load_img\n",
- "\n",
- "# Input data files are available in the \"../input/\" directory.\n",
- "# For example, running the following (by selecting 'Run' or pressing Shift+Enter) will list the files in the input directory:\n",
- "\n",
- "import matplotlib.pyplot as plt\n",
- "import random\n",
- "\n",
- "import os\n",
- "import zipfile\n",
- "\n",
- "# Define locations\n",
- "BASE_PATH = os.getcwd()\n",
- "DATA_PATH = BASE_PATH + \"/cats_and_dogs_filtered/\"\n",
- "!mkdir model\n",
- "MODEL_PATH = BASE_PATH + '/model/'\n",
- "\n",
- "# Define image parameters\n",
- "FAST_RUN = False\n",
- "IMAGE_WIDTH=128\n",
- "IMAGE_HEIGHT=128\n",
- "IMAGE_SIZE=(IMAGE_WIDTH, IMAGE_HEIGHT)\n",
- "IMAGE_CHANNELS=3 # RGB color\n",
- "\n",
- "# Any results you write to the current directory are saved as output."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {},
- "outputs": [],
- "source": [
- "DATA_PATH + 'catsndogs.zip'"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "## Download the Data"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {},
- "outputs": [],
- "source": [
- "!mkdir cats_and_dogs_filtered\n",
- "# Download a sample stocks file from Iguazio demo bucket in AWS S3\n",
- "!curl -L \"iguazio-sample-data.s3.amazonaws.com/catsndogs.zip\" > ./cats_and_dogs_filtered/catsndogs.zip"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {},
- "outputs": [],
- "source": [
- "zip_ref = zipfile.ZipFile(DATA_PATH + 'catsndogs.zip', 'r')\n",
- "zip_ref.extractall('cats_and_dogs_filtered')\n",
- "zip_ref.close()"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "_uuid": "7335a579cc0268fba5d34d6f7558f33c187eedb3"
- },
- "source": [
- "## Prepare the Traning Data"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {},
- "outputs": [],
- "source": [
- "import json"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {},
- "outputs": [],
- "source": [
- "def build_prediction_map(categories_map):\n",
- " return {v:k for k ,v in categories_map.items()}"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "_cell_guid": "79c7e3d0-c299-4dcb-8224-4455121ee9b0",
- "_uuid": "d629ff2d2480ee46fbb7e2d37f6b5fab8052498a"
- },
- "outputs": [],
- "source": [
- "# Create a file-names list (JPG image-files only)\n",
- "filenames = [file for file in os.listdir(DATA_PATH+\"/cats_n_dogs\") if file.endswith('jpg')]\n",
- "categories = []\n",
- "\n",
- "# Categories and prediction-classes map\n",
- "categories_map = {\n",
- " 'dog': 1,\n",
- " 'cat': 0,\n",
- "}\n",
- "prediction_map = build_prediction_map(categories_map)\n",
- "with open(MODEL_PATH + 'prediction_classes_map.json', 'w') as f:\n",
- " json.dump(prediction_map, f)\n",
- "\n",
- "# Create a pandas DataFrame for the full sample\n",
- "for filename in filenames:\n",
- " category = filename.split('.')[0]\n",
- " categories.append([categories_map[category]])\n",
- "\n",
- "df = pd.DataFrame({\n",
- " 'filename': filenames,\n",
- " 'category': categories\n",
- "})\n",
- "df['category'] = df['category'].astype('str');"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "_uuid": "915bb9ba7063ab4d5c07c542419ae119003a5f98"
- },
- "outputs": [],
- "source": [
- "df.head()"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "_uuid": "72bf69e817f67f5a2eaff8561217e22077248553"
- },
- "outputs": [],
- "source": [
- "df.tail()"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "_uuid": "a999484fc35b73373fafe2253ae9db7ff46fdb90"
- },
- "source": [
- "## Check the Total Image Count\n",
- "\n",
- "Check the total image count for each category.
\n",
- "The data set has 12,000 cat images and 12,000 dog images."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "_uuid": "fa26f0bc7a6d835a24989790b20f3c6f32946f45"
- },
- "outputs": [],
- "source": [
- "df['category'].value_counts().plot.bar()"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "_uuid": "400a293df3c8499059d9175f3915187074efd971"
- },
- "source": [
- "## Display the Sample Image"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "_uuid": "602b40f7353871cb161c60b5237f0da0096b2f47"
- },
- "outputs": [],
- "source": [
- "sample = random.choice(filenames)\n",
- "image = load_img(DATA_PATH+\"/cats_n_dogs/\"+sample)\n",
- "plt.imshow(image)"
- ]
- }
- ],
- "metadata": {
- "kernelspec": {
- "display_name": "Python 3",
- "language": "python",
- "name": "python3"
- },
- "language_info": {
- "codemirror_mode": {
- "name": "ipython",
- "version": 3
- },
- "file_extension": ".py",
- "mimetype": "text/x-python",
- "name": "python",
- "nbconvert_exporter": "python",
- "pygments_lexer": "ipython3",
- "version": "3.6.8"
- }
- },
- "nbformat": 4,
- "nbformat_minor": 2
-}
diff --git a/demos/gpu/horovod/cpu/image-classification/02-train-with-horovod-cats-n-dogs.ipynb b/demos/gpu/horovod/cpu/image-classification/02-train-with-horovod-cats-n-dogs.ipynb
deleted file mode 100644
index 281e82a7..00000000
--- a/demos/gpu/horovod/cpu/image-classification/02-train-with-horovod-cats-n-dogs.ipynb
+++ /dev/null
@@ -1,216 +0,0 @@
-{
- "cells": [
- {
- "cell_type": "code",
- "execution_count": 1,
- "metadata": {},
- "outputs": [],
- "source": [
- "!pip install git+https://github.com/v3io/v3io-gputils"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 2,
- "metadata": {},
- "outputs": [],
- "source": [
- "!rm -f /User/demos/gpu/horovod/cpu/image-classification/cats_dogs.hd5\n",
- "!mkdir /User/demos/gpu/horovod/cpu/image-classification/checkpoints"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 3,
- "metadata": {},
- "outputs": [],
- "source": [
- "HOROVOD_JOB_NAME = \"horovod-cats-n-dogs\""
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 4,
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "{'apiVersion': 'kubeflow.org/v1alpha1',\n",
- " 'kind': 'MPIJob',\n",
- " 'metadata': {'creationTimestamp': '2019-07-02T07:41:59Z',\n",
- " 'generation': 1,\n",
- " 'name': 'horovod-cats-n-dogs',\n",
- " 'namespace': 'default-tenant',\n",
- " 'resourceVersion': '1391131',\n",
- " 'selfLink': '/apis/kubeflow.org/v1alpha1/namespaces/default-tenant/mpijobs/horovod-cats-n-dogs',\n",
- " 'uid': 'df9b08a1-9c9c-11e9-98d3-d8c4972b0204'},\n",
- " 'spec': {'replicas': 8,\n",
- " 'template': {'spec': {'containers': [{'command': ['mpirun',\n",
- " 'python',\n",
- " '/User/demos/gpu/horovod/cpu/image-classification/horovod_train_cats_n_dogs.py',\n",
- " '/User/demos/gpu/horovod/cpu/image-classification/cats_and_dogs_filtered',\n",
- " '/User/demos/gpu/horovod/cpu/image-classification'],\n",
- " 'image': 'iguaziodocker/horovod-cpu:0.0.1',\n",
- " 'name': 'horovod-cats-n-dogs',\n",
- " 'resources': {'limits': {'nvidia.com/gpu': 0}},\n",
- " 'securityContext': {'capabilities': {'add': ['IPC_LOCK']}},\n",
- " 'volumeMounts': [{'mountPath': '/User',\n",
- " 'name': 'v3io'}]}],\n",
- " 'volumes': [{'flexVolume': {'driver': 'v3io/fuse',\n",
- " 'options': {'accessKey': '1e52ff93-a541-4880-abf1-d9b948af77de',\n",
- " 'container': 'users',\n",
- " 'subPath': '/iguazio'}},\n",
- " 'name': 'v3io'}]}}}}\n"
- ]
- }
- ],
- "source": [
- "from v3io_gputils.mpijob import MpiJob\n",
- "\n",
- "job = MpiJob(HOROVOD_JOB_NAME, 'iguaziodocker/horovod-cpu:0.0.1', ['/User/demos/gpu/horovod/cpu/image-classification/horovod_train_cats_n_dogs.py',\n",
- " '/User/demos/gpu/horovod/cpu/image-classification/cats_and_dogs_filtered',\n",
- " '/User/demos/gpu/horovod/cpu/image-classification'])\n",
- "\n",
- "job.replicas(2).gpus(0)\n",
- "job.submit()"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 5,
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "horovod-cats-n-dogs-launcher-8kg8c 1/1 Running 0 75s\n",
- "horovod-cats-n-dogs-worker-0 1/1 Running 0 83s\n",
- "horovod-cats-n-dogs-worker-1 1/1 Running 0 83s\n",
- "horovod-cats-n-dogs-worker-2 1/1 Running 0 83s\n",
- "horovod-cats-n-dogs-worker-3 1/1 Running 0 83s\n",
- "horovod-cats-n-dogs-worker-4 1/1 Running 0 83s\n",
- "horovod-cats-n-dogs-worker-5 1/1 Running 0 83s\n",
- "horovod-cats-n-dogs-worker-6 1/1 Running 0 83s\n",
- "horovod-cats-n-dogs-worker-7 1/1 Running 0 83s\n"
- ]
- }
- ],
- "source": [
- "\n",
- "!kubectl get pods | grep $HOROVOD_JOB_NAME"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 6,
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "apiVersion: kubeflow.org/v1alpha1\n",
- "kind: MPIJob\n",
- "metadata:\n",
- " creationTimestamp: 2019-07-02T06:49:07Z\n",
- " generation: 1\n",
- " name: horovod-cats-n-dogs\n",
- " namespace: default-tenant\n",
- " resourceVersion: \"1386982\"\n",
- " selfLink: /apis/kubeflow.org/v1alpha1/namespaces/default-tenant/mpijobs/horovod-cats-n-dogs\n",
- " uid: 7d0cd80c-9c95-11e9-98d3-d8c4972b0204\n",
- "spec:\n",
- " backoffLimit: 6\n",
- " replicas: 8\n",
- " template:\n",
- " metadata:\n",
- " creationTimestamp: null\n",
- " spec:\n",
- " containers:\n",
- " - command:\n",
- " - mpirun\n",
- " - python\n",
- " - /User/demos/gpu/horovod/cpu/image-classification/horovod_train_cats_n_dogs.py\n",
- " - /User/demos/gpu/horovod/cpu/image-classification/cats_and_dogs_filtered\n",
- " - /User/demos/gpu/horovod/cpu/image-classification\n",
- " image: iguaziodocker/cpu/horovod-cpu:0.1.1\n",
- " name: horovod-cats-n-dogs\n",
- " resources:\n",
- " limits:\n",
- " nvidia.com/gpu: \"1\"\n",
- " securityContext:\n",
- " capabilities:\n",
- " add:\n",
- " - IPC_LOCK\n",
- " volumeMounts:\n",
- " - mountPath: /User\n",
- " name: v3io\n",
- " volumes:\n",
- " - flexVolume:\n",
- " driver: v3io/fuse\n",
- " options:\n",
- " accessKey: 1e52ff93-a541-4880-abf1-d9b948af77de\n",
- " container: users\n",
- " subPath: /iguazio\n",
- " name: v3io\n",
- "status:\n",
- " completionTime: 2019-07-02T06:56:20Z\n",
- " launcherStatus: Succeeded\n",
- " startTime: 2019-07-02T06:49:14Z\n"
- ]
- }
- ],
- "source": [
- "!kubectl get mpijob $HOROVOD_JOB_NAME -o yaml"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 7,
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "{'apiVersion': 'v1',\n",
- " 'details': {'group': 'kubeflow.org',\n",
- " 'kind': 'mpijobs',\n",
- " 'name': 'horovod-cats-n-dogs',\n",
- " 'uid': '1b58dd58-9c97-11e9-98d3-d8c4972b0204'},\n",
- " 'kind': 'Status',\n",
- " 'metadata': {},\n",
- " 'status': 'Success'}\n"
- ]
- }
- ],
- "source": [
- "job.delete()"
- ]
- }
- ],
- "metadata": {
- "kernelspec": {
- "display_name": "Python 3",
- "language": "python",
- "name": "python3"
- },
- "language_info": {
- "codemirror_mode": {
- "name": "ipython",
- "version": 3
- },
- "file_extension": ".py",
- "mimetype": "text/x-python",
- "name": "python",
- "nbconvert_exporter": "python",
- "pygments_lexer": "ipython3",
- "version": "3.6.8"
- }
- },
- "nbformat": 4,
- "nbformat_minor": 2
-}
diff --git a/demos/gpu/horovod/cpu/image-classification/03-infer.ipynb b/demos/gpu/horovod/cpu/image-classification/03-infer.ipynb
deleted file mode 100644
index 995a17ba..00000000
--- a/demos/gpu/horovod/cpu/image-classification/03-infer.ipynb
+++ /dev/null
@@ -1,548 +0,0 @@
-{
- "cells": [
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "# Create and Test a Model-Serving Nuclio Function\n",
- "\n",
- "This notebook demonstrates how to write an inference server, test it, and turn it into an auto-scaling Nuclio serverless function.\n",
- "\n",
- "- [Initialize Nuclio Emulation, Environment Variables, and Configuration](#image-class-infer-init-func)\n",
- "- [Create and Load the Model and Set Up the Function Handler](#image-class-infer-create-n-load-model-n-set-up-func-handler)\n",
- "- [Trigger the Function](#image-class-infer-func-trigger)\n",
- "- [Prepare to Deploy the Function](#image-class-infer-func-deploy-prepare)\n",
- "- [Deploy the Function](#image-class-infer-func-deploy)\n",
- "- [Test the Function](#image-class-infer-func-test)"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "\n",
- "## Initialize Nuclio Emulation, Environment Variables, and Configuration\n",
- "\n",
- "> **Note:** Use `# nuclio: ignore` for sections that don't need to be copied to the function."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 1,
- "metadata": {},
- "outputs": [],
- "source": [
- "# nuclio: ignore\n",
- "import nuclio\n",
- "import random\n",
- "import matplotlib.pyplot as plt"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 2,
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "%nuclio: setting 'IMAGE_WIDTH' environment variable\n",
- "%nuclio: setting 'IMAGE_HEIGHT' environment variable\n",
- "%nuclio: setting 'version' environment variable\n"
- ]
- }
- ],
- "source": [
- "%%nuclio env\n",
- "IMAGE_WIDTH = 128\n",
- "IMAGE_HEIGHT = 128\n",
- "version = 1.0"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 3,
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "%nuclio: setting 'MODEL_PATH' environment variable\n",
- "%nuclio: setting 'PREDICTION_MAP_PATH' environment variable\n"
- ]
- }
- ],
- "source": [
- "%nuclio env -c MODEL_PATH=/model/\n",
- "%nuclio env -l MODEL_PATH=/User/demos/gpu/horovod/cpu/image-classification/cats_dogs.hd5\n",
- "%nuclio env -l PREDICTION_MAP_PATH=./model/prediction_classes_map.json"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 4,
- "metadata": {},
- "outputs": [],
- "source": [
- "%%nuclio cmd -c\n",
- "pip install keras==2.2.4\n",
- "pip install tensorflow==1.13.1 \n",
- "pip install 'numpy<1.15.0'\n",
- "pip install requests\n",
- "pip install pillow"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 5,
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "%nuclio: setting spec.build.baseImage to 'python:3.6-jessie'\n"
- ]
- }
- ],
- "source": [
- "%%nuclio config \n",
- "spec.build.baseImage = \"python:3.6-jessie\""
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 6,
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "mounting volume path /model as ~/demos/gpu/horovod/cpu/image-classification/cats_dogs/model\n"
- ]
- }
- ],
- "source": [
- "%nuclio mount /model ~/demos/gpu/horovod/cpu/image-classification/cats_dogs/model"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "\n",
- "## Create and Load the Model and Set Up the Function Handler"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 7,
- "metadata": {},
- "outputs": [
- {
- "name": "stderr",
- "output_type": "stream",
- "text": [
- "Using TensorFlow backend.\n"
- ]
- }
- ],
- "source": [
- "import numpy as np \n",
- "from tensorflow import keras\n",
- "from keras.models import load_model\n",
- "from keras.preprocessing import image\n",
- "from keras.preprocessing.image import load_img\n",
- "import json\n",
- "import requests\n",
- "\n",
- "import os\n",
- "from os import environ, path\n",
- "from tempfile import mktemp"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 8,
- "metadata": {},
- "outputs": [],
- "source": [
- "model_file = environ['MODEL_PATH']\n",
- "prediction_map_file = environ['PREDICTION_MAP_PATH']\n",
- "\n",
- "# Set image parameters\n",
- "IMAGE_WIDTH = int(environ['IMAGE_WIDTH'])\n",
- "IMAGE_HEIGHT = int(environ['IMAGE_HEIGHT'])\n",
- "\n",
- "# load model\n",
- "def init_context(context): \n",
- " context.model = load_model(model_file)\n",
- " with open(prediction_map_file, 'r') as f:\n",
- " context.prediction_map = json.load(f)"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 9,
- "metadata": {},
- "outputs": [],
- "source": [
- "def download_file(context, url, target_path):\n",
- " with requests.get(url, stream=True) as response:\n",
- " response.raise_for_status()\n",
- " with open(target_path, 'wb') as f:\n",
- " for chunk in response.iter_content(chunk_size=8192):\n",
- " if chunk:\n",
- " f.write(chunk)\n",
- "\n",
- " context.logger.info_with('Downloaded file',url=url)"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 10,
- "metadata": {},
- "outputs": [],
- "source": [
- "def handler(context, event):\n",
- " tmp_file = mktemp()\n",
- " image_url = event.body.decode('utf-8').strip()\n",
- " download_file(context, image_url, tmp_file)\n",
- " \n",
- " img = load_img(tmp_file, target_size=(IMAGE_WIDTH, IMAGE_HEIGHT))\n",
- " x = image.img_to_array(img)\n",
- " x = np.expand_dims(x, axis=0)\n",
- "\n",
- " images = np.vstack([x])\n",
- " predicted_probability = context.model.predict_proba(images, batch_size=10)\n",
- " predicted_class = list(zip(predicted_probability, map(lambda x: '1' if x >= 0.5 else '0', predicted_probability)))\n",
- " actual_class = [(context.prediction_map[x[1]],x[0][0]) for x in predicted_class] \n",
- " os.remove(tmp_file)\n",
- " result = {'class':actual_class[0][0], 'dog-probability':float(actual_class[0][1])}\n",
- " return json.dumps(result)"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "\n",
- "## Trigger the Function"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 11,
- "metadata": {},
- "outputs": [
- {
- "name": "stderr",
- "output_type": "stream",
- "text": [
- "WARNING: Logging before flag parsing goes to stderr.\n",
- "W0702 07:05:39.756876 140252009395584 deprecation_wrapper.py:119] From /User/.pythonlibs/lib/python3.6/site-packages/keras/backend/tensorflow_backend.py:529: The name tf.placeholder is deprecated. Please use tf.compat.v1.placeholder instead.\n",
- "\n",
- "W0702 07:05:39.784578 140252009395584 deprecation_wrapper.py:119] From /User/.pythonlibs/lib/python3.6/site-packages/keras/backend/tensorflow_backend.py:4420: The name tf.random_uniform is deprecated. Please use tf.random.uniform instead.\n",
- "\n",
- "W0702 07:05:39.814317 140252009395584 deprecation_wrapper.py:119] From /User/.pythonlibs/lib/python3.6/site-packages/keras/backend/tensorflow_backend.py:250: The name tf.get_default_graph is deprecated. Please use tf.compat.v1.get_default_graph instead.\n",
- "\n",
- "W0702 07:05:39.814856 140252009395584 deprecation_wrapper.py:119] From /User/.pythonlibs/lib/python3.6/site-packages/keras/backend/tensorflow_backend.py:178: The name tf.get_default_session is deprecated. Please use tf.compat.v1.get_default_session instead.\n",
- "\n",
- "W0702 07:05:39.815345 140252009395584 deprecation_wrapper.py:119] From /User/.pythonlibs/lib/python3.6/site-packages/keras/backend/tensorflow_backend.py:185: The name tf.ConfigProto is deprecated. Please use tf.compat.v1.ConfigProto instead.\n",
- "\n",
- "W0702 07:05:39.920376 140252009395584 deprecation_wrapper.py:119] From /User/.pythonlibs/lib/python3.6/site-packages/keras/backend/tensorflow_backend.py:2029: The name tf.nn.fused_batch_norm is deprecated. Please use tf.compat.v1.nn.fused_batch_norm instead.\n",
- "\n",
- "W0702 07:05:39.987011 140252009395584 deprecation_wrapper.py:119] From /User/.pythonlibs/lib/python3.6/site-packages/keras/backend/tensorflow_backend.py:4255: The name tf.nn.max_pool is deprecated. Please use tf.nn.max_pool2d instead.\n",
- "\n",
- "W0702 07:05:39.994644 140252009395584 deprecation.py:506] From /User/.pythonlibs/lib/python3.6/site-packages/keras/backend/tensorflow_backend.py:3721: calling dropout (from tensorflow.python.ops.nn_ops) with keep_prob is deprecated and will be removed in a future version.\n",
- "Instructions for updating:\n",
- "Please use `rate` instead of `keep_prob`. Rate should be set to `rate = 1 - keep_prob`.\n",
- "W0702 07:05:40.865994 140252009395584 deprecation_wrapper.py:119] From /User/.pythonlibs/lib/python3.6/site-packages/keras/optimizers.py:793: The name tf.train.Optimizer is deprecated. Please use tf.compat.v1.train.Optimizer instead.\n",
- "\n",
- "W0702 07:05:40.875694 140252009395584 deprecation.py:323] From /User/.pythonlibs/lib/python3.6/site-packages/tensorflow/python/ops/nn_impl.py:180: add_dispatch_support..wrapper (from tensorflow.python.ops.array_ops) is deprecated and will be removed in a future version.\n",
- "Instructions for updating:\n",
- "Use tf.where in 2.0, which has the same broadcast rule as np.where\n"
- ]
- }
- ],
- "source": [
- "# nuclio: ignore\n",
- "init_context(context)"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 16,
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "Python> 2019-07-02 07:06:07,287 [info] Downloaded file: {'url': 'https://s3.amazonaws.com/iguazio-sample-data/images/catanddog/dog.391.jpg'}\n"
- ]
- },
- {
- "name": "stderr",
- "output_type": "stream",
- "text": [
- "I0702 07:06:07.287323 140252009395584 logger.py:100] Downloaded file\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "{\"class\": \"dog\", \"dog-probability\": 1.0}\n"
- ]
- },
- {
- "data": {
- "image/png": "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\n",
- "text/plain": [
- ""
- ]
- },
- "metadata": {
- "needs_background": "light"
- },
- "output_type": "display_data"
- }
- ],
- "source": [
- "# nuclio: ignore\n",
- "# Select a sample for the test.\n",
- "# Set both the local path for the test and the URL for downloading the sample from AWS S3.\n",
- "DATA_LOCATION = \"./cats_and_dogs_filtered/\"\n",
- "sample = random.choice(os.listdir(DATA_LOCATION+\"/cats_n_dogs\"))\n",
- "image_local = DATA_LOCATION + \"cats_n_dogs/\"+sample # Temporary location for downloading the file \n",
- "image_url = 'https://s3.amazonaws.com/iguazio-sample-data/images/catanddog/' + sample \n",
- "\n",
- "# Show the image\n",
- "img = load_img(image_local, target_size=(IMAGE_WIDTH, IMAGE_HEIGHT))\n",
- "plt.imshow(img)\n",
- "\n",
- "event = nuclio.Event(body=bytes(image_url, 'utf-8'))\n",
- "output = handler(context, event)\n",
- "print(output)"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 17,
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "%nuclio: notebook infer exported\n",
- "Config:\n",
- "apiVersion: nuclio.io/v1\n",
- "kind: Function\n",
- "metadata:\n",
- " annotations:\n",
- " nuclio.io/generated_by: function generated at 02-07-2019 by iguazio from /User/demos/image-classification/infer.ipynb\n",
- " labels: {}\n",
- " name: infer\n",
- "spec:\n",
- " build:\n",
- " baseImage: python:3.6-jessie\n",
- " commands:\n",
- " - pip install git+https://github.com/fchollet/keras\n",
- " - pip install tensorflow\n",
- " - pip install numpy\n",
- " - pip install requests\n",
- " - pip install pillow\n",
- " functionSourceCode: IyBHZW5lcmF0ZWQgYnkgbnVjbGlvLmV4cG9ydC5OdWNsaW9FeHBvcnRlciBvbiAyMDE5LTA3LTAyIDA3OjA2CgppbXBvcnQgbnVtcHkgYXMgbnAgCmZyb20gdGVuc29yZmxvdyBpbXBvcnQga2VyYXMKZnJvbSBrZXJhcy5tb2RlbHMgaW1wb3J0IGxvYWRfbW9kZWwKZnJvbSBrZXJhcy5wcmVwcm9jZXNzaW5nIGltcG9ydCBpbWFnZQpmcm9tIGtlcmFzLnByZXByb2Nlc3NpbmcuaW1hZ2UgaW1wb3J0IGxvYWRfaW1nCmltcG9ydCBqc29uCmltcG9ydCByZXF1ZXN0cwoKaW1wb3J0IG9zCmZyb20gb3MgaW1wb3J0IGVudmlyb24sIHBhdGgKZnJvbSB0ZW1wZmlsZSBpbXBvcnQgbWt0ZW1wCgptb2RlbF9maWxlID0gZW52aXJvblsnTU9ERUxfUEFUSCddCnByZWRpY3Rpb25fbWFwX2ZpbGUgPSBlbnZpcm9uWydQUkVESUNUSU9OX01BUF9QQVRIJ10KCklNQUdFX1dJRFRIID0gaW50KGVudmlyb25bJ0lNQUdFX1dJRFRIJ10pCklNQUdFX0hFSUdIVCA9IGludChlbnZpcm9uWydJTUFHRV9IRUlHSFQnXSkKCmRlZiBpbml0X2NvbnRleHQoY29udGV4dCk6IAogICAgY29udGV4dC5tb2RlbCA9IGxvYWRfbW9kZWwobW9kZWxfZmlsZSkKICAgIHdpdGggb3BlbihwcmVkaWN0aW9uX21hcF9maWxlLCAncicpIGFzIGY6CiAgICAgICAgY29udGV4dC5wcmVkaWN0aW9uX21hcCA9IGpzb24ubG9hZChmKQoKZGVmIGRvd25sb2FkX2ZpbGUoY29udGV4dCwgdXJsLCB0YXJnZXRfcGF0aCk6CiAgICB3aXRoIHJlcXVlc3RzLmdldCh1cmwsIHN0cmVhbT1UcnVlKSBhcyByZXNwb25zZToKICAgICAgICByZXNwb25zZS5yYWlzZV9mb3Jfc3RhdHVzKCkKICAgICAgICB3aXRoIG9wZW4odGFyZ2V0X3BhdGgsICd3YicpIGFzIGY6CiAgICAgICAgICAgIGZvciBjaHVuayBpbiByZXNwb25zZS5pdGVyX2NvbnRlbnQoY2h1bmtfc2l6ZT04MTkyKToKICAgICAgICAgICAgICAgIGlmIGNodW5rOgogICAgICAgICAgICAgICAgICAgIGYud3JpdGUoY2h1bmspCgogICAgY29udGV4dC5sb2dnZXIuaW5mb193aXRoKCdEb3dubG9hZGVkIGZpbGUnLHVybD11cmwpCgpkZWYgaGFuZGxlcihjb250ZXh0LCBldmVudCk6CiAgICB0bXBfZmlsZSA9IG1rdGVtcCgpCiAgICBpbWFnZV91cmwgPSBldmVudC5ib2R5LmRlY29kZSgndXRmLTgnKS5zdHJpcCgpCiAgICBkb3dubG9hZF9maWxlKGNvbnRleHQsIGltYWdlX3VybCwgdG1wX2ZpbGUpCiAgICAKICAgIGltZyA9IGxvYWRfaW1nKHRtcF9maWxlLCB0YXJnZXRfc2l6ZT0oSU1BR0VfV0lEVEgsIElNQUdFX0hFSUdIVCkpCiAgICB4ID0gaW1hZ2UuaW1nX3RvX2FycmF5KGltZykKICAgIHggPSBucC5leHBhbmRfZGltcyh4LCBheGlzPTApCgogICAgaW1hZ2VzID0gbnAudnN0YWNrKFt4XSkKICAgIHByZWRpY3RlZF9wcm9iYWJpbGl0eSA9IGNvbnRleHQubW9kZWwucHJlZGljdF9wcm9iYShpbWFnZXMsIGJhdGNoX3NpemU9MTApCiAgICBwcmVkaWN0ZWRfY2xhc3MgPSBsaXN0KHppcChwcmVkaWN0ZWRfcHJvYmFiaWxpdHksIG1hcChsYW1iZGEgeDogJzEnIGlmIHggPj0gMC41IGVsc2UgJzAnLCBwcmVkaWN0ZWRfcHJvYmFiaWxpdHkpKSkKICAgIGFjdHVhbF9jbGFzcyA9IFsoY29udGV4dC5wcmVkaWN0aW9uX21hcFt4WzFdXSx4WzBdWzBdKSBmb3IgeCBpbiBwcmVkaWN0ZWRfY2xhc3NdICAgCiAgICBvcy5yZW1vdmUodG1wX2ZpbGUpCiAgICByZXN1bHQgPSB7J2NsYXNzJzphY3R1YWxfY2xhc3NbMF1bMF0sICdkb2ctcHJvYmFiaWxpdHknOmZsb2F0KGFjdHVhbF9jbGFzc1swXVsxXSl9CiAgICByZXR1cm4ganNvbi5kdW1wcyhyZXN1bHQpCgo=\n",
- " noBaseImagesPull: true\n",
- " env:\n",
- " - name: IMAGE_WIDTH\n",
- " value: '128'\n",
- " - name: IMAGE_HEIGHT\n",
- " value: '128'\n",
- " - name: version\n",
- " value: '1.0'\n",
- " - name: MODEL_PATH\n",
- " value: /model/\n",
- " handler: infer:handler\n",
- " runtime: python:3.6\n",
- " volumes:\n",
- " - volume:\n",
- " flexVolume:\n",
- " driver: v3io/fuse\n",
- " options:\n",
- " accessKey: 1e52ff93-a541-4880-abf1-d9b948af77de\n",
- " container: users\n",
- " subPath: /iguazio/demos/gpu/horovod/cpu/image-classification/cats_dogs/model\n",
- " name: fs\n",
- " volumeMount:\n",
- " mountPath: /model\n",
- " name: fs\n",
- "\n",
- "Code:\n",
- "# Generated by nuclio.export.NuclioExporter on 2019-07-02 07:06\n",
- "\n",
- "import numpy as np \n",
- "from tensorflow import keras\n",
- "from keras.models import load_model\n",
- "from keras.preprocessing import image\n",
- "from keras.preprocessing.image import load_img\n",
- "import json\n",
- "import requests\n",
- "\n",
- "import os\n",
- "from os import environ, path\n",
- "from tempfile import mktemp\n",
- "\n",
- "model_file = environ['MODEL_PATH']\n",
- "prediction_map_file = environ['PREDICTION_MAP_PATH']\n",
- "\n",
- "IMAGE_WIDTH = int(environ['IMAGE_WIDTH'])\n",
- "IMAGE_HEIGHT = int(environ['IMAGE_HEIGHT'])\n",
- "\n",
- "def init_context(context): \n",
- " context.model = load_model(model_file)\n",
- " with open(prediction_map_file, 'r') as f:\n",
- " context.prediction_map = json.load(f)\n",
- "\n",
- "def download_file(context, url, target_path):\n",
- " with requests.get(url, stream=True) as response:\n",
- " response.raise_for_status()\n",
- " with open(target_path, 'wb') as f:\n",
- " for chunk in response.iter_content(chunk_size=8192):\n",
- " if chunk:\n",
- " f.write(chunk)\n",
- "\n",
- " context.logger.info_with('Downloaded file',url=url)\n",
- "\n",
- "def handler(context, event):\n",
- " tmp_file = mktemp()\n",
- " image_url = event.body.decode('utf-8').strip()\n",
- " download_file(context, image_url, tmp_file)\n",
- " \n",
- " img = load_img(tmp_file, target_size=(IMAGE_WIDTH, IMAGE_HEIGHT))\n",
- " x = image.img_to_array(img)\n",
- " x = np.expand_dims(x, axis=0)\n",
- "\n",
- " images = np.vstack([x])\n",
- " predicted_probability = context.model.predict_proba(images, batch_size=10)\n",
- " predicted_class = list(zip(predicted_probability, map(lambda x: '1' if x >= 0.5 else '0', predicted_probability)))\n",
- " actual_class = [(context.prediction_map[x[1]],x[0][0]) for x in predicted_class] \n",
- " os.remove(tmp_file)\n",
- " result = {'class':actual_class[0][0], 'dog-probability':float(actual_class[0][1])}\n",
- " return json.dumps(result)\n",
- "\n",
- "\n"
- ]
- }
- ],
- "source": [
- "%nuclio show"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "\n",
- "## Prepare to Deploy the Function\n",
- "\n",
- "Before you deploy the function, open a Jupyter terminal and run the following command:\n",
- "\n",
- "`pip install --upgrade nuclio-jupyter`"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "\n",
- "## Deploy the Function\n",
- "\n",
- "Run the following command to deploy the function:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "[nuclio.deploy] 2019-07-02 07:07:08,424 project name not found created new (ai)\n"
- ]
- },
- {
- "name": "stderr",
- "output_type": "stream",
- "text": [
- "I0702 07:07:08.424754 140252009395584 deploy.py:317] project name not found created new (ai)\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "[nuclio.deploy] 2019-07-02 07:07:09,507 (info) Building processor image\n"
- ]
- },
- {
- "name": "stderr",
- "output_type": "stream",
- "text": [
- "I0702 07:07:09.507237 140252009395584 deploy.py:274] (info) Building processor image\n"
- ]
- }
- ],
- "source": [
- "%nuclio deploy -n cats-dogs -p ai -c"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "\n",
- "## Test the Function"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {},
- "outputs": [],
- "source": [
- "# Run a test with the new function. Replace \"function URL:port\" with the actual URL and port number.\n",
- "# To get the function's URL, in the platform dashboard, navigate to the function page - Functions > ai > cats-dogs - and select the 'Status' tab.\n",
- "!curl -X POST -d \"https://s3.amazonaws.com/iguazio-sample-data/images/catanddog/cat.123.jpg\" "
- ]
- }
- ],
- "metadata": {
- "kernelspec": {
- "display_name": "Python 3",
- "language": "python",
- "name": "python3"
- },
- "language_info": {
- "codemirror_mode": {
- "name": "ipython",
- "version": 3
- },
- "file_extension": ".py",
- "mimetype": "text/x-python",
- "name": "python",
- "nbconvert_exporter": "python",
- "pygments_lexer": "ipython3",
- "version": "3.6.8"
- }
- },
- "nbformat": 4,
- "nbformat_minor": 2
-}
diff --git a/demos/gpu/horovod/cpu/image-classification/horovod_train_cats_n_dogs.py b/demos/gpu/horovod/cpu/image-classification/horovod_train_cats_n_dogs.py
deleted file mode 100644
index f00b9973..00000000
--- a/demos/gpu/horovod/cpu/image-classification/horovod_train_cats_n_dogs.py
+++ /dev/null
@@ -1,177 +0,0 @@
-from __future__ import print_function
-import os
-import sys
-import json
-import keras
-from keras.datasets import mnist
-from keras.models import Sequential
-from keras.layers import Conv2D, MaxPooling2D, Dropout, Flatten, Dense, Activation, BatchNormalization
-from keras.preprocessing.image import ImageDataGenerator
-from keras import backend as K
-import tensorflow as tf
-import horovod.keras as hvd
-import pandas as pd
-from sklearn.model_selection import train_test_split
-
-# Get the images path
-DATA_PATH = sys.argv[1]
-HOROVOD_DIR = sys.argv[2]
-
-epochs = 6
-batch_size = 64
-os.environ["CUDA_VISIBLE_DEVICES"]="-1"
-
-# Define image parameters
-IMAGE_WIDTH=128
-IMAGE_HEIGHT=128
-IMAGE_SIZE=(IMAGE_WIDTH, IMAGE_HEIGHT)
-IMAGE_CHANNELS=3 # RGB color
-
-# Create a file-names list (JPG image-files only)
-filenames = [file for file in os.listdir(DATA_PATH + "/cats_n_dogs/") if file.endswith('jpg')]
-categories = []
-
-# Create a categories and prediction classes map
-categories_map = {
- 'dog': 1,
- 'cat': 0,
-}
-
-# Create a pandas DataFrame for the full sample
-for filename in filenames:
- category = filename.split('.')[0]
- categories.append([categories_map[category]])
-
-df = pd.DataFrame({
- 'filename': filenames,
- 'category': categories
-})
-df['category'] = df['category'].astype('str');
-
-# Prepare, test, and train the data
-train_df, validate_df = train_test_split(df, test_size=0.20, random_state=42)
-train_df = train_df.reset_index(drop=True)
-validate_df = validate_df.reset_index(drop=True)
-train_df['category'] = train_df['category'].astype('str');
-total_train = train_df.shape[0]
-total_validate = validate_df.shape[0]
-
-total_train = train_df.shape[0]
-total_validate = validate_df.shape[0]
-
-# Horovod: initialize Horovod.
-hvd.init()
-
-# Horovod: pin GPU to be used to process local rank (one GPU per process).
-config = tf.ConfigProto()
-K.set_session(tf.Session(config=config))
-
-
-model = Sequential()
-
-model.add(Conv2D(32, (3, 3), activation='relu', input_shape=(IMAGE_WIDTH, IMAGE_HEIGHT, IMAGE_CHANNELS)))
-model.add(BatchNormalization())
-model.add(MaxPooling2D(pool_size=(2, 2)))
-model.add(Dropout(0.25))
-
-model.add(Conv2D(64, (3, 3), activation='relu'))
-model.add(BatchNormalization())
-model.add(MaxPooling2D(pool_size=(2, 2)))
-model.add(Dropout(0.25))
-
-model.add(Conv2D(128, (3, 3), activation='relu'))
-model.add(BatchNormalization())
-model.add(MaxPooling2D(pool_size=(2, 2)))
-model.add(Dropout(0.25))
-
-model.add(Flatten())
-model.add(Dense(512, activation='relu'))
-model.add(BatchNormalization())
-model.add(Dropout(0.5))
-model.add(Dense(1, activation='sigmoid'))
-
-
-# Horovod: adjust learning rate based on number of GPUs.
-opt = keras.optimizers.Adadelta(lr=1.0 * hvd.size())
-
-# Horovod: add Horovod Distributed Optimizer.
-opt = hvd.DistributedOptimizer(opt)
-
-model.compile(loss='binary_crossentropy',
- optimizer=opt,
- metrics=['accuracy'])
-
-model.summary()
-
-callbacks = [
- # Horovod: broadcast initial variable states from rank 0 to all other processes.
- # This is necessary to ensure consistent initialization of all workers when
- # training is started with random weights or restored from a checkpoint.
- hvd.callbacks.BroadcastGlobalVariablesCallback(0),
-
- # Horovod: average metrics among workers at the end of every epoch.
- # Note: This callback must be in the list before the ReduceLROnPlateau,
- # TensorBoard or other metrics-based callbacks.
- hvd.callbacks.MetricAverageCallback(),
-
- # Horovod: using `lr = 1.0 * hvd.size()` from the very beginning leads to worse final
- # accuracy. Scale the learning rate `lr = 1.0` ---> `lr = 1.0 * hvd.size()` during
- # the first five epochs. See https://arxiv.org/abs/1706.02677 for details.
- hvd.callbacks.LearningRateWarmupCallback(warmup_epochs=5, verbose=1),
-
- # Reduce the learning rate if training plateaues.
- keras.callbacks.ReduceLROnPlateau(patience=10, verbose=1),
-]
-
-# Horovod: save checkpoints only on worker 0 to prevent other workers from corrupting them.
-if hvd.rank() == 0:
- callbacks.append(keras.callbacks.ModelCheckpoint(HOROVOD_DIR + '/checkpoints/checkpoint-{epoch}.h5'))
-
-# Set up ImageDataGenerators to do data augmentation for the training images.
-train_datagen = ImageDataGenerator(
- rotation_range=15,
- rescale=1./255,
- shear_range=0.1,
- zoom_range=0.2,
- horizontal_flip=True,
- width_shift_range=0.1,
- height_shift_range=0.1
-)
-train_generator = train_datagen.flow_from_dataframe(
- train_df,
- DATA_PATH + "/cats_n_dogs/",
- x_col = 'filename',
- y_col = 'category',
- target_size = IMAGE_SIZE,
- class_mode = 'binary',
- batch_size = batch_size
-)
-
-validation_datagen = ImageDataGenerator(rescale=1./255)
-validation_generator = validation_datagen.flow_from_dataframe(
- validate_df,
- DATA_PATH + "/cats_n_dogs/",
- x_col = 'filename',
- y_col = 'category',
- target_size = IMAGE_SIZE,
- class_mode = 'binary',
- batch_size = batch_size
-)
-
-# Train the model
-history = model.fit_generator(
- train_generator,
- steps_per_epoch=total_train // batch_size,
- callbacks=callbacks,
- epochs=epochs,
- verbose=1,
- validation_data=validation_generator,
- validation_steps=total_validate // batch_size
-)
-
-#save the model only on worker 0 to prevent failures ("cannot lock file")
-if hvd.rank() == 0:
- model.save(HOROVOD_DIR + '/cats_dogs.hd5')
-
-print(pd.DataFrame(history.history))
-
diff --git a/demos/gpu/horovod/image-classification/01-load-data-cats-n-dogs.ipynb b/demos/gpu/horovod/image-classification/01-load-data-cats-n-dogs.ipynb
deleted file mode 100644
index 9081de7b..00000000
--- a/demos/gpu/horovod/image-classification/01-load-data-cats-n-dogs.ipynb
+++ /dev/null
@@ -1,520 +0,0 @@
-{
- "cells": [
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "# Load Cats and Dogs Images"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "_uuid": "fe76d1d1ded592430e7548feacfa38dc42f085d9"
- },
- "source": [
- "## Install Packages"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 1,
- "metadata": {},
- "outputs": [],
- "source": [
- "!pip install --upgrade keras==2.2.4\n",
- "!pip install --upgrade tensorflow==1.13.1 \n",
- "!pip install --upgrade 'numpy<1.15.0'"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "> **Note:** After running the pip command you should restart the Jupyter kernel.
\n",
- "> To restart the kernel, click on the kernel-restart button in the notebook menu toolbar (the refresh icon next to the **Code** button)."
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "## Import Library"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 1,
- "metadata": {
- "_cell_guid": "b1076dfc-b9ad-4769-8c92-a6c4dae69d19",
- "_uuid": "8f2839f25d086af736a60e9eeb907d3b93b6e0e5"
- },
- "outputs": [
- {
- "name": "stderr",
- "output_type": "stream",
- "text": [
- "Using TensorFlow backend.\n",
- "/User/.pythonlibs/lib/python3.6/site-packages/tensorflow/python/framework/dtypes.py:516: FutureWarning: Passing (type, 1) or '1type' as a synonym of type is deprecated; in a future version of numpy, it will be understood as (type, (1,)) / '(1,)type'.\n",
- " _np_qint8 = np.dtype([(\"qint8\", np.int8, 1)])\n",
- "/User/.pythonlibs/lib/python3.6/site-packages/tensorflow/python/framework/dtypes.py:517: FutureWarning: Passing (type, 1) or '1type' as a synonym of type is deprecated; in a future version of numpy, it will be understood as (type, (1,)) / '(1,)type'.\n",
- " _np_quint8 = np.dtype([(\"quint8\", np.uint8, 1)])\n",
- "/User/.pythonlibs/lib/python3.6/site-packages/tensorflow/python/framework/dtypes.py:518: FutureWarning: Passing (type, 1) or '1type' as a synonym of type is deprecated; in a future version of numpy, it will be understood as (type, (1,)) / '(1,)type'.\n",
- " _np_qint16 = np.dtype([(\"qint16\", np.int16, 1)])\n",
- "/User/.pythonlibs/lib/python3.6/site-packages/tensorflow/python/framework/dtypes.py:519: FutureWarning: Passing (type, 1) or '1type' as a synonym of type is deprecated; in a future version of numpy, it will be understood as (type, (1,)) / '(1,)type'.\n",
- " _np_quint16 = np.dtype([(\"quint16\", np.uint16, 1)])\n",
- "/User/.pythonlibs/lib/python3.6/site-packages/tensorflow/python/framework/dtypes.py:520: FutureWarning: Passing (type, 1) or '1type' as a synonym of type is deprecated; in a future version of numpy, it will be understood as (type, (1,)) / '(1,)type'.\n",
- " _np_qint32 = np.dtype([(\"qint32\", np.int32, 1)])\n",
- "/User/.pythonlibs/lib/python3.6/site-packages/tensorflow/python/framework/dtypes.py:525: FutureWarning: Passing (type, 1) or '1type' as a synonym of type is deprecated; in a future version of numpy, it will be understood as (type, (1,)) / '(1,)type'.\n",
- " np_resource = np.dtype([(\"resource\", np.ubyte, 1)])\n",
- "/User/.pythonlibs/lib/python3.6/site-packages/tensorboard/compat/tensorflow_stub/dtypes.py:541: FutureWarning: Passing (type, 1) or '1type' as a synonym of type is deprecated; in a future version of numpy, it will be understood as (type, (1,)) / '(1,)type'.\n",
- " _np_qint8 = np.dtype([(\"qint8\", np.int8, 1)])\n",
- "/User/.pythonlibs/lib/python3.6/site-packages/tensorboard/compat/tensorflow_stub/dtypes.py:542: FutureWarning: Passing (type, 1) or '1type' as a synonym of type is deprecated; in a future version of numpy, it will be understood as (type, (1,)) / '(1,)type'.\n",
- " _np_quint8 = np.dtype([(\"quint8\", np.uint8, 1)])\n",
- "/User/.pythonlibs/lib/python3.6/site-packages/tensorboard/compat/tensorflow_stub/dtypes.py:543: FutureWarning: Passing (type, 1) or '1type' as a synonym of type is deprecated; in a future version of numpy, it will be understood as (type, (1,)) / '(1,)type'.\n",
- " _np_qint16 = np.dtype([(\"qint16\", np.int16, 1)])\n",
- "/User/.pythonlibs/lib/python3.6/site-packages/tensorboard/compat/tensorflow_stub/dtypes.py:544: FutureWarning: Passing (type, 1) or '1type' as a synonym of type is deprecated; in a future version of numpy, it will be understood as (type, (1,)) / '(1,)type'.\n",
- " _np_quint16 = np.dtype([(\"quint16\", np.uint16, 1)])\n",
- "/User/.pythonlibs/lib/python3.6/site-packages/tensorboard/compat/tensorflow_stub/dtypes.py:545: FutureWarning: Passing (type, 1) or '1type' as a synonym of type is deprecated; in a future version of numpy, it will be understood as (type, (1,)) / '(1,)type'.\n",
- " _np_qint32 = np.dtype([(\"qint32\", np.int32, 1)])\n",
- "/User/.pythonlibs/lib/python3.6/site-packages/tensorboard/compat/tensorflow_stub/dtypes.py:550: FutureWarning: Passing (type, 1) or '1type' as a synonym of type is deprecated; in a future version of numpy, it will be understood as (type, (1,)) / '(1,)type'.\n",
- " np_resource = np.dtype([(\"resource\", np.ubyte, 1)])\n"
- ]
- }
- ],
- "source": [
- "# This Python 3 environment comes with many helpful analytics libraries installed.\n",
- "# It is defined by the kaggle/python docker image: https://github.com/kaggle/docker-python.\n",
- "# For example, here are several helpful packages to load:\n",
- "\n",
- "import numpy as np # linear algebra\n",
- "import pandas as pd # data processing, CSV file I/O (e.g. pd.read_csv)\n",
- "from keras.preprocessing.image import load_img\n",
- "\n",
- "# Input data files are available in the \"../input/\" directory.\n",
- "# For example, running the following (by selecting 'Run' or pressing Shift+Enter) will list the files in the input directory:\n",
- "\n",
- "import matplotlib.pyplot as plt\n",
- "import random\n",
- "\n",
- "import os\n",
- "import zipfile\n",
- "\n",
- "# Define locations\n",
- "BASE_PATH = os.getcwd()\n",
- "DATA_PATH = BASE_PATH + \"/cats_and_dogs_filtered/\"\n",
- "!mkdir model\n",
- "MODEL_PATH = BASE_PATH + '/model/'\n",
- "\n",
- "# Define image parameters\n",
- "FAST_RUN = False\n",
- "IMAGE_WIDTH=128\n",
- "IMAGE_HEIGHT=128\n",
- "IMAGE_SIZE=(IMAGE_WIDTH, IMAGE_HEIGHT)\n",
- "IMAGE_CHANNELS=3 # RGB color\n",
- "\n",
- "# Any results you write to the current directory are saved as output."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 2,
- "metadata": {},
- "outputs": [
- {
- "data": {
- "text/plain": [
- "'/User/demos/gpu/horovod/image-classification/cats_and_dogs_filtered/catsndogs.zip'"
- ]
- },
- "execution_count": 2,
- "metadata": {},
- "output_type": "execute_result"
- }
- ],
- "source": [
- "DATA_PATH + 'catsndogs.zip'"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "## Download the Data"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 3,
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- " % Total % Received % Xferd Average Speed Time Time Time Current\n",
- " Dload Upload Total Spent Left Speed\n",
- "100 65.2M 100 65.2M 0 0 13.9M 0 0:00:04 0:00:04 --:--:-- 15.3M\n"
- ]
- }
- ],
- "source": [
- "!mkdir cats_and_dogs_filtered\n",
- "# Download a sample stocks file from Iguazio demo bucket in AWS S3\n",
- "!curl -L \"iguazio-sample-data.s3.amazonaws.com/catsndogs.zip\" > ./cats_and_dogs_filtered/catsndogs.zip"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 4,
- "metadata": {},
- "outputs": [],
- "source": [
- "zip_ref = zipfile.ZipFile(DATA_PATH + 'catsndogs.zip', 'r')\n",
- "zip_ref.extractall('cats_and_dogs_filtered')\n",
- "zip_ref.close()"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "_uuid": "7335a579cc0268fba5d34d6f7558f33c187eedb3"
- },
- "source": [
- "## Prepare the Traning Data"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 5,
- "metadata": {},
- "outputs": [],
- "source": [
- "import json"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 6,
- "metadata": {},
- "outputs": [],
- "source": [
- "def build_prediction_map(categories_map):\n",
- " return {v:k for k ,v in categories_map.items()}"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 7,
- "metadata": {
- "_cell_guid": "79c7e3d0-c299-4dcb-8224-4455121ee9b0",
- "_uuid": "d629ff2d2480ee46fbb7e2d37f6b5fab8052498a"
- },
- "outputs": [],
- "source": [
- "# Create a file-names list (JPG image-files only)\n",
- "filenames = [file for file in os.listdir(DATA_PATH+\"/cats_n_dogs\") if file.endswith('jpg')]\n",
- "categories = []\n",
- "\n",
- "# Categories and prediction-classes map\n",
- "categories_map = {\n",
- " 'dog': 1,\n",
- " 'cat': 0,\n",
- "}\n",
- "prediction_map = build_prediction_map(categories_map)\n",
- "with open(MODEL_PATH + 'prediction_classes_map.json', 'w') as f:\n",
- " json.dump(prediction_map, f)\n",
- "\n",
- "# Create a pandas DataFrame for the full sample\n",
- "for filename in filenames:\n",
- " category = filename.split('.')[0]\n",
- " categories.append([categories_map[category]])\n",
- "\n",
- "df = pd.DataFrame({\n",
- " 'filename': filenames,\n",
- " 'category': categories\n",
- "})\n",
- "df['category'] = df['category'].astype('str');"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 8,
- "metadata": {
- "_uuid": "915bb9ba7063ab4d5c07c542419ae119003a5f98"
- },
- "outputs": [
- {
- "data": {
- "text/html": [
- "\n",
- "\n",
- "
\n",
- " \n",
- " \n",
- " | \n",
- " filename | \n",
- " category | \n",
- "
\n",
- " \n",
- " \n",
- " \n",
- " 0 | \n",
- " cat.0.jpg | \n",
- " [0] | \n",
- "
\n",
- " \n",
- " 1 | \n",
- " cat.1.jpg | \n",
- " [0] | \n",
- "
\n",
- " \n",
- " 2 | \n",
- " cat.10.jpg | \n",
- " [0] | \n",
- "
\n",
- " \n",
- " 3 | \n",
- " cat.100.jpg | \n",
- " [0] | \n",
- "
\n",
- " \n",
- " 4 | \n",
- " cat.101.jpg | \n",
- " [0] | \n",
- "
\n",
- " \n",
- "
\n",
- "
"
- ],
- "text/plain": [
- " filename category\n",
- "0 cat.0.jpg [0]\n",
- "1 cat.1.jpg [0]\n",
- "2 cat.10.jpg [0]\n",
- "3 cat.100.jpg [0]\n",
- "4 cat.101.jpg [0]"
- ]
- },
- "execution_count": 8,
- "metadata": {},
- "output_type": "execute_result"
- }
- ],
- "source": [
- "df.head()"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 9,
- "metadata": {
- "_uuid": "72bf69e817f67f5a2eaff8561217e22077248553"
- },
- "outputs": [
- {
- "data": {
- "text/html": [
- "\n",
- "\n",
- "
\n",
- " \n",
- " \n",
- " | \n",
- " filename | \n",
- " category | \n",
- "
\n",
- " \n",
- " \n",
- " \n",
- " 1995 | \n",
- " dog.995.jpg | \n",
- " [1] | \n",
- "
\n",
- " \n",
- " 1996 | \n",
- " dog.996.jpg | \n",
- " [1] | \n",
- "
\n",
- " \n",
- " 1997 | \n",
- " dog.997.jpg | \n",
- " [1] | \n",
- "
\n",
- " \n",
- " 1998 | \n",
- " dog.998.jpg | \n",
- " [1] | \n",
- "
\n",
- " \n",
- " 1999 | \n",
- " dog.999.jpg | \n",
- " [1] | \n",
- "
\n",
- " \n",
- "
\n",
- "
"
- ],
- "text/plain": [
- " filename category\n",
- "1995 dog.995.jpg [1]\n",
- "1996 dog.996.jpg [1]\n",
- "1997 dog.997.jpg [1]\n",
- "1998 dog.998.jpg [1]\n",
- "1999 dog.999.jpg [1]"
- ]
- },
- "execution_count": 9,
- "metadata": {},
- "output_type": "execute_result"
- }
- ],
- "source": [
- "df.tail()"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "_uuid": "a999484fc35b73373fafe2253ae9db7ff46fdb90"
- },
- "source": [
- "## Check the Total Image Count\n",
- "\n",
- "Check the total image count for each category.
\n",
- "The data set has 12,000 cat images and 12,000 dog images."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 10,
- "metadata": {
- "_uuid": "fa26f0bc7a6d835a24989790b20f3c6f32946f45"
- },
- "outputs": [
- {
- "data": {
- "text/plain": [
- ""
- ]
- },
- "execution_count": 10,
- "metadata": {},
- "output_type": "execute_result"
- },
- {
- "data": {
- "image/png": "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\n",
- "text/plain": [
- "