Leading up to and during MS Build 2018 Microsoft has released a wide range of products that reduce the complexity that comes with building and deploying software. The focus this year was on Machine Learning and Artificial Intelligence. Some of the products I found particularly interesting are Azure Container Instances which makes it easier to run containerized applications without provisioning or managing servers and ML.NET which is a .NET cross-platform machine learning framework. In this writeup, I will make use of both these products by creating a machine learning classification model with
ML.NET, exposing it via an ASP.NET Core Web API, packaging it into a Docker container and deploying it to the cloud via Azure Container Instances. Source code for this project can be found here.
This writeup assumes that you have some familiarity with Docker. The following software/dependencies are also required to build and deploy the sample application. It’s important to note the application was built on a Ubuntu 16.04 PC, but all the software is cross-platform and should work on any environment.
The first thing we want to do is create a folder for our solution.
Then, we want to create a solution inside our newly created folder.
Inside our solution folder, we want to create a new console application which is where we’ll build and test our machine learning model.
First, we want to create the project. From the solution folder enter:
dotnet new console -o model
Now we want to add this new project to our solution.
dotnet sln mlnetacidemo.sln add model/model.csproj
Since we’ll be using the
ML.NET framework, we need to add it to our
Before we start training the model, we need to download the data we’ll be using to train. We do so by creating a directory called
data and downloading the data file onto there.
If we take a look at the data file, it should look something like this:
Now that we have all our dependencies set up, it’s time to build our model. I leveraged the demo that is used on the ML.NET Getting-Started website.
In the root directory of our
model project, let’s create two classes called
IrisPrediction which will define our features and predicted attribute respectively. Both of them will use
Microsoft.ML.Runtime.Api to add the property attributes.
Here is what our
IrisData class looks like:
public class IrisData
public float SepalLength;
public float SepalWidth;
public float PetalLength;
public float PetalWidth;
public string Label;
Similarly, here is the
The way the
ML.NET computations process is via a sequential pipeline of steps that are performed eventually leading up to the training of the model. Therefore, we can create a class called
Model to perform all of these tasks for us.
In addition to building our pipeline and training our machine learning model, the
Model class also serialized and persisted the model for future use in a file called
Now that we have our data structures and model training pipeline set up, it’s time to test everything to make sure it’s working. We’ll put our logic inside of our
All set to run. We can do so by entering the following command from our solution directory:
dotnet run -p model/model.csproj
Once the application has been run, the following output should display on the console.
Automatically adding a MinMax normalization transform, use 'norm=Warn' or
Additionally, you’ll notice that a file called
model.zip was created in the root directory of our
model project. This persisted model can now be used outside of our application to make predictions, which is what we’ll do next via an API.
Once a machine learning model is built, you want to deploy it so it can start making predictions. One way to do that is via a REST API. At it’s core, all our API needs to do is accept data input from the client and respond back with a prediction. To help us do that, we’ll be using an ASP.NET Core API.
The first thing we want to do is create the project.
dotnet new webapi -o api
Then we want to add this new project to our solution
dotnet sln mlnetacidemo.sln add api/api.csproj
Because we’ll be loading our model and making predictions via our API, we need to add the
ML.NET package to our
In the previous step when we built our machine learning model, it was saved to a file called
model.zip. This is the file we’ll be referencing in our API to help us make predictions. To reference it in our API, simply copy it from the model project directory into our
api project directory.
Our model was built using data structures
IrisPrediction to define the features as well as the predicted attribute. Therefore, when our model makes predictions via our API, it needs to reference these data types as well. As a result, we need to define
IrisPrediction classes inside of our
api project. The contents of the classes will be nearly identical to those in the
model project with the only exception of our namespace changing from
Now that our project is set up, it’s time to add a controller that will handle prediction requests from the client. In the
Controllers directory of our
api project we can create a new class called
PredictController with a single
POST endpoint. The contents of the file should look like the code below:
predict endpoint is set up, it’s time to test it. From the root directory of our
mlnetacidemo solution, enter the following command.
dotnet run -p api/api.csproj
In a client like POSTMAN or Insomnia, send an HHTP POST request to the endpoint
The body our request should look similar to the snippet below:
If successful, the output returned should equal
Iris-virginica just like our console application.
Great! Now that our application is successfully running locally, it’s time to package it up into a Docker container and push it to Docker Hub.
mlnetacidemo solution directory, create a
Dockerfile with the following content:
FROM microsoft/dotnet:2.0-sdk AS build
To build the image, we need to enter the following command into the command prompt. This make take a while because it needs to download the .NET Core SDK and ASP.NET Core runtime Docker images.
docker build -t <DOCKERUSERNAME>/<IMAGENAME>:latest .
We need to test our image locally to make sure it can run on the cloud. To do so, we can use the
docker run command.
docker run -d -p 5000:80 <DOCKERUSERNAME>/<IMAGENAME>:latest
Although the API is exposing port 80, we bind it to the local port 5000 just to keep our prior API request intact. When sending a POST request to
http://localhost:5000/api/predict with the appropriate body, the response should again equal
To stop the container, use
Ctrl + C.
Now that the Docker image is successfully running locally, it’s time to push to Docker Hub. Again, we use the Docker CLI to do this.
Now comes the final step which is to deploy and expose our machine learning model and API to the world. Our deployment will occur via Azure Container Instances because it requires almost no provisioning or management of servers.
Although deployments can be performed inline in the command line, it’s usually best to place all the configurations in a file for documentation and to save time not having to type in the parameters every time. With Azure, we can do that via a JSON file.
It’s a lot to look at but for now we can use this template and save it to the file
azuredeploy.json in the root directory of our
mlnetacidemo solution. The only thing that needs to be changed is the value of the
containerimage property. Replace it with your Docker Hub username and the name of the image you just pushed to Docker Hub.
In order to deploy our application we need to make sure to log into our Azure account. To do so via the Azure CLI, type into the command prompt:
Follow the prompts to log in. Once logged in, it’s time to create a resource group for our container.
az group create --name mlnetacidemogroup --location eastus
After the group has been successfully created it’s time to deploy our application.
az group deployment create --resource-group mlnetacidemogroup --template-file azuredeploy.json
Give it a few minutes for your deployment to initialize. If the deployment was successful, you should see some output on the command line. Look for the
ContainerIPv4Address property. This is the IP Address where your container is accessible. In POSTMAN or Insomnia, replace the URL to which you previously made a POST request to with
ContainerIPv4Address is the value that was returned to the command line after the deployment. If successful, the response should be just like previous requests
Once you’re finished, you can clean up resources with the following command:
az group delete --name mlnetacidemogroup
In this writeup, we built a classification machine learning model using
ML.NET that predicts the class of an iris plant given four measurement features, exposed it via an ASP.NET Core REST API, packaged it into a container and deployed it to the cloud using Azure Container Instances. As the model changes and becomes more complex, the process is standardized enough that extending this example would require minimal changes to our existing application. Happy Coding!