Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make it easier to use ML.NET in an ASP.NET app/service #3239

Closed
eerhardt opened this issue Apr 8, 2019 · 4 comments · Fixed by #3827
Closed

Make it easier to use ML.NET in an ASP.NET app/service #3239

eerhardt opened this issue Apr 8, 2019 · 4 comments · Fixed by #3827

Comments

@eerhardt
Copy link
Member

eerhardt commented Apr 8, 2019

Problem

With 1.0.0-preview bits, it is currently harder than it needs to be to use ML.NET inside an ASP.NET service or application. The first problem users hit is whether they can cache a PredictionEngine statically and reuse it for multiple requests. As described in #1789, you cannot use a PredictionEngine on multiple threads at the same time. Doing so will cause problems in your application.

Thus the recommendation is to use a pooling technique, but writing one from scratch is rather hard and potentially error prone.

Also, by default the MLContext's Log operation is not aware of any logging infrastructure currently used by ASP.NET apps/services. Thus the log goes nowhere, and is lost.

Proposal

We propose to add a new library (Microsoft.ML.Extensions?, Microsoft.Extensions.ML?) that is aware of both Microsoft.ML and Microsoft.Extensions.DependencyInjection/Microsoft.Extensions.Logging and glues the two together. This should make it much easier to consume ML.NET models inside ASP.NET apps/services, as well as any other app model that integrates with the Microsoft.Extensions.* libraries.

Usage

Adding a new ML.NET model into an ASP.NET application could be as simple as two steps:

  1. Add a PredictionEnginePool in your Startup.cs:
    // This method gets called by the runtime. Use this method to add services to the container.
    public void ConfigureServices(IServiceCollection services)
    {
        services
            .AddPredictionEnginePool<SentimentIssue, SentimentPrediction>("SentimentModel.zip");

        // other service configuration
    }
  1. In any controller that needs to make a prediction, inject the PredictionEngine pool in the constructor, and use it where necessary:
[ApiController]
public class PredictionController : ControllerBase
{
    private PredictionEnginePool<SentimentIssue, SentimentPrediction> _predictionEnginePool;

    public PredictionController(PredictionEnginePool<SentimentIssue, SentimentPrediction> predictionEnginePool)
    {
        _predictionEnginePool = predictionEnginePool;
    }

    [HttpGet()]
    public ActionResult<SentimentPrediction> GetSentiment([FromQuery]SentimentIssue input)
    {
        return _predictionEnginePool.Predict(input);
    }
}

Other potential scenarios

  1. Being able to add a model .zip file from sources other than a file path
    1. Azure Blob Storage
    2. A SQL Database
    3. Any URL
  2. Being able to automatically reload an updated model, if the file/URL changes (using FileWatcher or ETag or some other mechanism).
  3. Being able to have different "named" models for scenarios like A/B testing where you want 90% of users to get Model A and 10% to get Model B.

@glennc @CESARDELATORRE @glebuk @TomFinley

@CESARDELATORRE
Copy link
Contributor

CESARDELATORRE commented Apr 8, 2019

I certainly support this feature. 👍
Related to this Blog Post explaining the scenario, but this feature (.NET Integration Package working on DI) will simplify it a lot for the users:

https://devblogs.microsoft.com/cesardelatorre/how-to-optimize-and-run-ml-net-models-on-scalable-asp-net-core-webapis-or-web-apps/

@markusweimer
Copy link
Member

I like this. In terms of where it should be, I vote for Microsoft.ML.SOMETHING as the namespace. For the foreseeable future, ML.NET will move faster than ASP.NET, and keeping / building the integration on the ML.NET side is probably more stable.

@luisquintanilla
Copy link
Contributor

As far as distributing the work among the pool, would it be random based on resources available or would there be a strategy? Would that functionality instead be better served by something like a load balancer rather than logic built into the pool?

@CESARDELATORRE
Copy link
Contributor

CESARDELATORRE commented Apr 12, 2019

@markus Agree, we should host this in the place it will be able to evolve more agile.

@luisquintanilla- Load balancing makes sense when you are distributing load across multiple machines/servers, or distributed nodes in an orchestrator like when using containers. But in this case, it is a single pool of objects in a single hardware machine, same memory, same resources. I don’t think we need any load balancing here, it would be making it more complex without really needing it.

eerhardt added a commit to eerhardt/machinelearning that referenced this issue Jun 5, 2019
This package makes it easier to use ML.NET with app models that support Microsoft.Extensions - i.e. ASP.NET and Azure Functions.

Specifically it contains functionality for:

- Dependency Injection
- Pooling PredictionEngines
- Reloading models when the file or URI has changed
- Hooking ML.NET logging to Microsoft.Extensions.Logging

Fix dotnet#3239
eerhardt added a commit that referenced this issue Jun 13, 2019
* Add Microsoft.Extensions.ML integration package.

This package makes it easier to use ML.NET with app models that support Microsoft.Extensions - i.e. ASP.NET and Azure Functions.

Specifically it contains functionality for:

- Dependency Injection
- Pooling PredictionEngines
- Reloading models when the file or URI has changed
- Hooking ML.NET logging to Microsoft.Extensions.Logging

Fix #3239

* Add XML doc comments.
Format the code so lines aren't so long.
Remove unnecessary IPredictionEnginePoolBuilder interface, and just use a regular class.

* Respond to PR feedback.

* PR feedback

Rename MLContextOptions to MLOptions. Make MLContext lazy loaded, so callers can provide their own.

Use the loaded model in the test.
@ghost ghost locked as resolved and limited conversation to collaborators Mar 23, 2022
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

5 participants
@markusweimer @CESARDELATORRE @eerhardt @luisquintanilla and others