This project was built on a Windows PC but should work cross platform on Mac and Linux.
The solution built in this post contains three projects:
- SchemaLibrary: C# .NET Standard 2.0 class library that contains the schema definition classes of the data used to train the model as well as the prediction output generated by the model.
- TrainingConsole: C# .NET Core 3.1 console application used to train the machine learning model.
- BlazorWebApp: Blazor WebAssembly web application to make predictions using machine learning model trained by TrainingConsole application.
Use the .NET CLI to run the following command in the command prompt:
dotnet new -i Microsoft.AspNetCore.Blazor.Templates::3.2.0-preview1.20073.1
Create a new directory for the solution called MLNETBlazorWASMSample.
Navigate to the newly created solution directory and create a solution:
The data schema for the model input and output are shared during training as well as when making predictions. To share resources, create a class library that’s shared by the
BlazorWebApp projects. In the solution directory, enter the following command:
dotnet new classlib -o SchemaLibrary
Install the Microsoft.ML NuGet package (This solution is built with version 1.4.0). The Microsoft.ML package is used throughout the entire solution.
dotnet add SchemaLibrary package Microsoft.ML
Add the library project to the solution.
dotnet sln add SchemaLibrary
The console application contains the series of data transformations and algorithms used to train the model. In the solution directory, create a new console application.
dotnet new console -o TrainingConsole
Add the console application to the solution.
dotnet sln add TrainingConsole
Reference the SchemaLibrary project.
dotnet add TrainingConsole reference SchemaLibrary
The web application contains a few input elements so users can provide new data that the model then uses to make predictions. In the solution directory, create a new Blazor WebAssembly application.
dotnet new blazorwasm -o BlazorWebApp
Add the web application project to the solution.
dotnet sln add BlazorWebApp
Reference the SchemaLibrary project.
dotnet add BlazorWebApp reference SchemaLibrary
The data used to train the model comes from the iris dataset. It contains four numerical columns which are sepal and petal measurements and one numerical column for the species of iris flower. This is a sample of the data.
|Sepal length (cm)||Sepal width (cm)||Petal length (cm)||Petal width (cm)||Class (iris species)|
In the SchemaLibrary project, create a class called
ModelInput to model the data used for training and as model input.
ModelInput class should look like the following:
Notice that the
Class column is now a property called
Label. This is for two reasons:
- Avoid using the
- In ML.NET, the default column name of the column to predict expected by algorithms is
Also notice the
LoadColumn attributes at the top of each property. This is used to tell the loader the index of the column where the data for the respective property is.
Similar to the input schema, there’s a schema for the output of the model. The type of model used in this solution is a multiclass classification model since there are more than two categories to choose from for iris flower species. Multiclass classification models output a column called
PredictedLabel which contains the name of the predicted category. In the SchemaLibrary project, create a class called
ModelOutput to model the predictions made by the model.
ModelOutput class should look like the following:
Now it’s time to create the application that trains the model.
Download the data and save it inside the TrainingConsole project directory.
curl https://archive.ics.uci.edu/ml/machine-learning-databases/iris/iris.data -o iris.data
In the TrainingConsole project, open the Program.cs file and add the following using statements at the top:
Then, delete the contents inside the
Main method and replace it with the following.
// 1. Initialize MLContext
The training application loads the data from the
iris.data file and applies a series of transformations. First, all of the individual numerical columns are combined into a single vector and stored in a new column called
Features column is then normalized and the
MapValueToKey transform is used to convert the text in the
Label column to a number. Then, the transformed data is used to train a model using the
NaiveBayes algorithm. Note that at the time of this writing, for multiclass classification problems, only Naive Bayes has been confirmed to work with Blazor WebAssembly. Finally, the
PredictedLabel is stored as a number so it has to be converted back into text.
Fit method, the data is applied to the pipeline. Because the dataset is small, a technique called cross-validation is used to build a more robust model. Once the model is trained, the model with the top performance is then serialized and saved to a file called model.zip for later use in the web application.
The final Program.cs file should look like the content below:
In the TrainConsole project directory, use the following command to run the application and train the model:
Once you have the model saved, use the Azure Portal to create an Azure Storage account.
Then, navigate to your newly created storage account resource and create a blob container called
Once your container is created, navigate to it and upload the model.zip file.
To make predictions, create a web page to take in user input. Then, provide the user input to the model and display the prediction to the user.
In the BlazorWebApp project directory, open the _Imports.razor file. This contains the using statements for the pages and components in your application. Add the following using statements:
In the BlazorWebApp project, create a new razor page called Prediction.razor inside the Pages directory.
Add the following content to it:
The Predict.razor page contains a text input element for each of the columns the model was original trained on. When the page is initialized, the model is loaded from Azure Storage and a
PredictionEngine is created. Make sure to replace
<YOUR-MODEL-ENDPOINT> with the URL of the blob that contains your
PredictionEngine is a convenience API to make a single prediction. Traditionally when models are served as a web service, it is recommended to use the
PredictionEnginePool service because it’s thread-safe and more performant in multi-threaded application. However, in this case since the model is downloaded onto the individual user’s browser, it’s okay to use
PredictionEngine. After a user enters input values and clicks the “Make Prediction” button, the
GetPrediction method executes by taking the user input and making a prediction using the
PredictionEngine. The prediction is then displayed in the browser.
In the BlazorWebApp project, open the NavMenu.razor file in the Shared directory.
Add the following list item to the
<li class="nav-item px-3">
The final NavMenu.razor page should look like the following:
<div class="top-row pl-4 navbar navbar-dark">
The web application will he hosted as a static site on Azure Storage.
In the Azure Portal, navigate to the storage account resource where you are hosting your model.
Enable a static website for the storage account and set the index document name and error document path to index.html.
At this point, a new container called $web is created in your storage account. This is where all your site’s static files will reside. Additionally, a primary endpoint is created. This is the URL you will use to access your application
The storage account has some default CORS settings. In order to download and use your model from your application, you’ll have to configure them.
For “Allowed origins”, enter your primary endpoint.
To publish your application, run the following command:
dotnet publish -c Release
This generates all the files you’ll need to host your web application as a static site in the bin/Release/netstandard2.1/publish/BlazorWebApp/dist directory of your BlazorWebApp project.
To deploy your application, use Azure Storage Explorer to copy all of the files in the dist directory into the $web container of your Azure Storage Account.
In your browser, navigate to your static website’s primary endpoint and select the Prediction page. Input data and click “Make prediction”. The page should look as follows.
You may note that performance of Naive Bayes on this dataset is not the best so some of the predictions may not be as accurate. I am okay with this at the moment because this is a proof-of-concept to show how these technologies might work together. Perhaps using a better dataset may yield better results.
In this post, I went over how to deploy an ML.NET multiclass classification model alongside a Blazor WebAssembly static website to Azure Storage. While more limited than other methods of deployment due to the early stages of WebAssembly and Blazor WebAssembly, this shows the possibilities for the technologies. Deploying in this manner reduces the amount of resources required to deploy these models and shifts processing from a server or web service to the client’s browser making deployment and distribution of machine learning models more efficient, scalable and cost-efficient.