From 01a12c0b74502fd3a50b2c3a497c00f2226fb4eb Mon Sep 17 00:00:00 2001 From: Roope Astala Date: Thu, 13 Sep 2018 14:21:36 -0400 Subject: [PATCH] Add tutorials Add tutorial notebooks --- tutorials/01.train-models.ipynb | 693 +++++++++++++++++++++++++++ tutorials/02.deploy-models.ipynb | 478 ++++++++++++++++++ tutorials/03.auto-train-models.ipynb | 397 +++++++++++++++ tutorials/imgs/flow2.png | Bin 0 -> 106278 bytes tutorials/utils.py | 27 ++ 5 files changed, 1595 insertions(+) create mode 100644 tutorials/01.train-models.ipynb create mode 100644 tutorials/02.deploy-models.ipynb create mode 100644 tutorials/03.auto-train-models.ipynb create mode 100644 tutorials/imgs/flow2.png create mode 100644 tutorials/utils.py diff --git a/tutorials/01.train-models.ipynb b/tutorials/01.train-models.ipynb new file mode 100644 index 000000000..5c5688f03 --- /dev/null +++ b/tutorials/01.train-models.ipynb @@ -0,0 +1,693 @@ +{ + "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": [ + "# Tutorial #1: Train an image classification model with Azure Machine Learning\n", + "\n", + "In this tutorial, you train a machine learning model both locally and on remote compute resources. You'll use the training and deployment workflow for Azure Machine Learning service (preview) in a Python Jupyter notebook. You can then use the notebook as a template to train your own machine learning model with your own data. This tutorial is **part one of a two-part tutorial series**. \n", + "\n", + "This tutorial trains a simple logistic regression using the [MNIST](http://yann.lecun.com/exdb/mnist/) dataset and [scikit-learn](http://scikit-learn.org) with Azure Machine Learning. MNIST is a popular dataset consisting of 70,000 grayscale images. Each image is a handwritten digit of 28x28 pixels, representing a number from 0 to 9. The goal is to create a multi-class classifier to identify the digit a given image represents. \n", + "\n", + "Learn how to:\n", + "\n", + "> * Set up your development environment\n", + "> * Access and examine the data\n", + "> * Train a simple logistic regression model locally using the popular scikit-learn machine learning library \n", + "> * Train multiple models on a remote cluster\n", + "> * Review training results, find and register the best model\n", + "\n", + "You'll learn how to select a model and deploy it in [part two of this tutorial](deploy-models.ipynb) later. \n", + "\n", + "## Prerequisites\n", + "\n", + "Use [these instructions](https://aka.ms/aml-how-to-configure-environment) to: \n", + "* Create a workspace and its configuration file (**config.json**) \n", + "* Save your **config.json** to the same folder as this notebook" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Set up your development environment\n", + "\n", + "All the setup for your development work can be accomplished in a Python notebook. Setup includes:\n", + "\n", + "* Importing Python packages\n", + "* Connecting to a workspace to enable communication between your local computer and remote resources\n", + "* Creating an experiment to track all your runs\n", + "* Creating a remote compute target to use for training\n", + "\n", + "### Import packages\n", + "\n", + "Import Python packages you need in this session. Also display the Azure Machine Learning SDK version." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "%matplotlib inline\n", + "import numpy as np\n", + "import matplotlib\n", + "import matplotlib.pyplot as plt\n", + "\n", + "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": [ + "### Connect to workspace\n", + "\n", + "Create a workspace object from the existing workspace. `Workspace.from_config()` reads the file **config.json** and loads the details into an object named `ws`." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# load workspace configuration from the config.json file in the current folder.\n", + "ws = Workspace.from_config()\n", + "print(ws.name, ws.location, ws.resource_group, ws.location, sep = '\\t')" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Create experiment\n", + "\n", + "Create an experiment to track the runs in your workspace. A workspace can have muliple experiments; an experiment must belongn to a workspace." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "experiment_name = 'sklearn-mnist'\n", + "\n", + "from azureml.core import Experiment\n", + "exp = Experiment(workspace = ws, name = experiment_name)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Create remote compute target\n", + "\n", + "Azure Azure ML Managed Compute is a managed service that enables data scientists to train machine learning models on clusters of Azure virtual machines, including VMs with GPU support. In this tutorial, you create an Azure Managed Compute cluster as your training environment. This code creates a cluster for you if it does not already exist in your workspace. \n", + "\n", + " **Creation of the cluster takes approximately 5 minutes.** If the cluster is already in the workspace this code uses it and skips the creation process." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "tags": [ + "create mlc", + "batchai" + ] + }, + "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 = \"traincluster\"\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_D2_V2\", # small CPU-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": [ + "You now have the necessary packages and compute resources to train a model in the cloud. \n", + "\n", + "## Explore data\n", + "\n", + "Before you train a model, you need to understand the data that you are using to train it. You also need to copy the data into the cloud so it can be accessed by your cloud training environment. In this section you learn how to:\n", + "\n", + "* Download the MNIST dataset\n", + "* Display some sample images\n", + "* Upload data to the cloud\n", + "\n", + "### Download the MNIST dataset\n", + "\n", + "Download the MNIST dataset and save the files into a `data` directory locally. Images and labels for both training and testing are downloaded." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import os\n", + "import urllib.request\n", + "\n", + "os.makedirs('./data', exist_ok = True)\n", + "\n", + "urllib.request.urlretrieve('http://yann.lecun.com/exdb/mnist/train-images-idx3-ubyte.gz', filename = './data/train-images.gz')\n", + "urllib.request.urlretrieve('http://yann.lecun.com/exdb/mnist/train-labels-idx1-ubyte.gz', filename = './data/train-labels.gz')\n", + "urllib.request.urlretrieve('http://yann.lecun.com/exdb/mnist/t10k-images-idx3-ubyte.gz', filename = './data/test-images.gz')\n", + "urllib.request.urlretrieve('http://yann.lecun.com/exdb/mnist/t10k-labels-idx1-ubyte.gz', filename = './data/test-labels.gz')" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Display some sample images\n", + "\n", + "Load the compressed files into `numpy` arrays. Then use `matplotlib` to plot 30 random images from the dataset with their labels above them. Note this step requires a `load_data` function that's included in an `util.py` file. This file is included in the sample folder. Please make sure it is placed in the same folder as this notebook. The `load_data` function simply parses the compresse files into numpy arrays." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# make sure utils.py is in the same directory as this code\n", + "from utils import load_data\n", + "\n", + "# note we also shrink the intensity values (X) from 0-255 to 0-1. This helps the model converge faster.\n", + "X_train = load_data('./data/train-images.gz', False) / 255.0\n", + "y_train = load_data('./data/train-labels.gz', True).reshape(-1)\n", + "\n", + "X_test = load_data('./data/test-images.gz', False) / 255.0\n", + "y_test = load_data('./data/test-labels.gz', True).reshape(-1)\n", + "\n", + "# now let's show some randomly chosen images from the traininng set.\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": [ + "Now you have an idea of what these images look like and the expected prediction outcome.\n", + "\n", + "### Upload data to the cloud\n", + "\n", + "Now make the data accessible remotely by uploading that data from your local machine into Azure so it can be accessed for remote training. The datastore is a convenient construct associated with your workspace for you to upload/download data, and interact with it from your remote compute targets. It is backed by Azure blob storage account.\n", + "\n", + "The MNIST files are uploaded into a directory named `mnist` at the root of the datastore." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "ds = ws.get_default_datastore()\n", + "print(ds.datastore_type, ds.account_name, ds.container_name)\n", + "\n", + "ds.upload(src_dir = './data', target_path = 'mnist', overwrite = True, show_progress = True)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "You now have everything you need to start training a model. \n", + "\n", + "## Train a local model\n", + "\n", + "Train a simple logistic regression model using scikit-learn locally.\n", + "\n", + "**Training locally can take a minute or two** depending on your computer configuration." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "%%time\n", + "from sklearn.linear_model import LogisticRegression\n", + "\n", + "clf = LogisticRegression()\n", + "clf.fit(X_train, y_train)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Next, make predictions using the test set and calculate the accuracy." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "y_hat = clf.predict(X_test)\n", + "print(np.average(y_hat == y_test))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "With just a few lines of code, you have a 92% accuracy.\n", + "\n", + "## Train on a remote cluster\n", + "\n", + "Now you can expand on this simple model by building a model with a different regularization rate. This time you'll train the model on a remote resource. \n", + "\n", + "For this task, submit the job to the remote training cluster you set up earlier. To submit a job you:\n", + "* Create a directory\n", + "* Create a training script\n", + "* Create an estimator object\n", + "* Submit the job \n", + "\n", + "### Create a directory\n", + "\n", + "Create a directory to hold all script files are other assets." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import os\n", + "script_folder = './sklearn-mnist'\n", + "os.makedirs(script_folder, exist_ok = True)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Create a training script\n", + "\n", + "To submit the job to the cluster, first create a training script. Run the following code to create the training script called `train.py` in the directory you just created. This training adds a regularization rate to the training algorithm, so produces a slightly different model than the local version." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "%%writefile $script_folder/train.py\n", + "\n", + "import argparse\n", + "import os\n", + "import numpy as np\n", + "\n", + "from sklearn.linear_model import LogisticRegression\n", + "from sklearn.externals import joblib\n", + "\n", + "from azureml.core import Run\n", + "from utils import load_data\n", + "\n", + "# let user feed in 2 parameters, the location of the data files (from datastore), and the regularization rate of the logistic regression model\n", + "parser = argparse.ArgumentParser()\n", + "parser.add_argument('--data-folder', type = str, dest = 'data_folder', help = 'data folder mounting point')\n", + "parser.add_argument('--regularization', type = float, dest = 'reg', default = 0.01, help = 'regularization rate')\n", + "args = parser.parse_args()\n", + "\n", + "data_folder = os.path.join(args.data_folder, 'mnist')\n", + "print('Data folder:', data_folder)\n", + "\n", + "# load train and test set into numpy arrays\n", + "# note we scale the pixel intensity values to 0-1 (by dividing it with 255.0) so the model can converge faster.\n", + "X_train = load_data(os.path.join(data_folder, 'train-images.gz'), False) / 255.0\n", + "X_test = load_data(os.path.join(data_folder, 'test-images.gz'), False) / 255.0\n", + "y_train = load_data(os.path.join(data_folder, 'train-labels.gz'), True).reshape(-1)\n", + "y_test = load_data(os.path.join(data_folder, 'test-labels.gz'), True).reshape(-1)\n", + "print(X_train.shape, y_train.shape, X_test.shape, y_test.shape, sep = '\\n')\n", + "\n", + "# get hold of the current run\n", + "run = Run.get_submitted_run()\n", + "\n", + "# train a logistic regression model with specified regularization rate\n", + "print('Train a logistic regression model with regularizaion rate of', args.reg)\n", + "clf = LogisticRegression(C = 1.0/args.reg, random_state = 42)\n", + "clf.fit(X_train, y_train)\n", + "\n", + "print('Predict the test set')\n", + "# predict on the test set\n", + "y_hat = clf.predict(X_test)\n", + "\n", + "# calculate accuracy on the prediction\n", + "acc = np.average(y_hat == y_test)\n", + "print('Accuracy is', acc)\n", + "\n", + "# log regularization rate and accuracy \n", + "run.log('regularization rate', np.float(args.reg))\n", + "run.log('accuracy', np.float(acc))\n", + "\n", + "os.makedirs('outputs', exist_ok = True)\n", + "joblib.dump(value = clf, filename = 'outputs/sklearn_mnist_model.pkl')" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Notice how the script gets data and saves models:\n", + "\n", + "+ The training script reads an argument to find the directory containing the data. When you submit the job later, you point to the datastore for this argument:\n", + "`parser.add_argument('--data-folder', type = str, dest = 'data_folder', help = 'data directory mounting point')`" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n", + "+ The training script saves your model into a directory named outputs.
\n", + "`joblib.dump(value = clf, filename = 'outputs/sklearn_mnist_model.pkl')`
\n", + "Anything written in this directory is automatically uploaded into your workspace. You'll access your model from this directory later in the tutorial." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Copy the utility library that loads the dataset into the script folder to be accessed by the training script." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import shutil\n", + "shutil.copy('utils.py', script_folder)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Create an estimator\n", + "\n", + "An estimator object is used to submit the run. Create your estimator by running the following code to define:\n", + "\n", + "* The name of the estimator object, `est`\n", + "* The directory that contains your scripts. All the files in this directory are uploaded into the cluster nodes for execution. \n", + "* The compute target. In this case you will use the Managed Compute cluster you created\n", + "* The training script name, train.py\n", + "* The `data-folder` parameter used by the training script to access the data\n", + "* Any Python packages needed for training\n", + "In this tutorial, this target is the Managed Compute cluster. All files in the script folder are uploaded into the cluster nodes for execution." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from azureml.train.estimator import Estimator\n", + "\n", + "script_params = {\n", + " '--data-folder': ds.as_mount(),\n", + " '--regularization': 0.8\n", + "}\n", + "\n", + "est = Estimator(source_directory = script_folder,\n", + " script_params = script_params,\n", + " compute_target = compute_target,\n", + " entry_script = 'train.py',\n", + " conda_packages = ['scikit-learn'])" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Submit the job to the cluster\n", + "\n", + "Run the experiment by submitting the estimator object." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "run = exp.submit(config=est)\n", + "run" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Since the call is asynchronous, it returns a **Preparing** or **running** state as soon as the job is started.\n", + "\n", + "## Monitor a remote run\n", + "\n", + "In total, the first run takes **approximately 10 minutes**. But for subsequent runs, as long as the script dependencies don't change, the same image is reused and hence the container start up time is much faster.\n", + "\n", + "Here is what's happening while you wait:\n", + "\n", + "- **Image creation**: A Docker image is created matching the Python environment specified by the estimator. The image is uploaded to the workspace. Image creation and uploading takes **about 5 minutes**. \n", + "\n", + " This stage happens once for each Python environment since the container is cached for subsequent runs. During image creation, logs are streamed to the run history. You can monitor the image creation progress using these logs.\n", + "\n", + "- **Scaling**: If the remote cluster requires more nodes to execute the run than currently available, additional nodes are added automatically. Scaling typically takes **about 5 minutes.**\n", + "\n", + "- **Running**: In this stage, the necessary scripts and files are sent to the compute target, then data stores are mounted/copied, then the entry_script is run. While the job is running, stdout and the ./logs directory are streamed to the run history. You can monitor the run's progress using these logs.\n", + "\n", + "- **Post-Processing**: The ./outputs directory of the run is copied over to the run history in your workspace so you can access these results.\n", + "\n", + "\n", + "You can check the progress of a running job in multiple ways. This tutorial uses a Jupyter widget as well as a `wait_for_completion` method. \n", + "\n", + "### Jupyter widget\n", + "\n", + "Watch the progress of the run with a Jupyter widget. Like the run submission, the widget is asynchronous and provides live updates every 10-15 seconds until the job completes." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "tags": [ + "use notebook widget" + ] + }, + "outputs": [], + "source": [ + "from azureml.train.widgets import RunDetails\n", + "RunDetails(run).show()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Get log results upon completion\n", + "\n", + "Model training and monitoring happen in the background. Wait until the model has completed training before running more code. Use `wait_for_completion` to show when the model training is complete." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "run.wait_for_completion(show_output = True) # specify True for a verbose log" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Display run results\n", + "\n", + "You now have a model trained on a remote cluster. Retrieve the accuracy of the model:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "tags": [ + "get metrics" + ] + }, + "outputs": [], + "source": [ + "print(run.get_metrics())" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "In the next tutorial you will explore this model in more detail.\n", + "\n", + "## Register model\n", + "\n", + "The last step in the training script wrote the file `outputs/sklearn_mnist_model.pkl` in a folder named `outputs` in the VM of the cluster where the job is executed. `outputs` is a special folder in that all content in the `outputs` directory is automatically uploaded as part of the run record in the experiment under your workspace. Hence, the model file is now also available in your workspace. \n", + "\n", + "You can see files associated with that run." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "print(run.get_file_names())" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Register the model in the workspace so that you (or other collaborators) can later query, examine, and deploy this model." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# register model \n", + "model = run.register_model(model_name = 'sklearn_mnist', model_path = 'outputs/sklearn_mnist_model.pkl')\n", + "print(model.name, model.id, model.version, sep = '\\t')" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Clean up resources\n", + "\n", + "If you're not going to use what you've created here, delete the resources you just created with this quickstart so you don't incur any charges. In the Azure portal, select and delete your resource group. You can also keep the resource group, but delete a single workspace by displaying the workspace properties and selecting the Delete button.\n", + "\n", + "You can also just delete the Azure Managed Compute cluster. But even if you don't delete it, since `autoscale_enabled` is set to `True`, and `cluster_min_nodes` is set to `0`, when the jobs are done, all cluster nodes will be shut down and you will not incur any additional compute charges. " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# optionally, delete the Azure Managed Compute cluster\n", + "compute_target.delete()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Next steps\n", + "\n", + "In this Azure Machine Learning tutorial, you used Python to:\n", + "\n", + "> * Set up your development environment\n", + "> * Access and examine the data\n", + "> * Train a simple logistic regression locally using the popular scikit-learn machine learning library\n", + "> * Train multiple models on a remote cluster\n", + "> * Review training details and register the best model\n", + "\n", + "You are ready to deploy this registered model using the instructions in the next part of the tutorial series:\n", + "\n", + "> [Tutorial 2 - Deploy models](deploy-models.ipynb)" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python [default]", + "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.6" + }, + "msauthor": "sgilley" + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/tutorials/02.deploy-models.ipynb b/tutorials/02.deploy-models.ipynb new file mode 100644 index 000000000..7fc4e1a14 --- /dev/null +++ b/tutorials/02.deploy-models.ipynb @@ -0,0 +1,478 @@ +{ + "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": [ + "# Tutorial #2: Deploy an image classification model in Azure Container Instance (ACI)\n", + "\n", + "This tutorial is **part two of a two-part tutorial series**. In the [previous tutorial](01.train-models.ipynb), you trained machine learning models and then registered the best one in your workspace on the cloud. \n", + "\n", + "Now, you're ready to deploy the model as a web service in [Azure Container Instances](https://docs.microsoft.com/azure/container-instances/) (ACI). A web service is an image, in this case a Docker image, that encapsulates the scoring logic and the model itself. \n", + "\n", + "In this part of the tutorial, you use Azure Machine Learning service (Preview) to:\n", + "\n", + "> * Set up your testing environment\n", + "> * Retrieve the model from your workspace\n", + "> * Test the model locally\n", + "> * Deploy the model to ACI\n", + "> * Test the deployed model\n", + "\n", + "ACI is not ideal for production deployments, but it is great for testing and understanding the workflow. For scalable production deployments, consider using AKS.\n", + "\n", + "\n", + "## Prerequisites\n", + "\n", + "Complete the model training in the [Tutorial #1: Train an image classification model with Azure Machine Learning](01.train-models.ipynb) notebook. \n", + "\n", + "\n", + "## Set up the environment\n", + "\n", + "Start by setting up a testing environment.\n", + "\n", + "### Import packages\n", + "\n", + "Import the Python packages needed for this tutorial." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "%matplotlib inline\n", + "import numpy as np\n", + "import matplotlib\n", + "import matplotlib.pyplot as plt\n", + " \n", + "import azureml\n", + "from azureml.core import Workspace, Run\n", + "\n", + "# display the core SDK version number\n", + "print(\"Azure ML SDK Version: \", azureml.core.VERSION)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Retrieve the model\n", + "\n", + "You registered a model in your workspace in the previous tutorial. Now, load this workspace and download the model to your local directory." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from azureml.core import Workspace\n", + "from azureml.core.model import Model\n", + "\n", + "ws = Workspace.from_config()\n", + "model=Model(ws, 'sklearn_mnist')\n", + "model.download(target_dir = '.')\n", + "import os \n", + "# verify the downloaded model file\n", + "os.stat('./sklearn_mnist_model.pkl')" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Test model locally\n", + "\n", + "Before deploying, make sure your model is working locally by:\n", + "* Loading test data\n", + "* Predicting test data\n", + "* Examining the confusion matrix\n", + "\n", + "### Load test data\n", + "\n", + "Load the test data from the **./data/** directory created during the training tutorial." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "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", + "\n", + "X_test = load_data('./data/test-images.gz', False) / 255.0\n", + "y_test = load_data('./data/test-labels.gz', True).reshape(-1)\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Predict test data\n", + "\n", + "Feed the test dataset to the model to get predictions." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import pickle\n", + "from sklearn.externals import joblib\n", + "\n", + "clf = joblib.load('./sklearn_mnist_model.pkl')\n", + "y_hat = clf.predict(X_test)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Examine the confusion matrix\n", + "\n", + "Generate a confusion matrix to see how many samples from the test set are classified correctly. Notice the mis-classified value for the incorrect predictions." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from sklearn.metrics import confusion_matrix\n", + "\n", + "conf_mx = confusion_matrix(y_test, y_hat)\n", + "print(conf_mx)\n", + "print('Overall accuracy:', np.average(y_hat == y_test))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Use `matplotlib` to display the confusion matrix as a graph. In this graph, the X axis represents the actual values, and the Y axis represents the predicted values. The color in each grid represents the error rate. The lighter the color, the higher the error rate is. For example, many 5's are mis-classified as 3's. Hence you see a bright grid at (5,3)." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "row_sums = conf_mx.sum(axis = 1, keepdims = True)\n", + "norm_conf_mx = conf_mx / row_sums\n", + "np.fill_diagonal(norm_conf_mx, 0)\n", + "\n", + "fig = plt.figure(figsize = (8,5))\n", + "ax = fig.add_subplot(111)\n", + "cax = ax.matshow(norm_conf_mx, cmap = plt.cm.bone)\n", + "ticks = np.arange(0, 10, 1)\n", + "ax.set_xticks(ticks)\n", + "ax.set_yticks(ticks)\n", + "ax.set_xticklabels(ticks)\n", + "ax.set_yticklabels(ticks)\n", + "fig.colorbar(cax)\n", + "plt.ylabel('true labels', fontsize=14)\n", + "plt.xlabel('predicted values', fontsize=14)\n", + "plt.savefig('conf.png')\n", + "plt.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Deploy as web service\n", + "\n", + "Once you've tested the model and are satisfied with the results, deploy the model as a web service hosted in ACI. \n", + "\n", + "To build the correct environment for ACI, provide the following:\n", + "* A scoring script to show how to use the model\n", + "* An environment file to show what packages need to be installed\n", + "* A configuration file to build the ACI\n", + "* The model you trained before\n", + "\n", + "### Create scoring script\n", + "\n", + "Create the scoring script, called score.py, used by the web service call to show how to use the model.\n", + "\n", + "You must include two required functions into the scoring script:\n", + "* The `init()` function, which typically loads the model into a global object. This function is executed only once when the Docker container is started. \n", + "\n", + "* The `run(input_data)` function uses the model to predict a value based on the input data. Inputs and outputs to the run typically use JSON for serialization and de-serialization, but other formats are supported." + ] + }, + { + "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 pickle\n", + "from sklearn.externals import joblib\n", + "from sklearn.linear_model import LogisticRegression\n", + "\n", + "#from azureml.assets.persistence.persistence import get_model_path\n", + "from azureml.core.model import Model\n", + "\n", + "def init():\n", + " global model\n", + " # retreive the local path to the model using the model name\n", + " model_path = Model.get_model_path('sklearn_mnist')\n", + " model = joblib.load(model_path)\n", + "\n", + "def run(raw_data):\n", + " data = np.array(json.loads(raw_data)['data'])\n", + " # make prediction\n", + " y_hat = model.predict(data)\n", + " return json.dumps(y_hat.tolist())" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Create environment file\n", + "\n", + "Next, create an environment file, called myenv.yml, that specifies all of the script's package dependencies. This file is used to ensure that all of those dependencies are installed in the Docker image. This model needs `scikit-learn` and `azureml-sdk`." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "%%writefile myenv.yml\n", + "name: myenv\n", + "channels:\n", + " - defaults\n", + "dependencies:\n", + " - scikit-learn\n", + " - pip:\n", + " # Required packages for AzureML execution, history, and data preparation.\n", + " - --extra-index-url https://azuremlsdktestpypi.azureedge.net/sdk-release/Preview/E7501C02541B433786111FE8E140CAA1\n", + " - azureml-core" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Create configuration file\n", + "\n", + "Create a deployment configuration file and specify the number of CPUs and gigabyte of RAM needed for your ACI container. While it depends on your model, the default of 1 core and 1 gigabyte of RAM is usually sufficient for many models. If you feel you need more later, you would have to recreate the image and redeploy the service." + ] + }, + { + "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 = {\"data\": \"MNIST\", \"method\" : \"sklearn\"}, \n", + " description = 'Predict MNIST with sklearn')" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Deploy in ACI\n", + "Estimated time to complete: **about 7-8 minutes**\n", + "\n", + "Configure the image and deploy. The following code goes through these steps:\n", + "\n", + "1. Build an image using:\n", + " * The scoring file (`score.py`)\n", + " * The environment file (`myenv.yml`)\n", + " * The model file\n", + "1. Register that image under the workspace. \n", + "1. Send the image to the ACI container.\n", + "1. Start up a container in ACI using the image.\n", + "1. Get the web service HTTP endpoint." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "%%time\n", + "from azureml.core.webservice import Webservice\n", + "from azureml.core.image import ContainerImage\n", + "\n", + "# configure the image\n", + "image_config = ContainerImage.image_configuration(execution_script = \"score.py\", \n", + " runtime = \"python\", \n", + " conda_file = \"myenv.yml\")\n", + "\n", + "service = Webservice.deploy_from_model(workspace = ws,\n", + " name = 'sklearn-mnist-model',\n", + " deployment_config = aciconfig,\n", + " models = [model],\n", + " image_config = image_config)\n", + "\n", + "service.wait_for_deployment(show_output = True)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Get the scoring web service's HTTP endpoint, which accepts REST client calls. This endpoint can be shared with anyone who wants to test the web service or integrate it into an application." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "print(service.scoring_uri)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Test deployed service\n", + "\n", + "Earlier you scored all the test data with the local version of the model. Now, you can test the deployed model with a random sample of 30 images from the test data. \n", + "\n", + "The following code goes through these steps:\n", + "1. Send the data as a JSON array to the web service hosted in ACI. \n", + "\n", + "1. Use the SDK's `run` API to invoke the service. You can also make raw calls using any HTTP tool such as curl.\n", + "\n", + "1. Print the returned predictions and plot them along with the input images. Red font and inverse image (white on black) is used to highlight the misclassified samples. \n", + "\n", + " Since the model accuracy is high, you might have to run the following code 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 = result[i], 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": [ + "## Clean up resources\n", + "\n", + "To keep the resource group and workspace for other tutorials and exploration, you can delete only the ACI deployment using this API call:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "service.delete()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n", + "If you're not going to use what you've created here, delete the resources you just created with this quickstart so you don't incur any charges. In the Azure portal, select and delete your resource group. You can also keep the resource group, but delete a single workspace by displaying the workspace properties and selecting the Delete button.\n", + "\n", + "\n", + "## Next steps\n", + "\n", + "In this Azure Machine Learning tutorial, you used Python to:\n", + "\n", + "> * Set up your testing environment\n", + "> * Retrieve the model from your workspace\n", + "> * Test the model locally\n", + "> * Deploy the model to ACI\n", + "> * Test the deployed model\n", + " \n", + "You can also try out the [Automatic algorithm selection tutorial](03.auto-train-models.ipynb) to see how Azure Machine Learning can auto-select and tune the best algorithm for your model and build that model for you." + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python [default]", + "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.6" + }, + "msauthor": "sgilley" + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/tutorials/03.auto-train-models.ipynb b/tutorials/03.auto-train-models.ipynb new file mode 100644 index 000000000..cb5c5a308 --- /dev/null +++ b/tutorials/03.auto-train-models.ipynb @@ -0,0 +1,397 @@ +{ + "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": [ + "# Tutorial: Automatically train a classification model with Azure Automated Machine Learning\n", + "\n", + "In this tutorial, you'll learn how to automatically generate a machine learning model. This model can then be deployed following the workflow in the [Deploy a model](tutorial-deploy-models-with-aml.md) tutorial.\n", + "\n", + "[flow diagram](./imgs/nn.png)\n", + "\n", + "Similar to the [train models tutorial](tutorial-train-models-with-aml.md), this tutorial classifies handwritten images of digits (0-9) from the [MNIST](http://yann.lecun.com/exdb/mnist/) dataset.\n", + "\n", + "You'll learn how to:\n", + "\n", + "> * Set up your development environment\n", + "> * Access and examine the data\n", + "> * Train using an automated classifier locally with custom parameters\n", + "> * Explore the results\n", + "> * Review training results\n", + "> * Register the best model\n", + "\n", + "## Prerequisites\n", + "\n", + "Use [these instructions](https://aka.ms/aml-how-to-configure-environment) to: \n", + "* Create a workspace and its configuration file (**config.json**) \n", + "* Upload your **config.json** to the same folder as this notebook" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Start a notebook\n", + "\n", + "To follow along, start a new notebook from the same directory as **config.json** and copy the code from the sections below.\n", + "\n", + "\n", + "## Set up your development environment\n", + "\n", + "All the setup for your development work can be accomplished in the Python notebook. Setup includes:\n", + "\n", + "* Import Python packages\n", + "* Configure a workspace to enable communication between your local computer and remote resources\n", + "* Create a directory to store training scripts\n", + "\n", + "### Import packages\n", + "Import Python packages you need in this tutorial." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import azureml.core\n", + "import pandas as pd\n", + "from azureml.core.workspace import Workspace\n", + "from azureml.train.automl.run import AutoMLRun\n", + "import time\n", + "import logging\n", + "from sklearn import datasets\n", + "import seaborn as sns\n", + "from matplotlib import pyplot as plt\n", + "from matplotlib.pyplot import imshow\n", + "import random\n", + "import numpy as np" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Configure workspace\n", + "\n", + "Create a workspace object from the existing workspace. `Workspace.from_config()` reads the file **aml_config/config.json** and loads the details into an object named `ws`. `ws` is used throughout the rest of the code in this tutorial.\n", + "\n", + "Once you have a workspace object, specify a name for the experiment and create and register a local directory with the workspace. The history of all runs is recorded under the specified experiment." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "ws = Workspace.from_config()\n", + "# choose a name for the run history container in the workspace\n", + "experiment_name = 'automl-classifier'\n", + "# project folder\n", + "project_folder = './automl-classifier'\n", + "\n", + "import os\n", + "\n", + "output = {}\n", + "output['SDK version'] = azureml.core.VERSION\n", + "output['Subscription ID'] = ws.subscription_id\n", + "output['Workspace'] = ws.name\n", + "output['Resource Group'] = ws.resource_group\n", + "output['Location'] = ws.location\n", + "output['Project Directory'] = project_folder\n", + "pd.set_option('display.max_colwidth', -1)\n", + "pd.DataFrame(data=output, index=['']).T" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Explore data\n", + "\n", + "The initial training tutorial used a high-resolution version of the MNIST dataset (28x28 pixels). Since auto training requires many iterations, this tutorial uses a smaller resolution version of the images (8x8 pixels) to demonstrate the concepts while speeding up the time needed for each iteration." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from sklearn import datasets\n", + "\n", + "digits = datasets.load_digits()\n", + "\n", + "# only take the first 100 rows if you want the training steps to run faster\n", + "X_digits = digits.data[100:,:]\n", + "y_digits = digits.target[100:]\n", + "\n", + "# use full dataset\n", + "#X_digits = digits.data\n", + "#y_digits = digits.target" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Display some sample images\n", + "\n", + "Load the data into `numpy` arrays. Then use `matplotlib` to plot 30 random images from the dataset with their labels above them." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "count = 0\n", + "sample_size = 30\n", + "plt.figure(figsize = (16, 6))\n", + "for i in np.random.permutation(X_digits.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 = 2, y = -2, s = y_digits[i], fontsize = 18)\n", + " plt.imshow(X_digits[i].reshape(8, 8), cmap = plt.cm.Greys)\n", + "plt.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "You now have the necessary packages and data ready for auto training for your model. \n", + "\n", + "## Auto train a model \n", + "\n", + "To auto train a model, first define settings for autogeneration and tuning and then run the automatic classifier.\n", + "\n", + "\n", + "### Define settings for autogeneration and tuning\n", + "\n", + "Define the experiment parameters and models settings for autogeneration and tuning. \n", + "\n", + "\n", + "|Property| Value in this tutorial |Description|\n", + "|----|----|---|\n", + "|**primary_metric**|AUC Weighted | Metric that you want to optimize.|\n", + "|**max_time_sec**|12,000|Time limit in seconds for each iteration|\n", + "|**iterations**|20|Number of iterations. In each iteration, the model trains with the data with a specific pipeline|\n", + "|**n_cross_validations**|5|Number of cross validation splits|\n", + "|**preprocess**|True| *True/False* Enables experiment to perform preprocessing on the input. Preprocessing handles *missing data*, and performs some common *feature extraction*|\n", + "|**exit_score**|0.994|*double* value indicating the target for *primary_metric*. Once the target is surpassed the run terminates|\n", + "|**blacklist_algos**|['kNN','LinearSVM']|*Array* of *strings* indicating algorithms to ignore.\n", + "|**concurrent_iterations**|5|Max number of iterations that would be executed in parallel. This number should be less than the number of cores on the DSVM. Used in remote training.|" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from azureml.train.automl import AutoMLConfig\n", + "\n", + "##Local compute \n", + "Automl_config = AutoMLConfig(task = 'classification',\n", + " primary_metric = 'AUC_weighted',\n", + " max_time_sec = 12000,\n", + " iterations = 20,\n", + " n_cross_validations = 3,\n", + " blacklist_algos = ['kNN','LinearSVM'],\n", + " X = X_digits,\n", + " y = y_digits,\n", + " path=project_folder)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Run the automatic classifier\n", + "\n", + "Start the experiment to run locally. Define the compute target as local and set the output to true to view progress on the experiment." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from azureml.core.experiment import Experiment\n", + "experiment=Experiment(ws, experiment_name)\n", + "local_run = experiment.submit(Automl_config, show_output=True)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Explore the results\n", + "\n", + "Explore the results of automatic training with a Jupyter widget or by examining the experiment history.\n", + "\n", + "### Jupyter widget\n", + "\n", + "Use the Jupyter notebook widget to see a graph and a table of all results." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from azureml.train.widgets import RunDetails\n", + "RunDetails(local_run).show()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Retrieve all iterations\n", + "\n", + "View the experiment history and see individual metrics for each iteration run." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "children = list(local_run.get_children())\n", + "metricslist = {}\n", + "for run in children:\n", + " properties = run.get_properties()\n", + " metrics = {k: v for k, v in run.get_metrics().items() if isinstance(v, float)} \n", + " metricslist[int(properties['iteration'])] = metrics\n", + "\n", + "import pandas as pd\n", + "import seaborn as sns\n", + "rundata = pd.DataFrame(metricslist).sort_index(1)\n", + "cm = sns.light_palette(\"lightgreen\", as_cmap = True)\n", + "s = rundata.style.background_gradient(cmap = cm)\n", + "s" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# find the run with the highest accuracy value.\n", + "best_run, fitted_model = local_run.get_output()\n", + "\n", + "# register model in workspace\n", + "description = 'Automated Machine Learning Model'\n", + "tags = None\n", + "local_run.register_model(description=description, tags=tags)\n", + "local_run.model_id # Use this id to deploy the model as a web service in Azure" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Test the best model\n", + "\n", + "Use the model to predict a few random digits. Display the predicted and the image. Red font and inverse image (white on black) is used to highlight the misclassified samples.\n", + "\n", + "Since the model accuracy is high, you might have to run the following code a few times before you can see a misclassified sample." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# find 30 random samples from test set\n", + "n = 30\n", + "sample_indices = np.random.permutation(X_digits.shape[0])[0:n]\n", + "test_samples = X_digits[sample_indices]\n", + "\n", + "\n", + "# predict using the model\n", + "result = fitted_model.predict(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_digits[s] != result[i] else 'black'\n", + " clr_map = plt.cm.gray if y_digits[s] != result[i] else plt.cm.Greys\n", + " \n", + " plt.text(x = 2, y = -2, s = result[i], fontsize = 18, color = font_color)\n", + " plt.imshow(X_digits[s].reshape(8, 8), cmap = clr_map)\n", + " \n", + " i = i + 1\n", + "plt.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Next steps\n", + "\n", + "In this Azure Machine Learning tutorial, you used Python to:\n", + "\n", + "> * Set up your development environment\n", + "> * Access and examine the data\n", + "> * Train using an automated classifier locally with custom parameters\n", + "> * Explore the results\n", + "> * Review training results\n", + "> * Register the best model\n", + "\n", + "Learn more about [how to configure settings for automatic training]() or [how to use automatic training on a remote resource]()." + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python [default]", + "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.6" + }, + "msauthor": "sgilley" + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/tutorials/imgs/flow2.png b/tutorials/imgs/flow2.png new file mode 100644 index 0000000000000000000000000000000000000000..f5c8968b67a600696b0c46109cff6d4d9c5433f3 GIT binary patch literal 106278 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

0o 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 literal 0 HcmV?d00001 diff --git a/tutorials/utils.py b/tutorials/utils.py new file mode 100644 index 000000000..98170adae --- /dev/null +++ b/tutorials/utils.py @@ -0,0 +1,27 @@ +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. + +import gzip +import numpy as np +import struct + + +# load compressed MNIST gz files and return numpy arrays +def load_data(filename, label=False): + with gzip.open(filename) as gz: + struct.unpack('I', gz.read(4)) + n_items = struct.unpack('>I', gz.read(4)) + if not label: + n_rows = struct.unpack('>I', gz.read(4))[0] + n_cols = struct.unpack('>I', gz.read(4))[0] + res = np.frombuffer(gz.read(n_items[0] * n_rows * n_cols), dtype=np.uint8) + res = res.reshape(n_items[0], n_rows * n_cols) + else: + res = np.frombuffer(gz.read(n_items[0]), dtype=np.uint8) + res = res.reshape(n_items[0], 1) + return res + + +# one-hot encode a 1-D array +def one_hot_encode(array, num_of_classes): + return np.eye(num_of_classes)[array.reshape(-1)]