
ASP.NET Web API快速入門介紹
創建過程就不一一贅述了,根據自己的需要和錢包的厚度來決定所需的配置,待創建完成后,進入Storage Account的Access keys頁面,注意其中的Connection string部分的值,接下來構建RESTful API的時候,需要用到這些值。值得一提的是,Azure會同時給你提供兩個不同的Key和Connection String,因為經常更換Access key將會是一個良好的習慣,為了防止Access key更新時,應用程序無法正常工作,因此會有一個備用Key來保證程序的正常運行。我們先不管Azure Key Vault的事情,目前先把其中的某個Key復制下來。
然后,進入Blobs服務,新建一個容器(Container),比如命名為mlnetmodel,這個名字也要記下來。之后,在容器中上傳我們的模型文件即可,如下:
在準備好模型文件之后,我們就可以開始開發RESTful API了。
打開宇宙第一最強IDE Visual Studio,我用的是2019的版本,新建一個ASP.NET Core的應用程序,啟用docker支持,因為我們接下來會將這個應用程序編譯成docker鏡像,以便在容器中運行。詳細的項目創建過程以及RESTful API實現過程我也就不多說明了,網上相關資料實在太多了。這里只強調幾個需要重點注意的地方。
首先需要添加如下NuGet包的引用,由于我們需要使用ML.NET,并且需要訪問Azure Blob Storage,因此,以下依賴項不可缺少:
有點小坑的地方是,當你直接引用Microsoft.Azure.Storage.Blob時,編譯項目會出錯,提示所依賴的Microsoft.Azure.KeyVault.Core不支持.NET Standard。解決辦法就是手工添加Microsoft.Azure.KeyVault.Core的依賴,我使用的是3.0.3的版本。
接下來,通過ASP.NET Core的配置系統,從配置數據中讀入訪問Azure Blob Storage所需的連接字符串參數,然后初始化Storage Account以及Blob Client對象,以便將保存在Azure Blob Storage中的模型文件下載下來。代碼如下:
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
var defaultEndpointsProtocol = Configuration[BlobProtocolConfigName];
var accountName = Configuration[BlobAccountNameConfigName];
var accountKey = Configuration[BlobAccountKeyConfigName];
var endpointSuffix = Configuration[BlobEndpointSuffixConfigName];
var connectionString = $@"DefaultEndpointsProtocol={defaultEndpointsProtocol};
AccountName={accountName};
AccountKey={accountKey};
EndpointSuffix={endpointSuffix}";
var storageAccount = CloudStorageAccount.Parse(connectionString);
var blobClient = storageAccount.CreateCloudBlobClient();
var mlnetContainer = blobClient.GetContainerReference("mlnetmodel");
var blob = mlnetContainer.GetBlobReference("student_perf_model.zip");
using (var ms = new MemoryStream())
{
blob.DownloadToStream(ms);
}
}
上面高亮的代碼,通過blob對象,將模型文件下載到MemoryStream中。問題來了,干嘛不保存在本地文件中呢?因為我們接下來需要使用的ML.NET中的PredictionEngine(預測引擎)不是線程安全的,我們只能通過services.AddScoped方法來注冊PredictionEngine的實例,也就是說,每當有一個新的HTTP請求到來時,PredictionEngine實例都需要構建一次,而PredictionEngine的構建是需要訪問模型文件的,頻繁的訪問文件系統中的文件會損耗應用程序的性能。
因此,我構建了下面的數據結構,用來保存下載的模型數據:
public class ModelData
{
public ModelData(byte[] dataBytes)
{
this.DataBytes = dataBytes;
}
public byte[] DataBytes { get; }
}
于是,上面的blob.DownloadToStream這部分代碼,就可以改寫為:
using (var ms = new MemoryStream())
{
blob.DownloadToStream(ms);
services.AddSingleton(new ModelData(ms.ToArray()));
}
然后,通過如下方法來注冊PredictionEngine實例:
services.AddScoped(serviceProvider =>
{
var mlContext = serviceProvider.GetRequiredService<MLContext>();
var dataStream = serviceProvider.GetRequiredService<ModelData>().DataBytes;
using (var modelStream = new MemoryStream(dataStream))
{
var model = mlContext.Model.Load(modelStream);
return model.CreatePredictionEngine<StudentTrainingModel, StudentPredictionModel>(mlContext);
}
});
現在,我們已經完成了模型文件的下載,以及PredictionEngine實例的注冊,接下來就非常簡單了,只需要在API Controller中,使用構造器注入的PredictionEngine實例來實現我們的預測功能即可。代碼非常簡單:
[Route("api/[controller]")]
[ApiController]
public class StudentsController : ControllerBase
{
private readonly PredictionEngine<StudentTrainingModel, StudentPredictionModel> predictionEngine;
public StudentsController(PredictionEngine<StudentTrainingModel, StudentPredictionModel> predictionEngine)
{
this.predictionEngine = predictionEngine;
}
[HttpPost("predict")]
public IActionResult Predict([FromBody] StudentTrainingModel model)
=> Ok(predictionEngine.Predict(model));
}
至此,API編寫完成,將API運行起來,并進行簡單的測試:
測試成功。cURL命令從本地文件data.json中讀入學生問卷調查數據,并預測他的綜合成績是12.8184786分(實際是9分,還是有點偏差)。
由于在創建ASP.NET Core應用程序時,已經選擇了docker支持,因此,我們可以直接使用docker build命令來編譯鏡像,并使用docker run來運行容器。當然,在Windows環境下需要安裝Docker for Windows,不過這里就不多說明安裝步驟了,在我以前的博客中有詳細介紹。為了方便編譯和運行容器,我在ASP.NET Core的上層目錄中建了一個docker-compose.yml文件,以使用docker compose來實現容器鏡像的編譯與容器的運行。在這里我強調“上層目錄”,因為,docker-compose.yml文件中,已經通過相對路徑指定了docker build的context路徑。docker-compose.yml文件內容如下:
version: '3'
services:
mlnet_webapi:
image: daxnet/mlnet_webapi
build:
context: .
dockerfile: mlnet_webapi/Dockerfile
environment:
- BLOB_ACCOUNT_NAME=${BLOB_ACCOUNT_NAME}
- BLOB_DEFAULT_ENDPOINTS_PROTOCOL=${BLOB_DEFAULT_ENDPOINTS_PROTOCOL}
- BLOB_ENDPOINT_SUFFIX=${BLOB_ENDPOINT_SUFFIX}
- BLOB_ACCOUNT_KEY=${BLOB_ACCOUNT_KEY}
- Serilog__MinimumLevel=${Serilog__MinimumLevel:-Debug}
container_name: mlnet_webapi
ports:
- 880:80
- 8443:443
值得一提的是,文件中環境變量都是通過.env文件注入進來的,因此,訪問Azure Blob Storage的Connection String相關信息不會簽入到Github代碼庫中。
使用docker-compose up命令一鍵編譯并啟動容器,再次訪問我們的API以確保程序能夠正常工作:
本文主要介紹了如何在ASP.NET Core項目中使用ML.NET產生的訓練模型,并向外界提供RESTful API,案例使用了容器技術,使得所生成的RESTful API應用能夠在容器中運行,以便為下一步的持續部署做鋪墊。在下文中,我將介紹基于Azure DevOps的持續集成與持續部署。
文章轉自微信公眾號@dotNET跨平臺