鍵.png)
ASP.NET Web API快速入門介紹
創(chuàng)建過程就不一一贅述了,根據(jù)自己的需要和錢包的厚度來決定所需的配置,待創(chuàng)建完成后,進(jìn)入Storage Account的Access keys頁面,注意其中的Connection string部分的值,接下來構(gòu)建RESTful API的時(shí)候,需要用到這些值。值得一提的是,Azure會(huì)同時(shí)給你提供兩個(gè)不同的Key和Connection String,因?yàn)榻?jīng)常更換Access key將會(huì)是一個(gè)良好的習(xí)慣,為了防止Access key更新時(shí),應(yīng)用程序無法正常工作,因此會(huì)有一個(gè)備用Key來保證程序的正常運(yùn)行。我們先不管Azure Key Vault的事情,目前先把其中的某個(gè)Key復(fù)制下來。
然后,進(jìn)入Blobs服務(wù),新建一個(gè)容器(Container),比如命名為mlnetmodel,這個(gè)名字也要記下來。之后,在容器中上傳我們的模型文件即可,如下:
在準(zhǔn)備好模型文件之后,我們就可以開始開發(fā)RESTful API了。
打開宇宙第一最強(qiáng)IDE Visual Studio,我用的是2019的版本,新建一個(gè)ASP.NET Core的應(yīng)用程序,啟用docker支持,因?yàn)槲覀兘酉聛頃?huì)將這個(gè)應(yīng)用程序編譯成docker鏡像,以便在容器中運(yùn)行。詳細(xì)的項(xiàng)目創(chuàng)建過程以及RESTful API實(shí)現(xiàn)過程我也就不多說明了,網(wǎng)上相關(guān)資料實(shí)在太多了。這里只強(qiáng)調(diào)幾個(gè)需要重點(diǎn)注意的地方。
首先需要添加如下NuGet包的引用,由于我們需要使用ML.NET,并且需要訪問Azure Blob Storage,因此,以下依賴項(xiàng)不可缺少:
有點(diǎn)小坑的地方是,當(dāng)你直接引用Microsoft.Azure.Storage.Blob時(shí),編譯項(xiàng)目會(huì)出錯(cuò),提示所依賴的Microsoft.Azure.KeyVault.Core不支持.NET Standard。解決辦法就是手工添加Microsoft.Azure.KeyVault.Core的依賴,我使用的是3.0.3的版本。
接下來,通過ASP.NET Core的配置系統(tǒng),從配置數(shù)據(jù)中讀入訪問Azure Blob Storage所需的連接字符串參數(shù),然后初始化Storage Account以及Blob Client對(duì)象,以便將保存在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對(duì)象,將模型文件下載到MemoryStream中。問題來了,干嘛不保存在本地文件中呢?因?yàn)槲覀兘酉聛硇枰褂玫腗L.NET中的PredictionEngine(預(yù)測引擎)不是線程安全的,我們只能通過services.AddScoped方法來注冊(cè)PredictionEngine的實(shí)例,也就是說,每當(dāng)有一個(gè)新的HTTP請(qǐng)求到來時(shí),PredictionEngine實(shí)例都需要構(gòu)建一次,而PredictionEngine的構(gòu)建是需要訪問模型文件的,頻繁的訪問文件系統(tǒng)中的文件會(huì)損耗應(yīng)用程序的性能。
因此,我構(gòu)建了下面的數(shù)據(jù)結(jié)構(gòu),用來保存下載的模型數(shù)據(jù):
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()));
}
然后,通過如下方法來注冊(cè)PredictionEngine實(shí)例:
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);
}
});
現(xiàn)在,我們已經(jīng)完成了模型文件的下載,以及PredictionEngine實(shí)例的注冊(cè),接下來就非常簡單了,只需要在API Controller中,使用構(gòu)造器注入的PredictionEngine實(shí)例來實(shí)現(xiàn)我們的預(yù)測功能即可。代碼非常簡單:
[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運(yùn)行起來,并進(jìn)行簡單的測試:
測試成功。cURL命令從本地文件data.json中讀入學(xué)生問卷調(diào)查數(shù)據(jù),并預(yù)測他的綜合成績是12.8184786分(實(shí)際是9分,還是有點(diǎn)偏差)。
由于在創(chuàng)建ASP.NET Core應(yīng)用程序時(shí),已經(jīng)選擇了docker支持,因此,我們可以直接使用docker build命令來編譯鏡像,并使用docker run來運(yùn)行容器。當(dāng)然,在Windows環(huán)境下需要安裝Docker for Windows,不過這里就不多說明安裝步驟了,在我以前的博客中有詳細(xì)介紹。為了方便編譯和運(yùn)行容器,我在ASP.NET Core的上層目錄中建了一個(gè)docker-compose.yml文件,以使用docker compose來實(shí)現(xiàn)容器鏡像的編譯與容器的運(yùn)行。在這里我強(qiáng)調(diào)“上層目錄”,因?yàn)椋琩ocker-compose.yml文件中,已經(jīng)通過相對(duì)路徑指定了docker build的context路徑。docker-compose.yml文件內(nèi)容如下:
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
值得一提的是,文件中環(huán)境變量都是通過.env文件注入進(jìn)來的,因此,訪問Azure Blob Storage的Connection String相關(guān)信息不會(huì)簽入到Github代碼庫中。
使用docker-compose up命令一鍵編譯并啟動(dòng)容器,再次訪問我們的API以確保程序能夠正常工作:
本文主要介紹了如何在ASP.NET Core項(xiàng)目中使用ML.NET產(chǎn)生的訓(xùn)練模型,并向外界提供RESTful API,案例使用了容器技術(shù),使得所生成的RESTful API應(yīng)用能夠在容器中運(yùn)行,以便為下一步的持續(xù)部署做鋪墊。在下文中,我將介紹基于Azure DevOps的持續(xù)集成與持續(xù)部署。
文章轉(zhuǎn)自微信公眾號(hào)@dotNET跨平臺(tái)
對(duì)比大模型API的內(nèi)容創(chuàng)意新穎性、情感共鳴力、商業(yè)轉(zhuǎn)化潛力
一鍵對(duì)比試用API 限時(shí)免費(fèi)