Azure Functions에 모델 배포

Azure Functions 서버리스 환경을 통해 HTTP에서의 예측을 위해 미리 학습된 ML.NET 기계 학습 모델을 배포하는 방법을 알아봅니다.

필수 조건

Azure Functions 샘플 개요

이 샘플은 미리 학습된 이진 분류 모델을 사용하여 텍스트의 감정을 긍정 또는 부정으로 분류하는 C# HTTP 트리거 Azure Functions 애플리케이션입니다. Azure Functions는 클라우드의 관리되는 서버리스 환경에서 작은 코드를 대규모로 실행하는 간편한 방법을 제공합니다. 이 샘플의 코드는 GitHub의 dotnet/machinelearning-samples repository에서 찾을 수 있습니다.

Azure Functions 프로젝트 만들기

  1. Visual Studio 2022에서 새 프로젝트 만들기 대화 상자를 엽니다.

  2. "새 프로젝트 만들기" 대화 상자에서 Azure Functions 프로젝트 템플릿을 선택합니다.

  3. 이름 텍스트 상자에 "SentimentAnalysisFunctionsApp"을 입력하고 다음 단추를 선택합니다.

  4. "추가 정보 대화 상자"에서 모든 기본값을 그대로 두고 만들기 단추를 선택합니다.

  5. Microsoft.ML NuGet 패키지 설치

    1. 솔루션 탐색기에서 프로젝트를 마우스 오른쪽 단추로 클릭하고 NuGet 패키지 관리를 선택합니다.
    2. 패키지 원본으로 "nuget.org"를 선택합니다.
    3. "찾아보기" 탭을 선택합니다.
    4. Microsoft.ML을 검색합니다.
    5. 목록에서 해당 패키지를 선택하고 설치 단추를 선택합니다.
    6. 변경 내용 미리 보기 대화 상자에서 확인 단추를 선택합니다.
    7. 나열된 패키지 라이선스 조건에 동의하면 라이선스 수락 대화 상자에서 동의함 단추를 선택합니다.

    동일한 단계에 따라 Microsoft.Extensions.ML, Microsoft.Extensions.DependencyInjectionMicrosoft.Azure.Functions.Extensions NuGet packages를 설치합니다.

프로젝트에 미리 학습된 모델 추가

  1. 프로젝트에 MLModels라는 디렉터리를 만들어 빌드 전 모델을 저장합니다. 솔루션 탐색기에서 프로젝트를 마우스 오른쪽 단추로 클릭하고 새 폴더 >추가를 선택합니다. “MLModels”를 입력하고 Enter 키를 누릅니다.
  2. 미리 빌드된 모델을 MLModels 폴더에 복사합니다.
  3. 솔루션 탐색기에서 미리 빌드된 모델 파일을 마우스 오른쪽 단추로 클릭하고 속성을 선택합니다. 고급 아래에서 출력 디렉터리에 복사 값을 변경된 내용만 복사로 변경합니다.

감정을 분석하는 Azure Function 만들기

감정을 예측하는 클래스를 만듭니다. 새 클래스를 프로젝트에 추가합니다.

  1. 솔루션 탐색기에서 프로젝트를 마우스 오른쪽 단추로 클릭한 다음 추가>새 Azure Function을 선택합니다.

  2. 새 항목 추가 대화 상자에서 Azure 함수를 선택하고 이름 필드를 AnalyzeSentiment.cs로 변경합니다. 그런 다음, 추가 단추를 선택합니다.

  3. 새 Azure 함수 대화 상자에서 Http 트리거를 선택하고 권한 부여 수준 드롭다운에서 익명을 선택합니다. 그런 다음, 확인 단추를 선택합니다.

    AnalyzeSentiment.cs 파일이 코드 편집기에서 열립니다. 다음 using 문을 AnalyzeSentiment.cs의 맨 위에 추가합니다.

    using System;
    using System.IO;
    using System.Threading.Tasks;
    using Microsoft.AspNetCore.Mvc;
    using Microsoft.Azure.WebJobs;
    using Microsoft.Azure.WebJobs.Extensions.Http;
    using Microsoft.AspNetCore.Http;
    using Microsoft.Extensions.Logging;
    using Newtonsoft.Json;
    using Microsoft.Extensions.ML;
    using SentimentAnalysisFunctionsApp.DataModels;
    

    기본적으로 AnalyzeSentiment 클래스는 static입니다. 클래스 정의에서 static 키워드를 제거합니다.

    public class AnalyzeSentiment
    {
    
    }
    

데이터 모델 만들기

입력 데이터 및 예측에 대한 일부 클래스를 만들어야 합니다. 새 클래스를 프로젝트에 추가합니다.

  1. 프로젝트에 DataModels이라는 디렉터리를 만들어 데이터 모델을 저장합니다. 솔루션 탐색기에서 프로젝트를 마우스 오른쪽 단추로 클릭하고 새 폴더 >추가를 선택합니다. “DataModels”를 입력하고 Enter 키를 누릅니다.

  2. 솔루션 탐색기에서 DataModels 디렉터리를 마우스 오른쪽 단추로 클릭한 다음 클래스> 추가를 선택합니다.

  3. 새 항목 추가 대화 상자에서 클래스를 선택하고 이름 필드를 SentimentData.cs로 변경합니다. 그런 다음, 추가 단추를 선택합니다.

    SentimentData.cs 파일이 코드 편집기에서 열립니다. 다음 using 문을 SentimentData.cs의 맨 위에 추가합니다.

    using Microsoft.ML.Data;
    

    기존 클래스 정의를 제거하고 다음 코드를 SentimentData.cs 파일에 추가합니다.

    public class SentimentData
    {
        [LoadColumn(0)]
        public string SentimentText;
    
        [LoadColumn(1)]
        [ColumnName("Label")]
        public bool Sentiment;
    }
    
  4. 솔루션 탐색기에서 DataModels 디렉터리를 마우스 오른쪽 단추로 클릭한 다음 클래스> 추가를 선택합니다.

  5. 새 항목 추가 대화 상자에서 클래스를 선택하고 이름 필드를 SentimentPrediction.cs로 변경합니다. 그런 다음, 추가 단추를 선택합니다. SentimentPrediction.cs 파일이 코드 편집기에서 열립니다. 다음 using 문을 SentimentPrediction.cs의 맨 위에 추가합니다.

    using Microsoft.ML.Data;
    

    기존 클래스 정의를 제거하고 다음 코드를 SentimentPrediction.cs 파일에 추가합니다.

    public class SentimentPrediction : SentimentData
    {
    
        [ColumnName("PredictedLabel")]
        public bool Prediction { get; set; }
    
        public float Probability { get; set; }
    
        public float Score { get; set; }
    }
    

    SentimentPredictionSentimentText 속성의 원래 데이터와 모델에서 생성된 출력에 대한 액세스를 제공하는 SentimentData에서 상속됩니다.

PredictionEnginePool 서비스 등록

단일 예측을 수행하려면 PredictionEngine을 만들어야 합니다. PredictionEngine는 스레드로부터 안전하지 않습니다. 또한 애플리케이션 내에서 필요한 모든 위치에서 인스턴스를 만들어야 합니다. 애플리케이션이 커지면 이 프로세스를 관리할 수 없게 됩니다. 성능 및 스레드 보안을 개선하려면 종속성 주입과 PredictionEnginePool 서비스를 함께 사용합니다. 이 서비스는 애플리케이션 전체에서 사용할 PredictionEngine 개체의 ObjectPool을 만듭니다.

종속성 주입에 대한 자세한 내용은 다음 링크를 참조하세요.

  1. 솔루션 탐색기에서 프로젝트를 마우스 오른쪽 단추로 클릭한 다음 클래스>추가를 선택합니다.

  2. 새 항목 추가 대화 상자에서 클래스를 선택하고 이름 필드를 Startup.cs로 변경합니다. 그런 다음, 추가 단추를 선택합니다.

  3. 다음 using 문을 Startup.cs의 맨 위에 추가합니다.

    using Microsoft.Azure.Functions.Extensions.DependencyInjection;
    using Microsoft.Extensions.DependencyInjection;
    using Microsoft.Extensions.ML;
    using SentimentAnalysisFunctionsApp;
    using SentimentAnalysisFunctionsApp.DataModels;
    using System.IO;
    using System;
    
  4. using 문 아래의 기존 코드를 제거하고 다음 코드를 추가합니다.

    [assembly: FunctionsStartup(typeof(Startup))]
    namespace SentimentAnalysisFunctionsApp
    {
        public class Startup : FunctionsStartup
        {
    
        }
    }
    
  5. 앱이 실행되는 환경 및 모델이 Startup 클래스 내에 있는 파일 경로를 저장할 변수를 정의합니다.

    private readonly string _environment;
    private readonly string _modelPath;
    
  6. 그런 다음 _environment_modelPath 변수 값을 설정하는 생성자를 만듭니다. 애플리케이션을 로컬로 실행하는 경우 기본 환경은 개발입니다.

    public Startup()
    {
        _environment = Environment.GetEnvironmentVariable("AZURE_FUNCTIONS_ENVIRONMENT");
    
        if (_environment == "Development")
        {
            _modelPath = Path.Combine("MLModels", "sentiment_model.zip");
        }
        else
        {
            string deploymentPath = @"D:\home\site\wwwroot\";
            _modelPath = Path.Combine(deploymentPath, "MLModels", "sentiment_model.zip");
        }
    }
    
  7. 그런 다음 Configure라는 새 메서드를 추가하여 생성자 아래에 PredictionEnginePool 서비스를 등록합니다.

    public override void Configure(IFunctionsHostBuilder builder)
    {
        builder.Services.AddPredictionEnginePool<SentimentData, SentimentPrediction>()
            .FromFile(modelName: "SentimentAnalysisModel", filePath: _modelPath, watchForChanges: true);
    }
    

개략적으로 이 코드는 애플리케이션을 수동으로 초기화하지 않고도 애플리케이션에서 요청할 때 나중에 사용할 수 있도록 자동으로 개체 및 서비스를 초기화합니다.

기계 학습 모델은 정적이지 않습니다. 새 학습 데이터를 사용할 수 있게 되면 모델을 다시 학습하고 다시 배포합니다. 최신 버전의 모델을 애플리케이션으로 가져오는 한 가지 방법은 애플리케이션을 시작하거나 다시 배포하는 것입니다. 그러나 이 경우 애플리케이션 가동 중지 시간이 발생합니다. PredictionEnginePool 서비스는 애플리케이션을 다시 시작하거나 다시 배포하지 않으면서 업데이트된 모델을 다시 로드하는 메커니즘을 제공합니다.

watchForChanges 매개 변수를 true로 설정하면 PredictionEnginePool은 파일 시스템 변경 알림을 수신 대기하고 파일이 변경될 때 이벤트를 발생시키는 FileSystemWatcher를 시작합니다. 그러면 PredictionEnginePool에서 모델을 자동으로 다시 로드하도록 지정할 수 있는 메시지가 표시됩니다.

모델은 modelName 매개 변수로 식별되므로 변경 시, 애플리케이션당 두 개 이상의 모델이 다시 로드될 수 있습니다.

또는 원격으로 저장된 모델을 사용하는 경우 FromUri 메서드를 사용할 수 있습니다. FromUri는 파일 변경 이벤트를 감시하지 않고, 원격 위치에서 변경 내용을 폴링합니다. 폴링 간격의 기본값은 5분입니다. 애플리케이션의 요구 사항에 따라 폴링 간격을 늘리거나 줄일 수 있습니다. 아래 코드 샘플에서 PredictionEnginePool은 1분마다 지정된 URI에 저장된 모델을 폴링합니다.

builder.Services.AddPredictionEnginePool<SentimentData, SentimentPrediction>()
  .FromUri(
      modelName: "SentimentAnalysisModel",
      uri:"https://github.com/dotnet/samples/raw/main/machine-learning/models/sentimentanalysis/sentiment_model.zip",
      period: TimeSpan.FromMinutes(1));

함수에 모델 로드

AnalyzeSentiment 클래스 안에 다음 코드를 삽입합니다.

public AnalyzeSentiment(PredictionEnginePool<SentimentData, SentimentPrediction> predictionEnginePool)
{
    _predictionEnginePool = predictionEnginePool;
}

이 코드는 종속성 주입을 통해 가져오는 함수의 생성자에 PredictionEnginePool을 전달하여 할당합니다.

모델을 사용하여 예측하기

AnalyzeSentiment 클래스에서 Run 메서드의 기존 구현을 다음 코드로 바꿉니다.

public async Task<IActionResult> Run(
    [HttpTrigger(AuthorizationLevel.Anonymous, "post", Route = null)] HttpRequest req,
    ILogger log)
{
    log.LogInformation("C# HTTP trigger function processed a request.");

    // Parse HTTP Request Body
    string requestBody = await new StreamReader(req.Body).ReadToEndAsync();
    SentimentData data = JsonConvert.DeserializeObject<SentimentData>(requestBody);

    //Make Prediction
    SentimentPrediction prediction = _predictionEnginePool.Predict(modelName: "SentimentAnalysisModel", example: data);

    //Convert prediction to string
    string sentiment = Convert.ToBoolean(prediction.Prediction) ? "Positive" : "Negative";

    //Return Prediction
    return new OkObjectResult(sentiment);
}

Run 메서드가 실행되면 HTTP 요청에서 수신되는 데이터가 역직렬화되어 PredictionEnginePool에 대한 입력으로 사용됩니다. 그런 다음, Predict 메서드가 Startup 클래스에 등록된 SentimentAnalysisModel을 사용하여 예측을 수행하고 성공하면 결과를 사용자에게 다시 반환합니다.

로컬에서 테스트

이제 모든 것이 설정되었으므로 애플리케이션을 테스트할 수 있습니다.

  1. 애플리케이션 실행

  2. PowerShell을 열고 PORT가 애플리케이션을 실행 중인 포트인 경우 프롬프트에 코드를 입력합니다. 일반적으로 포트는 7071입니다.

    Invoke-RestMethod "http://localhost:<PORT>/api/AnalyzeSentiment" -Method Post -Body (@{SentimentText="This is a very bad steak"} | ConvertTo-Json) -ContentType "application/json"
    

    성공하면 출력이 아래 텍스트와 같이 표시됩니다.

    Negative
    

축하합니다! Azure 함수를 사용하여 인터넷을 통해 예측을 수행하도록 모델을 제공했습니다.

다음 단계