├── aml_config ├── local.compute ├── docker.compute ├── spark_dependencies.yml ├── conda_dependencies_srvc.yml ├── conda_dependencies.yml ├── local.runconfig ├── docker.runconfig └── jupyter_notebook_config.py ├── license.txt ├── README.md ├── 3-prepare-service.ipynb ├── 2-model-building.ipynb └── 4-deploy-model.ipynb /aml_config/local.compute: -------------------------------------------------------------------------------- 1 | # Defines a local compute target that uses an existing python environment. 2 | type: "local" 3 | 4 | # Specifies the user-managed python environment for the run. By default this 5 | # is "python" which uses the currently active python environment. The Azure ML 6 | # Workbench will use the python environment installed with it and the Azure ML 7 | # CLI will use whatever python environment it was installed into. 8 | # 9 | # You can change this to point at any python environment on your system, 10 | # including virtual environments and Conda environments. Note that backslashes 11 | # need to be escaped in this path, so it's easier to use forward slashes. 12 | pythonLocation: "python" 13 | 14 | # The $AZUREML_NATIVE_SHARE_DIRECTORY environment variable inside runs points 15 | # at a persistent directory that is shared between all runs of the same project 16 | # on the same target. This specifies the base path for those directories. 17 | nativeSharedDirectory: "~/.azureml/share/" -------------------------------------------------------------------------------- /aml_config/docker.compute: -------------------------------------------------------------------------------- 1 | # Defines a localdocker compute target that uses a local Docker container. 2 | type: "localdocker" 3 | 4 | # The base image for the Docker container. This is used to provision Spark and 5 | # the Conda package manager. Supported based images are microsoft/mmlspark:plus 6 | # variants. The default 0.7.91 version includes Spark 2.1.1. 7 | # Available mmlspark images: https://hub.docker.com/r/microsoft/mmlspark/tags/ 8 | baseDockerImage: "microsoft/mmlspark:plus-0.7.91" 9 | 10 | # Azure ML Workbench uses the Docker shared volumes feature to improve run 11 | # performance and to enable the automatic mounting of the shared directory. 12 | # This Docker features isn't completely stable yet on Windows, and so it's 13 | # disabled by default to ensure compatibility. 14 | sharedVolumes: false 15 | 16 | # The $AZUREML_NATIVE_SHARE_DIRECTORY environment variable inside runs points 17 | # at a persistent directory that is shared between all runs of the same project 18 | # on the same target. This specifies the base path for those directories. 19 | # Note that this is not available if sharedVolumes is false. 20 | nativeSharedDirectory: "~/.azureml/share/" 21 | -------------------------------------------------------------------------------- /license.txt: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) Microsoft Corporation. All rights reserved. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE 22 | -------------------------------------------------------------------------------- /aml_config/spark_dependencies.yml: -------------------------------------------------------------------------------- 1 | # Spark configuration and packages specification. The dependencies defined in 2 | # this file will be automatically provisioned for runs that use Spark. 3 | 4 | # For managing third-party python libraries, see conda_dependencies.yml. 5 | 6 | # Spark configuration properties. 7 | configuration: 8 | "spark.app.name": "Azure ML Experiment" 9 | "spark.yarn.maxAppAttempts": 1 10 | 11 | # Repositories to search for the specified Spark packages. 12 | repositories: 13 | - "https://mmlspark.azureedge.net/maven" 14 | 15 | # Spark packages to include in the run. 16 | packages: 17 | # Microsoft Machine Learning for Apache Spark provides a number of deep 18 | # learning and data science tools, including seamless integration of Spark 19 | # Machine Learning pipelines with Microsoft Cognitive Toolkit (CNTK) and 20 | # OpenCV, enabling you to quickly create powerful, highly-scalable 21 | # predictive and analytical models for large image and text datasets. 22 | # Details: https://github.com/Azure/mmlspark 23 | - group: "com.microsoft.ml.spark" 24 | artifact: "mmlspark_2.11" 25 | version: "0.7.91" 26 | 27 | # Required for SQL Server data sources. 28 | - group: "com.microsoft.sqlserver" 29 | artifact: "mssql-jdbc" 30 | version: "6.2.1.jre8" 31 | -------------------------------------------------------------------------------- /aml_config/conda_dependencies_srvc.yml: -------------------------------------------------------------------------------- 1 | # Conda environment specification. The dependencies defined in this file will 2 | # be automatically provisioned for managed runs. These include runs against 3 | # the localdocker, remotedocker, and cluster compute targets. 4 | 5 | # Note that this file is NOT used to automatically manage dependencies for the 6 | # local compute target. To provision these dependencies locally, run: 7 | # conda env update --file conda_dependencies.yml 8 | 9 | # Details about the Conda environment file format: 10 | # https://conda.io/docs/using/envs.html#create-environment-file-by-hand 11 | 12 | # For managing Spark packages and configuration, see spark_dependencies.yml. 13 | 14 | name: project_environment 15 | channels: 16 | # Required for jasper 17 | - conda-forge 18 | dependencies: 19 | # The python interpreter version. 20 | # Currently Azure ML Workbench only supports 3.5.2. 21 | - python=3.5.2 22 | - scikit-learn 23 | 24 | # Required for Jupyter Notebooks. 25 | #- ipykernel=4.6.1 26 | 27 | # Required for CNTK 2.2 28 | - libpng=1.2 29 | - jasper 30 | 31 | - pip: 32 | # The API for Azure Machine Learning Model Management Service. 33 | # Details: https://github.com/Azure/Machine-Learning-Operationalization 34 | - azure-ml-api-sdk==0.1.0a10 35 | - pillow 36 | - pandas 37 | #- tqdm 38 | - matplotlib 39 | - lightgbm==2.0.7 40 | #- lightgbm --install-option=--gpu 41 | - keras==2.0.8 42 | #- https://cntk.ai/PythonWheel/GPU/cntk-2.2-cp35-cp35m-win_amd64.whl 43 | #- https://cntk.ai/PythonWheel/GPU/cntk-2.2-cp35-cp35m-linux_x86_64.whl 44 | - https://cntk.ai/PythonWheel/CPU-Only/cntk-2.2-cp35-cp35m-linux_x86_64.whl 45 | - h5py 46 | 47 | # Helper utilities for dealing with Azure ML Workbench Assets. 48 | - https://azuremldownloads.blob.core.windows.net/wheels/latest/azureml.assets-1.0.0-py3-none-any.whl?sv=2016-05-31&si=ro-2017&sr=c&sig=xnUdTm0B%2F%2FfknhTaRInBXyu2QTTt8wA3OsXwGVgU%2BJk%3D 49 | -------------------------------------------------------------------------------- /aml_config/conda_dependencies.yml: -------------------------------------------------------------------------------- 1 | # Conda environment specification. The dependencies defined in this file will 2 | # be automatically provisioned for managed runs. These include runs against 3 | # the localdocker, remotedocker, and cluster compute targets. 4 | 5 | # Note that this file is NOT used to automatically manage dependencies for the 6 | # local compute target. To provision these dependencies locally, run: 7 | # conda env update --file conda_dependencies.yml 8 | 9 | # Details about the Conda environment file format: 10 | # https://conda.io/docs/using/envs.html#create-environment-file-by-hand 11 | 12 | # For managing Spark packages and configuration, see spark_dependencies.yml. 13 | 14 | name: project_environment 15 | channels: 16 | # Required for jasper 17 | - conda-forge 18 | dependencies: 19 | # The python interpreter version. 20 | # Currently Azure ML Workbench only supports 3.5.2. 21 | - python=3.5.2 22 | - scikit-learn 23 | 24 | # Required for Jupyter Notebooks. 25 | - ipykernel=4.6.1 26 | 27 | # Required for CNTK 2.2 28 | - libpng=1.2 29 | - jasper 30 | 31 | - pip: 32 | # The API for Azure Machine Learning Model Management Service. 33 | # Details: https://github.com/Azure/Machine-Learning-Operationalization 34 | - azure-ml-api-sdk==0.1.0a10 35 | - pillow 36 | - pandas 37 | - tqdm 38 | - matplotlib 39 | - notebook 40 | - lightgbm==2.0.7 41 | #- lightgbm --install-option=--gpu 42 | - keras==2.0.8 43 | #- https://cntk.ai/PythonWheel/GPU/cntk-2.2-cp35-cp35m-win_amd64.whl 44 | - https://cntk.ai/PythonWheel/GPU/cntk-2.2-cp35-cp35m-linux_x86_64.whl 45 | #- https://cntk.ai/PythonWheel/CPU-Only/cntk-2.2-cp35-cp35m-linux_x86_64.whl 46 | - h5py 47 | 48 | # Helper utilities for dealing with Azure ML Workbench Assets. 49 | - https://azuremldownloads.blob.core.windows.net/wheels/latest/azureml.assets-1.0.0-py3-none-any.whl?sv=2016-05-31&si=ro-2017&sr=c&sig=xnUdTm0B%2F%2FfknhTaRInBXyu2QTTt8wA3OsXwGVgU%2BJk%3D 50 | -------------------------------------------------------------------------------- /aml_config/local.runconfig: -------------------------------------------------------------------------------- 1 | # The program name and arguments to run when they aren't specified through 2 | # other means. The $file token is replaced with the currently selected file 3 | # by the Workbench application. 4 | ArgumentVector: 5 | - "$file" 6 | 7 | # The name of the compute target to use for this run. 8 | Target: "local" 9 | 10 | # Environment variables set for the run. 11 | EnvironmentVariables: 12 | "EXAMPLE_ENV_VAR": "Example Value" 13 | 14 | # Framework to execute inside. Allowed values are "Python" and "PySpark". 15 | Framework: "Python" 16 | 17 | # Path to the Conda dependencies file to use for this run. If a project 18 | # contains multiple programs with different sets of dependencies, it may be 19 | # convenient to manage those environments with separate files. 20 | CondaDependenciesFile: "aml_config/conda_dependencies.yml" 21 | 22 | # Path to the Spark dependencies file to use for this run. If a project 23 | # contains multiple programs with different sets of dependencies, it may be 24 | # convenient to manage those environments with separate files. 25 | SparkDependenciesFile: "aml_config/spark_dependencies.yml" 26 | 27 | # Automatically prepare the run environment as part of the run itself. 28 | # Manual preparation of a compute target can be perfomed with: 29 | # az ml experiment prepare --run-configuration 30 | PrepareEnvironment: false 31 | 32 | # Enable history tracking -- this allows status, logs, metrics, and outputs 33 | # to be collected by Azure ML Workbench and uploaded to the cloud project. 34 | TrackedRun: true 35 | 36 | # The UseSampling setting controls the use of sample data or complete data on 37 | # all .dsource and .dprep files. Setting this to false will read all the data 38 | # when loading data using either a .dsource or .dprep file. 39 | UseSampling: true 40 | 41 | # For each data source (.dsource or .dprep file), the Sample setting allows a 42 | # specific named sample to be used, overriding the active sample set in the 43 | # .dsource or .dprep file. 44 | # DataSourceSettings: 45 | # my.dsource: 46 | # Sampling: 47 | # Sample: MySampleName 48 | 49 | # Data source substitutions allows all references to a .dsource, be it in a 50 | # .dprep file or in your code to load data, to instead use a different .dsource. 51 | # It can be useful to setup two .dsources and then substitute the second one 52 | # when running in some compute targets. 53 | # DataSourceSubstitutions: 54 | # my.dsource: replacement.dsource 55 | 56 | -------------------------------------------------------------------------------- /aml_config/docker.runconfig: -------------------------------------------------------------------------------- 1 | # The program name and arguments to run when they aren't specified through 2 | # other means. The $file token is replaced with the currently selected file 3 | # by the Workbench application. 4 | ArgumentVector: 5 | - "$file" 6 | 7 | # The name of the compute target to use for this run. 8 | Target: "docker" 9 | 10 | # Environment variables set for the run. 11 | EnvironmentVariables: 12 | "EXAMPLE_ENV_VAR": "Example Value" 13 | 14 | # Framework to execute inside. Allowed values are "Python" and "PySpark". 15 | Framework: "PySpark" 16 | 17 | # Path to the Conda dependencies file to use for this run. If a project 18 | # contains multiple programs with different sets of dependencies, it may be 19 | # convenient to manage those environments with separate files. 20 | CondaDependenciesFile: "aml_config/conda_dependencies.yml" 21 | 22 | # Path to the Spark dependencies file to use for this run. If a project 23 | # contains multiple programs with different sets of dependencies, it may be 24 | # convenient to manage those environments with separate files. 25 | SparkDependenciesFile: "aml_config/spark_dependencies.yml" 26 | 27 | # Automatically prepare the run environment as part of the run itself. 28 | # Manual preparation of a compute target can be perfomed with: 29 | # az ml experiment prepare --run-configuration 30 | PrepareEnvironment: false 31 | 32 | # Enable history tracking -- this allows status, logs, metrics, and outputs 33 | # to be collected by Azure ML Workbench and uploaded to the cloud project. 34 | TrackedRun: true 35 | 36 | # The UseSampling setting controls the use of sample data or complete data on 37 | # all .dsource and .dprep files. Setting this to false will read all the data 38 | # when loading data using either a .dsource or .dprep file. 39 | UseSampling: true 40 | 41 | # For each data source (.dsource or .dprep file), the Sample setting allows a 42 | # specific named sample to be used, overriding the active sample set in the 43 | # .dsource or .dprep file. 44 | # DataSourceSettings: 45 | # my.dsource: 46 | # Sampling: 47 | # Sample: MySampleName 48 | 49 | # Data source substitutions allows all references to a .dsource, be it in a 50 | # .dprep file or in your code to load data, to instead use a different .dsource. 51 | # It can be useful to setup two .dsources and then substitute the second one 52 | # when running in some compute targets. 53 | # DataSourceSubstitutions: 54 | # my.dsource: replacement.dsource 55 | 56 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Copyright (C) Microsoft Corporation. 2 | 3 | # Image Classification Using Transfer Learning 4 | 5 | Image classification is one of the most popular areas of deep learning and there are many real world business scenarios that benefit from its application. As a real world example, one application area is detecting quality issues on assembly lines during the manufacturing process. 6 | 7 | In a typical production line, components travel down the assembly line from one station to the other and at the end of each station they are analyzed by human inspectors to spot any problems. However, this is a very manual process which involves a lot of human effort which may in turn lead to delays in the production lines. By using image classification, it is possible to reduce human effort and deploy a system that automatically classifies images of components into pass and fail. In turn, this can greatly speed up the efficiency of the human operators in their validation process and improves the quality of the overall manufacturing process. 8 | 9 | This example highlights the transfer learning approach which uses a pretrained model to generate visual features of images which are later used to train a binary classifier. You can run this example by importing your own image data assuming you have two classes of images or you can use a generic dataset as will be descibed in the step-by-step instructions. 10 | 11 | In this example, you will train your models through Azure Machine Learning Workbench’s Jupyter notebook service. You will be using GPU power on Linux Data Science Virtual Machines through containerized remote execution target capability provided by Azure Machine Learning Workbench. You will then operationalize your models on local docker and also on Azure Container Services Cluster and consume the web services by scoring your images in real time. 12 | 13 | You can start by following the step-by-step instructions below. 14 | 15 | # Prerequisites 16 | You will need an [Azure account](https://azure.microsoft.com/en-us/free/) (free trials are available) to perform the following tasks in the instructions below. 17 | - You will provision Azure Machine Learning Accounts. 18 | - You will create a Data Science Virtual Machine. 19 | - You will creat an Azure Storage Account to host your training data. 20 | 21 | # Create Azure Machine Learning Accounts 22 | In order to use Azure Machine Learning Workbench, you will first provision Azure Machine Learning accounts using the Azure portal. 23 | 24 | 1. Open your web browser and go to the [Azure portal](https://portal.azure.com/). 25 | 2. Follow the steps under [Create Azure Machine Learning Accounts](https://docs.microsoft.com/en-us/azure/machine-learning/preview/quickstart-installation#create-azure-machine-learning-accounts) to provision the experimentation and model management accounts. 26 | 3. If you don't already have Azure Machine Learning Workbench installed, follow the steps under [Install Azure Machine Learning Workbench on Windows](https://docs.microsoft.com/en-us/azure/machine-learning/preview/quickstart-installation#install-azure-machine-learning-workbench-on-windows) to complete the installation. 27 | 4. Find the shortcut to Azure Machine Learning Workbench on your desktop to start the app. 28 | 5. Sign in to Workbench by using the same account that you used earlier to provision your Azure Machine Learning accounts. 29 | 6. When the sign-in process has succeeded, Workbench attempts to find the Machine Learning Experimentation accounts that you created earlier. It searches for all Azure subscriptions to which your credential has access. When at least one Experimentation account is found, Workbench opens with that account. It then lists the workspaces and projects found in that account. 30 | 31 | # Creating a New Project from Visual Studio Team Services GIT Repository 32 | 33 | You will download the project from Visual Studio Team Services GIT Repository. In Workbench, follow the steps below: 34 | 35 | 1. Select **File > New Project** (or select the + sign in the **PROJECTS** pane). 36 | 2. Fill in the **Project name** and **Project directory** boxes. **Project description** is optional but helpful. Use the following URL for **Visualstudio GIT Repository URL** box: "URL for the project repository here!!" 37 | 3. Select the **Create** button to create the project. A new project is created and opened for you. At this point, you can explore the project home page, notebooks, and other files. 38 | 39 | # Login to Azure Account and Set Subscription 40 | 1. From your Azure Machine Learning Workbench, open a Command Line Interface (CLI) by clicking **File -> Open Command Prompt**. 41 | 2. From the CLI, log in to your Azure account by running the following command: 42 | ```bash 43 | az login 44 | ``` 45 | 3. You will be asked to visit a URL and type in a provided temporary code, the website will request your Azure account credentials. 46 | 4. Run the following to see a list of subscriptions available in your Azure account. 47 | ```bash 48 | az account list -o table 49 | ``` 50 | 4. Find the "name" of the subscription you would like to use and insert it where indicated in the following command and run it to set the active subscription. 51 | ```bash 52 | az account set --subscription "Your Subscription Name Here!!" 53 | ``` 54 | 5. Verify that your account is set by running the following command. 55 | ```bash 56 | az account show 57 | ``` 58 | 59 | # Configuring Execution in Docker Environment on Linux Data Science Virtual Machines with GPUs 60 | Main notebooks of this scenario will be executed in *docker* environment on a Linux Data Science Virtual Machine (DSVM) with GPUs. While it is possible to use GPUs on any Linux virtual machine, the Ubuntu-based DSVM comes with the required CUDA drivers and libraries pre-installed, making the set-up much easier. If you don't already have one, follow the below steps to create one. 61 | 62 | ## Create an Ubuntu-based Linux DSVM with GPU 63 | 1. Open your web browser and go to the [Azure portal](https://portal.azure.com/). 64 | 2. Select **+ New** on the left of the portal. 65 | 3. Search for "Data Science Virtual Machine for Linux (Ubuntu)" in the marketplace. 66 | 4. Click *Create* to create an Ubuntu DSVM. 67 | 5. Fill in the *Basics* form with the required information. For *Authentication type* select *Password* and take note of your user name and password. 68 | 6. When selecting the location for your VM, note that GPU VMs are only available in certain Azure regions, for example, South Central US. See compute products available by region. Click OK to save the *Basics* information. 69 | 6. Choose the size of the virtual machine. Select one of the sizes with NC-prefixed VMs, which are equipped with NVidia GPU chips. Click *View All* to see the full list as needed. 70 | 7. Finish the remaining settings and review the purchase information. Click *Purchase* to create the VM. Take note of the IP address allocated to the virtual machine. You can continue with the next steps while your DSVM is created. 71 | 72 | In the next section, you will be creating a compute context as remote docker on the Ubuntu-based Linux DSVM you created in the earlier step. This allows an easy path to execution on a remote DSVM with GPUs. 73 | 74 | ## Create compute target as remote docker 75 | 1. From Azure ML Workbench, launch the Command Line Window by clicking **Open Command Prompt** under **File** menu. 76 | 2. Enter the following command by providing your own values for IP address, username, and password for the Virtual Machine(VM) you created in the earlier section. 77 | 78 | ``` 79 | az ml computetarget attach remotedocker --name myvm --address [Your VM's IP Address Here!!] --username [Your User Name Here!!] --password 80 | [Your Password Here!!] 81 | ``` 82 | 83 | ## Configure Azure ML Workbench to Access GPU 84 | 1. Go back to Azure ML Workbench and click on the **Files** which is the last item under the home icon on the left bar. 85 | 2. Under **Search Files** expand the **aml_config** folder. You will see two new files **myvm.compute** and **myvm.runconfig** (if not , hit the **Refresh** button). 86 | 3. Open myvm.compute and change the **baseDockerImage** to **microsoft/mmlspark:plus-gpu-0.9.9** and add a new line **nvidiaDocker: true** and save the file. The file should have the following two lines: 87 | ``` 88 | baseDockerImage: microsoft/mmlspark:plus-gpu-0.9.9 89 | nvidiaDocker: true 90 | ``` 91 | 4. Now, open **myvm.runconfig** and change **Framework** to **Python** and save the file. The file should have the following line: 92 | ``` 93 | Framework: Python 94 | ``` 95 | 5. Next, run the following command on the Command Line Window to prepare the Docker image on your DSVM. 96 | 97 | ``` 98 | az ml experiment prepare -c myvm 99 | ``` 100 | # Prepare Training Data 101 | 102 | This scenario assumes that you have two classes of images that you would like to classify as fail or pass. You can choose to bring in your own dataset given that you have two categories of images or use a generic dataset from Kaggle. If you are bringing your own dataset, zip your images in a file called train.zip. If not using your own dataset, go to the [Kaggle dataset page](https://www.kaggle.com/c/dogs-vs-cats/data) and click download button for the train.zip file. You will be asked to log in to Kaggle and once logged in you will be redirected back to the dataset page. The size of this train.zip file is 543.16 MB. After downloading the zip file, follow the steps below to upload it to Azure Blob Storage. 103 | 104 | ## Create a storage account 105 | 106 | 1. Sign in to the [Azure portal](https://portal.azure.com/). 107 | 2. In the Azure portal, expand the menu on the left side to open the menu of services, and choose **More Services**. Then, scroll down to **Storage**, and choose **Storage accounts**. On the Storage Accounts window that appears, choose **+Add**. 108 | 3. Enter a name for your storage account which must be between 3 and 24 characters in length and may contain numbers and lowercase letters only. Your storage account name must be unique within Azure. The Azure portal will indicate if the storage account name you select is already in use. You will use this name when executing the data ingestion notebook. 109 | 4. Select the subscription in which you want to create the new storage account and specify a new resource group or select an existing resource group. 110 | 5. Click **Create** to create the storage account 111 | 112 | Next, you will create a container and upload your data. 113 | 114 | ## Create a container and upload your data 115 | 116 | 1. Once the storage account is created, click **Blobs** on the the storage account page and choose **+Container**. 117 | 2. In the name field, enter **images** and choose **OK**. 118 | 3. Choose the newly created container images and click **Upload**. Select the location of train.zip file from your computer and click **Upload**. 119 | 120 | Next, you will locate your storage account key which will also be used when executing data ingestion notebook. 121 | 122 | ## Locate your storage account name and key 123 | 124 | 1. On the storage account page, click on **Access keys** under Settings. 125 | 2. You will see the name of your storage account and two keys. 126 | 3. You will use the value for key1 when executing the data ingestion notebook. You can copy the key using the copy button on the right side when needed. 127 | 128 | # Start Notebook Server from command line 129 | To run the notebooks of this scenario, you will need to start the Azure Machine Learning Workbench notebook server. You can either run the notebooks from within the Workbench or you can use your browser. Follow the steps below to start the notebook server from command line (CLI). 130 | 1. From Azure ML Workbench, launch the Command Line Window by clicking **Open Command Prompt** under **File** menu. 131 | 2. Run the following command. 132 | ``` 133 | az ml notebook start 134 | ``` 135 | 3. You will leave this window open during the execution of your notebooks as closing it will cause the notebook server to stop. You can monitor this window for notebook related messages. 136 | 4. Your default browser will automatically be launched with Jupyter server pointing to the project home directory. You can also use the URL and token displayed in the CLI window to launch notebooks on other browser windows. 137 | 5. Next, click on the first notebook, if you receive a prompt asking for which kernel to use, select the kernel with the name **"Your project name myvm"** with the exception of last notebook which will use the kernel **"Your project name local"**. You will find a note at the beggining of each notebook highlighting which kernel to use for that notebook. 138 | 7. Follow the instructions and excute the notebook cells. 139 | 140 | created by a Microsoft employee. -------------------------------------------------------------------------------- /aml_config/jupyter_notebook_config.py: -------------------------------------------------------------------------------- 1 | # Configuration file for jupyter-notebook. 2 | 3 | #------------------------------------------------------------------------------ 4 | # Application(SingletonConfigurable) configuration 5 | #------------------------------------------------------------------------------ 6 | 7 | ## This is an application. 8 | 9 | ## The date format used by logging formatters for %(asctime)s 10 | #c.Application.log_datefmt = '%Y-%m-%d %H:%M:%S' 11 | 12 | ## The Logging format template 13 | #c.Application.log_format = '[%(name)s]%(highlevel)s %(message)s' 14 | 15 | ## Set the log level by value or name. 16 | #c.Application.log_level = 30 17 | 18 | #------------------------------------------------------------------------------ 19 | # JupyterApp(Application) configuration 20 | #------------------------------------------------------------------------------ 21 | 22 | ## Base class for Jupyter applications 23 | 24 | ## Answer yes to any prompts. 25 | #c.JupyterApp.answer_yes = False 26 | 27 | ## Full path of a config file. 28 | #c.JupyterApp.config_file = '' 29 | 30 | ## Specify a config file to load. 31 | #c.JupyterApp.config_file_name = '' 32 | 33 | ## Generate default config file. 34 | #c.JupyterApp.generate_config = False 35 | 36 | #------------------------------------------------------------------------------ 37 | # NotebookApp(JupyterApp) configuration 38 | #------------------------------------------------------------------------------ 39 | 40 | ## Set the Access-Control-Allow-Credentials: true header 41 | #c.NotebookApp.allow_credentials = False 42 | 43 | ## Set the Access-Control-Allow-Origin header 44 | # 45 | # Use '*' to allow any origin to access your server. 46 | # 47 | # Takes precedence over allow_origin_pat. 48 | #c.NotebookApp.allow_origin = '' 49 | 50 | ## Use a regular expression for the Access-Control-Allow-Origin header 51 | # 52 | # Requests from an origin matching the expression will get replies with: 53 | # 54 | # Access-Control-Allow-Origin: origin 55 | # 56 | # where `origin` is the origin of the request. 57 | # 58 | # Ignored if allow_origin is set. 59 | #c.NotebookApp.allow_origin_pat = '' 60 | 61 | ## Whether to allow the user to run the notebook as root. 62 | #c.NotebookApp.allow_root = False 63 | 64 | ## DEPRECATED use base_url 65 | #c.NotebookApp.base_project_url = '/' 66 | 67 | ## The base URL for the notebook server. 68 | # 69 | # Leading and trailing slashes can be omitted, and will automatically be added. 70 | #c.NotebookApp.base_url = '/' 71 | 72 | ## Specify what command to use to invoke a web browser when opening the notebook. 73 | # If not specified, the default browser will be determined by the `webbrowser` 74 | # standard library module, which allows setting of the BROWSER environment 75 | # variable to override it. 76 | #c.NotebookApp.browser = '' 77 | 78 | ## The full path to an SSL/TLS certificate file. 79 | #c.NotebookApp.certfile = '' 80 | 81 | ## The full path to a certificate authority certificate for SSL/TLS client 82 | # authentication. 83 | #c.NotebookApp.client_ca = '' 84 | 85 | ## The config manager class to use 86 | #c.NotebookApp.config_manager_class = 'notebook.services.config.manager.ConfigManager' 87 | 88 | ## The notebook manager class to use. 89 | #c.NotebookApp.contents_manager_class = 'notebook.services.contents.largefilemanager.LargeFileManager' 90 | 91 | ## Extra keyword arguments to pass to `set_secure_cookie`. See tornado's 92 | # set_secure_cookie docs for details. 93 | #c.NotebookApp.cookie_options = {} 94 | 95 | ## The random bytes used to secure cookies. By default this is a new random 96 | # number every time you start the Notebook. Set it to a value in a config file 97 | # to enable logins to persist across server sessions. 98 | # 99 | # Note: Cookie secrets should be kept private, do not share config files with 100 | # cookie_secret stored in plaintext (you can read the value from a file). 101 | #c.NotebookApp.cookie_secret = b'' 102 | 103 | ## The file where the cookie secret is stored. 104 | #c.NotebookApp.cookie_secret_file = '' 105 | 106 | ## The default URL to redirect to from `/` 107 | #c.NotebookApp.default_url = '/tree' 108 | 109 | ## Disable cross-site-request-forgery protection 110 | # 111 | # Jupyter notebook 4.3.1 introduces protection from cross-site request 112 | # forgeries, requiring API requests to either: 113 | # 114 | # - originate from pages served by this server (validated with XSRF cookie and 115 | # token), or - authenticate with a token 116 | # 117 | # Some anonymous compute resources still desire the ability to run code, 118 | # completely without authentication. These services can disable all 119 | # authentication and security checks, with the full knowledge of what that 120 | # implies. 121 | #c.NotebookApp.disable_check_xsrf = False 122 | 123 | ## Whether to enable MathJax for typesetting math/TeX 124 | # 125 | # MathJax is the javascript library Jupyter uses to render math/LaTeX. It is 126 | # very large, so you may want to disable it if you have a slow internet 127 | # connection, or for offline use of the notebook. 128 | # 129 | # When disabled, equations etc. will appear as their untransformed TeX source. 130 | #c.NotebookApp.enable_mathjax = True 131 | 132 | ## extra paths to look for Javascript notebook extensions 133 | #c.NotebookApp.extra_nbextensions_path = [] 134 | 135 | ## Extra paths to search for serving static files. 136 | # 137 | # This allows adding javascript/css to be available from the notebook server 138 | # machine, or overriding individual files in the IPython 139 | #c.NotebookApp.extra_static_paths = [] 140 | 141 | ## Extra paths to search for serving jinja templates. 142 | # 143 | # Can be used to override templates from notebook.templates. 144 | #c.NotebookApp.extra_template_paths = [] 145 | 146 | ## 147 | #c.NotebookApp.file_to_run = '' 148 | 149 | ## Deprecated: Use minified JS file or not, mainly use during dev to avoid JS 150 | # recompilation 151 | #c.NotebookApp.ignore_minified_js = False 152 | 153 | ## (bytes/sec) Maximum rate at which messages can be sent on iopub before they 154 | # are limited. 155 | #c.NotebookApp.iopub_data_rate_limit = 1000000 156 | 157 | ## (msgs/sec) Maximum rate at which messages can be sent on iopub before they are 158 | # limited. 159 | #c.NotebookApp.iopub_msg_rate_limit = 1000 160 | 161 | ## The IP address the notebook server will listen on. 162 | #c.NotebookApp.ip = 'localhost' 163 | 164 | ## Supply extra arguments that will be passed to Jinja environment. 165 | #c.NotebookApp.jinja_environment_options = {} 166 | 167 | ## Extra variables to supply to jinja templates when rendering. 168 | #c.NotebookApp.jinja_template_vars = {} 169 | 170 | ## The kernel manager class to use. 171 | #c.NotebookApp.kernel_manager_class = 'notebook.services.kernels.kernelmanager.MappingKernelManager' 172 | 173 | ## The kernel spec manager class to use. Should be a subclass of 174 | # `jupyter_client.kernelspec.KernelSpecManager`. 175 | # 176 | # The Api of KernelSpecManager is provisional and might change without warning 177 | # between this version of Jupyter and the next stable one. 178 | #c.NotebookApp.kernel_spec_manager_class = 'jupyter_client.kernelspec.KernelSpecManager' 179 | 180 | ## The full path to a private key file for usage with SSL/TLS. 181 | #c.NotebookApp.keyfile = '' 182 | 183 | ## The login handler class to use. 184 | #c.NotebookApp.login_handler_class = 'notebook.auth.login.LoginHandler' 185 | 186 | ## The logout handler class to use. 187 | #c.NotebookApp.logout_handler_class = 'notebook.auth.logout.LogoutHandler' 188 | 189 | ## The MathJax.js configuration file that is to be used. 190 | #c.NotebookApp.mathjax_config = 'TeX-AMS-MML_HTMLorMML-full,Safe' 191 | 192 | ## A custom url for MathJax.js. Should be in the form of a case-sensitive url to 193 | # MathJax, for example: /static/components/MathJax/MathJax.js 194 | #c.NotebookApp.mathjax_url = '' 195 | 196 | ## Dict of Python modules to load as notebook server extensions.Entry values can 197 | # be used to enable and disable the loading ofthe extensions. The extensions 198 | # will be loaded in alphabetical order. 199 | #c.NotebookApp.nbserver_extensions = {} 200 | 201 | ## The directory to use for notebooks and kernels. 202 | #c.NotebookApp.notebook_dir = '' 203 | 204 | ## Whether to open in a browser after starting. The specific browser used is 205 | # platform dependent and determined by the python standard library `webbrowser` 206 | # module, unless it is overridden using the --browser (NotebookApp.browser) 207 | # configuration option. 208 | #c.NotebookApp.open_browser = True 209 | 210 | ## Hashed password to use for web authentication. 211 | # 212 | # To generate, type in a python/IPython shell: 213 | # 214 | # from notebook.auth import passwd; passwd() 215 | # 216 | # The string should be of the form type:salt:hashed-password. 217 | #c.NotebookApp.password = '' 218 | 219 | ## Forces users to use a password for the Notebook server. This is useful in a 220 | # multi user environment, for instance when everybody in the LAN can access each 221 | # other's machine though ssh. 222 | # 223 | # In such a case, server the notebook server on localhost is not secure since 224 | # any user can connect to the notebook server via ssh. 225 | #c.NotebookApp.password_required = False 226 | 227 | ## The port the notebook server will listen on. 228 | #c.NotebookApp.port = 8888 229 | 230 | ## The number of additional ports to try if the specified port is not available. 231 | #c.NotebookApp.port_retries = 50 232 | 233 | ## DISABLED: use %pylab or %matplotlib in the notebook to enable matplotlib. 234 | #c.NotebookApp.pylab = 'disabled' 235 | 236 | ## (sec) Time window used to check the message and data rate limits. 237 | #c.NotebookApp.rate_limit_window = 3 238 | 239 | ## Reraise exceptions encountered loading server extensions? 240 | #c.NotebookApp.reraise_server_extension_failures = False 241 | 242 | ## DEPRECATED use the nbserver_extensions dict instead 243 | #c.NotebookApp.server_extensions = [] 244 | 245 | ## The session manager class to use. 246 | #c.NotebookApp.session_manager_class = 'notebook.services.sessions.sessionmanager.SessionManager' 247 | 248 | ## Supply SSL options for the tornado HTTPServer. See the tornado docs for 249 | # details. 250 | #c.NotebookApp.ssl_options = {} 251 | 252 | ## Supply overrides for terminado. Currently only supports "shell_command". 253 | #c.NotebookApp.terminado_settings = {} 254 | 255 | ## Token used for authenticating first-time connections to the server. 256 | # 257 | # When no password is enabled, the default is to generate a new, random token. 258 | # 259 | # Setting to an empty string disables authentication altogether, which is NOT 260 | # RECOMMENDED. 261 | #c.NotebookApp.token = '' 262 | 263 | ## Supply overrides for the tornado.web.Application that the Jupyter notebook 264 | # uses. 265 | #c.NotebookApp.tornado_settings = {} 266 | 267 | ## Whether to trust or not X-Scheme/X-Forwarded-Proto and X-Real-Ip/X-Forwarded- 268 | # For headerssent by the upstream reverse proxy. Necessary if the proxy handles 269 | # SSL 270 | #c.NotebookApp.trust_xheaders = False 271 | 272 | ## DEPRECATED, use tornado_settings 273 | #c.NotebookApp.webapp_settings = {} 274 | 275 | ## The base URL for websockets, if it differs from the HTTP server (hint: it 276 | # almost certainly doesn't). 277 | # 278 | # Should be in the form of an HTTP origin: ws[s]://hostname[:port] 279 | #c.NotebookApp.websocket_url = '' 280 | 281 | #------------------------------------------------------------------------------ 282 | # ConnectionFileMixin(LoggingConfigurable) configuration 283 | #------------------------------------------------------------------------------ 284 | 285 | ## Mixin for configurable classes that work with connection files 286 | 287 | ## JSON file in which to store connection info [default: kernel-.json] 288 | # 289 | # This file will contain the IP, ports, and authentication key needed to connect 290 | # clients to this kernel. By default, this file will be created in the security 291 | # dir of the current profile, but can be specified by absolute path. 292 | #c.ConnectionFileMixin.connection_file = '' 293 | 294 | ## set the control (ROUTER) port [default: random] 295 | #c.ConnectionFileMixin.control_port = 0 296 | 297 | ## set the heartbeat port [default: random] 298 | #c.ConnectionFileMixin.hb_port = 0 299 | 300 | ## set the iopub (PUB) port [default: random] 301 | #c.ConnectionFileMixin.iopub_port = 0 302 | 303 | ## Set the kernel's IP address [default localhost]. If the IP address is 304 | # something other than localhost, then Consoles on other machines will be able 305 | # to connect to the Kernel, so be careful! 306 | #c.ConnectionFileMixin.ip = '' 307 | 308 | ## set the shell (ROUTER) port [default: random] 309 | #c.ConnectionFileMixin.shell_port = 0 310 | 311 | ## set the stdin (ROUTER) port [default: random] 312 | #c.ConnectionFileMixin.stdin_port = 0 313 | 314 | ## 315 | #c.ConnectionFileMixin.transport = 'tcp' 316 | 317 | #------------------------------------------------------------------------------ 318 | # KernelManager(ConnectionFileMixin) configuration 319 | #------------------------------------------------------------------------------ 320 | 321 | ## Manages a single kernel in a subprocess on this host. 322 | # 323 | # This version starts kernels with Popen. 324 | 325 | ## Should we autorestart the kernel if it dies. 326 | #c.KernelManager.autorestart = True 327 | 328 | ## DEPRECATED: Use kernel_name instead. 329 | # 330 | # The Popen Command to launch the kernel. Override this if you have a custom 331 | # kernel. If kernel_cmd is specified in a configuration file, Jupyter does not 332 | # pass any arguments to the kernel, because it cannot make any assumptions about 333 | # the arguments that the kernel understands. In particular, this means that the 334 | # kernel does not receive the option --debug if it given on the Jupyter command 335 | # line. 336 | #c.KernelManager.kernel_cmd = [] 337 | 338 | ## Time to wait for a kernel to terminate before killing it, in seconds. 339 | #c.KernelManager.shutdown_wait_time = 5.0 340 | 341 | #------------------------------------------------------------------------------ 342 | # Session(Configurable) configuration 343 | #------------------------------------------------------------------------------ 344 | 345 | ## Object for handling serialization and sending of messages. 346 | # 347 | # The Session object handles building messages and sending them with ZMQ sockets 348 | # or ZMQStream objects. Objects can communicate with each other over the 349 | # network via Session objects, and only need to work with the dict-based IPython 350 | # message spec. The Session will handle serialization/deserialization, security, 351 | # and metadata. 352 | # 353 | # Sessions support configurable serialization via packer/unpacker traits, and 354 | # signing with HMAC digests via the key/keyfile traits. 355 | # 356 | # Parameters ---------- 357 | # 358 | # debug : bool 359 | # whether to trigger extra debugging statements 360 | # packer/unpacker : str : 'json', 'pickle' or import_string 361 | # importstrings for methods to serialize message parts. If just 362 | # 'json' or 'pickle', predefined JSON and pickle packers will be used. 363 | # Otherwise, the entire importstring must be used. 364 | # 365 | # The functions must accept at least valid JSON input, and output *bytes*. 366 | # 367 | # For example, to use msgpack: 368 | # packer = 'msgpack.packb', unpacker='msgpack.unpackb' 369 | # pack/unpack : callables 370 | # You can also set the pack/unpack callables for serialization directly. 371 | # session : bytes 372 | # the ID of this Session object. The default is to generate a new UUID. 373 | # username : unicode 374 | # username added to message headers. The default is to ask the OS. 375 | # key : bytes 376 | # The key used to initialize an HMAC signature. If unset, messages 377 | # will not be signed or checked. 378 | # keyfile : filepath 379 | # The file containing a key. If this is set, `key` will be initialized 380 | # to the contents of the file. 381 | 382 | ## Threshold (in bytes) beyond which an object's buffer should be extracted to 383 | # avoid pickling. 384 | #c.Session.buffer_threshold = 1024 385 | 386 | ## Whether to check PID to protect against calls after fork. 387 | # 388 | # This check can be disabled if fork-safety is handled elsewhere. 389 | #c.Session.check_pid = True 390 | 391 | ## Threshold (in bytes) beyond which a buffer should be sent without copying. 392 | #c.Session.copy_threshold = 65536 393 | 394 | ## Debug output in the Session 395 | #c.Session.debug = False 396 | 397 | ## The maximum number of digests to remember. 398 | # 399 | # The digest history will be culled when it exceeds this value. 400 | #c.Session.digest_history_size = 65536 401 | 402 | ## The maximum number of items for a container to be introspected for custom 403 | # serialization. Containers larger than this are pickled outright. 404 | #c.Session.item_threshold = 64 405 | 406 | ## execution key, for signing messages. 407 | #c.Session.key = b'' 408 | 409 | ## path to file containing execution key. 410 | #c.Session.keyfile = '' 411 | 412 | ## Metadata dictionary, which serves as the default top-level metadata dict for 413 | # each message. 414 | #c.Session.metadata = {} 415 | 416 | ## The name of the packer for serializing messages. Should be one of 'json', 417 | # 'pickle', or an import name for a custom callable serializer. 418 | #c.Session.packer = 'json' 419 | 420 | ## The UUID identifying this session. 421 | #c.Session.session = '' 422 | 423 | ## The digest scheme used to construct the message signatures. Must have the form 424 | # 'hmac-HASH'. 425 | #c.Session.signature_scheme = 'hmac-sha256' 426 | 427 | ## The name of the unpacker for unserializing messages. Only used with custom 428 | # functions for `packer`. 429 | #c.Session.unpacker = 'json' 430 | 431 | ## Username for the Session. Default is your system username. 432 | #c.Session.username = 'username' 433 | 434 | #------------------------------------------------------------------------------ 435 | # MultiKernelManager(LoggingConfigurable) configuration 436 | #------------------------------------------------------------------------------ 437 | 438 | ## A class for managing multiple kernels. 439 | 440 | ## The name of the default kernel to start 441 | #c.MultiKernelManager.default_kernel_name = 'python3' 442 | 443 | ## The kernel manager class. This is configurable to allow subclassing of the 444 | # KernelManager for customized behavior. 445 | #c.MultiKernelManager.kernel_manager_class = 'jupyter_client.ioloop.IOLoopKernelManager' 446 | 447 | #------------------------------------------------------------------------------ 448 | # MappingKernelManager(MultiKernelManager) configuration 449 | #------------------------------------------------------------------------------ 450 | 451 | ## A KernelManager that handles notebook mapping and HTTP error handling 452 | 453 | ## 454 | #c.MappingKernelManager.root_dir = '' 455 | 456 | #------------------------------------------------------------------------------ 457 | # ContentsManager(LoggingConfigurable) configuration 458 | #------------------------------------------------------------------------------ 459 | 460 | ## Base class for serving files and directories. 461 | # 462 | # This serves any text or binary file, as well as directories, with special 463 | # handling for JSON notebook documents. 464 | # 465 | # Most APIs take a path argument, which is always an API-style unicode path, and 466 | # always refers to a directory. 467 | # 468 | # - unicode, not url-escaped 469 | # - '/'-separated 470 | # - leading and trailing '/' will be stripped 471 | # - if unspecified, path defaults to '', 472 | # indicating the root path. 473 | 474 | ## 475 | #c.ContentsManager.checkpoints = None 476 | 477 | ## 478 | #c.ContentsManager.checkpoints_class = 'notebook.services.contents.checkpoints.Checkpoints' 479 | 480 | ## 481 | #c.ContentsManager.checkpoints_kwargs = {} 482 | 483 | ## Glob patterns to hide in file and directory listings. 484 | #c.ContentsManager.hide_globs = ['__pycache__', '*.pyc', '*.pyo', '.DS_Store', '*.so', '*.dylib', '*~'] 485 | 486 | ## Python callable or importstring thereof 487 | # 488 | # To be called on a contents model prior to save. 489 | # 490 | # This can be used to process the structure, such as removing notebook outputs 491 | # or other side effects that should not be saved. 492 | # 493 | # It will be called as (all arguments passed by keyword):: 494 | # 495 | # hook(path=path, model=model, contents_manager=self) 496 | # 497 | # - model: the model to be saved. Includes file contents. 498 | # Modifying this dict will affect the file that is stored. 499 | # - path: the API path of the save destination 500 | # - contents_manager: this ContentsManager instance 501 | #c.ContentsManager.pre_save_hook = None 502 | 503 | ## 504 | #c.ContentsManager.root_dir = '/' 505 | 506 | ## The base name used when creating untitled directories. 507 | #c.ContentsManager.untitled_directory = 'Untitled Folder' 508 | 509 | ## The base name used when creating untitled files. 510 | #c.ContentsManager.untitled_file = 'untitled' 511 | 512 | ## The base name used when creating untitled notebooks. 513 | #c.ContentsManager.untitled_notebook = 'Untitled' 514 | 515 | #------------------------------------------------------------------------------ 516 | # FileManagerMixin(Configurable) configuration 517 | #------------------------------------------------------------------------------ 518 | 519 | ## Mixin for ContentsAPI classes that interact with the filesystem. 520 | # 521 | # Provides facilities for reading, writing, and copying both notebooks and 522 | # generic files. 523 | # 524 | # Shared by FileContentsManager and FileCheckpoints. 525 | # 526 | # Note ---- Classes using this mixin must provide the following attributes: 527 | # 528 | # root_dir : unicode 529 | # A directory against against which API-style paths are to be resolved. 530 | # 531 | # log : logging.Logger 532 | 533 | ## By default notebooks are saved on disk on a temporary file and then if 534 | # succefully written, it replaces the old ones. This procedure, namely 535 | # 'atomic_writing', causes some bugs on file system whitout operation order 536 | # enforcement (like some networked fs). If set to False, the new notebook is 537 | # written directly on the old one which could fail (eg: full filesystem or quota 538 | # ) 539 | #c.FileManagerMixin.use_atomic_writing = True 540 | 541 | #------------------------------------------------------------------------------ 542 | # FileContentsManager(FileManagerMixin,ContentsManager) configuration 543 | #------------------------------------------------------------------------------ 544 | 545 | ## Python callable or importstring thereof 546 | # 547 | # to be called on the path of a file just saved. 548 | # 549 | # This can be used to process the file on disk, such as converting the notebook 550 | # to a script or HTML via nbconvert. 551 | # 552 | # It will be called as (all arguments passed by keyword):: 553 | # 554 | # hook(os_path=os_path, model=model, contents_manager=instance) 555 | # 556 | # - path: the filesystem path to the file just written - model: the model 557 | # representing the file - contents_manager: this ContentsManager instance 558 | #c.FileContentsManager.post_save_hook = None 559 | 560 | ## 561 | #c.FileContentsManager.root_dir = '' 562 | 563 | ## DEPRECATED, use post_save_hook. Will be removed in Notebook 5.0 564 | #c.FileContentsManager.save_script = False 565 | 566 | #------------------------------------------------------------------------------ 567 | # NotebookNotary(LoggingConfigurable) configuration 568 | #------------------------------------------------------------------------------ 569 | 570 | ## A class for computing and verifying notebook signatures. 571 | 572 | ## The hashing algorithm used to sign notebooks. 573 | #c.NotebookNotary.algorithm = 'sha256' 574 | 575 | ## The sqlite file in which to store notebook signatures. By default, this will 576 | # be in your Jupyter data directory. You can set it to ':memory:' to disable 577 | # sqlite writing to the filesystem. 578 | #c.NotebookNotary.db_file = '' 579 | 580 | ## The secret key with which notebooks are signed. 581 | #c.NotebookNotary.secret = b'' 582 | 583 | ## The file where the secret key is stored. 584 | #c.NotebookNotary.secret_file = '' 585 | 586 | ## A callable returning the storage backend for notebook signatures. The default 587 | # uses an SQLite database. 588 | #c.NotebookNotary.store_factory = traitlets.Undefined 589 | 590 | #------------------------------------------------------------------------------ 591 | # KernelSpecManager(LoggingConfigurable) configuration 592 | #------------------------------------------------------------------------------ 593 | 594 | ## If there is no Python kernelspec registered and the IPython kernel is 595 | # available, ensure it is added to the spec list. 596 | #c.KernelSpecManager.ensure_native_kernel = True 597 | 598 | ## The kernel spec class. This is configurable to allow subclassing of the 599 | # KernelSpecManager for customized behavior. 600 | #c.KernelSpecManager.kernel_spec_class = 'jupyter_client.kernelspec.KernelSpec' 601 | 602 | ## Whitelist of allowed kernel names. 603 | # 604 | # By default, all installed kernels are allowed. 605 | #c.KernelSpecManager.whitelist = set() 606 | -------------------------------------------------------------------------------- /3-prepare-service.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "Copyright (C) Microsoft Corporation." 8 | ] 9 | }, 10 | { 11 | "cell_type": "markdown", 12 | "metadata": { 13 | "collapsed": true 14 | }, 15 | "source": [ 16 | "# Author a real time web service" 17 | ] 18 | }, 19 | { 20 | "cell_type": "markdown", 21 | "metadata": { 22 | "collapsed": true 23 | }, 24 | "source": [ 25 | "**Important**: Make sure the kernel is set to \"Your project name myvm\" which can be done from the *Kernel* menu under *Change kernel*." 26 | ] 27 | }, 28 | { 29 | "cell_type": "markdown", 30 | "metadata": {}, 31 | "source": [ 32 | "In this notebook, you will be using the LightGBM classifer you trained in the second notebook to prepare the necessary artifacts to opertionalize your model." 33 | ] 34 | }, 35 | { 36 | "cell_type": "code", 37 | "execution_count": 1, 38 | "metadata": {}, 39 | "outputs": [], 40 | "source": [ 41 | "import os\n", 42 | "from os import path\n", 43 | "from glob import iglob\n", 44 | "from PIL import Image, ImageOps\n", 45 | "import numpy as np\n", 46 | "import base64\n", 47 | "import json\n", 48 | "from io import BytesIO\n", 49 | "import sys\n", 50 | "import lightgbm as lgb\n", 51 | "from azure.storage.blob import BlockBlobService\n", 52 | "import shutil" 53 | ] 54 | }, 55 | { 56 | "cell_type": "code", 57 | "execution_count": 2, 58 | "metadata": {}, 59 | "outputs": [ 60 | { 61 | "name": "stderr", 62 | "output_type": "stream", 63 | "text": [ 64 | "Using CNTK backend\n" 65 | ] 66 | } 67 | ], 68 | "source": [ 69 | "os.environ[\"KERAS_BACKEND\"] = \"cntk\"\n", 70 | "import keras\n", 71 | "from keras.applications.resnet50 import ResNet50\n", 72 | "from keras.preprocessing import image\n", 73 | "from keras.applications.imagenet_utils import preprocess_input" 74 | ] 75 | }, 76 | { 77 | "cell_type": "markdown", 78 | "metadata": {}, 79 | "source": [ 80 | "You will use the AZUREML_NATIVE_SHARE_DIRECTORY to save your operationalization artifacts." 81 | ] 82 | }, 83 | { 84 | "cell_type": "code", 85 | "execution_count": 3, 86 | "metadata": {}, 87 | "outputs": [], 88 | "source": [ 89 | "save_path = os.environ['AZUREML_NATIVE_SHARE_DIRECTORY']" 90 | ] 91 | }, 92 | { 93 | "cell_type": "markdown", 94 | "metadata": {}, 95 | "source": [ 96 | "You will use the first fail image as a test example to prepare your service." 97 | ] 98 | }, 99 | { 100 | "cell_type": "code", 101 | "execution_count": 4, 102 | "metadata": {}, 103 | "outputs": [], 104 | "source": [ 105 | "files_path = path.join(save_path, 'train')\n", 106 | "fail_files = sorted(iglob(path.join(files_path, '*fail*.jpg')))" 107 | ] 108 | }, 109 | { 110 | "cell_type": "markdown", 111 | "metadata": {}, 112 | "source": [ 113 | "Pick first fail image." 114 | ] 115 | }, 116 | { 117 | "cell_type": "code", 118 | "execution_count": 5, 119 | "metadata": {}, 120 | "outputs": [ 121 | { 122 | "name": "stdout", 123 | "output_type": "stream", 124 | "text": [ 125 | "(500, 374)\n" 126 | ] 127 | } 128 | ], 129 | "source": [ 130 | "fail_img_path = fail_files[0]\n", 131 | "fail_pil_image = Image.open(fail_img_path)\n", 132 | "print(fail_pil_image.size)\n", 133 | "#fail_pil_image" 134 | ] 135 | }, 136 | { 137 | "cell_type": "markdown", 138 | "metadata": {}, 139 | "source": [ 140 | "In order to transfer the image to call the web service, you will encode the image into string representation and then serialize it into json format." 141 | ] 142 | }, 143 | { 144 | "cell_type": "code", 145 | "execution_count": 6, 146 | "metadata": {}, 147 | "outputs": [ 148 | { 149 | "data": { 150 | "text/plain": [ 151 | "'\"b\\'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\\'\"'" 152 | ] 153 | }, 154 | "execution_count": 6, 155 | "metadata": {}, 156 | "output_type": "execute_result" 157 | } 158 | ], 159 | "source": [ 160 | "with open(fail_img_path, 'rb') as file:\n", 161 | " encoded = base64.b64encode(file.read())\n", 162 | "body = json.dumps(\"{}\".format(encoded))\n", 163 | "body" 164 | ] 165 | }, 166 | { 167 | "cell_type": "markdown", 168 | "metadata": {}, 169 | "source": [ 170 | "The above string representation will be sent to the web service.It will then be loaded, decoded and converted back into an image by the web service before featurization and prediction steps. Below, you will load this string and perform necessary steps to convert it back to its image representation." 171 | ] 172 | }, 173 | { 174 | "cell_type": "code", 175 | "execution_count": 7, 176 | "metadata": {}, 177 | "outputs": [], 178 | "source": [ 179 | "base64ImgString = json.loads(body)\n", 180 | "\n", 181 | "if base64ImgString.startswith('b\\''):\n", 182 | " base64ImgString = base64ImgString[2:-1]\n", 183 | "base64Img = base64ImgString.encode('utf-8')\n", 184 | "\n", 185 | "decoded_img = base64.b64decode(base64Img)\n", 186 | "img_buffer = BytesIO(decoded_img)\n", 187 | "imageData = Image.open(img_buffer).convert(\"RGB\")" 188 | ] 189 | }, 190 | { 191 | "cell_type": "markdown", 192 | "metadata": {}, 193 | "source": [ 194 | "Now, you will load ResNet50 model and LightGBM classifer to make a prediction on the first fail image." 195 | ] 196 | }, 197 | { 198 | "cell_type": "code", 199 | "execution_count": 8, 200 | "metadata": {}, 201 | "outputs": [ 202 | { 203 | "name": "stdout", 204 | "output_type": "stream", 205 | "text": [ 206 | "Downloading data from https://github.com/fchollet/deep-learning-models/releases/download/v0.2/resnet50_weights_tf_dim_ordering_tf_kernels_notop.h5\n", 207 | "92790784/94653016 [============================>.] - ETA: 0s" 208 | ] 209 | }, 210 | { 211 | "data": { 212 | "text/plain": [ 213 | "array([ 0.00011647])" 214 | ] 215 | }, 216 | "execution_count": 8, 217 | "metadata": {}, 218 | "output_type": "execute_result" 219 | } 220 | ], 221 | "source": [ 222 | "clf = lgb.Booster(model_file=path.join(save_path,'lightgbm_classifier.model'))\n", 223 | "model = ResNet50(include_top=False, input_shape=(224,224,3))\n", 224 | "img = ImageOps.fit(imageData, (224, 224), Image.ANTIALIAS)\n", 225 | "x = image.img_to_array(img)\n", 226 | "x = np.expand_dims(x, axis=0)\n", 227 | "x = preprocess_input(x)\n", 228 | "pred = model.predict(x).squeeze() \n", 229 | "feat = pred.reshape(1, pred.shape[0])\n", 230 | "resp = clf.predict(feat)\n", 231 | "resp" 232 | ] 233 | }, 234 | { 235 | "cell_type": "markdown", 236 | "metadata": {}, 237 | "source": [ 238 | "## Create a scoring script" 239 | ] 240 | }, 241 | { 242 | "cell_type": "markdown", 243 | "metadata": {}, 244 | "source": [ 245 | "In order to create a web service, you will create a scoring script that will load the models, perform the prediction, and return the result. Azure ML Workbench uses init() and run() functions inside this scoring script for that purpose. The init() function initializes the web service and loads the saved model. The run() function uses the model and the input data to return a prediction which is executed on a scoring call." 246 | ] 247 | }, 248 | { 249 | "cell_type": "markdown", 250 | "metadata": {}, 251 | "source": [ 252 | "You will first define init() and run() functions and test them. Notice that the init() function loads the LightGBM model and the run() function just wraps the above steps to convert the image string received into image format, load ResNet50 model to create features and use the lightGBM model to make a prediction." 253 | ] 254 | }, 255 | { 256 | "cell_type": "code", 257 | "execution_count": 9, 258 | "metadata": {}, 259 | "outputs": [], 260 | "source": [ 261 | "def init():\n", 262 | " global clf\n", 263 | " try:\n", 264 | " print(\"Executing init() method...\")\n", 265 | " print(\"Python version: \" + str(sys.version) + \", keras version: \" + keras.__version__)\n", 266 | " # Load the model \n", 267 | " clf = lgb.Booster(model_file='/azureml-share/lightgbm_classifier.model')\n", 268 | " except Exception as e:\n", 269 | " print(\"Exception in init:\")\n", 270 | " print(str(e))\n", 271 | " \n", 272 | "def run(inputString):\n", 273 | " try:\n", 274 | " responses = []\n", 275 | " base64ImgString = json.loads(inputString)\n", 276 | " \n", 277 | " if base64ImgString.startswith('b\\''):\n", 278 | " base64ImgString = base64ImgString[2:-1]\n", 279 | " base64Img = base64ImgString.encode('utf-8')\n", 280 | " \n", 281 | " # Preprocess the input data\n", 282 | " decoded_img = base64.b64decode(base64Img)\n", 283 | " img_buffer = BytesIO(decoded_img)\n", 284 | " imageData = Image.open(img_buffer).convert(\"RGB\")\n", 285 | " \n", 286 | " # Evaluate the model using the input data\n", 287 | " model = ResNet50(include_top=False, input_shape=(224,224,3))\n", 288 | " img = ImageOps.fit(imageData, (224, 224), Image.ANTIALIAS)\n", 289 | " x = image.img_to_array(img)\n", 290 | " x = np.expand_dims(x, axis=0)\n", 291 | " x = preprocess_input(x)\n", 292 | " pred = model.predict(x).squeeze() \n", 293 | " feat = pred.reshape(1, pred.shape[0])\n", 294 | " resp = clf.predict(feat)\n", 295 | " responses.append(list(resp))\n", 296 | " except Exception as e:\n", 297 | " print(\"Exception in run:\")\n", 298 | " print(str(e)) \n", 299 | " return responses" 300 | ] 301 | }, 302 | { 303 | "cell_type": "markdown", 304 | "metadata": {}, 305 | "source": [ 306 | "You will now test your functions. You will see that the same prediction value is produced by the run() function when the same image string is passed." 307 | ] 308 | }, 309 | { 310 | "cell_type": "code", 311 | "execution_count": 10, 312 | "metadata": {}, 313 | "outputs": [ 314 | { 315 | "name": "stdout", 316 | "output_type": "stream", 317 | "text": [ 318 | "Executing init() method...\n", 319 | "Python version: 3.5.2 | packaged by conda-forge | (default, Jan 19 2017, 15:28:33) \n", 320 | "[GCC 4.8.2 20140120 (Red Hat 4.8.2-15)], keras version: 2.0.8\n" 321 | ] 322 | }, 323 | { 324 | "data": { 325 | "text/plain": [ 326 | "[[0.00011646905475415675]]" 327 | ] 328 | }, 329 | "execution_count": 10, 330 | "metadata": {}, 331 | "output_type": "execute_result" 332 | } 333 | ], 334 | "source": [ 335 | "init()\n", 336 | "run(body)" 337 | ] 338 | }, 339 | { 340 | "cell_type": "markdown", 341 | "metadata": {}, 342 | "source": [ 343 | "Write the scoring script to the folder you created in the 2nd notebook to be used by operationalization later. Observe that the scoring script includes the init() and run() functions and also imports the necessary packages to run those. Also, notice that the path to the model file doesn't have the folder name in the scoring script as the web service will place all the operationalization files in one location." 344 | ] 345 | }, 346 | { 347 | "cell_type": "code", 348 | "execution_count": 32, 349 | "metadata": {}, 350 | "outputs": [], 351 | "source": [ 352 | "o16n_path = path.join(save_path,'o16n')\n", 353 | "write_path = path.join(o16n_path, 'imgscore.py')" 354 | ] 355 | }, 356 | { 357 | "cell_type": "code", 358 | "execution_count": 33, 359 | "metadata": {}, 360 | "outputs": [ 361 | { 362 | "name": "stdout", 363 | "output_type": "stream", 364 | "text": [ 365 | "Overwriting /azureml-share/o16n/imgscore.py\n" 366 | ] 367 | } 368 | ], 369 | "source": [ 370 | "%%writefile $write_path\n", 371 | "\n", 372 | "import os\n", 373 | "os.environ[\"KERAS_BACKEND\"] = \"cntk\"\n", 374 | "import keras\n", 375 | "from keras.applications.imagenet_utils import preprocess_input\n", 376 | "from keras.preprocessing import image\n", 377 | "from keras.applications.resnet50 import ResNet50\n", 378 | "\n", 379 | "import sys, base64, json\n", 380 | "from PIL import Image, ImageOps\n", 381 | "import lightgbm as lgb\n", 382 | "from io import BytesIO\n", 383 | "import numpy as np\n", 384 | "\n", 385 | "\n", 386 | "try:\n", 387 | " import lightgbm as lgb\n", 388 | "except OSError as e:\n", 389 | " print(str(e))\n", 390 | "\n", 391 | "\n", 392 | "def init():\n", 393 | " global clf\n", 394 | " try:\n", 395 | " print(\"Executing init() method...\")\n", 396 | " print(\"Python version: \" + str(sys.version) + \", keras version: \" + keras.__version__)\n", 397 | " # Load the model \n", 398 | " clf = lgb.Booster(model_file='lightgbm_classifier.model')\n", 399 | " except Exception as e:\n", 400 | " print(\"Exception in init:\")\n", 401 | " print(str(e))\n", 402 | " \n", 403 | "\n", 404 | "def run(inputString):\n", 405 | " try:\n", 406 | " responses = []\n", 407 | " base64ImgString = json.loads(inputString)\n", 408 | " \n", 409 | " if base64ImgString.startswith('b\\''):\n", 410 | " base64ImgString = base64ImgString[2:-1]\n", 411 | " base64Img = base64ImgString.encode('utf-8')\n", 412 | " \n", 413 | " # Preprocess the input data\n", 414 | " decoded_img = base64.b64decode(base64Img)\n", 415 | " img_buffer = BytesIO(decoded_img)\n", 416 | " imageData = Image.open(img_buffer).convert(\"RGB\")\n", 417 | " \n", 418 | " # Evaluate the model using the input data\n", 419 | " model = ResNet50(include_top=False, input_shape=(224,224,3))\n", 420 | " img = ImageOps.fit(imageData, (224, 224), Image.ANTIALIAS)\n", 421 | " x = image.img_to_array(img)\n", 422 | " x = np.expand_dims(x, axis=0)\n", 423 | " x = preprocess_input(x)\n", 424 | " pred = model.predict(x).squeeze() \n", 425 | " feat = pred.reshape(1, pred.shape[0])\n", 426 | " resp = clf.predict(feat)\n", 427 | " responses.append(list(resp))\n", 428 | " except Exception as e:\n", 429 | " print(\"Exception in run:\")\n", 430 | " print(str(e)) \n", 431 | " return responses\n", 432 | "\n", 433 | "if __name__ == \"__main__\":\n", 434 | " init()\n", 435 | " # input data\n", 436 | " img_path = 'fail.0.jpg'\n", 437 | " encoded = None\n", 438 | " with open(img_path, 'rb') as file:\n", 439 | " encoded = base64.b64encode(file.read())\n", 440 | " body = json.dumps(\"{}\".format(encoded))\n", 441 | " resp = run(body)\n", 442 | " print(resp)" 443 | ] 444 | }, 445 | { 446 | "cell_type": "markdown", 447 | "metadata": {}, 448 | "source": [ 449 | "You will also use the first failure image as sample data to test your web services. Next, you will compy the image into the operationalization folder." 450 | ] 451 | }, 452 | { 453 | "cell_type": "code", 454 | "execution_count": 34, 455 | "metadata": {}, 456 | "outputs": [ 457 | { 458 | "data": { 459 | "text/plain": [ 460 | "'/azureml-share/o16n/fail.0.jpg'" 461 | ] 462 | }, 463 | "execution_count": 34, 464 | "metadata": {}, 465 | "output_type": "execute_result" 466 | } 467 | ], 468 | "source": [ 469 | "shutil.copyfile(fail_img_path, path.join(o16n_path,path.split(fail_img_path)[1]))" 470 | ] 471 | }, 472 | { 473 | "cell_type": "markdown", 474 | "metadata": {}, 475 | "source": [ 476 | "Check if the image, scoring script and model are in the operationalizaton folder." 477 | ] 478 | }, 479 | { 480 | "cell_type": "code", 481 | "execution_count": 35, 482 | "metadata": {}, 483 | "outputs": [ 484 | { 485 | "name": "stdout", 486 | "output_type": "stream", 487 | "text": [ 488 | "fail.0.jpg imgscore.py lightgbm_classifier.model\r\n" 489 | ] 490 | } 491 | ], 492 | "source": [ 493 | "!ls $o16n_path" 494 | ] 495 | }, 496 | { 497 | "cell_type": "markdown", 498 | "metadata": {}, 499 | "source": [ 500 | "## Upload operationalization files to blob storage" 501 | ] 502 | }, 503 | { 504 | "cell_type": "markdown", 505 | "metadata": {}, 506 | "source": [ 507 | "Now, you will compress the operationalization folder and upload to blob storage. Locate your storage account name and key as described in the Getting Started document of this tutorial and provide your values in the following cell where indicated." 508 | ] 509 | }, 510 | { 511 | "cell_type": "code", 512 | "execution_count": 36, 513 | "metadata": {}, 514 | "outputs": [], 515 | "source": [ 516 | "ACCOUNT_NAME = 'Your Storage Account Name Here!!'\n", 517 | "ACCOUNT_KEY = 'Your Storage Account Key Here!!'\n", 518 | "CONTAINER_NAME = \"deploy\"\n", 519 | "ZIP_FILE = 'o16n.zip'" 520 | ] 521 | }, 522 | { 523 | "cell_type": "markdown", 524 | "metadata": {}, 525 | "source": [ 526 | "First, create a container. If the container already exists, the code will return \"False\"." 527 | ] 528 | }, 529 | { 530 | "cell_type": "code", 531 | "execution_count": 37, 532 | "metadata": {}, 533 | "outputs": [ 534 | { 535 | "data": { 536 | "text/plain": [ 537 | "False" 538 | ] 539 | }, 540 | "execution_count": 37, 541 | "metadata": {}, 542 | "output_type": "execute_result" 543 | } 544 | ], 545 | "source": [ 546 | "block_blob_service = BlockBlobService(account_name=ACCOUNT_NAME, account_key=ACCOUNT_KEY)\n", 547 | "block_blob_service.create_container(container_name=CONTAINER_NAME, fail_on_exist=False)" 548 | ] 549 | }, 550 | { 551 | "cell_type": "markdown", 552 | "metadata": {}, 553 | "source": [ 554 | "Check if the container is created." 555 | ] 556 | }, 557 | { 558 | "cell_type": "code", 559 | "execution_count": 38, 560 | "metadata": {}, 561 | "outputs": [ 562 | { 563 | "name": "stdout", 564 | "output_type": "stream", 565 | "text": [ 566 | "deploy\n", 567 | "images\n" 568 | ] 569 | } 570 | ], 571 | "source": [ 572 | "for container in block_blob_service.list_containers():\n", 573 | " print(container.name)" 574 | ] 575 | }, 576 | { 577 | "cell_type": "markdown", 578 | "metadata": {}, 579 | "source": [ 580 | "Compress the operationalization folder and upload to blob." 581 | ] 582 | }, 583 | { 584 | "cell_type": "code", 585 | "execution_count": 39, 586 | "metadata": {}, 587 | "outputs": [ 588 | { 589 | "data": { 590 | "text/plain": [ 591 | "" 592 | ] 593 | }, 594 | "execution_count": 39, 595 | "metadata": {}, 596 | "output_type": "execute_result" 597 | } 598 | ], 599 | "source": [ 600 | "shutil.make_archive('o16n', 'zip', o16n_path)\n", 601 | "block_blob_service.create_blob_from_path(container_name=CONTAINER_NAME,blob_name=ZIP_FILE, file_path=ZIP_FILE) " 602 | ] 603 | }, 604 | { 605 | "cell_type": "markdown", 606 | "metadata": {}, 607 | "source": [ 608 | "Check if the file is uploaded to blob." 609 | ] 610 | }, 611 | { 612 | "cell_type": "code", 613 | "execution_count": 40, 614 | "metadata": {}, 615 | "outputs": [ 616 | { 617 | "name": "stdout", 618 | "output_type": "stream", 619 | "text": [ 620 | "o16n.zip\n" 621 | ] 622 | } 623 | ], 624 | "source": [ 625 | "for blob in block_blob_service.list_blobs(container_name=CONTAINER_NAME):\n", 626 | " print(blob.name)" 627 | ] 628 | }, 629 | { 630 | "cell_type": "markdown", 631 | "metadata": {}, 632 | "source": [ 633 | "Next, go to the 4th notebook to deploy your model." 634 | ] 635 | } 636 | ], 637 | "metadata": { 638 | "kernelspec": { 639 | "display_name": "imgtutvso local", 640 | "language": "python", 641 | "name": "imgtutvso_local" 642 | }, 643 | "language_info": { 644 | "codemirror_mode": { 645 | "name": "ipython", 646 | "version": 3 647 | }, 648 | "file_extension": ".py", 649 | "mimetype": "text/x-python", 650 | "name": "python", 651 | "nbconvert_exporter": "python", 652 | "pygments_lexer": "ipython3", 653 | "version": "3.5.2" 654 | } 655 | }, 656 | "nbformat": 4, 657 | "nbformat_minor": 2 658 | } 659 | -------------------------------------------------------------------------------- /2-model-building.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "Copyright (C) Microsoft Corporation." 8 | ] 9 | }, 10 | { 11 | "cell_type": "markdown", 12 | "metadata": {}, 13 | "source": [ 14 | "# Transfer learning using Keras with CNTK backend and LightGBM" 15 | ] 16 | }, 17 | { 18 | "cell_type": "markdown", 19 | "metadata": {}, 20 | "source": [ 21 | "**Important**: Make sure the kernel is set to \"Your project name myvm\" which can be done from the *Kernel* menu under *Change kernel*." 22 | ] 23 | }, 24 | { 25 | "cell_type": "markdown", 26 | "metadata": {}, 27 | "source": [ 28 | "In this notebook, you will be classifying the images you prepared in the first notebook into two categories using transfer learning. One major transfer learning scenario is to use a network that is pretrained on a large image dataset. Here, you will use a [Convolutional Neural Network (CNN)](https://en.wikipedia.org/wiki/Convolutional_neural_network) architecture called [ResNet50](https://arxiv.org/abs/1512.03385), pretrained on [ImageNet](https://en.wikipedia.org/wiki/ImageNet) dataset which contains 1.2 million images with 1000 categories. You will first remove the last network layer to generate visual features of the images. You will then use these features to train a boosted decision tree to classify the images as pass or fail. You will use [Keras](https://keras.io/) with [Microsoft Cognitive Toolkit CNTK](https://github.com/Microsoft/cntk) backend for the CNN and [LightGBM](https://lightgbm.readthedocs.io/en/latest/) for binary classification. " 29 | ] 30 | }, 31 | { 32 | "cell_type": "code", 33 | "execution_count": 1, 34 | "metadata": {}, 35 | "outputs": [], 36 | "source": [ 37 | "%matplotlib inline\n", 38 | "import matplotlib\n", 39 | "import matplotlib.pyplot as plt" 40 | ] 41 | }, 42 | { 43 | "cell_type": "code", 44 | "execution_count": 2, 45 | "metadata": {}, 46 | "outputs": [], 47 | "source": [ 48 | "import os\n", 49 | "from os import path\n", 50 | "from glob import iglob\n", 51 | "from tqdm import tqdm\n", 52 | "import itertools\n", 53 | "import numpy as np\n", 54 | "import pandas as pd\n", 55 | "from PIL import Image\n", 56 | "from sklearn.model_selection import StratifiedKFold\n", 57 | "from sklearn.metrics import confusion_matrix, roc_auc_score, accuracy_score, precision_score, recall_score, f1_score\n", 58 | "import lightgbm as lgb\n", 59 | "import h5py" 60 | ] 61 | }, 62 | { 63 | "cell_type": "code", 64 | "execution_count": 3, 65 | "metadata": {}, 66 | "outputs": [ 67 | { 68 | "name": "stderr", 69 | "output_type": "stream", 70 | "text": [ 71 | "Using CNTK backend\n" 72 | ] 73 | } 74 | ], 75 | "source": [ 76 | "os.environ[\"KERAS_BACKEND\"] = \"cntk\"\n", 77 | "import keras\n", 78 | "from keras.applications.resnet50 import ResNet50\n", 79 | "from keras.preprocessing import image\n", 80 | "from keras.applications.imagenet_utils import preprocess_input" 81 | ] 82 | }, 83 | { 84 | "cell_type": "code", 85 | "execution_count": 4, 86 | "metadata": {}, 87 | "outputs": [], 88 | "source": [ 89 | "from azureml.logging import get_azureml_logger\n", 90 | "logger = get_azureml_logger()" 91 | ] 92 | }, 93 | { 94 | "cell_type": "markdown", 95 | "metadata": {}, 96 | "source": [ 97 | "## Prepare training features and labels" 98 | ] 99 | }, 100 | { 101 | "cell_type": "markdown", 102 | "metadata": {}, 103 | "source": [ 104 | "You will use the AZUREML_NATIVE_SHARE_DIRECTORY to save your intermediate results." 105 | ] 106 | }, 107 | { 108 | "cell_type": "code", 109 | "execution_count": 5, 110 | "metadata": {}, 111 | "outputs": [], 112 | "source": [ 113 | "save_path = os.environ['AZUREML_NATIVE_SHARE_DIRECTORY']" 114 | ] 115 | }, 116 | { 117 | "cell_type": "markdown", 118 | "metadata": {}, 119 | "source": [ 120 | "First, read in the fail and pass images and check the number of images in each category. " 121 | ] 122 | }, 123 | { 124 | "cell_type": "code", 125 | "execution_count": 6, 126 | "metadata": {}, 127 | "outputs": [ 128 | { 129 | "name": "stdout", 130 | "output_type": "stream", 131 | "text": [ 132 | "Number of fail images: 12500\n", 133 | "Number of pass images: 12500\n" 134 | ] 135 | } 136 | ], 137 | "source": [ 138 | "files_path = path.join(save_path, 'train')\n", 139 | "fail_files = sorted(iglob(path.join(files_path, '*fail*.jpg')))\n", 140 | "pass_files = sorted(iglob(path.join(files_path, '*pass*.jpg')))\n", 141 | "print('Number of fail images: ' + str(len(fail_files)))\n", 142 | "print('Number of pass images: ' + str(len(pass_files)))" 143 | ] 144 | }, 145 | { 146 | "cell_type": "markdown", 147 | "metadata": {}, 148 | "source": [ 149 | "### Generate labels" 150 | ] 151 | }, 152 | { 153 | "cell_type": "markdown", 154 | "metadata": {}, 155 | "source": [ 156 | "Next, prepare the labels of the images. You will be labeling fail images with 0 and pass images with 1." 157 | ] 158 | }, 159 | { 160 | "cell_type": "code", 161 | "execution_count": 7, 162 | "metadata": {}, 163 | "outputs": [ 164 | { 165 | "data": { 166 | "text/plain": [ 167 | "array([ 0., 0., 0., ..., 1., 1., 1.])" 168 | ] 169 | }, 170 | "execution_count": 7, 171 | "metadata": {}, 172 | "output_type": "execute_result" 173 | } 174 | ], 175 | "source": [ 176 | "image_paths = fail_files + pass_files\n", 177 | "total_files = len(fail_files) + len(pass_files)\n", 178 | "labels = np.zeros(total_files)\n", 179 | "labels[len(fail_files):] = 1\n", 180 | "labels" 181 | ] 182 | }, 183 | { 184 | "cell_type": "markdown", 185 | "metadata": {}, 186 | "source": [ 187 | "### Compute features" 188 | ] 189 | }, 190 | { 191 | "cell_type": "markdown", 192 | "metadata": {}, 193 | "source": [ 194 | "In this part, you will use ResNet50 model with weights pretrained on ImageNet to obtain the features of the images. Download the model and save it into a keras model object. The include_top=False parameter is required to indicate that you will not include the fully-connected layer at the top of the network since you will only use the network to featurize images. The default input image size for this model is (224, 244) with 3 color channnels and requires input_shape=(224, 224, 3) parameter to be set as 'channels_last'." 195 | ] 196 | }, 197 | { 198 | "cell_type": "code", 199 | "execution_count": 8, 200 | "metadata": {}, 201 | "outputs": [ 202 | { 203 | "name": "stdout", 204 | "output_type": "stream", 205 | "text": [ 206 | "Downloading data from https://github.com/fchollet/deep-learning-models/releases/download/v0.2/resnet50_weights_tf_dim_ordering_tf_kernels_notop.h5\n", 207 | "92905472/94653016 [============================>.] - ETA: 0s" 208 | ] 209 | } 210 | ], 211 | "source": [ 212 | "model = ResNet50(include_top=False, input_shape=(224, 224, 3))" 213 | ] 214 | }, 215 | { 216 | "cell_type": "markdown", 217 | "metadata": {}, 218 | "source": [ 219 | "Next, you will use two helper functions, one for yielding batch size image paths from a list of file paths and the other one for featurizing these batches of images." 220 | ] 221 | }, 222 | { 223 | "cell_type": "code", 224 | "execution_count": 9, 225 | "metadata": {}, 226 | "outputs": [], 227 | "source": [ 228 | "def file_batch(file_list, batch_size):\n", 229 | " for i in range(0, len(file_list), batch_size):\n", 230 | " yield file_list[i:i + batch_size]" 231 | ] 232 | }, 233 | { 234 | "cell_type": "code", 235 | "execution_count": 10, 236 | "metadata": {}, 237 | "outputs": [], 238 | "source": [ 239 | "def featurize_images(file_list, model, batch_size=32):\n", 240 | " features = []\n", 241 | " \n", 242 | " for fb in tqdm(file_batch(file_list, batch_size)):\n", 243 | " load_img = []\n", 244 | " for file_path in fb:\n", 245 | " img = image.load_img(file_path, target_size=(224, 224))\n", 246 | " x = image.img_to_array(img)\n", 247 | " x = np.expand_dims(x, axis=0)\n", 248 | " load_img.append(preprocess_input(x))\n", 249 | " features.extend(model.predict_on_batch(np.concatenate(load_img)).squeeze())\n", 250 | " return np.array(features)" 251 | ] 252 | }, 253 | { 254 | "cell_type": "markdown", 255 | "metadata": {}, 256 | "source": [ 257 | "The featurize_images function processes the image files in small chunks (default is set to batch_size=32) by first loading the images in the batch into a target size of (224,224), then converting them into four dimensional tensors which keras model expects as input. After, that model's batch prediction method is called to calculate the features of each image." 258 | ] 259 | }, 260 | { 261 | "cell_type": "markdown", 262 | "metadata": {}, 263 | "source": [ 264 | "Next, you will call the function to compute the features and save them into the share folder. The execution of the following cell may take up to 10-15 minutes so feel free to read through the links provided in the beginning of the notebook in order to learn more about CNNs, ResNet50, Keras, CNTK and LightGBM." 265 | ] 266 | }, 267 | { 268 | "cell_type": "code", 269 | "execution_count": 11, 270 | "metadata": {}, 271 | "outputs": [ 272 | { 273 | "name": "stderr", 274 | "output_type": "stream", 275 | "text": [ 276 | "\r", 277 | "0it [00:00, ?it/s]" 278 | ] 279 | }, 280 | { 281 | "name": "stdout", 282 | "output_type": "stream", 283 | "text": [ 284 | "Computing features\n" 285 | ] 286 | }, 287 | { 288 | "name": "stderr", 289 | "output_type": "stream", 290 | "text": [ 291 | "782it [11:47, 1.10it/s]\n" 292 | ] 293 | }, 294 | { 295 | "name": "stdout", 296 | "output_type": "stream", 297 | "text": [ 298 | "CPU times: user 10min 8s, sys: 58.3 s, total: 11min 7s\n", 299 | "Wall time: 11min 48s\n" 300 | ] 301 | } 302 | ], 303 | "source": [ 304 | "%%time\n", 305 | "features_filename = path.join(save_path, 'features_resnet50.npy')\n", 306 | "if path.isfile(features_filename):\n", 307 | " print(\"Features found!\")\n", 308 | " features = np.load(features_filename)\n", 309 | "else:\n", 310 | " print(\"Computing features\")\n", 311 | " features = featurize_images(image_paths, model) \n", 312 | " np.save(features_filename, features)" 313 | ] 314 | }, 315 | { 316 | "cell_type": "markdown", 317 | "metadata": {}, 318 | "source": [ 319 | "Check the shape of the computed features." 320 | ] 321 | }, 322 | { 323 | "cell_type": "code", 324 | "execution_count": 12, 325 | "metadata": {}, 326 | "outputs": [ 327 | { 328 | "data": { 329 | "text/plain": [ 330 | "(25000, 2048)" 331 | ] 332 | }, 333 | "execution_count": 12, 334 | "metadata": {}, 335 | "output_type": "execute_result" 336 | } 337 | ], 338 | "source": [ 339 | "features.shape" 340 | ] 341 | }, 342 | { 343 | "cell_type": "markdown", 344 | "metadata": {}, 345 | "source": [ 346 | "## Training and Cross Validation" 347 | ] 348 | }, 349 | { 350 | "cell_type": "markdown", 351 | "metadata": {}, 352 | "source": [ 353 | "In this section, you will use the features calcuated in the previous section to train a LightGBM classifier. You will be performing cross validation to understand the performance of the model. You will use [scikit-learn's stratified K-fold cross validation](http://scikit-learn.org/0.16/modules/generated/sklearn.cross_validation.StratifiedKFold.html) where the folds are made by preserving the percentage of samples from each class. This is an important step especially if you are using your own data and number of images in one of the categories is substentially less than the other category. You will be using 5 fold cross validation. You can pick other values by changing n_splits in the next cell to the desired number of folds." 354 | ] 355 | }, 356 | { 357 | "cell_type": "code", 358 | "execution_count": 13, 359 | "metadata": {}, 360 | "outputs": [], 361 | "source": [ 362 | "n_splits = 5\n", 363 | "skf = StratifiedKFold(n_splits=n_splits, random_state=2048, shuffle=True)\n", 364 | "cv_results = pd.DataFrame(columns=['Accuracy', 'Precision', 'Recall', 'F1', 'AUC', 'Confusion Matrix'])" 365 | ] 366 | }, 367 | { 368 | "cell_type": "markdown", 369 | "metadata": {}, 370 | "source": [ 371 | "You will use a helper function called classification_metircs to calculate the evaluation metrics for each fold. " 372 | ] 373 | }, 374 | { 375 | "cell_type": "code", 376 | "execution_count": 14, 377 | "metadata": {}, 378 | "outputs": [], 379 | "source": [ 380 | "def classification_metrics(y_true, y_pred_proba, threshold=0.5):\n", 381 | " y_pred = np.where(y_pred_proba > threshold, 1, 0)\n", 382 | " cm_dict = {}\n", 383 | " cm_dict['Accuracy'] = accuracy_score(y_true, y_pred)\n", 384 | " cm_dict['Precision'] = precision_score(y_true, y_pred)\n", 385 | " cm_dict['Recall'] = recall_score(y_true, y_pred)\n", 386 | " cm_dict['F1'] = f1_score(y_true, y_pred) \n", 387 | " cm_dict['AUC'] = roc_auc_score(y_true, y_pred_proba)\n", 388 | " cm_dict['Confusion Matrix'] = confusion_matrix(y_true, y_pred).tolist()\n", 389 | " return cm_dict" 390 | ] 391 | }, 392 | { 393 | "cell_type": "markdown", 394 | "metadata": {}, 395 | "source": [ 396 | "Next, provide parameters of the LightGBM model. You can experiment with different parameters." 397 | ] 398 | }, 399 | { 400 | "cell_type": "code", 401 | "execution_count": 15, 402 | "metadata": {}, 403 | "outputs": [], 404 | "source": [ 405 | "params = {'num_leaves': 256,\n", 406 | " 'learning_rate': 0.1,\n", 407 | " 'min_split_gain': 0.1,\n", 408 | " 'min_child_weight': 30,\n", 409 | " 'reg_lambda': 1,\n", 410 | " 'subsample': 1,\n", 411 | " 'objective':'binary',\n", 412 | " 'task': 'train',\n", 413 | " 'verbose': 4\n", 414 | " }" 415 | ] 416 | }, 417 | { 418 | "cell_type": "markdown", 419 | "metadata": {}, 420 | "source": [ 421 | "Below, you will train your classifer for each fold and print the metrics." 422 | ] 423 | }, 424 | { 425 | "cell_type": "code", 426 | "execution_count": 16, 427 | "metadata": {}, 428 | "outputs": [ 429 | { 430 | "name": "stderr", 431 | "output_type": "stream", 432 | "text": [ 433 | "1it [00:17, 17.65s/it]" 434 | ] 435 | }, 436 | { 437 | "name": "stdout", 438 | "output_type": "stream", 439 | "text": [ 440 | "{'Precision': 0.98685782556750301, 'Recall': 0.99119999999999997, 'F1': 0.9890241468768709, 'AUC': 0.99931535999999999, 'Accuracy': 0.98899999999999999, 'Confusion Matrix': [[2467, 33], [22, 2478]]}\n" 441 | ] 442 | }, 443 | { 444 | "name": "stderr", 445 | "output_type": "stream", 446 | "text": [ 447 | "\r", 448 | "2it [00:35, 17.87s/it]" 449 | ] 450 | }, 451 | { 452 | "name": "stdout", 453 | "output_type": "stream", 454 | "text": [ 455 | "{'Precision': 0.98374306106264864, 'Recall': 0.99239999999999995, 'F1': 0.98805256869772995, 'AUC': 0.99951455999999994, 'Accuracy': 0.98799999999999999, 'Confusion Matrix': [[2459, 41], [19, 2481]]}\n" 456 | ] 457 | }, 458 | { 459 | "name": "stderr", 460 | "output_type": "stream", 461 | "text": [ 462 | "\r", 463 | "3it [00:52, 17.58s/it]" 464 | ] 465 | }, 466 | { 467 | "name": "stdout", 468 | "output_type": "stream", 469 | "text": [ 470 | "{'Precision': 0.98445595854922274, 'Recall': 0.98799999999999999, 'F1': 0.98622479536833696, 'AUC': 0.99931712000000006, 'Accuracy': 0.98619999999999997, 'Confusion Matrix': [[2461, 39], [30, 2470]]}\n" 471 | ] 472 | }, 473 | { 474 | "name": "stderr", 475 | "output_type": "stream", 476 | "text": [ 477 | "\r", 478 | "4it [01:10, 17.69s/it]" 479 | ] 480 | }, 481 | { 482 | "name": "stdout", 483 | "output_type": "stream", 484 | "text": [ 485 | "{'Precision': 0.98447452229299359, 'Recall': 0.98919999999999997, 'F1': 0.98683160415003979, 'AUC': 0.9994075200000001, 'Accuracy': 0.98680000000000001, 'Confusion Matrix': [[2461, 39], [27, 2473]]}\n" 486 | ] 487 | }, 488 | { 489 | "name": "stderr", 490 | "output_type": "stream", 491 | "text": [ 492 | "\r", 493 | "5it [01:28, 17.76s/it]" 494 | ] 495 | }, 496 | { 497 | "name": "stdout", 498 | "output_type": "stream", 499 | "text": [ 500 | "{'Precision': 0.98758510212254702, 'Recall': 0.98640000000000005, 'F1': 0.98699219531719029, 'AUC': 0.99890223999999994, 'Accuracy': 0.98699999999999999, 'Confusion Matrix': [[2469, 31], [34, 2466]]}\n", 501 | "CPU times: user 12min 19s, sys: 9.28 s, total: 12min 29s\n", 502 | "Wall time: 1min 28s\n" 503 | ] 504 | }, 505 | { 506 | "name": "stderr", 507 | "output_type": "stream", 508 | "text": [ 509 | "\n" 510 | ] 511 | } 512 | ], 513 | "source": [ 514 | "%%time \n", 515 | "for train_index, test_index in tqdm(skf.split(features, labels)):\n", 516 | " X_train, X_test = features[train_index], features[test_index]\n", 517 | " y_train, y_test = labels[train_index], labels[test_index]\n", 518 | " lgb_train = lgb.Dataset(X_train, y_train, free_raw_data=False)\n", 519 | " clf = lgb.train(params, lgb_train, num_boost_round=500)\n", 520 | " y_pred_proba = clf.predict(X_test)\n", 521 | " cm_dict = classification_metrics(y_test, y_pred_proba)\n", 522 | " print(cm_dict)\n", 523 | " cv_results = cv_results.append(classification_metrics(y_test, y_pred_proba),ignore_index=True)" 524 | ] 525 | }, 526 | { 527 | "cell_type": "code", 528 | "execution_count": 17, 529 | "metadata": {}, 530 | "outputs": [ 531 | { 532 | "data": { 533 | "text/plain": [ 534 | "Accuracy 0.987400\n", 535 | "Precision 0.985423\n", 536 | "Recall 0.989440\n", 537 | "F1 0.987425\n", 538 | "AUC 0.999291\n", 539 | "dtype: float64" 540 | ] 541 | }, 542 | "execution_count": 17, 543 | "metadata": {}, 544 | "output_type": "execute_result" 545 | } 546 | ], 547 | "source": [ 548 | "cv_results.mean()" 549 | ] 550 | }, 551 | { 552 | "cell_type": "markdown", 553 | "metadata": {}, 554 | "source": [ 555 | "Below, you will be using Azure Machine Learning Workbench's data collector to log the metrics of your cross validation. The metrics are stored by the history service and tied to the notebook that produced them. You can later view these metrics in the Run History tab of Azure ML Workbench. We will turn the cell level logging on before logging and turn it off after so that only the history of cell runs where metrics are collected are captured." 556 | ] 557 | }, 558 | { 559 | "cell_type": "code", 560 | "execution_count": 18, 561 | "metadata": {}, 562 | "outputs": [ 563 | { 564 | "name": "stdout", 565 | "output_type": "stream", 566 | "text": [ 567 | "History logging enabled\n", 568 | "History logging is enabled\n" 569 | ] 570 | } 571 | ], 572 | "source": [ 573 | "%azureml history on\n", 574 | "%azureml history show" 575 | ] 576 | }, 577 | { 578 | "cell_type": "code", 579 | "execution_count": 19, 580 | "metadata": {}, 581 | "outputs": [ 582 | { 583 | "data": { 584 | "text/plain": [ 585 | "" 586 | ] 587 | }, 588 | "execution_count": 19, 589 | "metadata": {}, 590 | "output_type": "execute_result" 591 | } 592 | ], 593 | "source": [ 594 | "logger.log('Accuracy',cv_results.mean()['Accuracy'])\n", 595 | "logger.log('Precision',cv_results.mean()['Precision'])\n", 596 | "logger.log('Recall',cv_results.mean()['Recall'])\n", 597 | "logger.log('F1',cv_results.mean()['F1'])\n", 598 | "logger.log('AUC',cv_results.mean()['AUC'])" 599 | ] 600 | }, 601 | { 602 | "cell_type": "code", 603 | "execution_count": 20, 604 | "metadata": {}, 605 | "outputs": [ 606 | { 607 | "name": "stdout", 608 | "output_type": "stream", 609 | "text": [ 610 | "History logging disabled\n" 611 | ] 612 | } 613 | ], 614 | "source": [ 615 | "%azureml history off" 616 | ] 617 | }, 618 | { 619 | "cell_type": "markdown", 620 | "metadata": {}, 621 | "source": [ 622 | "Now, you will train our model on the whole dataset one last time to obtain the final trained model for operationalization. You will also plot the confusion matrix for this model." 623 | ] 624 | }, 625 | { 626 | "cell_type": "code", 627 | "execution_count": 21, 628 | "metadata": {}, 629 | "outputs": [], 630 | "source": [ 631 | "lgb_train = lgb.Dataset(features, labels, free_raw_data=False)\n", 632 | "clf = lgb.train(params, lgb_train, num_boost_round=500)\n", 633 | "y_pred_proba = clf.predict(features)" 634 | ] 635 | }, 636 | { 637 | "cell_type": "markdown", 638 | "metadata": {}, 639 | "source": [ 640 | "You will use the following helper function to plot the confusion matrix." 641 | ] 642 | }, 643 | { 644 | "cell_type": "code", 645 | "execution_count": 22, 646 | "metadata": {}, 647 | "outputs": [], 648 | "source": [ 649 | "def plot_confusion_matrix(cm, classes, normalize=False, title='Confusion matrix', cmap=plt.cm.Blues):\n", 650 | " \"\"\"Plots a confusion matrix.\n", 651 | " Source: http://scikit-learn.org/stable/auto_examples/model_selection/plot_confusion_matrix.html\n", 652 | " \"\"\"\n", 653 | " cm_max = cm.max()\n", 654 | " cm_min = cm.min()\n", 655 | " if cm_min > 0: cm_min = 0\n", 656 | " if normalize:\n", 657 | " cm = cm.astype('float') / cm.sum(axis=1)[:, np.newaxis]\n", 658 | " cm_max = 1\n", 659 | " plt.imshow(cm, interpolation='nearest', cmap=cmap)\n", 660 | " plt.title(title)\n", 661 | " plt.colorbar()\n", 662 | " tick_marks = np.arange(len(classes))\n", 663 | " plt.xticks(tick_marks, classes, rotation=45)\n", 664 | " plt.yticks(tick_marks, classes)\n", 665 | " thresh = cm_max / 2.\n", 666 | " plt.clim(cm_min, cm_max)\n", 667 | "\n", 668 | " for i, j in itertools.product(range(cm.shape[0]), range(cm.shape[1])):\n", 669 | " plt.text(j, i,\n", 670 | " round(cm[i, j], 3), # round to 3 decimals if they are float\n", 671 | " horizontalalignment=\"center\",\n", 672 | " color=\"white\" if cm[i, j] > thresh else \"black\")\n", 673 | " plt.ylabel('True label')\n", 674 | " plt.xlabel('Predicted label')\n", 675 | " plt.show()" 676 | ] 677 | }, 678 | { 679 | "cell_type": "code", 680 | "execution_count": 23, 681 | "metadata": {}, 682 | "outputs": [ 683 | { 684 | "data": { 685 | "image/png": "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\n", 686 | "text/plain": [ 687 | "" 688 | ] 689 | }, 690 | "metadata": {}, 691 | "output_type": "display_data" 692 | }, 693 | { 694 | "name": "stdout", 695 | "output_type": "stream", 696 | "text": [ 697 | "Confusion Matrix:\n", 698 | "[[12497 3]\n", 699 | " [ 0 12500]]\n" 700 | ] 701 | } 702 | ], 703 | "source": [ 704 | "cm = np.asarray(classification_metrics(labels, y_pred_proba)['Confusion Matrix'])\n", 705 | "plot_confusion_matrix(cm, ['fail','pass'], normalize=True)\n", 706 | "print('Confusion Matrix:')\n", 707 | "print(cm)" 708 | ] 709 | }, 710 | { 711 | "cell_type": "markdown", 712 | "metadata": {}, 713 | "source": [ 714 | "## Save the model" 715 | ] 716 | }, 717 | { 718 | "cell_type": "markdown", 719 | "metadata": {}, 720 | "source": [ 721 | "Save the model file to the shared folder. You will load this file in the next notebook for operationalization." 722 | ] 723 | }, 724 | { 725 | "cell_type": "code", 726 | "execution_count": 24, 727 | "metadata": {}, 728 | "outputs": [], 729 | "source": [ 730 | "model_path = path.join(save_path,'lightgbm_classifier.model')\n", 731 | "clf.save_model(model_path)" 732 | ] 733 | }, 734 | { 735 | "cell_type": "markdown", 736 | "metadata": {}, 737 | "source": [ 738 | "As one option to retrive your model for operationalization, you can write the file to the special outputs folder which can be accessed from Azure ML workbench under the run history tab for this notebook. " 739 | ] 740 | }, 741 | { 742 | "cell_type": "code", 743 | "execution_count": 25, 744 | "metadata": {}, 745 | "outputs": [], 746 | "source": [ 747 | "outputs = 'outputs'" 748 | ] 749 | }, 750 | { 751 | "cell_type": "code", 752 | "execution_count": 26, 753 | "metadata": {}, 754 | "outputs": [], 755 | "source": [ 756 | "model_path = path.join('outputs','lightgbm_classifier.model')\n", 757 | "clf.save_model(model_path)" 758 | ] 759 | }, 760 | { 761 | "cell_type": "code", 762 | "execution_count": 27, 763 | "metadata": {}, 764 | "outputs": [ 765 | { 766 | "name": "stdout", 767 | "output_type": "stream", 768 | "text": [ 769 | "History logging enabled\n" 770 | ] 771 | } 772 | ], 773 | "source": [ 774 | "%azureml history on" 775 | ] 776 | }, 777 | { 778 | "cell_type": "code", 779 | "execution_count": 28, 780 | "metadata": {}, 781 | "outputs": [], 782 | "source": [ 783 | "filepath = path.join('outputs','lightgbm_classifier.model')\n", 784 | "with open(filepath, \"rb\") as file:\n", 785 | " get_azureml_logger().upload(path.normpath(filepath), file.read())" 786 | ] 787 | }, 788 | { 789 | "cell_type": "markdown", 790 | "metadata": {}, 791 | "source": [ 792 | "Now, as an option, you can locate the model file in Azure ML Workbench by clicking the last completed run under the notebook run history and download it." 793 | ] 794 | }, 795 | { 796 | "cell_type": "code", 797 | "execution_count": 29, 798 | "metadata": {}, 799 | "outputs": [ 800 | { 801 | "name": "stdout", 802 | "output_type": "stream", 803 | "text": [ 804 | "History logging disabled\n" 805 | ] 806 | } 807 | ], 808 | "source": [ 809 | "%azureml history off" 810 | ] 811 | }, 812 | { 813 | "cell_type": "markdown", 814 | "metadata": {}, 815 | "source": [ 816 | "As another option, you will create a folder under AZUREML_NATIVE_SHARE_DIRECTORY where you will save the files needed for operationalization. In the next notebook, you will compress and upload this folder to blob storage for later retrieval and usage for web service deployment." 817 | ] 818 | }, 819 | { 820 | "cell_type": "code", 821 | "execution_count": 43, 822 | "metadata": {}, 823 | "outputs": [], 824 | "source": [ 825 | "o16n_path = path.join(save_path,'o16n') \n", 826 | "if not os.path.exists(o16n_path):\n", 827 | " os.makedirs(o16n_path)" 828 | ] 829 | }, 830 | { 831 | "cell_type": "code", 832 | "execution_count": 45, 833 | "metadata": {}, 834 | "outputs": [], 835 | "source": [ 836 | "model_path = path.join(o16n_path,'lightgbm_classifier.model')\n", 837 | "clf.save_model(model_path)" 838 | ] 839 | }, 840 | { 841 | "cell_type": "markdown", 842 | "metadata": {}, 843 | "source": [ 844 | "Next, go to the 3rd notebook to prepare other files needed for operationalization of your model." 845 | ] 846 | } 847 | ], 848 | "metadata": { 849 | "kernelspec": { 850 | "display_name": "imgtutvso local", 851 | "language": "python", 852 | "name": "imgtutvso_local" 853 | }, 854 | "language_info": { 855 | "codemirror_mode": { 856 | "name": "ipython", 857 | "version": 3 858 | }, 859 | "file_extension": ".py", 860 | "mimetype": "text/x-python", 861 | "name": "python", 862 | "nbconvert_exporter": "python", 863 | "pygments_lexer": "ipython3", 864 | "version": "3.5.2" 865 | } 866 | }, 867 | "nbformat": 4, 868 | "nbformat_minor": 2 869 | } 870 | -------------------------------------------------------------------------------- /4-deploy-model.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "Copyright (C) Microsoft Corporation." 8 | ] 9 | }, 10 | { 11 | "cell_type": "markdown", 12 | "metadata": {}, 13 | "source": [ 14 | "# Deploy model as a Web Service" 15 | ] 16 | }, 17 | { 18 | "cell_type": "markdown", 19 | "metadata": {}, 20 | "source": [ 21 | "**Important**: Make sure the kernel is set to \"Your project name local\" which can be done from the *Kernel* menu under *Change kernel*. " 22 | ] 23 | }, 24 | { 25 | "cell_type": "markdown", 26 | "metadata": {}, 27 | "source": [ 28 | "The web services can be deployed locally or to a cluster. Best practice is to start with a local deployment, validate that your model and code work, and then deploy to a cluster for production scale use. In this notebook, you will first download your operationalization files, register machine learning compute resource provider in your subscription and create a model management account.\n", 29 | "\n", 30 | "Next, **If you have Docker engine running locally**, you can continue with the instuctions for [local environment setup](#Local-environment-setup) section and follow the rest of the instructions to deploy your models locally. **If you are not running Docker locally**, then you will need to skip the local environment setup section and follow the steps for [cluster environment setup](#Cluster-environment-setup) and the rest of the instructions to deploy your web service to a cluster." 31 | ] 32 | }, 33 | { 34 | "cell_type": "code", 35 | "execution_count": 1, 36 | "metadata": {}, 37 | "outputs": [], 38 | "source": [ 39 | "import os\n", 40 | "import base64\n", 41 | "import requests\n", 42 | "import json\n", 43 | "import zipfile\n", 44 | "from azure.storage.blob import BlockBlobService" 45 | ] 46 | }, 47 | { 48 | "cell_type": "markdown", 49 | "metadata": {}, 50 | "source": [ 51 | "## Download operationalization files" 52 | ] 53 | }, 54 | { 55 | "cell_type": "markdown", 56 | "metadata": {}, 57 | "source": [ 58 | "First, you will download the zip file you uploaded to blob in the earlier notebook and extract it to get the model and scoring script. Locate your storage account name and key as described in the Getting Started document of this tutorial and provide your values in the following cell where indicated." 59 | ] 60 | }, 61 | { 62 | "cell_type": "code", 63 | "execution_count": 1, 64 | "metadata": {}, 65 | "outputs": [], 66 | "source": [ 67 | "ACCOUNT_NAME = 'Your Storage Account Name Here!!'\n", 68 | "ACCOUNT_KEY = 'Your Storage Account Key Here!!'\n", 69 | "CONTAINER_NAME = \"deploy\"\n", 70 | "ZIP_FILE = 'o16n.zip'\n", 71 | "DESTINATION_FILE = ZIP_FILE" 72 | ] 73 | }, 74 | { 75 | "cell_type": "markdown", 76 | "metadata": {}, 77 | "source": [ 78 | "Download and extract the files to the current working directory." 79 | ] 80 | }, 81 | { 82 | "cell_type": "code", 83 | "execution_count": 4, 84 | "metadata": {}, 85 | "outputs": [], 86 | "source": [ 87 | "block_blob_service = BlockBlobService(account_name=ACCOUNT_NAME, account_key=ACCOUNT_KEY)\n", 88 | "block_blob_service.get_blob_to_path(container_name=CONTAINER_NAME, blob_name=ZIP_FILE, file_path=DESTINATION_FILE)\n", 89 | "zip = zipfile.ZipFile(ZIP_FILE)\n", 90 | "zip.extractall()" 91 | ] 92 | }, 93 | { 94 | "cell_type": "markdown", 95 | "metadata": {}, 96 | "source": [ 97 | "## Register resource provider" 98 | ] 99 | }, 100 | { 101 | "cell_type": "markdown", 102 | "metadata": {}, 103 | "source": [ 104 | "First, make sure the Azure resource provider Microsoft.MachineLearningCompute is registered in your subscription.This is a one time task per subscription." 105 | ] 106 | }, 107 | { 108 | "cell_type": "code", 109 | "execution_count": 5, 110 | "metadata": {}, 111 | "outputs": [ 112 | { 113 | "name": "stderr", 114 | "output_type": "stream", 115 | "text": [ 116 | "WARNING: Registering is still on-going. You can monitor using 'az provider show -n Microsoft.MachineLearningCompute'\n" 117 | ] 118 | } 119 | ], 120 | "source": [ 121 | "!az provider register -n Microsoft.MachineLearningCompute" 122 | ] 123 | }, 124 | { 125 | "cell_type": "markdown", 126 | "metadata": {}, 127 | "source": [ 128 | "Make sure you see \"registrationState\": \"Registered\" in the next cell output before you proceed to the next steps." 129 | ] 130 | }, 131 | { 132 | "cell_type": "code", 133 | "execution_count": 6, 134 | "metadata": {}, 135 | "outputs": [ 136 | { 137 | "name": "stdout", 138 | "output_type": "stream", 139 | "text": [ 140 | "{\n", 141 | " \"id\": \"/subscriptions/xxxxxxx-xxxx-xxxx-xxxxx-xxxxxxxx/providers/Microsoft.MachineLearningCompute\",\n", 142 | " \"namespace\": \"Microsoft.MachineLearningCompute\",\n", 143 | " \"registrationState\": \"Registered\",\n", 144 | " \"resourceTypes\": [\n", 145 | " {\n", 146 | " \"aliases\": null,\n", 147 | " \"apiVersions\": [\n", 148 | " \"2017-08-01-preview\",\n", 149 | " \"2017-06-01-preview\"\n", 150 | " ],\n", 151 | " \"locations\": [\n", 152 | " \"East US 2\",\n", 153 | " \"West Central US\",\n", 154 | " \"Australia East\",\n", 155 | " \"West Europe\",\n", 156 | " \"Southeast Asia\"\n", 157 | " ],\n", 158 | " \"properties\": null,\n", 159 | " \"resourceType\": \"operationalizationClusters\"\n", 160 | " },\n", 161 | " {\n", 162 | " \"aliases\": null,\n", 163 | " \"apiVersions\": [\n", 164 | " \"2017-08-01-preview\",\n", 165 | " \"2017-06-01-preview\"\n", 166 | " ],\n", 167 | " \"locations\": [\n", 168 | " \"East US 2\"\n", 169 | " ],\n", 170 | " \"properties\": null,\n", 171 | " \"resourceType\": \"operations\"\n", 172 | " },\n", 173 | " {\n", 174 | " \"aliases\": null,\n", 175 | " \"apiVersions\": [\n", 176 | " \"2017-08-01-preview\",\n", 177 | " \"2017-06-01-preview\"\n", 178 | " ],\n", 179 | " \"locations\": [\n", 180 | " \"East US 2\"\n", 181 | " ],\n", 182 | " \"properties\": null,\n", 183 | " \"resourceType\": \"locations\"\n", 184 | " },\n", 185 | " {\n", 186 | " \"aliases\": null,\n", 187 | " \"apiVersions\": [\n", 188 | " \"2017-08-01-preview\",\n", 189 | " \"2017-06-01-preview\"\n", 190 | " ],\n", 191 | " \"locations\": [\n", 192 | " \"East US 2\",\n", 193 | " \"West Central US\",\n", 194 | " \"Australia East\",\n", 195 | " \"West Europe\",\n", 196 | " \"Southeast Asia\"\n", 197 | " ],\n", 198 | " \"properties\": null,\n", 199 | " \"resourceType\": \"locations/operations\"\n", 200 | " },\n", 201 | " {\n", 202 | " \"aliases\": null,\n", 203 | " \"apiVersions\": [\n", 204 | " \"2017-08-01-preview\",\n", 205 | " \"2017-06-01-preview\"\n", 206 | " ],\n", 207 | " \"locations\": [\n", 208 | " \"East US 2\",\n", 209 | " \"West Central US\",\n", 210 | " \"Australia East\",\n", 211 | " \"West Europe\",\n", 212 | " \"Southeast Asia\"\n", 213 | " ],\n", 214 | " \"properties\": null,\n", 215 | " \"resourceType\": \"locations/operationsStatus\"\n", 216 | " }\n", 217 | " ]\n", 218 | "}\n" 219 | ] 220 | } 221 | ], 222 | "source": [ 223 | "!az provider show -n Microsoft.MachineLearningCompute" 224 | ] 225 | }, 226 | { 227 | "cell_type": "markdown", 228 | "metadata": {}, 229 | "source": [ 230 | "## Create names for deployment" 231 | ] 232 | }, 233 | { 234 | "cell_type": "markdown", 235 | "metadata": {}, 236 | "source": [ 237 | "To deploy and test your web services, provide an Azure region and a deployment name in the following cell where indicated." 238 | ] 239 | }, 240 | { 241 | "cell_type": "code", 242 | "execution_count": 5, 243 | "metadata": {}, 244 | "outputs": [], 245 | "source": [ 246 | "LOCATION = 'Pick an Azure Region Here!! e.g. eastus2'\n", 247 | "DEPLOY_NAME = 'Pick a Deployment Name Here!!'" 248 | ] 249 | }, 250 | { 251 | "cell_type": "markdown", 252 | "metadata": {}, 253 | "source": [ 254 | "## Create a model management account" 255 | ] 256 | }, 257 | { 258 | "cell_type": "markdown", 259 | "metadata": {}, 260 | "source": [ 261 | "You need to create a [Model Management Account](https://docs.microsoft.com/en-us/azure/machine-learning/preview/model-management-overview) to deploy your models. You need to do this once per subscription, and can reuse the same account in multiple deployments." 262 | ] 263 | }, 264 | { 265 | "cell_type": "code", 266 | "execution_count": 6, 267 | "metadata": {}, 268 | "outputs": [], 269 | "source": [ 270 | "MM_ACCOUNT = DEPLOY_NAME + 'mmacc' \n", 271 | "MM_ACCOUNT_RESOURCE_GROUP = MM_ACCOUNT + 'rg'" 272 | ] 273 | }, 274 | { 275 | "cell_type": "markdown", 276 | "metadata": {}, 277 | "source": [ 278 | "Create a resource group for model management account which will be used for both local and cluster deployments." 279 | ] 280 | }, 281 | { 282 | "cell_type": "code", 283 | "execution_count": 9, 284 | "metadata": {}, 285 | "outputs": [ 286 | { 287 | "name": "stdout", 288 | "output_type": "stream", 289 | "text": [ 290 | "{\n", 291 | " \"id\": \"/subscriptions/edf507a2-6235-46c5-b560-fd463ba2e771/resourceGroups/imagetutmmaccrg\",\n", 292 | " \"location\": \"eastus2\",\n", 293 | " \"managedBy\": null,\n", 294 | " \"name\": \"imagetutmmaccrg\",\n", 295 | " \"properties\": {\n", 296 | " \"provisioningState\": \"Succeeded\"\n", 297 | " },\n", 298 | " \"tags\": null\n", 299 | "}\n" 300 | ] 301 | } 302 | ], 303 | "source": [ 304 | "!az group create --l $LOCATION --name $MM_ACCOUNT_RESOURCE_GROUP" 305 | ] 306 | }, 307 | { 308 | "cell_type": "markdown", 309 | "metadata": {}, 310 | "source": [ 311 | "Now, create the model management account in the resource group created above." 312 | ] 313 | }, 314 | { 315 | "cell_type": "code", 316 | "execution_count": 10, 317 | "metadata": {}, 318 | "outputs": [ 319 | { 320 | "name": "stdout", 321 | "output_type": "stream", 322 | "text": [ 323 | "{\n", 324 | " \"created_on\": \"2017-11-03T17:51:11.9226449999999999Z\",\n", 325 | " \"description\": \"\",\n", 326 | " \"id\": \"/subscriptions/edf507a2-6235-46c5-b560-fd463ba2e771/resourceGroups/imagetutmmaccrg/providers/Microsoft.MachineLearningModelManagement/accounts/imagetutmmacc\",\n", 327 | " \"location\": \"eastus2\",\n", 328 | " \"model_management_swagger_location\": \"https://eastus2.modelmanagement.azureml.net/api/subscriptions/edf507a2-6235-46c5-b560-fd463ba2e771/resourceGroups/imagetutmmaccrg/accounts/imagetutmmacc/swagger.json?api-version=2017-09-01-preview\",\n", 329 | " \"modified_on\": \"2017-11-03T17:51:11.9226449999999999Z\",\n", 330 | " \"name\": \"imagetutmmacc\",\n", 331 | " \"resource_group\": \"imagetutmmaccrg\",\n", 332 | " \"sku\": {\n", 333 | " \"capacity\": 1,\n", 334 | " \"name\": \"S1\"\n", 335 | " },\n", 336 | " \"subscription\": \"edf507a2-6235-46c5-b560-fd463ba2e771\",\n", 337 | " \"tags\": {},\n", 338 | " \"type\": \"Microsoft.MachineLearningModelManagement/accounts\"\n", 339 | "}\n" 340 | ] 341 | } 342 | ], 343 | "source": [ 344 | "!az ml account modelmanagement create -l $LOCATION -n $MM_ACCOUNT -g $MM_ACCOUNT_RESOURCE_GROUP" 345 | ] 346 | }, 347 | { 348 | "cell_type": "markdown", 349 | "metadata": {}, 350 | "source": [ 351 | "Verify that model management account is set." 352 | ] 353 | }, 354 | { 355 | "cell_type": "code", 356 | "execution_count": 11, 357 | "metadata": {}, 358 | "outputs": [ 359 | { 360 | "name": "stdout", 361 | "output_type": "stream", 362 | "text": [ 363 | "{\n", 364 | " \"created_on\": \"2017-11-03T17:51:11.9226449999999999Z\",\n", 365 | " \"description\": \"\",\n", 366 | " \"id\": \"/subscriptions/edf507a2-6235-46c5-b560-fd463ba2e771/resourceGroups/imagetutmmaccrg/providers/Microsoft.MachineLearningModelManagement/accounts/imagetutmmacc\",\n", 367 | " \"location\": \"eastus2\",\n", 368 | " \"model_management_swagger_location\": \"https://eastus2.modelmanagement.azureml.net/api/subscriptions/edf507a2-6235-46c5-b560-fd463ba2e771/resourceGroups/imagetutmmaccrg/accounts/imagetutmmacc/swagger.json?api-version=2017-09-01-preview\",\n", 369 | " \"modified_on\": \"2017-11-03T17:51:11.9226449999999999Z\",\n", 370 | " \"name\": \"imagetutmmacc\",\n", 371 | " \"resource_group\": \"imagetutmmaccrg\",\n", 372 | " \"sku\": {\n", 373 | " \"capacity\": 1,\n", 374 | " \"name\": \"S1\"\n", 375 | " },\n", 376 | " \"subscription\": \"edf507a2-6235-46c5-b560-fd463ba2e771\",\n", 377 | " \"tags\": {},\n", 378 | " \"type\": \"Microsoft.MachineLearningModelManagement/accounts\"\n", 379 | "}\n" 380 | ] 381 | } 382 | ], 383 | "source": [ 384 | "!az ml account modelmanagement show" 385 | ] 386 | }, 387 | { 388 | "cell_type": "markdown", 389 | "metadata": {}, 390 | "source": [ 391 | "## Local environment setup" 392 | ] 393 | }, 394 | { 395 | "cell_type": "markdown", 396 | "metadata": {}, 397 | "source": [ 398 | "In the following steps, you will deploy and test your web service on the local machine running Docker. If you are not running Docker locally, please jump to [the cluster environment setup](#Cluster-environment-setup) section." 399 | ] 400 | }, 401 | { 402 | "cell_type": "code", 403 | "execution_count": 14, 404 | "metadata": {}, 405 | "outputs": [], 406 | "source": [ 407 | "LOCAL_ENV_NAME = DEPLOY_NAME + 'local'+ 'env' \n", 408 | "LOCAL_RESOURCE_GROUP = LOCAL_ENV_NAME + 'rg'" 409 | ] 410 | }, 411 | { 412 | "cell_type": "markdown", 413 | "metadata": {}, 414 | "source": [ 415 | "Set up a local environment using the following command which creates the following resources in your subscription:\n", 416 | "- A resource group with \"rg\" added at the end of the environment name you picked\n", 417 | "- A storage account\n", 418 | "- An Azure Container Registry (ACR)\n", 419 | "- An Application insights account" 420 | ] 421 | }, 422 | { 423 | "cell_type": "code", 424 | "execution_count": 15, 425 | "metadata": {}, 426 | "outputs": [ 427 | { 428 | "name": "stdout", 429 | "output_type": "stream", 430 | "text": [ 431 | "Creating resource group imagetutlocalenvrg\n", 432 | "Provisioning compute resources...\n", 433 | "Resource creation submitted successfully.\n", 434 | "To see more information for your environment, run:\n", 435 | " az ml env show -g imagetutlocalenvrg -n imagetutlocalenv\n", 436 | "You can set the new environment as your target context using:\n", 437 | " az ml env set -g imagetutlocalenvrg -n imagetutlocalenv\n" 438 | ] 439 | } 440 | ], 441 | "source": [ 442 | "!az ml env setup -l $LOCATION -n $LOCAL_ENV_NAME --yes" 443 | ] 444 | }, 445 | { 446 | "cell_type": "markdown", 447 | "metadata": {}, 448 | "source": [ 449 | "You can check the information on the environment with the following. You will need to wait until \"Provisioning State\" is set to \"Succeeded\" to continue with the next steps." 450 | ] 451 | }, 452 | { 453 | "cell_type": "code", 454 | "execution_count": 20, 455 | "metadata": {}, 456 | "outputs": [ 457 | { 458 | "name": "stdout", 459 | "output_type": "stream", 460 | "text": [ 461 | "{\n", 462 | " \"Cluster Name\": \"imagetutlocalenv\",\n", 463 | " \"Cluster Size\": \"N/A\",\n", 464 | " \"Created On\": \"2017-11-06T14:34:11.309Z\",\n", 465 | " \"Location\": \"eastus2\",\n", 466 | " \"Provisioning State\": \"Succeeded\",\n", 467 | " \"Resource Group\": \"imagetutlocalenvrg\",\n", 468 | " \"Subscription\": \"edf507a2-6235-46c5-b560-fd463ba2e771\"\n", 469 | "}\n" 470 | ] 471 | } 472 | ], 473 | "source": [ 474 | "!az ml env show -g $LOCAL_RESOURCE_GROUP -n $LOCAL_ENV_NAME" 475 | ] 476 | }, 477 | { 478 | "cell_type": "markdown", 479 | "metadata": {}, 480 | "source": [ 481 | "Now, set the environment to be used." 482 | ] 483 | }, 484 | { 485 | "cell_type": "code", 486 | "execution_count": 21, 487 | "metadata": {}, 488 | "outputs": [ 489 | { 490 | "name": "stdout", 491 | "output_type": "stream", 492 | "text": [ 493 | "Compute set to imagetutlocalenv.\n" 494 | ] 495 | } 496 | ], 497 | "source": [ 498 | "!az ml env set -g $LOCAL_RESOURCE_GROUP -n $LOCAL_ENV_NAME" 499 | ] 500 | }, 501 | { 502 | "cell_type": "markdown", 503 | "metadata": {}, 504 | "source": [ 505 | "Check if your environment is set." 506 | ] 507 | }, 508 | { 509 | "cell_type": "code", 510 | "execution_count": 22, 511 | "metadata": {}, 512 | "outputs": [ 513 | { 514 | "name": "stdout", 515 | "output_type": "stream", 516 | "text": [ 517 | "{\n", 518 | " \"Cluster Name\": \"imagetutlocalenv\",\n", 519 | " \"Cluster Size\": \"N/A\",\n", 520 | " \"Created On\": \"2017-11-06T14:34:11.309Z\",\n", 521 | " \"Current Mode\": \"local\",\n", 522 | " \"Location\": \"eastus2\",\n", 523 | " \"Provisioning State\": \"Succeeded\",\n", 524 | " \"Resource Group\": \"imagetutlocalenvrg\",\n", 525 | " \"Subscription\": \"edf507a2-6235-46c5-b560-fd463ba2e771\"\n", 526 | "}\n" 527 | ] 528 | } 529 | ], 530 | "source": [ 531 | "!az ml env show" 532 | ] 533 | }, 534 | { 535 | "cell_type": "markdown", 536 | "metadata": {}, 537 | "source": [ 538 | "### Deploy model locally" 539 | ] 540 | }, 541 | { 542 | "cell_type": "markdown", 543 | "metadata": {}, 544 | "source": [ 545 | "Now you can deploy your model with the following command.\n", 546 | "```bash\n", 547 | "az ml service create realtime --model-file [model file/folder path] -f [scoring file e.g. score.py] -n [your service name] -s [schema file e.g. service_schema.json] -r [runtime for the Docker container e.g. spark-py or python] -c [conda dependencies file for additional python packages]\n", 548 | "```" 549 | ] 550 | }, 551 | { 552 | "cell_type": "markdown", 553 | "metadata": {}, 554 | "source": [ 555 | "First, set the service name." 556 | ] 557 | }, 558 | { 559 | "cell_type": "code", 560 | "execution_count": 24, 561 | "metadata": {}, 562 | "outputs": [], 563 | "source": [ 564 | "LOCAL_SERVICE_NAME = DEPLOY_NAME + 'local' + 'srvc'" 565 | ] 566 | }, 567 | { 568 | "cell_type": "markdown", 569 | "metadata": {}, 570 | "source": [ 571 | "Create the real time service." 572 | ] 573 | }, 574 | { 575 | "cell_type": "code", 576 | "execution_count": 25, 577 | "metadata": {}, 578 | "outputs": [ 579 | { 580 | "name": "stdout", 581 | "output_type": "stream", 582 | "text": [ 583 | " lightgbm_classifier.model\n", 584 | "Successfully registered model\n", 585 | "Id: 6328903bd0854d34808aea3832ab8299\n", 586 | "More information: 'az ml model show -m 6328903bd0854d34808aea3832ab8299'\n", 587 | "Creating new driver at C:\\Users\\fboylu\\AppData\\Local\\Temp\\tmp329rld32.py\n", 588 | " imgscore.py\n", 589 | "Successfully created manifest\n", 590 | "Id: 291cc2c7-6783-4294-89f4-540f2e79ee1c\n", 591 | "More information: 'az ml manifest show -i 291cc2c7-6783-4294-89f4-540f2e79ee1c'\n", 592 | "Creating image..................................................................................Done.\n", 593 | "Image ID: b7416cdf-f3e3-44f9-821c-d7d21e5c8960\n", 594 | "More details: 'az ml image show -i b7416cdf-f3e3-44f9-821c-d7d21e5c8960'\n", 595 | "Usage information: 'az ml image usage -i b7416cdf-f3e3-44f9-821c-d7d21e5c8960'\n", 596 | "[Local mode] Running docker container.\n", 597 | "[Local mode] Pulling the image from mlcrpacrfcc24453077f.azurecr.io/imagetutlocalsrvc:6. This may take a few minutes, depending on your connection speed...\n", 598 | "[Local mode] Pulling...................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................\n", 599 | "[Local mode] Waiting for container to initialize\n", 600 | "[Local mode] Done\n", 601 | "[Local mode] Service ID: imagetutlocalsrvc\n", 602 | "[Local mode] Usage for cmd: az ml service run realtime -i imagetutlocalsrvc -d !! YOUR DATA HERE !!\n", 603 | "[Local mode] Usage for powershell: az ml service run realtime -i imagetutlocalsrvc --% -d !! YOUR DATA HERE !!\n", 604 | "[Local mode] Additional usage information: 'az ml service usage realtime -i imagetutlocalsrvc'\n" 605 | ] 606 | } 607 | ], 608 | "source": [ 609 | "!az ml service create realtime --model-file lightgbm_classifier.model -f imgscore.py -n $LOCAL_SERVICE_NAME -r python -c aml_config/conda_dependencies_srvc.yml" 610 | ] 611 | }, 612 | { 613 | "cell_type": "markdown", 614 | "metadata": {}, 615 | "source": [ 616 | "Check the state of the service." 617 | ] 618 | }, 619 | { 620 | "cell_type": "code", 621 | "execution_count": 26, 622 | "metadata": {}, 623 | "outputs": [ 624 | { 625 | "name": "stdout", 626 | "output_type": "stream", 627 | "text": [ 628 | "Name Id UpdatedAt State\n", 629 | "----------------- ----------------- ------------------- -------\n", 630 | "imagetutlocalsrvc imagetutlocalsrvc 2017-11-03T20:12:12 running\n" 631 | ] 632 | } 633 | ], 634 | "source": [ 635 | "!az ml service list realtime -o table" 636 | ] 637 | }, 638 | { 639 | "cell_type": "markdown", 640 | "metadata": {}, 641 | "source": [ 642 | "After the web service has been successfully deployed, you can use the following command to get the service URL and other details for calling the service endpoint. You will use the Scoring URL in the output below for calling the service in the following steps." 643 | ] 644 | }, 645 | { 646 | "cell_type": "code", 647 | "execution_count": 27, 648 | "metadata": {}, 649 | "outputs": [ 650 | { 651 | "name": "stdout", 652 | "output_type": "stream", 653 | "text": [ 654 | "Scoring URL:\n", 655 | " http://127.0.0.1:32771/score\n", 656 | "\n", 657 | "Headers:\n", 658 | " Content-Type: application/json\n", 659 | "\n", 660 | "Swagger URL:\n", 661 | " http://127.0.0.1:32771/swagger.json\n", 662 | "\n", 663 | "Sample CLI command:\n", 664 | " Usage for cmd: az ml service run realtime -i imagetutlocalsrvc -d !! YOUR DATA HERE !!\n", 665 | " Usage for powershell: az ml service run realtime -i imagetutlocalsrvc --% -d !! YOUR DATA HERE !!\n", 666 | "\n", 667 | "Sample CURL call:\n", 668 | " curl -X POST -H \"Content-Type:application/json\" --data !! YOUR DATA HERE !! http://127.0.0.1:32771/score\n", 669 | "\n", 670 | "Get debug logs by calling:\n", 671 | " az ml service logs realtime -i imagetutlocalsrvc\n", 672 | "\n", 673 | "\n" 674 | ] 675 | } 676 | ], 677 | "source": [ 678 | "!az ml service usage realtime -i $LOCAL_SERVICE_NAME" 679 | ] 680 | }, 681 | { 682 | "cell_type": "markdown", 683 | "metadata": {}, 684 | "source": [ 685 | "Now, you will send a request to your real-time web service. Provide the scoring URL from the output above in the following cell." 686 | ] 687 | }, 688 | { 689 | "cell_type": "code", 690 | "execution_count": 28, 691 | "metadata": {}, 692 | "outputs": [], 693 | "source": [ 694 | "url = 'Your Scoring URL Here!!'" 695 | ] 696 | }, 697 | { 698 | "cell_type": "markdown", 699 | "metadata": {}, 700 | "source": [ 701 | "You will now call the service with the sample image you extracted from the operationalization folder. Encode the image into string representation and then serialize it into json format." 702 | ] 703 | }, 704 | { 705 | "cell_type": "code", 706 | "execution_count": 29, 707 | "metadata": {}, 708 | "outputs": [], 709 | "source": [ 710 | "img_path = 'fail.0.jpg'\n", 711 | "with open(img_path, 'rb') as file:\n", 712 | " encoded = base64.b64encode(file.read())\n", 713 | "body = json.dumps(\"{}\".format(encoded))" 714 | ] 715 | }, 716 | { 717 | "cell_type": "markdown", 718 | "metadata": {}, 719 | "source": [ 720 | "Use the following code to make a prediction." 721 | ] 722 | }, 723 | { 724 | "cell_type": "code", 725 | "execution_count": 30, 726 | "metadata": {}, 727 | "outputs": [ 728 | { 729 | "data": { 730 | "text/plain": [ 731 | "[[0.00011646905475415675]]" 732 | ] 733 | }, 734 | "execution_count": 30, 735 | "metadata": {}, 736 | "output_type": "execute_result" 737 | } 738 | ], 739 | "source": [ 740 | "headers = {'Content-Type':'application/json'}\n", 741 | "response = requests.post(url, headers=headers, data=body)\n", 742 | "prediction = json.loads(response.content.decode('ascii'))\n", 743 | "prediction" 744 | ] 745 | }, 746 | { 747 | "cell_type": "markdown", 748 | "metadata": {}, 749 | "source": [ 750 | "Delete local deployment to avoid any conflicts with the cluster deployment. The service can be deleted with the following command which will delete the local docker image and the container." 751 | ] 752 | }, 753 | { 754 | "cell_type": "code", 755 | "execution_count": 31, 756 | "metadata": {}, 757 | "outputs": [ 758 | { 759 | "name": "stdout", 760 | "output_type": "stream", 761 | "text": [ 762 | "Service deleted.\n" 763 | ] 764 | } 765 | ], 766 | "source": [ 767 | "!az ml service delete realtime -i $LOCAL_SERVICE_NAME" 768 | ] 769 | }, 770 | { 771 | "cell_type": "markdown", 772 | "metadata": {}, 773 | "source": [ 774 | "Delete the environment." 775 | ] 776 | }, 777 | { 778 | "cell_type": "code", 779 | "execution_count": 23, 780 | "metadata": {}, 781 | "outputs": [ 782 | { 783 | "name": "stdout", 784 | "output_type": "stream", 785 | "text": [ 786 | "Resource deletion successfully submitted.\n", 787 | "Resources may take 1-2 minutes to be completely deprovisioned.\n" 788 | ] 789 | } 790 | ], 791 | "source": [ 792 | "!az ml env delete -n $LOCAL_ENV_NAME -g $LOCAL_RESOURCE_GROUP" 793 | ] 794 | }, 795 | { 796 | "cell_type": "markdown", 797 | "metadata": {}, 798 | "source": [ 799 | "You can also delete the resource group you created for the local deployment." 800 | ] 801 | }, 802 | { 803 | "cell_type": "code", 804 | "execution_count": 24, 805 | "metadata": {}, 806 | "outputs": [], 807 | "source": [ 808 | "!az group delete --name $LOCAL_RESOURCE_GROUP --yes" 809 | ] 810 | }, 811 | { 812 | "cell_type": "markdown", 813 | "metadata": {}, 814 | "source": [ 815 | "The following cell will display a WARNING and ERROR which is expected behaviour since we deleted the resource group which leads to desired outcome of unsetting the current environment to prepare it for cluster deployment in the next section." 816 | ] 817 | }, 818 | { 819 | "cell_type": "code", 820 | "execution_count": 25, 821 | "metadata": {}, 822 | "outputs": [ 823 | { 824 | "name": "stderr", 825 | "output_type": "stream", 826 | "text": [ 827 | "WARNING: Unable to find env with group imagetutlocalenvrg and name imagetutlocalenv. It may have been moved or deleted, or you could have the wrong active subscription set. Unsetting current env.\n", 828 | "WARNING: To see available environments in the subscription, run:\n", 829 | " az ml env list\n", 830 | "To set an active environment, run:\n", 831 | " az ml env set -n -g \n", 832 | "To see available subscriptions, run:\n", 833 | " az account show -o table\n", 834 | "To set active accout, run:\n", 835 | " az account set -s \n", 836 | "\n", 837 | "ERROR: ResourceGroupNotFound: Resource group 'imagetutlocalenvrg' could not be found.\n" 838 | ] 839 | } 840 | ], 841 | "source": [ 842 | "!az ml env show" 843 | ] 844 | }, 845 | { 846 | "cell_type": "markdown", 847 | "metadata": {}, 848 | "source": [ 849 | "## Cluster environment setup " 850 | ] 851 | }, 852 | { 853 | "cell_type": "markdown", 854 | "metadata": {}, 855 | "source": [ 856 | "Use Cluster deployment for high-scale production scenarios. It sets up an ACS cluster with Kubernetes as the orchestrator. The ACS cluster can be scaled out to handle larger throughput for your web service calls. First, we provide a name for the cluster environment." 857 | ] 858 | }, 859 | { 860 | "cell_type": "code", 861 | "execution_count": 7, 862 | "metadata": {}, 863 | "outputs": [], 864 | "source": [ 865 | "CLUSTER_SERVICE_NAME = DEPLOY_NAME + 'clus' + 'srvc'\n", 866 | "CLUSTER_ENV_NAME = DEPLOY_NAME + 'clus' + 'env'\n", 867 | "CLUSTER_RESOURCE_GROUP = CLUSTER_ENV_NAME + 'rg'" 868 | ] 869 | }, 870 | { 871 | "cell_type": "markdown", 872 | "metadata": {}, 873 | "source": [ 874 | "Setup cluster environment using the following command which creates the following resources in your subscription:\n", 875 | "- A resource group with \"rg\" added at the end of the environment name\n", 876 | "- A storage account\n", 877 | "- An Azure Container Registry (ACR)\n", 878 | "- A Kubernetes deployment on an Azure Container Service (ACS) cluster\n", 879 | "- An Application insights account" 880 | ] 881 | }, 882 | { 883 | "cell_type": "markdown", 884 | "metadata": {}, 885 | "source": [ 886 | "To deploy your web service to a production environment, first set up the environment using the following command:" 887 | ] 888 | }, 889 | { 890 | "cell_type": "code", 891 | "execution_count": 8, 892 | "metadata": {}, 893 | "outputs": [ 894 | { 895 | "name": "stdout", 896 | "output_type": "stream", 897 | "text": [ 898 | "Creating resource group imagetutclusenvrg\n", 899 | "waiting for AAD role to propagate.done\n", 900 | "Provisioning compute resources...\n", 901 | "Resource creation submitted successfully.\n", 902 | "Resources may take 10-20 minutes to be completely provisioned.\n", 903 | "To see if your environment is ready to use, run:\n", 904 | " az ml env show -g imagetutclusenvrg -n imagetutclusenv\n", 905 | "Once your environment has successfully provisioned, you can set it as your target context using:\n", 906 | " az ml env set -g imagetutclusenvrg -n imagetutclusenv\n" 907 | ] 908 | } 909 | ], 910 | "source": [ 911 | "!az ml env setup --cluster -l $LOCATION -n $CLUSTER_ENV_NAME --yes" 912 | ] 913 | }, 914 | { 915 | "cell_type": "markdown", 916 | "metadata": {}, 917 | "source": [ 918 | "You can check the information on the environment with the following. You will need to wait until \"Provisioning State\" is set to \"Succeeded\" to continue with the next steps." 919 | ] 920 | }, 921 | { 922 | "cell_type": "code", 923 | "execution_count": 15, 924 | "metadata": {}, 925 | "outputs": [ 926 | { 927 | "name": "stdout", 928 | "output_type": "stream", 929 | "text": [ 930 | "{\n", 931 | " \"Cluster Name\": \"imagetutclusenv\",\n", 932 | " \"Cluster Size\": 2,\n", 933 | " \"Created On\": \"2017-11-06T21:08:38.636Z\",\n", 934 | " \"Location\": \"eastus2\",\n", 935 | " \"Provisioning State\": \"Succeeded\",\n", 936 | " \"Resource Group\": \"imagetutclusenvrg\",\n", 937 | " \"Subscription\": \"edf507a2-6235-46c5-b560-fd463ba2e771\"\n", 938 | "}\n" 939 | ] 940 | } 941 | ], 942 | "source": [ 943 | "!az ml env show -g $CLUSTER_RESOURCE_GROUP -n $CLUSTER_ENV_NAME" 944 | ] 945 | }, 946 | { 947 | "cell_type": "markdown", 948 | "metadata": {}, 949 | "source": [ 950 | "Next step is to set the cluster environment for deployment." 951 | ] 952 | }, 953 | { 954 | "cell_type": "code", 955 | "execution_count": 16, 956 | "metadata": {}, 957 | "outputs": [ 958 | { 959 | "name": "stdout", 960 | "output_type": "stream", 961 | "text": [ 962 | "Compute set to imagetutclusenv.\n" 963 | ] 964 | } 965 | ], 966 | "source": [ 967 | "!az ml env set -g $CLUSTER_RESOURCE_GROUP -n $CLUSTER_ENV_NAME --disable-dashboard" 968 | ] 969 | }, 970 | { 971 | "cell_type": "markdown", 972 | "metadata": {}, 973 | "source": [ 974 | "Check that environment is set to the cluster." 975 | ] 976 | }, 977 | { 978 | "cell_type": "code", 979 | "execution_count": 18, 980 | "metadata": {}, 981 | "outputs": [ 982 | { 983 | "name": "stdout", 984 | "output_type": "stream", 985 | "text": [ 986 | "{\n", 987 | " \"Cluster Name\": \"imagetutclusenv\",\n", 988 | " \"Cluster Size\": 2,\n", 989 | " \"Created On\": \"2017-11-06T21:08:38.636Z\",\n", 990 | " \"Current Mode\": \"cluster\",\n", 991 | " \"Location\": \"eastus2\",\n", 992 | " \"Provisioning State\": \"Succeeded\",\n", 993 | " \"Resource Group\": \"imagetutclusenvrg\",\n", 994 | " \"Subscription\": \"edf507a2-6235-46c5-b560-fd463ba2e771\"\n", 995 | "}\n" 996 | ] 997 | } 998 | ], 999 | "source": [ 1000 | "!az ml env show" 1001 | ] 1002 | }, 1003 | { 1004 | "cell_type": "markdown", 1005 | "metadata": {}, 1006 | "source": [ 1007 | "Now, set the model management account for the environment." 1008 | ] 1009 | }, 1010 | { 1011 | "cell_type": "code", 1012 | "execution_count": 33, 1013 | "metadata": {}, 1014 | "outputs": [ 1015 | { 1016 | "name": "stdout", 1017 | "output_type": "stream", 1018 | "text": [ 1019 | "{\n", 1020 | " \"created_on\": \"2017-11-03T17:51:11.9226449999999999Z\",\n", 1021 | " \"description\": \"\",\n", 1022 | " \"id\": \"/subscriptions/edf507a2-6235-46c5-b560-fd463ba2e771/resourceGroups/imagetutmmaccrg/providers/Microsoft.MachineLearningModelManagement/accounts/imagetutmmacc\",\n", 1023 | " \"location\": \"eastus2\",\n", 1024 | " \"model_management_swagger_location\": \"https://eastus2.modelmanagement.azureml.net/api/subscriptions/edf507a2-6235-46c5-b560-fd463ba2e771/resourceGroups/imagetutmmaccrg/accounts/imagetutmmacc/swagger.json?api-version=2017-09-01-preview\",\n", 1025 | " \"modified_on\": \"2017-11-03T17:51:11.9226449999999999Z\",\n", 1026 | " \"name\": \"imagetutmmacc\",\n", 1027 | " \"resource_group\": \"imagetutmmaccrg\",\n", 1028 | " \"sku\": {\n", 1029 | " \"capacity\": 1,\n", 1030 | " \"name\": \"S1\"\n", 1031 | " },\n", 1032 | " \"subscription\": \"edf507a2-6235-46c5-b560-fd463ba2e771\",\n", 1033 | " \"tags\": {},\n", 1034 | " \"type\": \"Microsoft.MachineLearningModelManagement/accounts\"\n", 1035 | "}\n" 1036 | ] 1037 | } 1038 | ], 1039 | "source": [ 1040 | "!az ml account modelmanagement set -n $MM_ACCOUNT -g $MM_ACCOUNT_RESOURCE_GROUP" 1041 | ] 1042 | }, 1043 | { 1044 | "cell_type": "markdown", 1045 | "metadata": {}, 1046 | "source": [ 1047 | "### Deploy model to cluster" 1048 | ] 1049 | }, 1050 | { 1051 | "cell_type": "markdown", 1052 | "metadata": {}, 1053 | "source": [ 1054 | "Print your cluster service name for creating the service in the next steps." 1055 | ] 1056 | }, 1057 | { 1058 | "cell_type": "code", 1059 | "execution_count": 19, 1060 | "metadata": {}, 1061 | "outputs": [ 1062 | { 1063 | "name": "stdout", 1064 | "output_type": "stream", 1065 | "text": [ 1066 | "Your Cluster Service Name: imagetutclussrvc\n" 1067 | ] 1068 | } 1069 | ], 1070 | "source": [ 1071 | "print('Your Cluster Service Name: ' + CLUSTER_SERVICE_NAME)" 1072 | ] 1073 | }, 1074 | { 1075 | "cell_type": "markdown", 1076 | "metadata": {}, 1077 | "source": [ 1078 | "You will need to change your directory to the current directory on Workbench CLI. Use the output in the next cell." 1079 | ] 1080 | }, 1081 | { 1082 | "cell_type": "code", 1083 | "execution_count": 20, 1084 | "metadata": {}, 1085 | "outputs": [ 1086 | { 1087 | "name": "stdout", 1088 | "output_type": "stream", 1089 | "text": [ 1090 | "Your Working Directory: C:\\Users\\fboylu\\AppData\\Local\\Temp\\azureml_runs\\imagetutorial3_1510002303767\n" 1091 | ] 1092 | } 1093 | ], 1094 | "source": [ 1095 | "print('Your Working Directory: '+ os.getcwd())" 1096 | ] 1097 | }, 1098 | { 1099 | "cell_type": "markdown", 1100 | "metadata": {}, 1101 | "source": [ 1102 | "Now, go to the Azure Machine Learning Workbench CLI window and change your directory using:\n", 1103 | "```\n", 1104 | "cd [Your Working Directory Here!!]\n", 1105 | "```" 1106 | ] 1107 | }, 1108 | { 1109 | "cell_type": "markdown", 1110 | "metadata": {}, 1111 | "source": [ 1112 | "Next step is to deploy your model to the cluster environment. However, since the command below is not available through Jupyter notebooks for cluster environment set up, we will use Azure Machine Learning Workbench CLI." 1113 | ] 1114 | }, 1115 | { 1116 | "cell_type": "code", 1117 | "execution_count": 39, 1118 | "metadata": {}, 1119 | "outputs": [], 1120 | "source": [ 1121 | "# This command is not available through Jupyter notebooks for cluster set up.\n", 1122 | "#!az ml service create realtime --model-file lightgbm_classifier.model -f imgscore.py -n $CLUSTER_SERVICE_NAME -r python -c aml_config/conda_dependencies_srvc.yml" 1123 | ] 1124 | }, 1125 | { 1126 | "cell_type": "markdown", 1127 | "metadata": {}, 1128 | "source": [ 1129 | "Go to the Azure Machine Learning Workbench CLI window and issue the following command by providing your cluster service name printed earlier where indicated in the following command.\n", 1130 | "```bash\n", 1131 | "az ml service create realtime --model-file lightgbm_classifier.model -f imgscore.py -n [Your Cluster Service Name Here!!] -r python -c aml_config/conda_dependencies_srvc.yml\n", 1132 | "```\n", 1133 | "You can watch the steps of the deployment from the CLI output which can take up to 20 minutes. Once completed, return back to the notebook." 1134 | ] 1135 | }, 1136 | { 1137 | "cell_type": "markdown", 1138 | "metadata": {}, 1139 | "source": [ 1140 | "List the service you deployed." 1141 | ] 1142 | }, 1143 | { 1144 | "cell_type": "code", 1145 | "execution_count": 41, 1146 | "metadata": {}, 1147 | "outputs": [ 1148 | { 1149 | "name": "stdout", 1150 | "output_type": "stream", 1151 | "text": [ 1152 | "Name Id UpdatedAt State\n", 1153 | "---------------- ------------------------------------------------- ------------------- ---------\n", 1154 | "imagetutclussrvc imagetutclussrvc.imagetutclusenv-47424707.eastus2 2017-11-06T16:51:30 Succeeded\n" 1155 | ] 1156 | } 1157 | ], 1158 | "source": [ 1159 | "!az ml service list realtime -o table" 1160 | ] 1161 | }, 1162 | { 1163 | "cell_type": "markdown", 1164 | "metadata": {}, 1165 | "source": [ 1166 | "Print your cluster service Id in order to use it in the following steps." 1167 | ] 1168 | }, 1169 | { 1170 | "cell_type": "code", 1171 | "execution_count": 42, 1172 | "metadata": {}, 1173 | "outputs": [ 1174 | { 1175 | "name": "stdout", 1176 | "output_type": "stream", 1177 | "text": [ 1178 | " \"Id\": \"imagetutclussrvc.imagetutclusenv-47424707.eastus2\",\n" 1179 | ] 1180 | } 1181 | ], 1182 | "source": [ 1183 | "!az ml service list realtime | grep Id" 1184 | ] 1185 | }, 1186 | { 1187 | "cell_type": "markdown", 1188 | "metadata": {}, 1189 | "source": [ 1190 | "Provide your cluster service Id printed earlier in the cell below where indicated." 1191 | ] 1192 | }, 1193 | { 1194 | "cell_type": "code", 1195 | "execution_count": 43, 1196 | "metadata": {}, 1197 | "outputs": [], 1198 | "source": [ 1199 | "CLUSTER_SERVICE_ID = 'Your cluster service Id here!!'" 1200 | ] 1201 | }, 1202 | { 1203 | "cell_type": "code", 1204 | "execution_count": 44, 1205 | "metadata": {}, 1206 | "outputs": [ 1207 | { 1208 | "name": "stdout", 1209 | "output_type": "stream", 1210 | "text": [ 1211 | "Scoring URL:\n", 1212 | " http://40.79.80.172:80/api/v1/service/imagetutclussrvc/score\n", 1213 | "\n", 1214 | "Headers:\n", 1215 | " Content-Type: application/json\n", 1216 | " Authorization: Bearer \n", 1217 | " ( can be found by running 'az ml service keys realtime -i imagetutclussrvc.imagetutclusenv-47424707.eastus2')\n", 1218 | "\n", 1219 | "Swagger URL:\n", 1220 | " http://40.79.80.172:80/api/v1/service/imagetutclussrvc/swagger.json\n", 1221 | "\n", 1222 | "Sample CLI command:\n", 1223 | " Usage for cmd: az ml service run realtime -i imagetutclussrvc.imagetutclusenv-47424707.eastus2 -d !! YOUR DATA HERE !!\n", 1224 | " Usage for powershell: az ml service run realtime -i imagetutclussrvc.imagetutclusenv-47424707.eastus2 --% -d !! YOUR DATA HERE !!\n", 1225 | "\n", 1226 | "Sample CURL call:\n", 1227 | " curl -X POST -H \"Content-Type:application/json\" -H \"Authorization:Bearer \" --data !! YOUR DATA HERE !! http://40.79.80.172:80/api/v1/service/imagetutclussrvc/score\n", 1228 | "\n", 1229 | "Get debug logs by calling:\n", 1230 | " az ml service logs realtime -i imagetutclussrvc.imagetutclusenv-47424707.eastus2\n", 1231 | "\n", 1232 | "Get STDOUT/STDERR or Request/Response logs in App Insights:\n", 1233 | " https://analytics.applicationinsights.io/subscriptions/edf507a2-6235-46c5-b560-fd463ba2e771/resourcegroups/imagetutclusenvrg-azureml-f1412/components/mlcrpai55c93038d316#/discover/home?apptype=Other%20(preview)\n", 1234 | "\n" 1235 | ] 1236 | } 1237 | ], 1238 | "source": [ 1239 | "!az ml service usage realtime -i $CLUSTER_SERVICE_ID" 1240 | ] 1241 | }, 1242 | { 1243 | "cell_type": "markdown", 1244 | "metadata": {}, 1245 | "source": [ 1246 | "Now, you can send a request to your real-time web service. Provide the scoring URL from the output above in the following cell." 1247 | ] 1248 | }, 1249 | { 1250 | "cell_type": "code", 1251 | "execution_count": 45, 1252 | "metadata": {}, 1253 | "outputs": [], 1254 | "source": [ 1255 | "url = 'Your Scoring URL Here!!'" 1256 | ] 1257 | }, 1258 | { 1259 | "cell_type": "markdown", 1260 | "metadata": {}, 1261 | "source": [ 1262 | "For cluster deployments, an api key needs to be provided. " 1263 | ] 1264 | }, 1265 | { 1266 | "cell_type": "code", 1267 | "execution_count": 46, 1268 | "metadata": {}, 1269 | "outputs": [ 1270 | { 1271 | "name": "stdout", 1272 | "output_type": "stream", 1273 | "text": [ 1274 | "PrimaryKey: 62bcbaaeac7e455eabd87177607dadee\n", 1275 | "SecondaryKey: 09fec15f3d124686894345e675d3e95b\n" 1276 | ] 1277 | } 1278 | ], 1279 | "source": [ 1280 | "!az ml service keys realtime -i $CLUSTER_SERVICE_ID" 1281 | ] 1282 | }, 1283 | { 1284 | "cell_type": "markdown", 1285 | "metadata": {}, 1286 | "source": [ 1287 | "Provide the \"PrimaryKey\" from the output above as your Api Key in the following cell where indicated." 1288 | ] 1289 | }, 1290 | { 1291 | "cell_type": "code", 1292 | "execution_count": 47, 1293 | "metadata": {}, 1294 | "outputs": [], 1295 | "source": [ 1296 | "api_key = 'Your Api Key Here!!'" 1297 | ] 1298 | }, 1299 | { 1300 | "cell_type": "markdown", 1301 | "metadata": {}, 1302 | "source": [ 1303 | "You will now call the service with the sample image you extracted from the operationalization folder. Encode the image into string representation and then serialize it into json format." 1304 | ] 1305 | }, 1306 | { 1307 | "cell_type": "code", 1308 | "execution_count": 49, 1309 | "metadata": {}, 1310 | "outputs": [], 1311 | "source": [ 1312 | "import base64, requests, json\n", 1313 | "img_path = 'fail.0.jpg'\n", 1314 | "with open(img_path, 'rb') as file:\n", 1315 | " encoded = base64.b64encode(file.read())\n", 1316 | "body = json.dumps(\"{}\".format(encoded))" 1317 | ] 1318 | }, 1319 | { 1320 | "cell_type": "markdown", 1321 | "metadata": {}, 1322 | "source": [ 1323 | "Use the following code to make a prediction." 1324 | ] 1325 | }, 1326 | { 1327 | "cell_type": "code", 1328 | "execution_count": 50, 1329 | "metadata": {}, 1330 | "outputs": [ 1331 | { 1332 | "data": { 1333 | "text/plain": [ 1334 | "[[0.00011646905475415675]]" 1335 | ] 1336 | }, 1337 | "execution_count": 50, 1338 | "metadata": {}, 1339 | "output_type": "execute_result" 1340 | } 1341 | ], 1342 | "source": [ 1343 | "headers = {'Content-Type':'application/json', 'Authorization':('Bearer '+ api_key)}\n", 1344 | "response = requests.post(url, headers=headers, data=body)\n", 1345 | "prediction = json.loads(response.content.decode('ascii'))\n", 1346 | "prediction" 1347 | ] 1348 | }, 1349 | { 1350 | "cell_type": "markdown", 1351 | "metadata": {}, 1352 | "source": [ 1353 | "The service can be deleted with the following command." 1354 | ] 1355 | }, 1356 | { 1357 | "cell_type": "code", 1358 | "execution_count": 54, 1359 | "metadata": {}, 1360 | "outputs": [], 1361 | "source": [ 1362 | "!az ml service delete realtime -i $CLUSTER_SERVICE_ID" 1363 | ] 1364 | }, 1365 | { 1366 | "cell_type": "markdown", 1367 | "metadata": {}, 1368 | "source": [ 1369 | "Delete cluster environment." 1370 | ] 1371 | }, 1372 | { 1373 | "cell_type": "code", 1374 | "execution_count": 51, 1375 | "metadata": {}, 1376 | "outputs": [ 1377 | { 1378 | "name": "stdout", 1379 | "output_type": "stream", 1380 | "text": [ 1381 | "Resource deletion successfully submitted.\n", 1382 | "Resources may take 1-2 minutes to be completely deprovisioned.\n" 1383 | ] 1384 | } 1385 | ], 1386 | "source": [ 1387 | "!az ml env delete -n $CLUSTER_ENV_NAME -g $CLUSTER_RESOURCE_GROUP" 1388 | ] 1389 | }, 1390 | { 1391 | "cell_type": "markdown", 1392 | "metadata": {}, 1393 | "source": [ 1394 | "Delete the resource group to clean up." 1395 | ] 1396 | }, 1397 | { 1398 | "cell_type": "code", 1399 | "execution_count": 55, 1400 | "metadata": {}, 1401 | "outputs": [], 1402 | "source": [ 1403 | "!az group delete --name $CLUSTER_RESOURCE_GROUP --yes" 1404 | ] 1405 | }, 1406 | { 1407 | "cell_type": "markdown", 1408 | "metadata": {}, 1409 | "source": [ 1410 | "Check that current environment is unset." 1411 | ] 1412 | }, 1413 | { 1414 | "cell_type": "code", 1415 | "execution_count": 56, 1416 | "metadata": {}, 1417 | "outputs": [ 1418 | { 1419 | "name": "stderr", 1420 | "output_type": "stream", 1421 | "text": [ 1422 | "WARNING: Unable to find env with group imagetutclusenvrg and name imagetutclusenv. It may have been moved or deleted, or you could have the wrong active subscription set. Unsetting current env.\n", 1423 | "WARNING: To see available environments in the subscription, run:\n", 1424 | " az ml env list\n", 1425 | "To set an active environment, run:\n", 1426 | " az ml env set -n -g \n", 1427 | "To see available subscriptions, run:\n", 1428 | " az account show -o table\n", 1429 | "To set active accout, run:\n", 1430 | " az account set -s \n", 1431 | "\n", 1432 | "ERROR: ResourceGroupNotFound: Resource group 'imagetutclusenvrg' could not be found.\n" 1433 | ] 1434 | } 1435 | ], 1436 | "source": [ 1437 | "!az ml env show" 1438 | ] 1439 | } 1440 | ], 1441 | "metadata": { 1442 | "kernelspec": { 1443 | "display_name": "imgtutvso local", 1444 | "language": "python", 1445 | "name": "imgtutvso_local" 1446 | }, 1447 | "language_info": { 1448 | "codemirror_mode": { 1449 | "name": "ipython", 1450 | "version": 3 1451 | }, 1452 | "file_extension": ".py", 1453 | "mimetype": "text/x-python", 1454 | "name": "python", 1455 | "nbconvert_exporter": "python", 1456 | "pygments_lexer": "ipython3", 1457 | "version": "3.5.2" 1458 | } 1459 | }, 1460 | "nbformat": 4, 1461 | "nbformat_minor": 2 1462 | } 1463 | --------------------------------------------------------------------------------