From b3cc1b61a235795d9a528427722d36f02dce55dd Mon Sep 17 00:00:00 2001 From: Roope Astala Date: Fri, 12 Oct 2018 14:43:18 -0400 Subject: [PATCH] more updates --- .../02.train-on-local-checkpoint.ipynb | 473 ----- .../03.train-on-aci-checkpoint.ipynb | 325 ---- .../04.train-on-remote-vm-checkpoint.ipynb | 321 ---- .../05.train-in-spark-checkpoint.ipynb | 257 --- onnx/README.md | 27 + onnx/mnist.py | 124 ++ .../01.Installation_and_Configuration.ipynb | 1 - .../databricks/02.Ingest_data.ipynb | 1 - .../databricks/03a.Build_model.ipynb | 1 - .../03b.Build_model_runHistory.ipynb | 1 - .../databricks/04.Deploy_to_ACI.ipynb | 1 - .../05.Deploy_to_AKS_existingImage.ipynb | 1 - .../databricks/Databricks_AMLSDK_github.dbc | Bin 42332 -> 0 bytes project-brainwave/databricks/readme.md | 26 - ...ne-deploy-with-tensorflow-checkpoint.ipynb | 1624 ----------------- 15 files changed, 151 insertions(+), 3032 deletions(-) delete mode 100644 01.getting-started/02.train-on-local/.ipynb_checkpoints/02.train-on-local-checkpoint.ipynb delete mode 100644 01.getting-started/03.train-on-aci/.ipynb_checkpoints/03.train-on-aci-checkpoint.ipynb delete mode 100644 01.getting-started/04.train-on-remote-vm/.ipynb_checkpoints/04.train-on-remote-vm-checkpoint.ipynb delete mode 100644 01.getting-started/05.train-in-spark/.ipynb_checkpoints/05.train-in-spark-checkpoint.ipynb create mode 100644 onnx/README.md create mode 100644 onnx/mnist.py delete mode 100644 project-brainwave/databricks/01.Installation_and_Configuration.ipynb delete mode 100644 project-brainwave/databricks/02.Ingest_data.ipynb delete mode 100644 project-brainwave/databricks/03a.Build_model.ipynb delete mode 100644 project-brainwave/databricks/03b.Build_model_runHistory.ipynb delete mode 100644 project-brainwave/databricks/04.Deploy_to_ACI.ipynb delete mode 100644 project-brainwave/databricks/05.Deploy_to_AKS_existingImage.ipynb delete mode 100644 project-brainwave/databricks/Databricks_AMLSDK_github.dbc delete mode 100644 project-brainwave/databricks/readme.md delete mode 100644 training/03.train-hyperparameter-tune-deploy-with-tensorflow/.ipynb_checkpoints/03.train-hyperparameter-tune-deploy-with-tensorflow-checkpoint.ipynb diff --git a/01.getting-started/02.train-on-local/.ipynb_checkpoints/02.train-on-local-checkpoint.ipynb b/01.getting-started/02.train-on-local/.ipynb_checkpoints/02.train-on-local-checkpoint.ipynb deleted file mode 100644 index 0a07ca6b..00000000 --- a/01.getting-started/02.train-on-local/.ipynb_checkpoints/02.train-on-local-checkpoint.ipynb +++ /dev/null @@ -1,473 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Copyright (c) Microsoft Corporation. All rights reserved.\n", - "\n", - "Licensed under the MIT License." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# 02. Train locally\n", - "* Create or load workspace.\n", - "* Create scripts locally.\n", - "* Create `train.py` in a folder, along with a `my.lib` file.\n", - "* Configure & execute a local run in a user-managed Python environment.\n", - "* Configure & execute a local run in a system-managed Python environment.\n", - "* Configure & execute a local run in a Docker environment.\n", - "* Query run metrics to find the best model\n", - "* Register model for operationalization." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Prerequisites\n", - "Make sure you go through the [00. Installation and Configuration](00.configuration.ipynb) Notebook first if you haven't." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Check core SDK version number\n", - "import azureml.core\n", - "\n", - "print(\"SDK version:\", azureml.core.VERSION)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Initialize Workspace\n", - "\n", - "Initialize a workspace object from persisted configuration." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "from azureml.core.workspace import Workspace\n", - "\n", - "ws = Workspace.from_config()\n", - "print(ws.name, ws.resource_group, ws.location, ws.subscription_id, sep='\\n')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Create An Experiment\n", - "**Experiment** is a logical container in an Azure ML Workspace. It hosts run records which can include run metrics and output artifacts from your experiments." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "from azureml.core import Experiment\n", - "experiment_name = 'train-on-local'\n", - "exp = Experiment(workspace=ws, name=experiment_name)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## View `train.py`\n", - "\n", - "`train.py` is already created for you." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "with open('./train.py', 'r') as f:\n", - " print(f.read())" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Note `train.py` also references a `mylib.py` file." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "with open('./mylib.py', 'r') as f:\n", - " print(f.read())" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Configure & Run\n", - "### User-managed environment\n", - "Below, we use a user-managed run, which means you are responsible to ensure all the necessary packages are available in the Python environment you choose to run the script." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "from azureml.core.runconfig import RunConfiguration\n", - "\n", - "# Editing a run configuration property on-fly.\n", - "run_config_user_managed = RunConfiguration()\n", - "\n", - "run_config_user_managed.environment.python.user_managed_dependencies = True\n", - "\n", - "# You can choose a specific Python environment by pointing to a Python path \n", - "#run_config.environment.python.interpreter_path = '/home/johndoe/miniconda3/envs/sdk2/bin/python'" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "#### Submit script to run in the user-managed environment\n", - "Note whole script folder is submitted for execution, including the `mylib.py` file." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "from azureml.core import ScriptRunConfig\n", - "\n", - "src = ScriptRunConfig(source_directory='./', script='train.py', run_config=run_config_user_managed)\n", - "run = exp.submit(src)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "#### Get run history details" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "run" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Block to wait till run finishes." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "run.wait_for_completion(show_output=True)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### System-managed environment\n", - "You can also ask the system to build a new conda environment and execute your scripts in it. The environment is built once and will be reused in subsequent executions as long as the conda dependencies remain unchanged. " - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "from azureml.core.runconfig import RunConfiguration\n", - "from azureml.core.conda_dependencies import CondaDependencies\n", - "\n", - "run_config_system_managed = RunConfiguration()\n", - "\n", - "run_config_system_managed.environment.python.user_managed_dependencies = False\n", - "run_config_system_managed.auto_prepare_environment = True\n", - "\n", - "# Specify conda dependencies with scikit-learn\n", - "cd = CondaDependencies.create(conda_packages=['scikit-learn'])\n", - "run_config_system_managed.environment.python.conda_dependencies = cd" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "#### Submit script to run in the system-managed environment\n", - "A new conda environment is built based on the conda dependencies object. If you are running this for the first time, this might take up to 5 mninutes. But this conda environment is reused so long as you don't change the conda dependencies." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "src = ScriptRunConfig(source_directory=\"./\", script='train.py', run_config=run_config_system_managed)\n", - "run = exp.submit(src)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "#### Get run history details" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "run" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Block and wait till run finishes." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "run.wait_for_completion(show_output = True)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Docker-based execution\n", - "**IMPORTANT**: You must have Docker engine installed locally in order to use this execution mode. If your kernel is already running in a Docker container, such as **Azure Notebooks**, this mode will **NOT** work.\n", - "\n", - "You can also ask the system to pull down a Docker image and execute your scripts in it." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "run_config_docker = RunConfiguration()\n", - "run_config_docker.environment.python.user_managed_dependencies = False\n", - "run_config_docker.auto_prepare_environment = True\n", - "run_config_docker.environment.docker.enabled = True\n", - "run_config_docker.environment.docker.base_image = azureml.core.runconfig.DEFAULT_CPU_IMAGE\n", - "\n", - "# Specify conda dependencies with scikit-learn\n", - "cd = CondaDependencies.create(conda_packages=['scikit-learn'])\n", - "run_config_docker.environment.python.conda_dependencies = cd\n", - "\n", - "src = ScriptRunConfig(source_directory=\"./\", script='train.py', run_config=run_config_docker)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Submit script to run in the system-managed environment\n", - "A new conda environment is built based on the conda dependencies object. If you are running this for the first time, this might take up to 5 mninutes. But this conda environment is reused so long as you don't change the conda dependencies.\n", - "\n", - "\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "import os\n", - "\n", - "# Check if Docker is installed\n", - "if os.system(\"docker -v\") == 0:\n", - " run = exp.submit(src)\n", - "else:\n", - " print(\"Docker engine not installed.\")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "#Get run history details\n", - "run" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "run.wait_for_completion(show_output=True)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Query run metrics" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "tags": [ - "query history", - "get metrics" - ] - }, - "outputs": [], - "source": [ - "# get all metris logged in the run\n", - "run.get_metrics()\n", - "metrics = run.get_metrics()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Let's find the model that has the lowest MSE value logged." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "import numpy as np\n", - "\n", - "best_alpha = metrics['alpha'][np.argmin(metrics['mse'])]\n", - "\n", - "print('When alpha is {1:0.2f}, we have min MSE {0:0.2f}.'.format(\n", - " min(metrics['mse']), \n", - " best_alpha\n", - "))" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "You can also list all the files that are associated with this run record" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "run.get_file_names()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We know the model `ridge_0.40.pkl` is the best performing model from the eariler queries. So let's register it with the workspace." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# supply a model name, and the full path to the serialized model file.\n", - "model = run.register_model(model_name='best_ridge_model', model_path='./outputs/ridge_0.40.pkl')" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "print(model.name, model.version, model.url)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Now you can deploy this model following the example in the 01 notebook." - ] - } - ], - "metadata": { - "authors": [ - { - "name": "roastala" - } - ], - "kernelspec": { - "display_name": "Python 3.6", - "language": "python", - "name": "python36" - }, - "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.6" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/01.getting-started/03.train-on-aci/.ipynb_checkpoints/03.train-on-aci-checkpoint.ipynb b/01.getting-started/03.train-on-aci/.ipynb_checkpoints/03.train-on-aci-checkpoint.ipynb deleted file mode 100644 index 00667e74..00000000 --- a/01.getting-started/03.train-on-aci/.ipynb_checkpoints/03.train-on-aci-checkpoint.ipynb +++ /dev/null @@ -1,325 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Copyright (c) Microsoft Corporation. All rights reserved.\n", - "\n", - "Licensed under the MIT License." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# 03. Train on Azure Container Instance (EXPERIMENTAL)\n", - "\n", - "* Create Workspace\n", - "* Create Project\n", - "* Create `train.py` in the project folder.\n", - "* Configure an ACI (Azure Container Instance) run\n", - "* Execute in ACI" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Prerequisites\n", - "Make sure you go through the [00. Installation and Configuration](00.configuration.ipynb) Notebook first if you haven't." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Check core SDK version number\n", - "import azureml.core\n", - "\n", - "print(\"SDK version:\", azureml.core.VERSION)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Initialize Workspace\n", - "\n", - "Initialize a workspace object from persisted configuration" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "tags": [ - "create workspace" - ] - }, - "outputs": [], - "source": [ - "from azureml.core import Workspace\n", - "\n", - "ws = Workspace.from_config()\n", - "print(ws.name, ws.resource_group, ws.location, ws.subscription_id, sep = '\\n')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Create An Experiment\n", - "\n", - "**Experiment** is a logical container in an Azure ML Workspace. It hosts run records which can include run metrics and output artifacts from your experiments." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "from azureml.core import Experiment\n", - "experiment_name = 'train-on-aci'\n", - "experiment = Experiment(workspace = ws, name = experiment_name)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Create a folder to store the training script." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "import os\n", - "script_folder = './samples/train-on-aci'\n", - "os.makedirs(script_folder, exist_ok = True)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Remote execution on ACI\n", - "\n", - "Use `%%writefile` magic to write training code to `train.py` file under the project folder." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "%%writefile $script_folder/train.py\n", - "\n", - "import os\n", - "from sklearn.datasets import load_diabetes\n", - "from sklearn.linear_model import Ridge\n", - "from sklearn.metrics import mean_squared_error\n", - "from sklearn.model_selection import train_test_split\n", - "from azureml.core.run import Run\n", - "from sklearn.externals import joblib\n", - "\n", - "import numpy as np\n", - "\n", - "os.makedirs('./outputs', exist_ok=True)\n", - "\n", - "X, y = load_diabetes(return_X_y = True)\n", - "\n", - "run = Run.get_submitted_run()\n", - "\n", - "X_train, X_test, y_train, y_test = train_test_split(X, y, test_size = 0.2, random_state = 0)\n", - "data = {\"train\": {\"X\": X_train, \"y\": y_train},\n", - " \"test\": {\"X\": X_test, \"y\": y_test}}\n", - "\n", - "# list of numbers from 0.0 to 1.0 with a 0.05 interval\n", - "alphas = np.arange(0.0, 1.0, 0.05)\n", - "\n", - "for alpha in alphas:\n", - " # Use Ridge algorithm to create a regression model\n", - " reg = Ridge(alpha = alpha)\n", - " reg.fit(data[\"train\"][\"X\"], data[\"train\"][\"y\"])\n", - "\n", - " preds = reg.predict(data[\"test\"][\"X\"])\n", - " mse = mean_squared_error(preds, data[\"test\"][\"y\"])\n", - " run.log('alpha', alpha)\n", - " run.log('mse', mse)\n", - " \n", - " model_file_name = 'ridge_{0:.2f}.pkl'.format(alpha)\n", - " with open(model_file_name, \"wb\") as file:\n", - " joblib.dump(value = reg, filename = 'outputs/' + model_file_name)\n", - "\n", - " print('alpha is {0:.2f}, and mse is {1:0.2f}'.format(alpha, mse))" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Configure for using ACI\n", - "Linux-based ACI is available in `westus`, `eastus`, `westeurope`, `northeurope`, `westus2` and `southeastasia` regions. See details [here](https://docs.microsoft.com/en-us/azure/container-instances/container-instances-quotas#region-availability)." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "tags": [ - "configure run" - ] - }, - "outputs": [], - "source": [ - "from azureml.core.runconfig import RunConfiguration\n", - "from azureml.core.conda_dependencies import CondaDependencies\n", - "\n", - "# create a new runconfig object\n", - "run_config = RunConfiguration()\n", - "\n", - "# signal that you want to use ACI to execute script.\n", - "run_config.target = \"containerinstance\"\n", - "\n", - "# ACI container group is only supported in certain regions, which can be different than the region the Workspace is in.\n", - "run_config.container_instance.region = 'eastus'\n", - "\n", - "# set the ACI CPU and Memory \n", - "run_config.container_instance.cpu_cores = 1\n", - "run_config.container_instance.memory_gb = 2\n", - "\n", - "# enable Docker \n", - "run_config.environment.docker.enabled = True\n", - "\n", - "# set Docker base image to the default CPU-based image\n", - "run_config.environment.docker.base_image = azureml.core.runconfig.DEFAULT_CPU_IMAGE\n", - "#run_config.environment.docker.base_image = 'microsoft/mmlspark:plus-0.9.9'\n", - "\n", - "# use conda_dependencies.yml to create a conda environment in the Docker image for execution\n", - "run_config.environment.python.user_managed_dependencies = False\n", - "\n", - "# auto-prepare the Docker image when used for execution (if it is not already prepared)\n", - "run_config.auto_prepare_environment = True\n", - "\n", - "# specify CondaDependencies obj\n", - "run_config.environment.python.conda_dependencies = CondaDependencies.create(conda_packages=['scikit-learn'])" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Submit the Experiment\n", - "Finally, run the training job on the ACI" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "tags": [ - "remote run", - "aci" - ] - }, - "outputs": [], - "source": [ - "%%time \n", - "from azureml.core.script_run_config import ScriptRunConfig\n", - "\n", - "script_run_config = ScriptRunConfig(source_directory = script_folder,\n", - " script= 'train.py',\n", - " run_config = run_config)\n", - "\n", - "run = experiment.submit(script_run_config)\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "tags": [ - "remote run", - "aci" - ] - }, - "outputs": [], - "source": [ - "%%time\n", - "# Shows output of the run on stdout.\n", - "run.wait_for_completion(show_output = True)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "tags": [ - "query history" - ] - }, - "outputs": [], - "source": [ - "# Show run details\n", - "run" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "tags": [ - "get metrics" - ] - }, - "outputs": [], - "source": [ - "# get all metris logged in the run\n", - "run.get_metrics()\n", - "metrics = run.get_metrics()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "import numpy as np\n", - "print('When alpha is {1:0.2f}, we have min MSE {0:0.2f}.'.format(\n", - " min(metrics['mse']), \n", - " metrics['alpha'][np.argmin(metrics['mse'])]\n", - "))" - ] - } - ], - "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.5" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/01.getting-started/04.train-on-remote-vm/.ipynb_checkpoints/04.train-on-remote-vm-checkpoint.ipynb b/01.getting-started/04.train-on-remote-vm/.ipynb_checkpoints/04.train-on-remote-vm-checkpoint.ipynb deleted file mode 100644 index f5fe77c5..00000000 --- a/01.getting-started/04.train-on-remote-vm/.ipynb_checkpoints/04.train-on-remote-vm-checkpoint.ipynb +++ /dev/null @@ -1,321 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Copyright (c) Microsoft Corporation. All rights reserved.\n", - "\n", - "Licensed under the MIT License." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# 04. Train in a remote VM (MLC managed DSVM)\n", - "* Create Workspace\n", - "* Create Project\n", - "* Create `train.py` file\n", - "* Create DSVM as Machine Learning Compute (MLC) resource\n", - "* Configure & execute a run in a conda environment in the default miniconda Docker container on DSVM" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Prerequisites\n", - "Make sure you go through the [00. Installation and Configuration](00.configuration.ipynb) Notebook first if you haven't." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Check core SDK version number\n", - "import azureml.core\n", - "\n", - "print(\"SDK version:\", azureml.core.VERSION)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Initialize Workspace\n", - "\n", - "Initialize a workspace object from persisted configuration." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "from azureml.core import Workspace\n", - "\n", - "ws = Workspace.from_config()\n", - "print(ws.name, ws.resource_group, ws.location, ws.subscription_id, sep = '\\n')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Create Experiment\n", - "\n", - "**Experiment** is a logical container in an Azure ML Workspace. It hosts run records which can include run metrics and output artifacts from your experiments." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "experiment_name = 'train-on-remote-vm'\n", - "\n", - "from azureml.core import Experiment\n", - "\n", - "exp = Experiment(workspace = ws, name = experiment_name)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## View `train.py`\n", - "\n", - "For convenience, we created a training script for you. It is printed below as a text, but you can also run `%pfile ./train.py` in a cell to show the file." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "with open('./train.py', 'r') as training_script:\n", - " print(training_script.read())" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Create Linux DSVM as a compute target\n", - "\n", - "**Note**: If creation fails with a message about Marketplace purchase eligibilty, go to portal.azure.com, start creating DSVM there, and select \"Want to create programmatically\" to enable programmatic creation. Once you've enabled it, you can exit without actually creating VM.\n", - " \n", - "**Note**: By default SSH runs on port 22 and you don't need to specify it. But if for security reasons you switch to a different port (such as 5022), you can append the port number to the address like the example below. [Read more](../../documentation/sdk/ssh-issue.md) on this." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "from azureml.core.compute import DsvmCompute\n", - "from azureml.core.compute_target import ComputeTargetException\n", - "\n", - "compute_target_name = 'mydsvm'\n", - "\n", - "try:\n", - " dsvm_compute = DsvmCompute(workspace = ws, name = compute_target_name)\n", - " print('found existing:', dsvm_compute.name)\n", - "except ComputeTargetException:\n", - " print('creating new.')\n", - " dsvm_config = DsvmCompute.provisioning_configuration(vm_size = \"Standard_D2_v2\")\n", - " dsvm_compute = DsvmCompute.create(ws, name = compute_target_name, provisioning_configuration = dsvm_config)\n", - " dsvm_compute.wait_for_completion(show_output = True)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Attach an existing Linux DSVM as a compute target\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "'''\n", - " from azureml.core.compute import RemoteCompute \n", - " # if you want to connect using SSH key instead of username/password you can provide parameters private_key_file and private_key_passphrase \n", - " dsvm_compute = RemoteCompute.attach(ws,name=\"attach-from-sdk6\",username=,address=,ssh_port=22,password=)\n", - "'''" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Configure & Run" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Configure a Docker run with new conda environment on the VM\n", - "You can execute in a Docker container in the VM. If you choose this route, you don't need to install anything on the VM yourself. Azure ML execution service will take care of it for you." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "from azureml.core.runconfig import RunConfiguration\n", - "from azureml.core.conda_dependencies import CondaDependencies\n", - "\n", - "\n", - "# Load the \"cpu-dsvm.runconfig\" file (created by the above attach operation) in memory\n", - "run_config = RunConfiguration(framework = \"python\")\n", - "\n", - "# Set compute target to the Linux DSVM\n", - "run_config.target = compute_target_name\n", - "\n", - "# Use Docker in the remote VM\n", - "run_config.environment.docker.enabled = True\n", - "\n", - "# Use CPU base image from DockerHub\n", - "run_config.environment.docker.base_image = azureml.core.runconfig.DEFAULT_CPU_IMAGE\n", - "print('Base Docker image is:', run_config.environment.docker.base_image)\n", - "\n", - "# Ask system to provision a new one based on the conda_dependencies.yml file\n", - "run_config.environment.python.user_managed_dependencies = False\n", - "\n", - "# Prepare the Docker and conda environment automatically when executingfor the first time.\n", - "run_config.prepare_environment = True\n", - "\n", - "# specify CondaDependencies obj\n", - "run_config.environment.python.conda_dependencies = CondaDependencies.create(conda_packages=['scikit-learn'])" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Submit the Experiment\n", - "Submit script to run in the Docker image in the remote VM. If you run this for the first time, the system will download the base image, layer in packages specified in the `conda_dependencies.yml` file on top of the base image, create a container and then execute the script in the container." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "from azureml.core import Run\n", - "from azureml.core import ScriptRunConfig\n", - "\n", - "src = ScriptRunConfig(source_directory = '.', script = 'train.py', run_config = run_config)\n", - "run = exp.submit(src)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### View run history details" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "run" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "run.wait_for_completion(show_output = True)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Find the best run" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# get all metris logged in the run\n", - "run.get_metrics()\n", - "metrics = run.get_metrics()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "import numpy as np\n", - "print('When alpha is {1:0.2f}, we have min MSE {0:0.2f}.'.format(\n", - " min(metrics['mse']), \n", - " metrics['alpha'][np.argmin(metrics['mse'])]\n", - "))" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Clean up compute resource" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "dsvm_compute.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.5" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/01.getting-started/05.train-in-spark/.ipynb_checkpoints/05.train-in-spark-checkpoint.ipynb b/01.getting-started/05.train-in-spark/.ipynb_checkpoints/05.train-in-spark-checkpoint.ipynb deleted file mode 100644 index 0eb8763f..00000000 --- a/01.getting-started/05.train-in-spark/.ipynb_checkpoints/05.train-in-spark-checkpoint.ipynb +++ /dev/null @@ -1,257 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Copyright (c) Microsoft Corporation. All rights reserved.\n", - "\n", - "Licensed under the MIT License." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# 05. Train in Spark\n", - "* Create Workspace\n", - "* Create Experiment\n", - "* Copy relevant files to the script folder\n", - "* Configure and Run" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Prerequisites\n", - "Make sure you go through the [00. Installation and Configuration](00.configuration.ipynb) Notebook first if you haven't." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Check core SDK version number\n", - "import azureml.core\n", - "\n", - "print(\"SDK version:\", azureml.core.VERSION)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Initialize Workspace\n", - "\n", - "Initialize a workspace object from persisted configuration." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "from azureml.core import Workspace\n", - "\n", - "ws = Workspace.from_config()\n", - "print(ws.name, ws.resource_group, ws.location, ws.subscription_id, sep = '\\n')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Create Experiment\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "experiment_name = 'train-on-remote-vm'\n", - "\n", - "from azureml.core import Experiment\n", - "\n", - "exp = Experiment(workspace = ws, name = experiment_name)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## View `train-spark.py`\n", - "\n", - "For convenience, we created a training script for you. It is printed below as a text, but you can also run `%pfile ./train-spark.py` in a cell to show the file." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "with open('train-spark.py', 'r') as training_script:\n", - " print(training_script.read())" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Configure & Run" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Attach an HDI cluster\n", - "To use HDI commpute target:\n", - " 1. Create an Spark for HDI cluster in Azure. Here is some [quick instructions](https://docs.microsoft.com/en-us/azure/machine-learning/desktop-workbench/how-to-create-dsvm-hdi). Make sure you use the Ubuntu flavor, NOT CentOS.\n", - " 2. Enter the IP address, username and password below" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "from azureml.core.compute import HDInsightCompute\n", - "\n", - "try:\n", - " # if you want to connect using SSH key instead of username/password you can provide parameters private_key_file and private_key_passphrase\n", - " hdi_compute_new = HDInsightCompute.attach(ws, \n", - " name=\"hdi-attach\", \n", - " address=\"hdi-ignite-demo-ssh.azurehdinsight.net\", \n", - " ssh_port=22, \n", - " username='', \n", - " password='')\n", - "\n", - "except UserErrorException as e:\n", - " print(\"Caught = {}\".format(e.message))\n", - " print(\"Compute config already attached.\")\n", - " \n", - " \n", - "hdi_compute_new.wait_for_completion(show_output=True)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Configure HDI run" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "from azureml.core.runconfig import RunConfiguration\n", - "from azureml.core.conda_dependencies import CondaDependencies\n", - "\n", - "\n", - "# Load the \"cpu-dsvm.runconfig\" file (created by the above attach operation) in memory\n", - "run_config = RunConfiguration(framework = \"python\")\n", - "\n", - "# Set compute target to the Linux DSVM\n", - "run_config.target = hdi_compute.name\n", - "\n", - "# Use Docker in the remote VM\n", - "# run_config.environment.docker.enabled = True\n", - "\n", - "# Use CPU base image from DockerHub\n", - "# run_config.environment.docker.base_image = azureml.core.runconfig.DEFAULT_CPU_IMAGE\n", - "# print('Base Docker image is:', run_config.environment.docker.base_image)\n", - "\n", - "# Ask system to provision a new one based on the conda_dependencies.yml file\n", - "run_config.environment.python.user_managed_dependencies = False\n", - "\n", - "# Prepare the Docker and conda environment automatically when executingfor the first time.\n", - "# run_config.prepare_environment = True\n", - "\n", - "# specify CondaDependencies obj\n", - "# run_config.environment.python.conda_dependencies = CondaDependencies.create(conda_packages=['scikit-learn'])\n", - "# load the runconfig object from the \"myhdi.runconfig\" file generated by the attach operaton above." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Submit the script to HDI" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "script_run_config = ScriptRunConfig(source_directory = '.',\n", - " script= 'train-spark.py',\n", - " run_config = run_config)\n", - "run = experiment.submit(script_run_config)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# get the URL of the run history web page\n", - "run" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "run.wait_for_completion(show_output = True)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# get all metris logged in the run\n", - "metrics = run.get_metrics()\n", - "print(metrics)" - ] - } - ], - "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.5" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/onnx/README.md b/onnx/README.md new file mode 100644 index 00000000..e59aa14d --- /dev/null +++ b/onnx/README.md @@ -0,0 +1,27 @@ +# ONNX on Azure Machine Learning + +These tutorials show how to create and deploy [ONNX](http://onnx.ai) models using Azure Machine Learning and the [ONNX Runtime](https://docs.microsoft.com/en-us/azure/machine-learning/service/how-to-build-deploy-onnx). +Once deployed as web services, you can ping the models with your own images to be analyzed! + +## Tutorials +- [Obtain ONNX model from ONNX Model Zoo and deploy - ResNet50](https://github.com/Azure/MachineLearningNotebooks/blob/master/onnx/onnx-modelzoo-aml-deploy-resnet50.ipynb) +- [Convert ONNX model from CoreML and deploy - TinyYOLO](https://github.com/Azure/MachineLearningNotebooks/blob/master/onnx/onnx-convert-aml-deploy-tinyyolo.ipynb) +- [Train ONNX model in PyTorch and deploy - MNIST](https://github.com/Azure/MachineLearningNotebooks/blob/master/onnx/onnx-train-pytorch-aml-deploy-mnist.ipynb) +- [Handwritten Digit Classification (MNIST) using ONNX Runtime on AzureML](https://github.com/Azure/MachineLearningNotebooks/blob/master/onnx/onnx-inference-mnist.ipynb) +- [Facial Expression Recognition using ONNX Runtime on AzureML](https://github.com/Azure/MachineLearningNotebooks/blob/master/onnx/onnx-inference-emotion-recognition.ipynb) + +## Documentation +- [ONNX Runtime Python API Documentation](http://aka.ms/onnxruntime-python) +- [Azure Machine Learning API Documentation](http://aka.ms/aml-docs) + +## Related Articles +- [Building and Deploying ONNX Runtime Models](https://docs.microsoft.com/en-us/azure/machine-learning/service/how-to-build-deploy-onnx) +- [Azure AI – Making AI Real for Business](https://aka.ms/aml-blog-overview) +- [What’s new in Azure Machine Learning](https://aka.ms/aml-blog-whats-new) + + +## License + +Copyright (c) Microsoft Corporation. All rights reserved. +Licensed under the MIT License. + diff --git a/onnx/mnist.py b/onnx/mnist.py new file mode 100644 index 00000000..a9a41853 --- /dev/null +++ b/onnx/mnist.py @@ -0,0 +1,124 @@ +# This is a modified version of https://github.com/pytorch/examples/blob/master/mnist/main.py which is +# licensed under BSD 3-Clause (https://github.com/pytorch/examples/blob/master/LICENSE) + +from __future__ import print_function +import argparse +import torch +import torch.nn as nn +import torch.nn.functional as F +import torch.optim as optim +from torchvision import datasets, transforms +import os + + +class Net(nn.Module): + def __init__(self): + super(Net, self).__init__() + self.conv1 = nn.Conv2d(1, 10, kernel_size=5) + self.conv2 = nn.Conv2d(10, 20, kernel_size=5) + self.conv2_drop = nn.Dropout2d() + self.fc1 = nn.Linear(320, 50) + self.fc2 = nn.Linear(50, 10) + + def forward(self, x): + x = F.relu(F.max_pool2d(self.conv1(x), 2)) + x = F.relu(F.max_pool2d(self.conv2_drop(self.conv2(x)), 2)) + x = x.view(-1, 320) + x = F.relu(self.fc1(x)) + x = F.dropout(x, training=self.training) + x = self.fc2(x) + return F.log_softmax(x, dim=1) + + +def train(args, model, device, train_loader, optimizer, epoch, output_dir): + model.train() + for batch_idx, (data, target) in enumerate(train_loader): + data, target = data.to(device), target.to(device) + optimizer.zero_grad() + output = model(data) + loss = F.nll_loss(output, target) + loss.backward() + optimizer.step() + if batch_idx % args.log_interval == 0: + print('Train Epoch: {} [{}/{} ({:.0f}%)]\tLoss: {:.6f}'.format( + epoch, batch_idx * len(data), len(train_loader.dataset), + 100. * batch_idx / len(train_loader), loss.item())) + + +def test(args, model, device, test_loader): + model.eval() + test_loss = 0 + correct = 0 + with torch.no_grad(): + for data, target in test_loader: + data, target = data.to(device), target.to(device) + output = model(data) + test_loss += F.nll_loss(output, target, size_average=False, reduce=True).item() # sum up batch loss + pred = output.max(1, keepdim=True)[1] # get the index of the max log-probability + correct += pred.eq(target.view_as(pred)).sum().item() + + test_loss /= len(test_loader.dataset) + print('\nTest set: Average loss: {:.4f}, Accuracy: {}/{} ({:.0f}%)\n'.format( + test_loss, correct, len(test_loader.dataset), + 100. * correct / len(test_loader.dataset))) + + +def main(): + # Training settings + parser = argparse.ArgumentParser(description='PyTorch MNIST Example') + parser.add_argument('--batch-size', type=int, default=64, metavar='N', + help='input batch size for training (default: 64)') + parser.add_argument('--test-batch-size', type=int, default=1000, metavar='N', + help='input batch size for testing (default: 1000)') + parser.add_argument('--epochs', type=int, default=10, metavar='N', + help='number of epochs to train (default: 10)') + parser.add_argument('--lr', type=float, default=0.01, metavar='LR', + help='learning rate (default: 0.01)') + parser.add_argument('--momentum', type=float, default=0.5, metavar='M', + help='SGD momentum (default: 0.5)') + parser.add_argument('--no-cuda', action='store_true', default=False, + help='disables CUDA training') + parser.add_argument('--seed', type=int, default=1, metavar='S', + help='random seed (default: 1)') + parser.add_argument('--log-interval', type=int, default=10, metavar='N', + help='how many batches to wait before logging training status') + parser.add_argument('--output-dir', type=str, default='outputs') + args = parser.parse_args() + use_cuda = not args.no_cuda and torch.cuda.is_available() + + torch.manual_seed(args.seed) + + device = torch.device("cuda" if use_cuda else "cpu") + + output_dir = args.output_dir + os.makedirs(output_dir, exist_ok=True) + + kwargs = {'num_workers': 1, 'pin_memory': True} if use_cuda else {} + train_loader = torch.utils.data.DataLoader( + datasets.MNIST('data', train=True, download=True, + transform=transforms.Compose([transforms.ToTensor(), + transforms.Normalize((0.1307,), (0.3081,))]) + ), + batch_size=args.batch_size, shuffle=True, **kwargs) + test_loader = torch.utils.data.DataLoader( + datasets.MNIST('data', train=False, + transform=transforms.Compose([transforms.ToTensor(), + transforms.Normalize((0.1307,), (0.3081,))]) + ), + batch_size=args.test_batch_size, shuffle=True, **kwargs) + + model = Net().to(device) + optimizer = optim.SGD(model.parameters(), lr=args.lr, momentum=args.momentum) + + for epoch in range(1, args.epochs + 1): + train(args, model, device, train_loader, optimizer, epoch, output_dir) + test(args, model, device, test_loader) + + # save model + dummy_input = torch.randn(1, 1, 28, 28, device=device) + model_path = os.path.join(output_dir, 'mnist.onnx') + torch.onnx.export(model, dummy_input, model_path) + + +if __name__ == '__main__': + main() diff --git a/project-brainwave/databricks/01.Installation_and_Configuration.ipynb b/project-brainwave/databricks/01.Installation_and_Configuration.ipynb deleted file mode 100644 index 166661b1..00000000 --- a/project-brainwave/databricks/01.Installation_and_Configuration.ipynb +++ /dev/null @@ -1 +0,0 @@ -{"cells":[{"cell_type":"markdown","source":["Azure ML & Azure Databricks notebooks by Parashar Shah.\n\nCopyright (c) Microsoft Corporation. All rights reserved.\n\nLicensed under the MIT License."],"metadata":{}},{"cell_type":"markdown","source":["Please ensure you have run this notebook before proceeding."],"metadata":{}},{"cell_type":"markdown","source":["Now we support installing AML SDK as library from GUI. When attaching a library follow this https://docs.databricks.com/user-guide/libraries.html and add the below string as your PyPi package (during private preview). You can select the option to attach the library to all clusters or just one cluster.\n\nProvide this full string to install the SDK:\n\nazureml-sdk[databricks]"],"metadata":{}},{"cell_type":"code","source":["import azureml.core\n\n# Check core SDK version number - based on build number of preview/master.\nprint(\"SDK version:\", azureml.core.VERSION)"],"metadata":{},"outputs":[],"execution_count":4},{"cell_type":"code","source":["subscription_id = \"\"\nresource_group = \"\"\nworkspace_name = \"\"\nworkspace_region = \"\""],"metadata":{},"outputs":[],"execution_count":5},{"cell_type":"code","source":["# import the Workspace class and check the azureml SDK version\n# exist_ok checks if workspace exists or not.\n\nfrom azureml.core import Workspace\n\nws = Workspace.create(name = workspace_name,\n subscription_id = subscription_id,\n resource_group = resource_group, \n location = workspace_region,\n exist_ok=True)\n\nws.get_details()"],"metadata":{},"outputs":[],"execution_count":6},{"cell_type":"code","source":["ws = Workspace(workspace_name = workspace_name,\n subscription_id = subscription_id,\n resource_group = resource_group)\n\n# persist the subscription id, resource group name, and workspace name in aml_config/config.json.\nws.write_config()"],"metadata":{},"outputs":[],"execution_count":7},{"cell_type":"code","source":["%sh\ncat /databricks/driver/aml_config/config.json"],"metadata":{},"outputs":[],"execution_count":8},{"cell_type":"code","source":["# import the Workspace class and check the azureml SDK version\nfrom azureml.core import Workspace\n\nws = Workspace.from_config()\nprint('Workspace name: ' + ws.name, \n 'Azure region: ' + ws.location, \n 'Subscription id: ' + ws.subscription_id, \n 'Resource group: ' + ws.resource_group, sep = '\\n')"],"metadata":{},"outputs":[],"execution_count":9},{"cell_type":"code","source":["dbutils.notebook.exit(\"success\")"],"metadata":{},"outputs":[],"execution_count":10},{"cell_type":"code","source":[""],"metadata":{},"outputs":[],"execution_count":11}],"metadata":{"name":"01.Installation_and_Configuration","notebookId":3874566296719377},"nbformat":4,"nbformat_minor":0} diff --git a/project-brainwave/databricks/02.Ingest_data.ipynb b/project-brainwave/databricks/02.Ingest_data.ipynb deleted file mode 100644 index b8075a4c..00000000 --- a/project-brainwave/databricks/02.Ingest_data.ipynb +++ /dev/null @@ -1 +0,0 @@ -{"cells":[{"cell_type":"markdown","source":["Azure ML & Azure Databricks notebooks by Parashar Shah.\n\nCopyright (c) Microsoft Corporation. All rights reserved.\n\nLicensed under the MIT License."],"metadata":{}},{"cell_type":"markdown","source":["Please ensure you have run all previous notebooks in sequence before running this."],"metadata":{}},{"cell_type":"markdown","source":["#Data Ingestion"],"metadata":{}},{"cell_type":"code","source":["import os\nimport urllib"],"metadata":{},"outputs":[],"execution_count":4},{"cell_type":"code","source":["# Download AdultCensusIncome.csv from Azure CDN. This file has 32,561 rows.\nbasedataurl = \"https://amldockerdatasets.azureedge.net\"\ndatafile = \"AdultCensusIncome.csv\"\ndatafile_dbfs = os.path.join(\"/dbfs\", datafile)\n\nif os.path.isfile(datafile_dbfs):\n print(\"found {} at {}\".format(datafile, datafile_dbfs))\nelse:\n print(\"downloading {} to {}\".format(datafile, datafile_dbfs))\n urllib.request.urlretrieve(os.path.join(basedataurl, datafile), datafile_dbfs)"],"metadata":{},"outputs":[],"execution_count":5},{"cell_type":"code","source":["# Create a Spark dataframe out of the csv file.\ndata_all = sqlContext.read.format('csv').options(header='true', inferSchema='true', ignoreLeadingWhiteSpace='true', ignoreTrailingWhiteSpace='true').load(datafile)\nprint(\"({}, {})\".format(data_all.count(), len(data_all.columns)))\ndata_all.printSchema()"],"metadata":{},"outputs":[],"execution_count":6},{"cell_type":"code","source":["#renaming columns\ncolumns_new = [col.replace(\"-\", \"_\") for col in data_all.columns]\ndata_all = data_all.toDF(*columns_new)\ndata_all.printSchema()"],"metadata":{},"outputs":[],"execution_count":7},{"cell_type":"code","source":["display(data_all.limit(5))"],"metadata":{},"outputs":[],"execution_count":8},{"cell_type":"markdown","source":["#Data Preparation"],"metadata":{}},{"cell_type":"code","source":["# Choose feature columns and the label column.\nlabel = \"income\"\nxvals_all = set(data_all.columns) - {label}\n\n#dbutils.widgets.remove(\"xvars_multiselect\")\ndbutils.widgets.removeAll()\n\ndbutils.widgets.multiselect('xvars_multiselect', 'hours_per_week', xvals_all)\nxvars_multiselect = dbutils.widgets.get(\"xvars_multiselect\")\nxvars = xvars_multiselect.split(\",\")\n\nprint(\"label = {}\".format(label))\nprint(\"features = {}\".format(xvars))\n\ndata = data_all.select([*xvars, label])\n\n# Split data into train and test.\ntrain, test = data.randomSplit([0.75, 0.25], seed=123)\n\nprint(\"train ({}, {})\".format(train.count(), len(train.columns)))\nprint(\"test ({}, {})\".format(test.count(), len(test.columns)))"],"metadata":{},"outputs":[],"execution_count":10},{"cell_type":"markdown","source":["#Data Persistence"],"metadata":{}},{"cell_type":"code","source":["# Write the train and test data sets to intermediate storage\ntrain_data_path = \"AdultCensusIncomeTrain\"\ntest_data_path = \"AdultCensusIncomeTest\"\n\ntrain_data_path_dbfs = os.path.join(\"/dbfs\", \"AdultCensusIncomeTrain\")\ntest_data_path_dbfs = os.path.join(\"/dbfs\", \"AdultCensusIncomeTest\")\n\ntrain.write.mode('overwrite').parquet(train_data_path)\ntest.write.mode('overwrite').parquet(test_data_path)\nprint(\"train and test datasets saved to {} and {}\".format(train_data_path_dbfs, test_data_path_dbfs))"],"metadata":{},"outputs":[],"execution_count":12},{"cell_type":"code","source":["dbutils.notebook.exit(\"success\")"],"metadata":{},"outputs":[],"execution_count":13},{"cell_type":"code","source":[""],"metadata":{},"outputs":[],"execution_count":14}],"metadata":{"name":"02.Ingest_data","notebookId":3874566296719393},"nbformat":4,"nbformat_minor":0} diff --git a/project-brainwave/databricks/03a.Build_model.ipynb b/project-brainwave/databricks/03a.Build_model.ipynb deleted file mode 100644 index 15da2ac9..00000000 --- a/project-brainwave/databricks/03a.Build_model.ipynb +++ /dev/null @@ -1 +0,0 @@ -{"cells":[{"cell_type":"markdown","source":["Azure ML & Azure Databricks notebooks by Parashar Shah.\n\nCopyright (c) Microsoft Corporation. All rights reserved.\n\nLicensed under the MIT License."],"metadata":{}},{"cell_type":"markdown","source":["Please ensure you have run all previous notebooks in sequence before running this."],"metadata":{}},{"cell_type":"markdown","source":["#Model Building"],"metadata":{}},{"cell_type":"code","source":["import os\nimport pprint\nimport numpy as np\n\nfrom pyspark.ml import Pipeline, PipelineModel\nfrom pyspark.ml.feature import OneHotEncoder, StringIndexer, VectorAssembler\nfrom pyspark.ml.classification import LogisticRegression\nfrom pyspark.ml.evaluation import BinaryClassificationEvaluator\nfrom pyspark.ml.tuning import CrossValidator, ParamGridBuilder"],"metadata":{},"outputs":[],"execution_count":4},{"cell_type":"code","source":["#get the train and test datasets\ntrain_data_path = \"AdultCensusIncomeTrain\"\ntest_data_path = \"AdultCensusIncomeTest\"\n\ntrain = spark.read.parquet(train_data_path)\ntest = spark.read.parquet(test_data_path)\n\nprint(\"train: ({}, {})\".format(train.count(), len(train.columns)))\nprint(\"test: ({}, {})\".format(test.count(), len(test.columns)))\n\ntrain.printSchema()"],"metadata":{},"outputs":[],"execution_count":5},{"cell_type":"markdown","source":["#Define ML Pipeline"],"metadata":{}},{"cell_type":"code","source":["label = \"income\"\n\nreg = 0.1\nprint(\"Regularization Rate is {}.\".format(reg))\n\n# create a new Logistic Regression model.\nlr = LogisticRegression(regParam=reg)\n\ndtypes = dict(train.dtypes)\ndtypes.pop(label)\n\nsi_xvars = []\nohe_xvars = []\nfeatureCols = []\nfor idx,key in enumerate(dtypes):\n if dtypes[key] == \"string\":\n featureCol = \"-\".join([key, \"encoded\"])\n featureCols.append(featureCol)\n \n tmpCol = \"-\".join([key, \"tmp\"])\n # string-index and one-hot encode the string column\n #https://spark.apache.org/docs/2.3.0/api/java/org/apache/spark/ml/feature/StringIndexer.html\n #handleInvalid: Param for how to handle invalid data (unseen labels or NULL values). \n #Options are 'skip' (filter out rows with invalid data), 'error' (throw an error), \n #or 'keep' (put invalid data in a special additional bucket, at index numLabels). Default: \"error\"\n si_xvars.append(StringIndexer(inputCol=key, outputCol=tmpCol, handleInvalid=\"skip\")) #, handleInvalid=\"keep\"\n ohe_xvars.append(OneHotEncoder(inputCol=tmpCol, outputCol=featureCol))\n else:\n featureCols.append(key)\n\n# string-index the label column into a column named \"label\"\nsi_label = StringIndexer(inputCol=label, outputCol='label')\n\n# assemble the encoded feature columns in to a column named \"features\"\nassembler = VectorAssembler(inputCols=featureCols, outputCol=\"features\")\n\n# put together the pipeline\npipe = Pipeline(stages=[*si_xvars, *ohe_xvars, si_label, assembler, lr])\n\n# train the model\nmodel = pipe.fit(train)\nprint(model)"],"metadata":{},"outputs":[],"execution_count":7},{"cell_type":"markdown","source":["#Tune ML Pipeline"],"metadata":{}},{"cell_type":"code","source":["regs = np.arange(0.0, 1.0, 0.2)\n\nparamGrid = ParamGridBuilder().addGrid(lr.regParam, regs).build()\ncv = CrossValidator(estimator=pipe, evaluator=BinaryClassificationEvaluator(), estimatorParamMaps=paramGrid)"],"metadata":{},"outputs":[],"execution_count":9},{"cell_type":"code","source":["cvModel = cv.fit(train)"],"metadata":{},"outputs":[],"execution_count":10},{"cell_type":"code","source":["model = cvModel.bestModel"],"metadata":{},"outputs":[],"execution_count":11},{"cell_type":"markdown","source":["#Model Evaluation"],"metadata":{}},{"cell_type":"code","source":["# make prediction\npred = model.transform(test)\noutput = pred[['hours_per_week','age','workclass','marital_status','income','prediction']]\ndisplay(output.limit(5))"],"metadata":{},"outputs":[],"execution_count":13},{"cell_type":"code","source":["# evaluate. note only 2 metrics are supported out of the box by Spark ML.\nbce = BinaryClassificationEvaluator(rawPredictionCol='rawPrediction')\nau_roc = bce.setMetricName('areaUnderROC').evaluate(pred)\nau_prc = bce.setMetricName('areaUnderPR').evaluate(pred)\n\nprint(\"Area under ROC: {}\".format(au_roc))\nprint(\"Area Under PR: {}\".format(au_prc))"],"metadata":{},"outputs":[],"execution_count":14},{"cell_type":"markdown","source":["#Model Persistence"],"metadata":{}},{"cell_type":"code","source":["##NOTE: by default the model is saved to and loaded from /dbfs/ instead of cwd!\nmodel_name = \"AdultCensus.mml\"\nmodel_dbfs = os.path.join(\"/dbfs\", model_name)\n\nmodel.write().overwrite().save(model_name)\nprint(\"saved model to {}\".format(model_dbfs))"],"metadata":{},"outputs":[],"execution_count":16},{"cell_type":"code","source":["%sh\n\nls -la /dbfs/AdultCensus.mml/*"],"metadata":{},"outputs":[],"execution_count":17},{"cell_type":"code","source":["dbutils.notebook.exit(\"success\")"],"metadata":{},"outputs":[],"execution_count":18}],"metadata":{"name":"03a.Build_model","notebookId":3874566296719409},"nbformat":4,"nbformat_minor":0} diff --git a/project-brainwave/databricks/03b.Build_model_runHistory.ipynb b/project-brainwave/databricks/03b.Build_model_runHistory.ipynb deleted file mode 100644 index 6bf308e7..00000000 --- a/project-brainwave/databricks/03b.Build_model_runHistory.ipynb +++ /dev/null @@ -1 +0,0 @@ -{"cells":[{"cell_type":"markdown","source":["Azure ML & Azure Databricks notebooks by Parashar Shah.\n\nCopyright (c) Microsoft Corporation. All rights reserved.\n\nLicensed under the MIT License."],"metadata":{}},{"cell_type":"markdown","source":["Please ensure you have run all previous notebooks in sequence before running this."],"metadata":{}},{"cell_type":"markdown","source":["#Model Building"],"metadata":{}},{"cell_type":"code","source":["import os\nimport pprint\nimport numpy as np\n\nfrom pyspark.ml import Pipeline, PipelineModel\nfrom pyspark.ml.feature import OneHotEncoder, StringIndexer, VectorAssembler\nfrom pyspark.ml.classification import LogisticRegression\nfrom pyspark.ml.evaluation import BinaryClassificationEvaluator\nfrom pyspark.ml.tuning import CrossValidator, ParamGridBuilder"],"metadata":{},"outputs":[],"execution_count":4},{"cell_type":"code","source":["import azureml.core\n\n# Check core SDK version number\nprint(\"SDK version:\", azureml.core.VERSION)"],"metadata":{},"outputs":[],"execution_count":5},{"cell_type":"code","source":["# import the Workspace class and check the azureml SDK version\nfrom azureml.core import Workspace\n\nws = Workspace.from_config()\nprint('Workspace name: ' + ws.name, \n 'Azure region: ' + ws.location, \n 'Subscription id: ' + ws.subscription_id, \n 'Resource group: ' + ws.resource_group, sep = '\\n')"],"metadata":{},"outputs":[],"execution_count":6},{"cell_type":"code","source":["#get the train and test datasets\ntrain_data_path = \"AdultCensusIncomeTrain\"\ntest_data_path = \"AdultCensusIncomeTest\"\n\ntrain = spark.read.parquet(train_data_path)\ntest = spark.read.parquet(test_data_path)\n\nprint(\"train: ({}, {})\".format(train.count(), len(train.columns)))\nprint(\"test: ({}, {})\".format(test.count(), len(test.columns)))\n\ntrain.printSchema()"],"metadata":{},"outputs":[],"execution_count":7},{"cell_type":"markdown","source":["#Define ML Pipeline"],"metadata":{}},{"cell_type":"code","source":["label = \"income\"\ndtypes = dict(train.dtypes)\ndtypes.pop(label)\n\nsi_xvars = []\nohe_xvars = []\nfeatureCols = []\nfor idx,key in enumerate(dtypes):\n if dtypes[key] == \"string\":\n featureCol = \"-\".join([key, \"encoded\"])\n featureCols.append(featureCol)\n \n tmpCol = \"-\".join([key, \"tmp\"])\n # string-index and one-hot encode the string column\n #https://spark.apache.org/docs/2.3.0/api/java/org/apache/spark/ml/feature/StringIndexer.html\n #handleInvalid: Param for how to handle invalid data (unseen labels or NULL values). \n #Options are 'skip' (filter out rows with invalid data), 'error' (throw an error), \n #or 'keep' (put invalid data in a special additional bucket, at index numLabels). Default: \"error\"\n si_xvars.append(StringIndexer(inputCol=key, outputCol=tmpCol, handleInvalid=\"skip\"))\n ohe_xvars.append(OneHotEncoder(inputCol=tmpCol, outputCol=featureCol))\n else:\n featureCols.append(key)\n\n# string-index the label column into a column named \"label\"\nsi_label = StringIndexer(inputCol=label, outputCol='label')\n\n# assemble the encoded feature columns in to a column named \"features\"\nassembler = VectorAssembler(inputCols=featureCols, outputCol=\"features\")"],"metadata":{},"outputs":[],"execution_count":9},{"cell_type":"code","source":["from azureml.core.run import Run\nfrom azureml.core.experiment import Experiment\nimport numpy as np\nimport os\nimport shutil\n\nmodel_name = \"AdultCensus_runHistory.mml\"\nmodel_dbfs = os.path.join(\"/dbfs\", model_name)\nrun_history_name = 'spark-ml-notebook'\n\n# start a training run by defining an experiment\nmyexperiment = Experiment(ws, \"Azure_Databricks_Experiment\")\nroot_run = myexperiment.start_logging()\n\n# Regularization Rates\nregs = np.arange(0.0, 1.0, 0.2)\n\n# try a bunch of alpha values in a Linear Regression (Ridge) model\nfor reg in regs:\n print(\"Regularization rate: {}\".format(reg))\n # create a bunch of child runs\n with root_run.child_run(\"reg-\" + str(reg)) as run:\n # create a new Logistic Regression model.\n lr = LogisticRegression(regParam=reg)\n \n # put together the pipeline\n pipe = Pipeline(stages=[*si_xvars, *ohe_xvars, si_label, assembler, lr])\n\n # train the model\n model_pipeline = pipe.fit(train)\n \n # make prediction\n pred = model_pipeline.transform(test)\n \n # evaluate. note only 2 metrics are supported out of the box by Spark ML.\n bce = BinaryClassificationEvaluator(rawPredictionCol='rawPrediction')\n au_roc = bce.setMetricName('areaUnderROC').evaluate(pred)\n au_prc = bce.setMetricName('areaUnderPR').evaluate(pred)\n\n print(\"Area under ROC: {}\".format(au_roc))\n print(\"Area Under PR: {}\".format(au_prc))\n \n # log reg, au_roc, au_prc and feature names in run history\n run.log(\"reg\", reg)\n run.log(\"au_roc\", au_roc)\n run.log(\"au_prc\", au_prc)\n run.log_list(\"columns\", train.columns)\n\n # save model\n model_pipeline.write().overwrite().save(model_name)\n \n # upload the serialized model into run history record\n mdl, ext = model_name.split(\".\")\n model_zip = mdl + \".zip\"\n shutil.make_archive(mdl, 'zip', model_dbfs)\n run.upload_file(\"outputs/\" + model_name, model_zip) \n #run.upload_file(\"outputs/\" + model_name, path_or_stream = model_dbfs) #cannot deal with folders\n\n # now delete the serialized model from local folder since it is already uploaded to run history \n shutil.rmtree(model_dbfs)\n os.remove(model_zip)\n \n# Declare run completed\nroot_run.complete()\nroot_run_id = root_run.id\nprint (\"run id:\", root_run.id)"],"metadata":{},"outputs":[],"execution_count":10},{"cell_type":"code","source":["#Load all run metrics from run history into a dictionary object.\nchild_runs = {}\nchild_run_metrics = {}\n\nfor r in root_run.get_children():\n child_runs[r.id] = r\n child_run_metrics[r.id] = r.get_metrics()"],"metadata":{},"outputs":[],"execution_count":11},{"cell_type":"code","source":["best_run_id = max(child_run_metrics, key = lambda k: child_run_metrics[k]['au_roc'])\nbest_run = child_runs[best_run_id]\nprint('Best run is:', best_run_id)\nprint('Metrics:', child_run_metrics[best_run_id])"],"metadata":{},"outputs":[],"execution_count":12},{"cell_type":"code","source":["best_reg = child_run_metrics[best_run_id]['reg']\nmax_auc = child_run_metrics[best_run_id]['au_roc']\n\nreg_auc = np.array([(child_run_metrics[k]['reg'], child_run_metrics[k]['au_roc']) for k in child_run_metrics.keys()])\nreg_auc_sorted = reg_auc[reg_auc[:,0].argsort()]\n\nimport pandas as pd\ndf = pd.DataFrame(reg_auc_sorted)\nspdf = spark.createDataFrame(df)\ndisplay(spdf)"],"metadata":{},"outputs":[],"execution_count":13},{"cell_type":"code","source":["#Download the model from the best run to a local folder\nbest_model_file_name = \"best_model.zip\"\nbest_run.download_file(name = 'outputs/' + model_name, output_file_path = best_model_file_name)"],"metadata":{},"outputs":[],"execution_count":14},{"cell_type":"markdown","source":["#Model Evaluation"],"metadata":{}},{"cell_type":"code","source":["##unzip the model to dbfs (as load() seems to require that) and load it.\nif os.path.isfile(model_dbfs) or os.path.isdir(model_dbfs):\n shutil.rmtree(model_dbfs)\nshutil.unpack_archive(best_model_file_name, model_dbfs)\n\nmodel_pipeline_best = PipelineModel.load(model_name)"],"metadata":{},"outputs":[],"execution_count":16},{"cell_type":"code","source":["# make prediction\npred = model_pipeline_best.transform(test)\noutput = pred[['hours_per_week','age','workclass','marital_status','income','prediction']]\ndisplay(output.limit(5))"],"metadata":{},"outputs":[],"execution_count":17},{"cell_type":"code","source":["# evaluate. note only 2 metrics are supported out of the box by Spark ML.\nbce = BinaryClassificationEvaluator(rawPredictionCol='rawPrediction')\nau_roc = bce.setMetricName('areaUnderROC').evaluate(pred)\nau_prc = bce.setMetricName('areaUnderPR').evaluate(pred)\n\nprint(\"Area under ROC: {}\".format(au_roc))\nprint(\"Area Under PR: {}\".format(au_prc))"],"metadata":{},"outputs":[],"execution_count":18},{"cell_type":"markdown","source":["#Model Persistence"],"metadata":{}},{"cell_type":"code","source":["##NOTE: by default the model is saved to and loaded from /dbfs/ instead of cwd!\nmodel_pipeline_best.write().overwrite().save(model_name)\nprint(\"saved model to {}\".format(model_dbfs))"],"metadata":{},"outputs":[],"execution_count":20},{"cell_type":"code","source":["%sh\n\nls -la /dbfs/AdultCensus_runHistory.mml/*"],"metadata":{},"outputs":[],"execution_count":21},{"cell_type":"code","source":["dbutils.notebook.exit(\"success\")"],"metadata":{},"outputs":[],"execution_count":22}],"metadata":{"name":"03b.Build_model_runHistory","notebookId":3874566296719353},"nbformat":4,"nbformat_minor":0} diff --git a/project-brainwave/databricks/04.Deploy_to_ACI.ipynb b/project-brainwave/databricks/04.Deploy_to_ACI.ipynb deleted file mode 100644 index a2c04a19..00000000 --- a/project-brainwave/databricks/04.Deploy_to_ACI.ipynb +++ /dev/null @@ -1 +0,0 @@ -{"cells":[{"cell_type":"markdown","source":["Azure ML & Azure Databricks notebooks by Parashar Shah.\n\nCopyright (c) Microsoft Corporation. All rights reserved.\n\nLicensed under the MIT License."],"metadata":{}},{"cell_type":"markdown","source":["Please ensure you have run all previous notebooks in sequence before running this."],"metadata":{}},{"cell_type":"markdown","source":["Please Register Azure Container Instance(ACI) using Azure Portal: https://docs.microsoft.com/en-us/azure/azure-resource-manager/resource-manager-supported-services#portal in your subscription before using the SDK to deploy your ML model to ACI."],"metadata":{}},{"cell_type":"code","source":["from azureml.core import Workspace\nimport azureml.core\n\n# Check core SDK version number\nprint(\"SDK version:\", azureml.core.VERSION)\n\n#'''\nws = Workspace.from_config()\nprint('Workspace name: ' + ws.name, \n 'Azure region: ' + ws.location, \n 'Subscription id: ' + ws.subscription_id, \n 'Resource group: ' + ws.resource_group, sep = '\\n')\n#'''"],"metadata":{},"outputs":[],"execution_count":4},{"cell_type":"code","source":["##NOTE: service deployment always gets the model from the current working dir.\nimport os\n\nmodel_name = \"AdultCensus.mml\" # OR AdultCensus_runHistory.mml\nmodel_name_dbfs = os.path.join(\"/dbfs\", model_name)\n\nprint(\"copy model from dbfs to local\")\nmodel_local = \"file:\" + os.getcwd() + \"/\" + model_name\ndbutils.fs.cp(model_name, model_local, True)"],"metadata":{},"outputs":[],"execution_count":5},{"cell_type":"code","source":["#Register the model\nfrom azureml.core.model import Model\nmymodel = Model.register(model_path = model_name, # this points to a local file\n model_name = model_name, # this is the name the model is registered as, am using same name for both path and name. \n description = \"ADB trained model by Parashar\",\n workspace = ws)\n\nprint(mymodel.name, mymodel.description, mymodel.version)"],"metadata":{},"outputs":[],"execution_count":6},{"cell_type":"code","source":["#%%writefile score_sparkml.py\nscore_sparkml = \"\"\"\n\nimport json\n\ndef init():\n try:\n # One-time initialization of PySpark and predictive model\n import pyspark\n from azureml.core.model import Model\n from pyspark.ml import PipelineModel\n \n global trainedModel\n global spark\n \n spark = pyspark.sql.SparkSession.builder.appName(\"ADB and AML notebook by Parashar\").getOrCreate()\n model_name = \"{model_name}\" #interpolated\n model_path = Model.get_model_path(model_name)\n trainedModel = PipelineModel.load(model_path)\n except Exception as e:\n trainedModel = e\n \ndef run(input_json):\n if isinstance(trainedModel, Exception):\n return json.dumps({{\"trainedModel\":str(trainedModel)}})\n \n try:\n sc = spark.sparkContext\n input_list = json.loads(input_json)\n input_rdd = sc.parallelize(input_list)\n input_df = spark.read.json(input_rdd)\n \n # Compute prediction\n prediction = trainedModel.transform(input_df)\n #result = prediction.first().prediction\n predictions = prediction.collect()\n\n #Get each scored result\n preds = [str(x['prediction']) for x in predictions]\n result = \",\".join(preds)\n except Exception as e:\n result = str(e)\n return json.dumps({{\"result\":result}})\n \n\"\"\".format(model_name=model_name)\n\nexec(score_sparkml)\n\nwith open(\"score_sparkml.py\", \"w\") as file:\n file.write(score_sparkml)"],"metadata":{},"outputs":[],"execution_count":7},{"cell_type":"code","source":["from azureml.core.conda_dependencies import CondaDependencies \n\nmyacienv = CondaDependencies.create(conda_packages=['scikit-learn','numpy','pandas'])\n\nwith open(\"mydeployenv.yml\",\"w\") as f:\n f.write(myacienv.serialize_to_string())"],"metadata":{},"outputs":[],"execution_count":8},{"cell_type":"code","source":["#deploy to ACI\nfrom azureml.core.webservice import AciWebservice, Webservice\n\nmyaci_config = AciWebservice.deploy_configuration(\n cpu_cores = 1, \n memory_gb = 1, \n tags = {'name':'Databricks Azure ML ACI'}, \n description = 'This is for ADB and AML example. Azure Databricks & Azure ML SDK demo with ACI by Parashar.')"],"metadata":{},"outputs":[],"execution_count":9},{"cell_type":"code","source":["# this will take 10-15 minutes to finish\n\nservice_name = \"aciws\"\nruntime = \"spark-py\" \ndriver_file = \"score_sparkml.py\"\nmy_conda_file = \"mydeployenv.yml\"\n\n# image creation\nfrom azureml.core.image import ContainerImage\nmyimage_config = ContainerImage.image_configuration(execution_script = driver_file, \n runtime = runtime, \n conda_file = my_conda_file)\n\n# Webservice creation\nmyservice = Webservice.deploy_from_model(\n workspace=ws, \n name=service_name,\n deployment_config = myaci_config,\n models = [mymodel],\n image_config = myimage_config\n )\n\nmyservice.wait_for_deployment(show_output=True)"],"metadata":{},"outputs":[],"execution_count":10},{"cell_type":"code","source":["help(ContainerImage)"],"metadata":{},"outputs":[],"execution_count":11},{"cell_type":"code","source":["# List images by ws\n\nfor i in ContainerImage.list(workspace = ws):\n print('{}(v.{} [{}]) stored at {} with build log {}'.format(i.name, i.version, i.creation_state, i.image_location, i.image_build_log_uri))"],"metadata":{},"outputs":[],"execution_count":12},{"cell_type":"code","source":["#for using the Web HTTP API \nprint(myservice.scoring_uri)"],"metadata":{},"outputs":[],"execution_count":13},{"cell_type":"code","source":["import json\n\n#get the some sample data\ntest_data_path = \"AdultCensusIncomeTest\"\ntest = spark.read.parquet(test_data_path).limit(5)\n\ntest_json = json.dumps(test.toJSON().collect())\n\nprint(test_json)"],"metadata":{},"outputs":[],"execution_count":14},{"cell_type":"code","source":["#using data defined above predict if income is >50K (1) or <=50K (0)\nmyservice.run(input_data=test_json)"],"metadata":{},"outputs":[],"execution_count":15},{"cell_type":"code","source":["#comment to not delete the web service\nmyservice.delete()"],"metadata":{},"outputs":[],"execution_count":16},{"cell_type":"code","source":[""],"metadata":{},"outputs":[],"execution_count":17}],"metadata":{"name":"04.DeploytoACI","notebookId":3874566296719333},"nbformat":4,"nbformat_minor":0} diff --git a/project-brainwave/databricks/05.Deploy_to_AKS_existingImage.ipynb b/project-brainwave/databricks/05.Deploy_to_AKS_existingImage.ipynb deleted file mode 100644 index e6a08783..00000000 --- a/project-brainwave/databricks/05.Deploy_to_AKS_existingImage.ipynb +++ /dev/null @@ -1 +0,0 @@ -{"cells":[{"cell_type":"markdown","source":["Azure ML & Azure Databricks notebooks by Parashar Shah.\n\nCopyright (c) Microsoft Corporation. All rights reserved.\n\nLicensed under the MIT License."],"metadata":{}},{"cell_type":"markdown","source":["Please ensure you have run all previous notebooks in sequence before running this. This notebook uses image from ACI notebook for deploying to AKS."],"metadata":{}},{"cell_type":"code","source":["from azureml.core import Workspace\nimport azureml.core\n\n# Check core SDK version number\nprint(\"SDK version:\", azureml.core.VERSION)\n\n#'''\nws = Workspace.from_config()\nprint('Workspace name: ' + ws.name, \n 'Azure region: ' + ws.location, \n 'Subscription id: ' + ws.subscription_id, \n 'Resource group: ' + ws.resource_group, sep = '\\n')\n#'''"],"metadata":{},"outputs":[],"execution_count":3},{"cell_type":"code","source":["# List images by ws\n\nfrom azureml.core.image import ContainerImage\nfor i in ContainerImage.list(workspace = ws):\n print('{}(v.{} [{}]) stored at {} with build log {}'.format(i.name, i.version, i.creation_state, i.image_location, i.image_build_log_uri))"],"metadata":{},"outputs":[],"execution_count":4},{"cell_type":"code","source":["from azureml.core.image import Image\nmyimage = Image(workspace=ws, id=\"aciws:25\")"],"metadata":{},"outputs":[],"execution_count":5},{"cell_type":"code","source":["#create AKS compute\n#it may take 20-25 minutes to create a new cluster\n\nfrom azureml.core.compute import AksCompute, ComputeTarget\n\n# Use the default configuration (can also provide parameters to customize)\nprov_config = AksCompute.provisioning_configuration()\n\naks_name = 'ps-aks-clus2' \n\n# Create the cluster\naks_target = ComputeTarget.create(workspace = ws, \n name = aks_name, \n provisioning_configuration = prov_config)\n\naks_target.wait_for_completion(show_output = True)\n\nprint(aks_target.provisioning_state)\nprint(aks_target.provisioning_errors)"],"metadata":{},"outputs":[],"execution_count":6},{"cell_type":"code","source":["from azureml.core.webservice import Webservice\nhelp( Webservice.deploy_from_image)"],"metadata":{},"outputs":[],"execution_count":7},{"cell_type":"code","source":["from azureml.core.webservice import Webservice, AksWebservice\nfrom azureml.core.image import ContainerImage\n\n#Set the web service configuration (using default here)\naks_config = AksWebservice.deploy_configuration()\n\n#unique service name\nservice_name ='ps-aks-service'\n\n# Webservice creation using single command, there is a variant to use image directly as well.\naks_service = Webservice.deploy_from_image(\n workspace=ws, \n name=service_name,\n deployment_config = aks_config,\n image = myimage,\n deployment_target = aks_target\n )\n\naks_service.wait_for_deployment(show_output=True)"],"metadata":{},"outputs":[],"execution_count":8},{"cell_type":"code","source":["#for using the Web HTTP API \nprint(aks_service.scoring_uri)\nprint(aks_service.get_keys())"],"metadata":{},"outputs":[],"execution_count":9},{"cell_type":"code","source":["import json\n\n#get the some sample data\ntest_data_path = \"AdultCensusIncomeTest\"\ntest = spark.read.parquet(test_data_path).limit(5)\n\ntest_json = json.dumps(test.toJSON().collect())\n\nprint(test_json)"],"metadata":{},"outputs":[],"execution_count":10},{"cell_type":"code","source":["#using data defined above predict if income is >50K (1) or <=50K (0)\naks_service.run(input_data=test_json)"],"metadata":{},"outputs":[],"execution_count":11},{"cell_type":"code","source":["#comment to not delete the web service\naks_service.delete()\n#image.delete()\n#model.delete()\n#aks_target.delete()"],"metadata":{},"outputs":[],"execution_count":12},{"cell_type":"code","source":[""],"metadata":{},"outputs":[],"execution_count":13}],"metadata":{"name":"04.DeploytoACI","notebookId":3874566296719318},"nbformat":4,"nbformat_minor":0} diff --git a/project-brainwave/databricks/Databricks_AMLSDK_github.dbc b/project-brainwave/databricks/Databricks_AMLSDK_github.dbc deleted file mode 100644 index fdbad0493a713503064fd1ff20dc65c25eeeb87b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 42332 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 diff --git a/project-brainwave/databricks/readme.md b/project-brainwave/databricks/readme.md deleted file mode 100644 index 005a0b41..00000000 --- a/project-brainwave/databricks/readme.md +++ /dev/null @@ -1,26 +0,0 @@ -# Azure Databricks - Azure ML SDK Sample Notebooks - -**NOTE**: With the latest version of our AML SDK, there are some API changes due to which previous version of notebooks will not work. -Kindly use this v4 notebooks (updated Sep 18)– if you had installed the AML SDK in your Databricks cluster please update to latest SDK version by installing azureml-sdk[databricks] as a library from GUI. - -**NOTE**: Please create your Azure Databricks cluster as v4.x (high concurrency preferred) with **Python 3** (dropdown). We are extending it to more runtimes asap. - -**NOTE**: Some packages like psutil upgrade libs that can cause a conflict, please install such packages by freezing lib version. Eg. "pstuil **cryptography==1.5 pyopenssl==16.0.0 ipython=2.2.0**" to avoid install error. This issue is related to Databricks and not related to AML SDK. - -**NOTE**: You should at least have contributor access to your Azure subcription to run some of the notebooks. - -The iPython Notebooks have to be run sequentially after making changes based on your subscription. The corresponding DBC archive contains all the notebooks and can be imported into your Databricks workspace. You can the run notebooks after importing .dbc instead of downloading individually. - -This set of notebooks are related to Income prediction experiment based on this [dataset](https://archive.ics.uci.edu/ml/datasets/adult) and demonstrate how to data prep, train and operationalize a Spark ML model with Azure ML Python SDK from within Azure Databricks. For details on SDK concepts, please refer to [Private preview notebooks](https://github.com/Azure/ViennaDocs/tree/master/PrivatePreview/notebooks) - -(Recommended) [Azure Databricks AML SDK notebooks](Databricks_AMLSDK_github.dbc) A single DBC package to import all notebooks in your Databricks workspace. - -01. [Installation and Configuration](01.Installation_and_Configuration.ipynb): Install the Azure ML Python SDK and Initialize an Azure ML Workspace and save the Workspace configuration file. -02. [Ingest data](02.Ingest_data.ipynb): Download the Adult Census Income dataset and split it into train and test sets. -03. [Build model](03a.Build_model.ipynb): Train a binary classification model in Azure Databricks with a Spark ML Pipeline. -04. [Build model with Run History](03b.Build_model_runHistory.ipynb): Train model and also capture run history (tracking) with Azure ML Python SDK. -05. [Deploy to ACI](04.Deploy_to_ACI.ipynb): Deploy model to Azure Container Instance (ACI) with Azure ML Python SDK. -06. [Deploy to AKS](04.Deploy_to_AKS_existingImage.ipynb): Deploy model to Azure Kubernetis Service (AKS) with Azure ML Python SDK from an existing Image with model, conda and score file. - -Copyright (c) Microsoft Corporation. All rights reserved. -All notebooks in this folder are licensed under the MIT License. diff --git a/training/03.train-hyperparameter-tune-deploy-with-tensorflow/.ipynb_checkpoints/03.train-hyperparameter-tune-deploy-with-tensorflow-checkpoint.ipynb b/training/03.train-hyperparameter-tune-deploy-with-tensorflow/.ipynb_checkpoints/03.train-hyperparameter-tune-deploy-with-tensorflow-checkpoint.ipynb deleted file mode 100644 index 4d62b5f4..00000000 --- a/training/03.train-hyperparameter-tune-deploy-with-tensorflow/.ipynb_checkpoints/03.train-hyperparameter-tune-deploy-with-tensorflow-checkpoint.ipynb +++ /dev/null @@ -1,1624 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Copyright (c) Microsoft Corporation. All rights reserved.\n", - "\n", - "Licensed under the MIT License." - ] - }, - { - "cell_type": "markdown", - "metadata": { - "nbpresent": { - "id": "bf74d2e9-2708-49b1-934b-e0ede342f475" - } - }, - "source": [ - "# 03. Training MNIST dataset with hyperparameter tuning & deploy to ACI\n", - "\n", - "## Introduction\n", - "This tutorial shows how to train a simple deep neural network using the MNIST dataset and TensorFlow on Azure Machine Learning. MNIST is a popular dataset consisting of 70,000 grayscale images. Each image is a handwritten digit of `28x28` pixels, representing number from 0 to 9. The goal is to create a multi-class classifier to identify the digit each image represents, and deploy it as a web service in Azure.\n", - "\n", - "For more information about the MNIST dataset, please visit [Yan LeCun's website](http://yann.lecun.com/exdb/mnist/).\n", - "\n", - "## Prerequisite:\n", - "* Understand the [architecture and terms](https://docs.microsoft.com/azure/machine-learning/service/concept-azure-machine-learning-architecture) introduced by Azure Machine Learning\n", - "* Go through the [00.configuration.ipynb](https://github.com/Azure/MachineLearningNotebooks/blob/master/00.configuration.ipynb) notebook to:\n", - " * install the AML SDK\n", - " * create a workspace and its configuration file (`config.json`)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Let's get started. First let's import some Python libraries." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "nbpresent": { - "id": "c377ea0c-0cd9-4345-9be2-e20fb29c94c3" - } - }, - "outputs": [], - "source": [ - "%matplotlib inline\n", - "import numpy as np\n", - "import os\n", - "import matplotlib\n", - "import matplotlib.pyplot as plt" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "nbpresent": { - "id": "edaa7f2f-2439-4148-b57a-8c794c0945ec" - } - }, - "outputs": [], - "source": [ - "import azureml\n", - "from azureml.core import Workspace, Run\n", - "\n", - "# check core SDK version number\n", - "print(\"Azure ML SDK Version: \", azureml.core.VERSION)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Initialize workspace\n", - "Initialize a [Workspace](https://docs.microsoft.com/azure/machine-learning/service/concept-azure-machine-learning-architecture#workspace) object from the existing workspace you created in the Prerequisites step. `Workspace.from_config()` creates a workspace object from the details stored in `config.json`." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "from azureml.core.workspace import Workspace\n", - "\n", - "ws = Workspace.from_config()\n", - "print('Workspace name: ' + ws.name, \n", - " 'Azure region: ' + ws.location, \n", - " 'Subscription id: ' + ws.subscription_id, \n", - " 'Resource group: ' + ws.resource_group, sep = '\\n')" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "nbpresent": { - "id": "59f52294-4a25-4c92-bab8-3b07f0f44d15" - } - }, - "source": [ - "## Create an Azure ML experiment\n", - "Let's create an experiment named \"tf-mnist\" and a folder to hold the training scripts. The script runs will be recorded under the experiment in Azure." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "nbpresent": { - "id": "bc70f780-c240-4779-96f3-bc5ef9a37d59" - } - }, - "outputs": [], - "source": [ - "from azureml.core import Experiment\n", - "\n", - "script_folder = './tf-mnist'\n", - "os.makedirs(script_folder, exist_ok=True)\n", - "\n", - "exp = Experiment(workspace=ws, name='tf-mnist')" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "nbpresent": { - "id": "defe921f-8097-44c3-8336-8af6700804a7" - } - }, - "source": [ - "## Download MNIST dataset\n", - "In order to train on the MNIST dataset we will first need to download it from Yan LeCun's web site directly and save them in a `data` folder locally." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "import os\n", - "import urllib\n", - "\n", - "os.makedirs('./data/mnist', exist_ok=True)\n", - "\n", - "urllib.request.urlretrieve('http://yann.lecun.com/exdb/mnist/train-images-idx3-ubyte.gz', filename = './data/mnist/train-images.gz')\n", - "urllib.request.urlretrieve('http://yann.lecun.com/exdb/mnist/train-labels-idx1-ubyte.gz', filename = './data/mnist/train-labels.gz')\n", - "urllib.request.urlretrieve('http://yann.lecun.com/exdb/mnist/t10k-images-idx3-ubyte.gz', filename = './data/mnist/test-images.gz')\n", - "urllib.request.urlretrieve('http://yann.lecun.com/exdb/mnist/t10k-labels-idx1-ubyte.gz', filename = './data/mnist/test-labels.gz')" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "nbpresent": { - "id": "c3f2f57c-7454-4d3e-b38d-b0946cf066ea" - } - }, - "source": [ - "## Show some sample images\n", - "Let's load the downloaded compressed file into numpy arrays using some utility functions included in the `utils.py` library file from the current folder. Then we use `matplotlib` to plot 30 random images from the dataset along with their labels." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "nbpresent": { - "id": "396d478b-34aa-4afa-9898-cdce8222a516" - } - }, - "outputs": [], - "source": [ - "from utils import load_data\n", - "\n", - "# note we also shrink the intensity values (X) from 0-255 to 0-1. This helps the neural network converge faster.\n", - "X_train = load_data('./data/mnist/train-images.gz', False) / 255.0\n", - "y_train = load_data('./data/mnist/train-labels.gz', True).reshape(-1)\n", - "\n", - "X_test = load_data('./data/mnist/test-images.gz', False) / 255.0\n", - "y_test = load_data('./data/mnist/test-labels.gz', True).reshape(-1)\n", - "\n", - "count = 0\n", - "sample_size = 30\n", - "plt.figure(figsize = (16, 6))\n", - "for i in np.random.permutation(X_train.shape[0])[:sample_size]:\n", - " count = count + 1\n", - " plt.subplot(1, sample_size, count)\n", - " plt.axhline('')\n", - " plt.axvline('')\n", - " plt.text(x = 10, y = -10, s = y_train[i], fontsize = 18)\n", - " plt.imshow(X_train[i].reshape(28, 28), cmap = plt.cm.Greys)\n", - "plt.show()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Upload MNIST dataset to default datastore \n", - "A [datastore](https://docs.microsoft.com/azure/machine-learning/service/how-to-access-data) is a place where data can be stored that is then made accessible to a Run either by means of mounting or copying the data to the compute target. A datastore can either be backed by an Azure Blob Storage or and Azure File Share (ADLS will be supported in the future). For simple data handling, each workspace provides a default datastore that can be used, in case the data is not already in Blob Storage or File Share.\n", - "\n", - "In this next step, we will upload the training and test set into the workspace's default datastore, which we will then later be mount on a Batch AI cluster for training.\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "ds = ws.get_default_datastore()\n", - "ds.upload(src_dir='./data/mnist', target_path='mnist', overwrite=True, show_progress=True)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Create Batch AI cluster as compute target\n", - "[Batch AI](https://docs.microsoft.com/en-us/azure/batch-ai/overview) is a service for provisioning and managing clusters of Azure virtual machines for running machine learning workloads. Let's create a new Batch AI cluster in the current workspace, if it doesn't already exist. We will then run the training script on this compute target." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "If we could not find the cluster with the given name in the previous cell, then we will create a new cluster here. We will create a Batch AI Cluster of `STANDARD_D2_V2` CPU VMs. This process is broken down into 3 steps:\n", - "1. create the configuration (this step is local and only takes a second)\n", - "2. create the Batch AI cluster (this step will take about **20 seconds**)\n", - "3. provision the VMs to bring the cluster to the initial size (of 1 in this case). This step will take about **3-5 minutes** and is providing only sparse output in the process. Please make sure to wait until the call returns before moving to the next cell" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "from azureml.core.compute import ComputeTarget, BatchAiCompute\n", - "from azureml.core.compute_target import ComputeTargetException\n", - "\n", - "# choose a name for your cluster\n", - "batchai_cluster_name = \"gpucluster\"\n", - "\n", - "try:\n", - " # look for the existing cluster by name\n", - " compute_target = ComputeTarget(workspace=ws, name=batchai_cluster_name)\n", - " if compute_target is BatchAiCompute:\n", - " print('found compute target {}, just use it.'.format(batchai_cluster_name))\n", - " else:\n", - " print('{} exists but it is not a Batch AI cluster. Please choose a different name.'.format(batchai_cluster_name))\n", - "except ComputeTargetException:\n", - " print('creating a new compute target...')\n", - " compute_config = BatchAiCompute.provisioning_configuration(vm_size=\"STANDARD_NC6\", # GPU-based VM\n", - " #vm_priority='lowpriority', # optional\n", - " autoscale_enabled=True,\n", - " cluster_min_nodes=0, \n", - " cluster_max_nodes=4)\n", - "\n", - " # create the cluster\n", - " compute_target = ComputeTarget.create(ws, batchai_cluster_name, compute_config)\n", - " \n", - " # can poll for a minimum number of nodes and for a specific timeout. \n", - " # if no min node count is provided it uses the scale settings for the cluster\n", - " compute_target.wait_for_completion(show_output=True, min_node_count=None, timeout_in_minutes=20)\n", - " \n", - " # Use the 'status' property to get a detailed status for the current cluster. \n", - " print(compute_target.status.serialize())" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Now that you have created the compute target, let's see what the workspace's `compute_targets()` function returns. You should now see one entry named 'cpucluster' of type BatchAI." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "for ct in ws.compute_targets():\n", - " print(ct.name, ct.type, ct.provisioning_state)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Copy the training files into the script folder\n", - "The TensorFlow training script is already created for you. You can simply copy it into the script folder, together with the utility library used to load compressed data file into numpy array." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "import shutil\n", - "# the training logic is in the tf_mnist.py file.\n", - "shutil.copy('./tf_mnist.py', script_folder)\n", - "\n", - "# the utils.py just helps loading data from the downloaded MNIST dataset into numpy arrays.\n", - "shutil.copy('./utils.py', script_folder)" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "nbpresent": { - "id": "2039d2d5-aca6-4f25-a12f-df9ae6529cae" - } - }, - "source": [ - "## Construct neural network in TensorFlow\n", - "In the training script `tf_mnist.py`, it creates a very simple DNN (deep neural network), with just 2 hidden layers. The input layer has 28 * 28 = 784 neurons, each representing a pixel in an image. The first hidden layer has 300 neurons, and the second hidden layer has 100 neurons. The output layer has 10 neurons, each representing a targeted label from 0 to 9.\n", - "\n", - "![DNN](nn.png)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Azure ML concepts \n", - "Please note the following three things in the code below:\n", - "1. The script accepts arguments using the argparse package. In this case there is one argument `--data_folder` which specifies the file system folder in which the script can find the MNIST data\n", - "```\n", - " parser = argparse.ArgumentParser()\n", - " parser.add_argument('--data_folder')\n", - "```\n", - "2. The script is accessing the Azure ML `Run` object by executing `run = Run.get_submitted_run()`. Further down the script is using the `run` to report the training accuracy and the validation accuracy as training progresses.\n", - "```\n", - " run.log('training_acc', np.float(acc_train))\n", - " run.log('validation_acc', np.float(acc_val))\n", - "```\n", - "3. When running the script on Azure ML, you can write files out to a folder `./outputs` that is relative to the root directory. This folder is specially tracked by Azure ML in the sense that any files written to that folder during script execution on the remote target will be picked up by Run History; these files (known as artifacts) will be available as part of the run history record." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The next cell will print out the training code for you to inspect it." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "with open(os.path.join(script_folder, './tf_mnist.py'), 'r') as f:\n", - " print(f.read())" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Create TensorFlow estimator\n", - "Next, we construct an `azureml.train.dnn.TensorFlow` estimator object, use the Batch AI cluster as compute target, and pass the mount-point of the datastore to the training code as a parameter.\n", - "The TensorFlow estimator is providing a simple way of launching a TensorFlow training job on a compute target. It will automatically provide a docker image that has TensorFlow installed -- if additional pip or conda packages are required, their names can be passed in via the `pip_packages` and `conda_packages` arguments and they will be included in the resulting docker." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "from azureml.train.dnn import TensorFlow\n", - "\n", - "script_params = {\n", - " '--data-folder': ws.get_default_datastore().as_mount(),\n", - " '--batch-size': 50,\n", - " '--first-layer-neurons': 300,\n", - " '--second-layer-neurons': 100,\n", - " '--learning-rate': 0.01\n", - "}\n", - "\n", - "est = TensorFlow(source_directory=script_folder,\n", - " script_params=script_params,\n", - " compute_target=compute_target,\n", - " entry_script='tf_mnist.py', \n", - " use_gpu=True)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Submit job to run\n", - "Calling the `fit` function on the estimator submits the job to Azure ML for execution. Submitting the job should only take a few seconds." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "run = exp.submit(config=est)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Monitor the Run\n", - "As the Run is executed, it will go through the following stages:\n", - "1. Preparing: A docker image is created matching the Python environment specified by the TensorFlow estimator and it will be uploaded to the workspace's Azure Container Registry. This step will only happen once for each Python environment -- the container will then be cached for subsequent runs. Creating and uploading the image takes about **5 minutes**. While the job is preparing, logs are streamed to the run history and can be viewed to monitor the progress of the image creation.\n", - "\n", - "2. Scaling: If the compute needs to be scaled up (i.e. the Batch AI cluster requires more nodes to execute the run than currently available), the Batch AI cluster will attempt to scale up in order to make the required amount of nodes available. Scaling typically takes about **5 minutes**.\n", - "\n", - "3. Running: All scripts in the script folder are uploaded to the compute target, data stores are mounted/copied and the `entry_script` is executed. While the job is running, stdout and the `./logs` folder are streamed to the run history and can be viewed to monitor the progress of the run.\n", - "\n", - "4. Post-Processing: The `./outputs` folder of the run is copied over to the run history\n", - "\n", - "There are multiple ways to check the progress of a running job. We can use a Jupyter notebook widget. \n", - "\n", - "**Note: The widget will automatically update ever 10-15 seconds, always showing you the most up-to-date information about the run**" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "from azureml.train.widgets import RunDetails\n", - "RunDetails(run).show()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We can also periodically check the status of the run object, and navigate to Azure portal to monitor the run." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "run" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### The Run object\n", - "The Run object provides the interface to the run history -- both to the job and to the control plane (this notebook), and both while the job is running and after it has completed. It provides a number of interesting features for instance:\n", - "* `run.get_details()`: Provides a rich set of properties of the run\n", - "* `run.get_metrics()`: Provides a dictionary with all the metrics that were reported for the Run\n", - "* `run.get_file_names()`: List all the files that were uploaded to the run history for this Run. This will include the `outputs` and `logs` folder, azureml-logs and other logs, as well as files that were explicitly uploaded to the run using `run.upload_file()`\n", - "\n", - "Below are some examples -- please run through them and inspect their output. " - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "run.get_details()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "run.get_metrics()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "run.get_file_names()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Plot accuracy over epochs\n", - "Since we can retrieve the metrics from the run, we can easily make plots using `matplotlib` in the notebook. Then we can add the plotted image to the run using `run.log_image()`, so all information about the run is kept together." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "import os\n", - "os.makedirs('./imgs', exist_ok = True)\n", - "metrics = run.get_metrics()\n", - "\n", - "plt.figure(figsize = (13,5))\n", - "plt.plot(metrics['validation_acc'], 'r-', lw = 4, alpha = .6)\n", - "plt.plot(metrics['training_acc'], 'b--', alpha = 0.5)\n", - "plt.legend(['Full evaluation set', 'Training set mini-batch'])\n", - "plt.xlabel('epochs', fontsize = 14)\n", - "plt.ylabel('accuracy', fontsize = 14)\n", - "plt.title('Accuracy over Epochs', fontsize = 16)\n", - "run.log_image(name = 'acc_over_epochs.png', plot = plt)\n", - "plt.show()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Download the saved model" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "In the training script, a TensorFlow `saver` object is used to persist the model in a local folder (local to the compute target). The model was saved to the `./outputs` folder on the disk of the Batch AI cluster node where the job is run. Azure ML automatically uploaded anything written in the `./outputs` folder into run history file store. Subsequently, we can use the `Run` object to download the model files the `saver` object saved. They are under the the `outputs/model` folder in the run history file store, and are downloaded into a local folder named `model`. Note the TensorFlow model consists of four files in binary format and they are not human-readable." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# create a model folder in the current directory\n", - "os.makedirs('./model', exist_ok = True)\n", - "\n", - "for f in run.get_file_names():\n", - " if f.startswith('outputs/model'):\n", - " output_file_path = os.path.join('./model', f.split('/')[-1])\n", - " print('Downloading from {} to {} ...'.format(f, output_file_path))\n", - " run.download_file(name = f, output_file_path = output_file_path)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Predict on the test set\n", - "Now load the saved TensorFlow graph, and list all operations under the `network` scope. This way we can discover the input tensor `network/X:0` and the output tensor `network/output/MatMul:0`, and use them in the scoring script in the next step.\n", - "\n", - "Note: if your local TensorFlow version is different than the version running in the cluster where the model is trained, you might see a \"compiletime version mismatch\" warning. You can ignore it." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "import tensorflow as tf\n", - "tf.reset_default_graph()\n", - "\n", - "saver = tf.train.import_meta_graph(\"./model/mnist-tf.model.meta\")\n", - "graph = tf.get_default_graph()\n", - "\n", - "for op in graph.get_operations():\n", - " if op.name.startswith('network'):\n", - " print(op.name)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Feed test dataset to the persisted model to get predictions." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# input tensor. this is an array of 784 elements, each representing the intensity of a pixel in the digit image.\n", - "X = tf.get_default_graph().get_tensor_by_name(\"network/X:0\")\n", - "# output tensor. this is an array of 10 elements, each representing the probability of predicted value of the digit.\n", - "output = tf.get_default_graph().get_tensor_by_name(\"network/output/MatMul:0\")\n", - "\n", - "with tf.Session() as sess:\n", - " saver.restore(sess, './model/mnist-tf.model')\n", - " k = output.eval(feed_dict = {X : X_test})\n", - "# get the prediction, which is the index of the element that has the largest probability value.\n", - "y_hat = np.argmax(k, axis = 1)\n", - "\n", - "# print the first 30 labels and predictions\n", - "print('labels: \\t', y_test[:30])\n", - "print('predictions:\\t', y_hat[:30])" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Calculate the overall accuracy by comparing the predicted value against the test set." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "print(\"Accuracy on the test set:\", np.average(y_hat == y_test))" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Intelligent hyperparameter tuning\n", - "We have trained the model with one set of hyperparameters, now let's how we can do hyperparameter tuning by launching multiple runs on the cluster. First let's define the parameter space using random sampling." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "from azureml.train.hyperdrive import *\n", - "\n", - "ps = RandomParameterSampling(\n", - " {\n", - " '--batch-size': choice(25, 50, 100),\n", - " '--first-layer-neurons': choice(10, 50, 200, 300, 500),\n", - " '--second-layer-neurons': choice(10, 50, 200, 500),\n", - " '--learning-rate': loguniform(-6, -1)\n", - " }\n", - ")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Next, we will create a new estimator without the above parameters since they will be passed in later. Note we still need to keep the `data-folder` parameter since that's not a hyperparamter we will sweep." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "est = TensorFlow(source_directory=script_folder,\n", - " script_params={'--data-folder': ws.get_default_datastore().as_mount()},\n", - " compute_target=compute_target,\n", - " entry_script='tf_mnist.py', \n", - " use_gpu=True)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Now we will define an early termnination policy. The `BanditPolicy` basically states to check the job every 2 iterations. If the primary metric (defined later) falls outside of the top 10% range, Azure ML terminate the job. This saves us from continuing to explore hyperparameters that don't show promise of helping reach our target metric." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "policy = BanditPolicy(evaluation_interval=2, slack_factor=0.1)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Now we are ready to configure a run configuration object, and specify the primary metric `validation_acc` that's recorded in your training runs. If you go back to visit the training script, you will notice that this value is being logged after every epoch (a full batch set). We also want to tell the service that we are looking to maximizing this value. We also set the number of samples to 20, and maximal concurrent job to 4, which is the same as the number of nodes in our computer cluster." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "htc = HyperDriveRunConfig(estimator=est, \n", - " hyperparameter_sampling=ps, \n", - " primary_metric_name='validation_acc', \n", - " primary_metric_goal=PrimaryMetricGoal.MAXIMIZE, \n", - " max_total_runs=20,\n", - " max_concurrent_runs=4)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Finally, let's launch the hyperparameter tuning job." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "htr = exp.submit(config=htc)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We can use a run history widget to show the progress. Be patient as this might take a while to complete." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "RunDetails(htr).show()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Find and register best model\n", - "When all the jobs finish, we can find out the one that has the highest accuracy." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "best_run = htr.get_best_run_by_primary_metric()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Now let's list the model files uploaded during the run." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "print(best_run.get_file_names()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We can then register the folder (and all files in it) as a model named `tf-dnn-mnist` under the workspace for deployment." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "model = best_run.register_model(model_name='tf-dnn-mnist', model_path='outputs/model')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Deploy the model in ACI\n", - "Now we are ready to deploy the model as a web service running in Azure Container Instance [ACI](https://azure.microsoft.com/en-us/services/container-instances/). Azure Machine Learning accomplishes this by constructing a Docker image with the scoring logic and model baked in.\n", - "### Create score.py\n", - "First, we will create a scoring script that will be invoked by the web service call. \n", - "\n", - "* Note that the scoring script must have two required functions, `init()` and `run(input_data)`. \n", - " * In `init()` function, you typically load the model into a global object. This function is executed only once when the Docker container is started. \n", - " * In `run(input_data)` function, the model is used to predict a value based on the input data. The input and output to `run` typically use JSON as serialization and de-serialization format but you are not limited to that." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "%%writefile score.py\n", - "import json\n", - "import numpy as np\n", - "import os\n", - "import tensorflow as tf\n", - "\n", - "from azureml.core.model import Model\n", - "\n", - "def init():\n", - " global X, output, sess\n", - " tf.reset_default_graph()\n", - " model_root = Model.get_model_path('tf-dnn-mnist')\n", - " saver = tf.train.import_meta_graph(os.path.join(model_root, 'mnist-tf.model.meta'))\n", - " X = tf.get_default_graph().get_tensor_by_name(\"network/X:0\")\n", - " output = tf.get_default_graph().get_tensor_by_name(\"network/output/MatMul:0\")\n", - " \n", - " sess = tf.Session()\n", - " saver.restore(sess, os.path.join(model_root, 'mnist-tf.model'))\n", - "\n", - "def run(raw_data):\n", - " data = np.array(json.loads(raw_data)['data'])\n", - " # make prediction\n", - " out = output.eval(session = sess, feed_dict = {X: data})\n", - " y_hat = np.argmax(out, axis = 1)\n", - " return json.dumps(y_hat.tolist())" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Create myenv.yml\n", - "We also need to create an environment file so that Azure Machine Learning can install the necessary packages in the Docker image which are required by your scoring script. In this case, we need to specify packages `numpy`, `tensorflow`." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "from azureml.core.runconfig import CondaDependencies\n", - "cd = CondaDependencies.create()\n", - "cd.add_conda_package('numpy')\n", - "cd.add_tensorflow_conda_package()\n", - "cd.save_to_file(base_directory='./', conda_file_path='myenv.yml')\n", - "\n", - "print(cd.serialize_to_string())" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Deploy to ACI\n", - "We are almost ready to deploy. Create a deployment configuration and specify the number of CPUs and gigbyte of RAM needed for your ACI container. " - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "from azureml.core.webservice import AciWebservice\n", - "\n", - "aciconfig = AciWebservice.deploy_configuration(cpu_cores=1, \n", - " memory_gb=1, \n", - " tags={'name':'mnist', 'framework': 'TensorFlow DNN'},\n", - " description='Tensorflow DNN on MNIST')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "#### Deployment Process\n", - "Now we can deploy. **This cell will run for about 7-8 minutes**. Behind the scene, it will do the following:\n", - "1. **Register model** \n", - "Take the local `model` folder (which contains our previously downloaded trained model files) and register it (and the files inside that folder) as a model named `model` under the workspace. Azure ML will register the model directory or model file(s) we specify to the `model_paths` parameter of the `Webservice.deploy` call.\n", - "2. **Build Docker image** \n", - "Build a Docker image using the scoring file (`score.py`), the environment file (`myenv.yml`), and the `model` folder containing the TensorFlow model files. \n", - "3. **Register image** \n", - "Register that image under the workspace. \n", - "4. **Ship to ACI** \n", - "And finally ship the image to the ACI infrastructure, start up a container in ACI using that image, and expose an HTTP endpoint to accept REST client calls." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "from azureml.core.image import ContainerImage\n", - "imgconfig = ContainerImage.image_configuration(execution_script=\"score.py\", \n", - " runtime=\"python\", \n", - " conda_file=\"myenv.yml\")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "%%time\n", - "from azureml.core.webservice import Webservice\n", - "\n", - "service = Webservice.deploy_from_model(workspace=ws,\n", - " name='tf-mnist-svc',\n", - " deployment_config=aciconfig,\n", - " models=[model],\n", - " image_config=imgconfig)\n", - "\n", - "service.wait_for_deployment(show_output=True)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "**Tip: If something goes wrong with the deployment, the first thing to look at is the logs from the service by running the following command:**" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "print(service.get_logs())" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "This is the scoring web service endpoint:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "print(service.scoring_uri)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Test the deployed model\n", - "Let's test the deployed model. Pick 30 random samples from the test set, and send it to the web service hosted in ACI. Note here we are using the `run` API in the SDK to invoke the service. You can also make raw HTTP calls using any HTTP tool such as curl.\n", - "\n", - "After the invocation, we print the returned predictions and plot them along with the input images. Use red font color and inversed image (white on black) to highlight the misclassified samples. Note since the model accuracy is pretty high, you might have to run the below cell a few times before you can see a misclassified sample." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "import json\n", - "\n", - "# find 30 random samples from test set\n", - "n = 30\n", - "sample_indices = np.random.permutation(X_test.shape[0])[0:n]\n", - "\n", - "test_samples = json.dumps({\"data\": X_test[sample_indices].tolist()})\n", - "test_samples = bytes(test_samples, encoding = 'utf8')\n", - "\n", - "# predict using the deployed model\n", - "result = json.loads(service.run(input_data = test_samples))\n", - "\n", - "# compare actual value vs. the predicted values:\n", - "i = 0\n", - "plt.figure(figsize = (20, 1))\n", - "\n", - "for s in sample_indices:\n", - " plt.subplot(1, n, i + 1)\n", - " plt.axhline('')\n", - " plt.axvline('')\n", - " \n", - " # use different color for misclassified sample\n", - " font_color = 'red' if y_test[s] != result[i] else 'black'\n", - " clr_map = plt.cm.gray if y_test[s] != result[i] else plt.cm.Greys\n", - " \n", - " plt.text(x = 10, y = -10, s = y_hat[s], fontsize = 18, color = font_color)\n", - " plt.imshow(X_test[s].reshape(28, 28), cmap = clr_map)\n", - " \n", - " i = i + 1\n", - "plt.show()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We can also send raw HTTP request to the service." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "import requests\n", - "import json\n", - "\n", - "# send a random row from the test set to score\n", - "random_index = np.random.randint(0, len(X_test)-1)\n", - "input_data = \"{\\\"data\\\": [\" + str(list(X_test[random_index])) + \"]}\"\n", - "\n", - "headers = {'Content-Type':'application/json'}\n", - "\n", - "resp = requests.post(service.scoring_uri, input_data, headers=headers)\n", - "\n", - "print(\"POST to url\", service.scoring_uri)\n", - "#print(\"input data:\", input_data)\n", - "print(\"label:\", y_test[random_index])\n", - "print(\"prediction:\", resp.text)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Let's look at the workspace after the web service was deployed. You should see \n", - "* a registered model named 'model' and with the id 'model:1'\n", - "* an image called 'tf-mnist' and with a docker image location pointing to your workspace's Azure Container Registry (ACR) \n", - "* a webservice called 'tf-mnist' with some scoring URL" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "for model in ws.models():\n", - " print(\"Model:\", model.name, model.id)\n", - "\n", - "for image in ws.images():\n", - " print(\"Image:\", image.name, image.image_location)\n", - "\n", - "for webservice in ws.webservices():\n", - " print(\"Webservice:\", webservice.name, webservice.scoring_uri)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Clean up\n", - "You can delete the ACI deployment with a simple delete API call." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "service.delete()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We can also delete the computer cluster. But remember if you set the `cluster_min_nodes` value to 0 when you created the cluster, once the jobs are finished, all nodes are deleted automatically. So you don't have to delete the cluster itself since it won't incur any cost. Next time you submit jobs to it, the cluster will then automatically \"grow\" up to the `cluster_min_nodes` which is set to 4." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# delete the cluster if you need to.\n", - "compute_target.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.5" - }, - "nbpresent": { - "slides": { - "05bb34ad-74b0-42b3-9654-8357d1ba9c99": { - "id": "05bb34ad-74b0-42b3-9654-8357d1ba9c99", - "prev": "851089af-9725-40c9-8f0b-9bf892b2b1fe", - "regions": { - "23fb396d-50f9-4770-adb3-0d6abcb40767": { - "attrs": { - "height": 0.8, - "width": 0.8, - "x": 0.1, - "y": 0.1 - }, - "content": { - "cell": "2039d2d5-aca6-4f25-a12f-df9ae6529cae", - "part": "whole" - }, - "id": "23fb396d-50f9-4770-adb3-0d6abcb40767" - } - } - }, - "11bebe14-d1dc-476d-a31a-5828b9c3adf0": { - "id": "11bebe14-d1dc-476d-a31a-5828b9c3adf0", - "prev": "502648cb-26fe-496b-899f-84c8fe1dcbc0", - "regions": { - "a42499db-623e-4414-bea2-ff3617fd8fc5": { - "attrs": { - "height": 0.8, - "width": 0.8, - "x": 0.1, - "y": 0.1 - }, - "content": { - "cell": "4788c040-27a2-4dc1-8ed0-378a99b3a255", - "part": "whole" - }, - "id": "a42499db-623e-4414-bea2-ff3617fd8fc5" - } - } - }, - "134f92d0-6389-4226-af51-1134ae8e8278": { - "id": "134f92d0-6389-4226-af51-1134ae8e8278", - "prev": "36b8728c-32ad-4941-be03-5cef51cdc430", - "regions": { - "b6d82a77-2d58-4b9e-a375-3103214b826c": { - "attrs": { - "height": 0.8, - "width": 0.8, - "x": 0.1, - "y": 0.1 - }, - "content": { - "cell": "7ab0e6d0-1f1c-451b-8ac5-687da44a8287", - "part": "whole" - }, - "id": "b6d82a77-2d58-4b9e-a375-3103214b826c" - } - } - }, - "282a2421-697b-4fd0-9485-755abf5a0c18": { - "id": "282a2421-697b-4fd0-9485-755abf5a0c18", - "prev": "a8b9ceb9-b38f-4489-84df-b644c6fe28f2", - "regions": { - "522fec96-abe7-4a34-bd34-633733afecc8": { - "attrs": { - "height": 0.8, - "width": 0.8, - "x": 0.1, - "y": 0.1 - }, - "content": { - "cell": "d58e7785-c2ee-4a45-8e3d-4c538bf8075a", - "part": "whole" - }, - "id": "522fec96-abe7-4a34-bd34-633733afecc8" - } - } - }, - "2dfec088-8a70-411a-9199-904ef3fa2383": { - "id": "2dfec088-8a70-411a-9199-904ef3fa2383", - "prev": "282a2421-697b-4fd0-9485-755abf5a0c18", - "regions": { - "0535fcb6-3a2b-4b46-98a7-3ebb1a38c47e": { - "attrs": { - "height": 0.8, - "width": 0.8, - "x": 0.1, - "y": 0.1 - }, - "content": { - "cell": "c377ea0c-0cd9-4345-9be2-e20fb29c94c3", - "part": "whole" - }, - "id": "0535fcb6-3a2b-4b46-98a7-3ebb1a38c47e" - } - } - }, - "36a814c9-c540-4a6d-92d9-c03553d3d2c2": { - "id": "36a814c9-c540-4a6d-92d9-c03553d3d2c2", - "prev": "b52e4d09-5186-44e5-84db-3371c087acde", - "regions": { - "8bfba503-9907-43f0-b1a6-46a0b4311793": { - "attrs": { - "height": 0.8, - "width": 0.8, - "x": 0.1, - "y": 0.1 - }, - "content": { - "cell": "d5e4a56c-dfac-4346-be83-1c15b503deac", - "part": "whole" - }, - "id": "8bfba503-9907-43f0-b1a6-46a0b4311793" - } - } - }, - "36b8728c-32ad-4941-be03-5cef51cdc430": { - "id": "36b8728c-32ad-4941-be03-5cef51cdc430", - "prev": "05bb34ad-74b0-42b3-9654-8357d1ba9c99", - "regions": { - "a36a5bdf-7f62-49b0-8634-e155a98851dc": { - "attrs": { - "height": 0.8, - "width": 0.8, - "x": 0.1, - "y": 0.1 - }, - "content": { - "cell": "e33dfc47-e7df-4623-a7a6-ab6bcf944629", - "part": "whole" - }, - "id": "a36a5bdf-7f62-49b0-8634-e155a98851dc" - } - } - }, - "3f136f2a-f14c-4a4b-afea-13380556a79c": { - "id": "3f136f2a-f14c-4a4b-afea-13380556a79c", - "prev": "54cb8dfd-a89c-4922-867b-3c87d8b67cd3", - "regions": { - "80ecf237-d1b0-401e-83d2-6d04b7fcebd3": { - "attrs": { - "height": 0.8, - "width": 0.8, - "x": 0.1, - "y": 0.1 - }, - "content": { - "cell": "7debeb2b-ecea-414f-9b50-49657abb3e6a", - "part": "whole" - }, - "id": "80ecf237-d1b0-401e-83d2-6d04b7fcebd3" - } - } - }, - "502648cb-26fe-496b-899f-84c8fe1dcbc0": { - "id": "502648cb-26fe-496b-899f-84c8fe1dcbc0", - "prev": "3f136f2a-f14c-4a4b-afea-13380556a79c", - "regions": { - "4c83bb4d-2a52-41ba-a77f-0c6efebd83a6": { - "attrs": { - "height": 0.8, - "width": 0.8, - "x": 0.1, - "y": 0.1 - }, - "content": { - "cell": "dbd22f6b-6d49-4005-b8fe-422ef8ef1d42", - "part": "whole" - }, - "id": "4c83bb4d-2a52-41ba-a77f-0c6efebd83a6" - } - } - }, - "54cb8dfd-a89c-4922-867b-3c87d8b67cd3": { - "id": "54cb8dfd-a89c-4922-867b-3c87d8b67cd3", - "prev": "aa224267-f885-4c0c-95af-7bacfcc186d9", - "regions": { - "0848f0a7-032d-46c7-b35c-bfb69c83f961": { - "attrs": { - "height": 0.8, - "width": 0.8, - "x": 0.1, - "y": 0.1 - }, - "content": { - "cell": "3c32c557-d0e8-4bb3-a61a-aa51a767cd4e", - "part": "whole" - }, - "id": "0848f0a7-032d-46c7-b35c-bfb69c83f961" - } - } - }, - "636b563c-faee-4c9e-a6a3-f46a905bfa82": { - "id": "636b563c-faee-4c9e-a6a3-f46a905bfa82", - "prev": "c5f59b98-a227-4344-9d6d-03abdd01c6aa", - "regions": { - "9c64f662-05dc-4b14-9cdc-d450b96f4368": { - "attrs": { - "height": 0.8, - "width": 0.8, - "x": 0.1, - "y": 0.1 - }, - "content": { - "cell": "70640ac0-7041-47a8-9a7f-e871defd74b2", - "part": "whole" - }, - "id": "9c64f662-05dc-4b14-9cdc-d450b96f4368" - } - } - }, - "793cec2f-8413-484d-aa1e-388fd2b53a45": { - "id": "793cec2f-8413-484d-aa1e-388fd2b53a45", - "prev": "c66f3dfd-2d27-482b-be78-10ba733e826b", - "regions": { - "d08f9cfa-3b8d-4fb4-91ba-82d9858ea93e": { - "attrs": { - "height": 0.8, - "width": 0.8, - "x": 0.1, - "y": 0.1 - }, - "content": { - "cell": "dd56113e-e3db-41ae-91b7-2472ed194308", - "part": "whole" - }, - "id": "d08f9cfa-3b8d-4fb4-91ba-82d9858ea93e" - } - } - }, - "83e912ff-260a-4391-8a12-331aba098506": { - "id": "83e912ff-260a-4391-8a12-331aba098506", - "prev": "fe5a0732-69f5-462a-8af6-851f84a9fdec", - "regions": { - "2fefcf5f-ea20-4604-a528-5e6c91bcb100": { - "attrs": { - "height": 0.8, - "width": 0.8, - "x": 0.1, - "y": 0.1 - }, - "content": { - "cell": "c3f2f57c-7454-4d3e-b38d-b0946cf066ea", - "part": "whole" - }, - "id": "2fefcf5f-ea20-4604-a528-5e6c91bcb100" - } - } - }, - "851089af-9725-40c9-8f0b-9bf892b2b1fe": { - "id": "851089af-9725-40c9-8f0b-9bf892b2b1fe", - "prev": "636b563c-faee-4c9e-a6a3-f46a905bfa82", - "regions": { - "31c9dda5-fdf4-45e2-bcb7-12aa0f30e1d8": { - "attrs": { - "height": 0.8, - "width": 0.8, - "x": 0.1, - "y": 0.1 - }, - "content": { - "cell": "8408b90e-6cdd-44d1-86d3-648c23f877ac", - "part": "whole" - }, - "id": "31c9dda5-fdf4-45e2-bcb7-12aa0f30e1d8" - } - } - }, - "87ab653d-e804-470f-bde9-c67caaa0f354": { - "id": "87ab653d-e804-470f-bde9-c67caaa0f354", - "prev": "a8c2d446-caee-42c8-886a-ed98f4935d78", - "regions": { - "bc3aeb56-c465-4868-a1ea-2de82584de98": { - "attrs": { - "height": 0.8, - "width": 0.8, - "x": 0.1, - "y": 0.1 - }, - "content": { - "cell": "59f52294-4a25-4c92-bab8-3b07f0f44d15", - "part": "whole" - }, - "id": "bc3aeb56-c465-4868-a1ea-2de82584de98" - } - } - }, - "8b887c97-83bc-4395-83ac-f6703cbe243d": { - "id": "8b887c97-83bc-4395-83ac-f6703cbe243d", - "prev": "36a814c9-c540-4a6d-92d9-c03553d3d2c2", - "regions": { - "9d0bc72a-cb13-483f-a572-2bf60d0d145f": { - "attrs": { - "height": 0.8, - "width": 0.8, - "x": 0.1, - "y": 0.1 - }, - "content": { - "cell": "75499c85-d0a1-43db-8244-25778b9b2736", - "part": "whole" - }, - "id": "9d0bc72a-cb13-483f-a572-2bf60d0d145f" - } - } - }, - "a8b9ceb9-b38f-4489-84df-b644c6fe28f2": { - "id": "a8b9ceb9-b38f-4489-84df-b644c6fe28f2", - "prev": null, - "regions": { - "f741ed94-3f24-4427-b615-3ab8753e5814": { - "attrs": { - "height": 0.8, - "width": 0.8, - "x": 0.1, - "y": 0.1 - }, - "content": { - "cell": "bf74d2e9-2708-49b1-934b-e0ede342f475", - "part": "whole" - }, - "id": "f741ed94-3f24-4427-b615-3ab8753e5814" - } - } - }, - "a8c2d446-caee-42c8-886a-ed98f4935d78": { - "id": "a8c2d446-caee-42c8-886a-ed98f4935d78", - "prev": "2dfec088-8a70-411a-9199-904ef3fa2383", - "regions": { - "f03457d8-b2a7-4e14-9a73-cab80c5b815d": { - "attrs": { - "height": 0.8, - "width": 0.8, - "x": 0.1, - "y": 0.1 - }, - "content": { - "cell": "edaa7f2f-2439-4148-b57a-8c794c0945ec", - "part": "whole" - }, - "id": "f03457d8-b2a7-4e14-9a73-cab80c5b815d" - } - } - }, - "aa224267-f885-4c0c-95af-7bacfcc186d9": { - "id": "aa224267-f885-4c0c-95af-7bacfcc186d9", - "prev": "793cec2f-8413-484d-aa1e-388fd2b53a45", - "regions": { - "0d7ac442-5e1d-49a5-91b3-1432d72449d8": { - "attrs": { - "height": 0.8, - "width": 0.8, - "x": 0.1, - "y": 0.1 - }, - "content": { - "cell": "4d6826fe-2cb8-4468-85ed-a242a1ce7155", - "part": "whole" - }, - "id": "0d7ac442-5e1d-49a5-91b3-1432d72449d8" - } - } - }, - "b52e4d09-5186-44e5-84db-3371c087acde": { - "id": "b52e4d09-5186-44e5-84db-3371c087acde", - "prev": "134f92d0-6389-4226-af51-1134ae8e8278", - "regions": { - "7af7d997-80b2-497d-bced-ef8341763439": { - "attrs": { - "height": 0.8, - "width": 0.8, - "x": 0.1, - "y": 0.1 - }, - "content": { - "cell": "376882ec-d469-4fad-9462-18e4bbea64ca", - "part": "whole" - }, - "id": "7af7d997-80b2-497d-bced-ef8341763439" - } - } - }, - "c5f59b98-a227-4344-9d6d-03abdd01c6aa": { - "id": "c5f59b98-a227-4344-9d6d-03abdd01c6aa", - "prev": "83e912ff-260a-4391-8a12-331aba098506", - "regions": { - "7268abff-0540-4c06-aefc-c386410c0953": { - "attrs": { - "height": 0.8, - "width": 0.8, - "x": 0.1, - "y": 0.1 - }, - "content": { - "cell": "396d478b-34aa-4afa-9898-cdce8222a516", - "part": "whole" - }, - "id": "7268abff-0540-4c06-aefc-c386410c0953" - } - } - }, - "c66f3dfd-2d27-482b-be78-10ba733e826b": { - "id": "c66f3dfd-2d27-482b-be78-10ba733e826b", - "prev": "8b887c97-83bc-4395-83ac-f6703cbe243d", - "regions": { - "6cbe8e0e-8645-41a1-8a38-e44acb81be4b": { - "attrs": { - "height": 0.8, - "width": 0.8, - "x": 0.1, - "y": 0.1 - }, - "content": { - "cell": "7594c7c7-b808-48f7-9500-d7830a07968a", - "part": "whole" - }, - "id": "6cbe8e0e-8645-41a1-8a38-e44acb81be4b" - } - } - }, - "d22045e5-7e3e-452e-bc7b-c6c4a893da8e": { - "id": "d22045e5-7e3e-452e-bc7b-c6c4a893da8e", - "prev": "ec41f96a-63a3-4825-9295-f4657a440ddb", - "regions": { - "24e2a3a9-bf65-4dab-927f-0bf6ffbe581d": { - "attrs": { - "height": 0.8, - "width": 0.8, - "x": 0.1, - "y": 0.1 - }, - "content": { - "cell": "defe921f-8097-44c3-8336-8af6700804a7", - "part": "whole" - }, - "id": "24e2a3a9-bf65-4dab-927f-0bf6ffbe581d" - } - } - }, - "d24c958c-e419-4e4d-aa9c-d228a8ca55e4": { - "id": "d24c958c-e419-4e4d-aa9c-d228a8ca55e4", - "prev": "11bebe14-d1dc-476d-a31a-5828b9c3adf0", - "regions": { - "25312144-9faa-4680-bb8e-6307ea71370f": { - "attrs": { - "height": 0.8, - "width": 0.8, - "x": 0.1, - "y": 0.1 - }, - "content": { - "cell": "bed09a92-9a7a-473b-9464-90e479883a3e", - "part": "whole" - }, - "id": "25312144-9faa-4680-bb8e-6307ea71370f" - } - } - }, - "ec41f96a-63a3-4825-9295-f4657a440ddb": { - "id": "ec41f96a-63a3-4825-9295-f4657a440ddb", - "prev": "87ab653d-e804-470f-bde9-c67caaa0f354", - "regions": { - "22e8be98-c254-4d04-b0e4-b9b5ae46eefe": { - "attrs": { - "height": 0.8, - "width": 0.8, - "x": 0.1, - "y": 0.1 - }, - "content": { - "cell": "bc70f780-c240-4779-96f3-bc5ef9a37d59", - "part": "whole" - }, - "id": "22e8be98-c254-4d04-b0e4-b9b5ae46eefe" - } - } - }, - "fe5a0732-69f5-462a-8af6-851f84a9fdec": { - "id": "fe5a0732-69f5-462a-8af6-851f84a9fdec", - "prev": "d22045e5-7e3e-452e-bc7b-c6c4a893da8e", - "regions": { - "671b89f5-fa9c-4bc1-bdeb-6e0a4ce8939b": { - "attrs": { - "height": 0.8, - "width": 0.8, - "x": 0.1, - "y": 0.1 - }, - "content": { - "cell": "fd46e2ab-4ab6-4001-b536-1f323525d7d3", - "part": "whole" - }, - "id": "671b89f5-fa9c-4bc1-bdeb-6e0a4ce8939b" - } - } - } - }, - "themes": {} - } - }, - "nbformat": 4, - "nbformat_minor": 2 -}