在本文中,我們將探討如何為生產準備機器學習模型,并將其部署在簡單的 Web 應用程序中。部署機器學習模型本身就是一門藝術。事實上,將機器學習模型成功投入生產超出了數據科學知識的范圍,并且涉及許多軟件開發和DevOps技能。你為什么要關心這一切?目前,數據科學團隊中最有價值的角色之一是機器學習工程師。這個角色聚集了兩全其美的精華。
這些工程師不僅知道如何將不同的機器學習和深度學習模型應用于適當的問題,還知道如何測試它們,驗證它們并最終部署它們。擁有一個能夠將機器學習模型投入生產的人對任何公司來說都是一筆巨大的資產。一般來說,這是Rubik's Code提供的主要服務類型。為了成為一名成功的機器學習工程師,你需要具備各種技能,而不僅僅是關注數據。實際上,為機器學習模型編寫的代碼量遠小于支持測試和提供該模型的代碼量。
MLOps,一個圍繞這種需求構建的工程術語,正在成為一種新的趨勢。這就是為什么在本文中,我們將重點介紹您需要了解的幾種技術,以便構建一個用戶想要的成功機器學習應用程序。本教程中提供的完整解決方案由兩個組件組成:服務器端和客戶端。這是基本的 Web 體系結構,其中客戶端與應用程序的用戶交互并將其發送到服務器端。服務器端執行數據處理,或者在本例中運行預測,并將結果返回到客戶端,客戶端將其呈現給用戶。從本質上講,在本文中,我們構建了一個可以用下圖表示的小系統:
MLOps,一個圍繞這種需求構建的工程術語,正在成為一種新的趨勢。這就是為什么在本文中,我們將重點介紹您需要了解的幾種技術,以便構建一個用戶想要的成功機器學習應用程序。本教程中提供的完整解決方案由兩個組件組成:服務器端和客戶端。這是基本的 Web 體系結構,其中客戶端與應用程序的用戶交互并將其發送到服務器端。服務器端執行數據處理,或者在本例中運行預測,并將結果返回到客戶端,客戶端將其呈現給用戶。
為了涵蓋所有內容,我們需要涵蓋幾個主題:
- 安裝和數據集
- Rest API 基礎知識
- FastAPI 基礎知識
- 客戶端 – 用戶界面
- 服務器端 – 使用 FastAPI 進行模型訓練的解決方案
- 服務器端 – 使用 FastAPI 加載模型的解決方案
1. 安裝和數據集
要成功運行本教程中的示例,需要安裝 Python 3.6 或更高版本。最簡單的方法是使用Anaconda分發。它附帶了本教程所需的所有其他必要庫,如Pandas,NumPy,SciKit Learn等。要安裝 FastAPI 及其所有依賴項,請使用以下命令:
pip install fastapi[all]
這包括 Uvicorn,一個運行代碼的 ASGI 服務器。如果您對其他一些ASGI服務器(如Hypercorn)更滿意,那也很好,您可以在本教程中使用它。
對于Web應用程序,我們使用Angular。為此,您需要安裝 Node.js 和 npm。使用Angular框架進行操作的最常見方法是使用Angular命令行界面 - Angular-CLI。這個工具的好處之一是,一旦我們用它初始化了我們的應用程序,我們就可以使用TypeScript,它將自動轉換為JAVAScript。安裝此接口是通過 npm 完成的,當然,通過運行以下命令:
npm install -g angular-cli
當您要創建新的Angular應用程序時,您可以使用以下命令執行此操作:
ng new Application_name
此命令將創建一個文件夾結構,供我們的應用程序使用。要運行此應用程序,請將 shell 放在剛剛創建的應用程序的根文件夾 (cd application_name) 中,然后調用命令:
ng serve
如果您在轉到瀏覽器并打開localhost:4200后按照上述步驟操作,您將能夠看到默認的Angular應用程序。
我們在本文中使用的數據來自PalmerPenguins Dataset。該數據集最近被引入,作為著名的Iris數據集的替代方案。它由Kristen Gorman博士和南極洲LTER的Palmer站創建。您可以在此處或通過Kaggle獲取此數據集。該數據集主要由兩個數據集組成,每個數據集包含344只企鵝的數據。就像在鳶尾花數據集中一樣,有3種不同種類的企鵝來自帕爾默群島的3個島嶼。此外,這些數據集還包含每個物種的 culmen 維度。Culmen是鳥喙的上脊。在簡化的企鵝數據中,culmen的長度和深度被重命名為變量culmen_length_mm和culmen_depth_mm。以下是數據集:
2. REST API 基礎知識
在我們開始使用Python和Flask實現Web應用程序之前,讓我們首先找出什么是REST API。現在,我相信你一生中已經看過這個詞兩次了。術語的第二部分 - API代表應用程序編程接口。從本質上講,它 API 表示程序用于相互通信的一組規則。例如,在服務器-客戶端體系結構中,應用程序的服務器端以公開可由應用程序客戶端調用的方法進行編程。這意味著客戶端可以在其代碼中調用服務器上的方法,并從中獲取特定結果。REST 代表"具象狀態轉移"。這表示開發人員在構建 API 時應遵循的一組規則。它定義了 API 的外觀,因此 API 是標準化的。
其中一個規則定義了在鏈接特定 URL 時可以收集數據或資源。例如,您可以鏈接"api.rubikscode.com/blogs"并獲取博客列表作為響應。URL api.rubikscode.com/blogs "稱為"請求",返回的客戶端列表稱為響應。每個請求由 4 個部分組成:
- 終結點(路由) – 這是我們之前提到的 URL。它的結構是這樣的 - "根端點/?根終結點是起點,后跟路徑和查詢參數。該路徑定義所需的特定資源。例如,GitHub 的 API 的根端點是"api.github.com",而我在 GitHub 上的存儲庫列表的完整端點是 https://api.github.com/users/nmzivkovic/repos。
- 方法 – 可以發送五種類型的請求,該方法定義此類型:GET – 用于獲取或讀取信息。開機自檢 – 用于創建新資源。PUT 和 PATCH – 它們用于更新資源。刪除 – 刪除資源。
- 標頭 – 標頭用于以屬性值對的形式向客戶端和服務器提供其他信息。MDN的HTTP頭引用上的有效頭列表。
- 正文 – 此部分包含客戶端發送到服務器的信息。它不用于 GET 請求。
我們要在本文中創建的是 Web 服務器,它為 Iris 預測提供模型。我們希望構建 API,應用程序客戶端可以使用該 API 從模型中獲取預測。這是使用Python框架Flask完成的。
2. 快速API基礎知識
在本文中,我們使用 FastAPI 來構建 REST API。我們為什么要使用這個庫?這有幾個原因。與其他主要的Python框架(如Flask和Django)相比,FastAPI更快。此外,此框架還支持使用 async/await Python 關鍵字的開箱即用異步代碼。這進一步提高了其性能。使FastAPI成為最好的API庫之一的最顯著的功能可能是內置的交互式文檔。稍后我們將更詳細地探討此功能。最后,使用 FastAPI 構建的應用程序非常易于測試和部署。由于所有這些,FastAPI成為使用Python構建Web API應用程序的標準。
2.1 首次快速API應用
好了,讓我們構建第一個 FastAPI 應用程序。很酷的事情是,一個簡單的HelloWorld示例與FastAPI可以用5行代碼創建。我不是在開玩笑。準備好了嗎?以下是您需要執行的操作:
- 創建一個新文件夾并將其命名為"my_first_fastapi"(或者您認為:)的任何內容)
- 創建一個名為 main.py 的新Python腳本(或者你覺得它:)的任何內容)
- 將以下代碼添加到 main.py:
from fastapi import FastAPI
app = FastAPI()
@app.get("/")
async def root():
return {"message": "Hello World"}
- 轉到終端并放入my_first_fastapi文件夾中
- 運行以下命令:
uvicorn main:app --reload
INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
INFO: Started reloader process [28720]
INFO: Started server process [28722]
INFO: Waiting for application startup.
INFO: Application startup complete.
太棒了,現在你有一個Web服務器在 http://127.0.0.1:8000 運行。如果您在瀏覽器中轉到此地址,您將看到如下內容:
請注意,在上面的代碼中@app.get("route") 裝飾器?此修飾器確定可以在特定終結點上發出的請求類型。這意味著,例如,如果我們放置 app.get("/users"),我們將 REST API 端點定義為"users",我們可以在其上發出 GET http 請求。
2.2 快速 API 文檔
FastAPI最酷的事情之一是內置的交互式文檔。它以 Swagger UI 呈現。如果我們使用剛剛構建的示例并轉到
http://127.0.0.1:8000/docs 我們將看到 API 的文檔頁面:
您可以單擊任何端點,進一步瀏覽并了解它們。本文檔的最大好處可能是,您可以通過單擊"試用"按鈕來執行實時瀏覽器內測試。
我知道,對吧?!
好了,回到我們的應用程序,讓我們看看如何利用 FastAPI 進行機器學習部署。
3. 客戶端和用戶界面
好的,為了讓用戶與我們的模型進行通信,我們需要某種用戶界面。有許多可用的框架和技術可供您使用。在這里,您可以看到我們如何用Flask和Python做類似的事情。在本教程中,我們使用 Angular 構建用戶可以與之交互的 Web 應用程序。
我們不會在這里過多地討論實現的細節,因為重點是FastAPI和機器學習模型。從本質上講,應用程序Train和Predict的工具欄中有兩個項目,它將我們引導到兩個具有相同名稱的網頁。完整的內容在兩個組件中實現:訓練組件和預測組件。還有一項服務 - fastapi.service。此服務包含對服務器端實現的 REST API 的調用。
在訓練選項卡中,您可以選擇要訓練的模型,選擇要試驗的數據(采用類似 PalmerPenguing 數據集的格式),以及定義測試數據集的大小。按下訓練按鈕后,包含此信息的 HTTP 請求將發送到服務器端,我們希望它將訓練定義的機器學習模型。
在"預測"選項卡中,用戶可以使用通過上一個選項卡訓練的機器學習模型進行預測。在這里,用戶可以輸入描述企鵝的參數,用戶希望對此進行預測。按下"預測"按鈕后,此信息將發送到服務器,該服務器使用機器學習模型進行預測并將其發送回用戶。
要運行此應用程序(我們假設您已經克隆了 GitHub 存儲庫),請打開終端并放入 train_solutionclient 并運行以下命令:
npm install
ng serve
完成此操作后,此 Web 應用將在 localhost:4200 上可用。好吧,讓我們看看服務器端實現的樣子。
3. 模型訓練解決方案
服務器解決方案由多個組件組成,這些組件可以在train_solutionserver 文件夾中的文件中找到。整體架構如下所示:
讓我們來探索每個組件。
3.1 數據合同
train_parameters.py文件包含從 Web 應用程序的"訓練"選項卡傳遞的模型。從本質上講,此文件包含一個數據協定,當來自客戶端的HTTP請求到達我們的服務器時,將使用該協定。
from pydantic import BaseModel
class TrainParameters(BaseModel):
model: str
path: str
testsize: float
請注意,我們使用 Pydantic、數據驗證和設置管理庫進行類型注釋。此庫在運行時強制實施類型提示,并在數據無效時提供用戶友好的錯誤。我們在penguin_sample.py中使用的同一庫,其中我們定義了從Web應用程序的預測頁面發送HTTP請求時使用的數據協定:
from pydantic import BaseModel
class PenguinSample(BaseModel):
island: str
culmenLength: float
culmenDepth: float
flipperLength: float
bodyMass: float
sex: str
species: str
def __getitem__(self, item):
return getattr(self, item)
3.2 數據加載器
此類用于加載和準備數據。這是它的樣子:
import pandas as pd
import numpy as np
from scipy.stats import norm
from sklearn.preprocessing import StandardScaler
from sklearn.model_selection import train_test_split
from penguin_sample import PenguinSample
class DataLoader():
def __init__(self, test_size = 0.2, scale = True):
self.test_size = test_size
self.scale = scale
self.scaler = StandardScaler()
self._island_map = {}
self._sex_map = {}
def load_preprocess(self, path):
data = pd.read_csv(path)
data = self._feature_engineering_pipeline(data)
X = data.drop(['species', "island", "sex"], axis=1)
if(self.scale):
X = self.scaler.fit_transform(X)
y = data['species']
spicies = {'Adelie': 0, 'Chinstrap': 1, 'Gentoo': 2}
y = [spicies[item] for item in y]
y = np.array(y)
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=self.test_size,
random_state=33)
return X_train, X_test, y_train, y_test
def prepare_sample(self, raw_sample: PenguinSample):
island = self._island_map[raw_sample.island]
sex = self._sex_map[raw_sample.sex]
sample = [raw_sample.culmenLength, raw_sample.culmenDepth, raw_sample.flipperLength,
raw_sample.bodyMass, island, sex]
sample = np.array([np.asarray(sample)]).reshape(-1, 1)
if(self.scale):
self.scaler.fit_transform(sample)
return sample.reshape(1, -1)
def _feature_engineering_pipeline(self, data):
data['culmen_length_mm'].fillna((data['culmen_length_mm'].mean()), inplace=True)
data['culmen_depth_mm'].fillna((data['culmen_depth_mm'].mean()), inplace=True)
data['flipper_length_mm'].fillna((data['flipper_length_mm'].mean()), inplace=True)
data['body_mass_g'].fillna((data['body_mass_g'].mean()), inplace=True)
data["species"] = data["species"].astype('category')
data["island"] = data["island"].astype('category')
data["sex"] = data["sex"].astype('category')
data["island_cat"] = data["island"].cat.codes
data["sex_cat"] = data["sex"].cat.codes
self._island_map = dict(zip(data['island'], data['island'].cat.codes))
self._sex_map = dict(zip(data['sex'], data['sex'].cat.codes))
return data
作為構造函數中的參數,它接收測試數據集大小和指示數據是否應縮放的標志。此類中有兩個公共方法和一個私有方法:
- _feature_engineering_pipeline – 此方法在現有集合上進行小型特征工程。也就是說,填充缺失的數據并對分類數據進行編碼。如果您想了解有關功能工程的更多信息,請查看此博客文章。
- load_preprocess – 此方法執行繁重的工作,它從定義的路徑加載數據,并將數據拆分為訓練和測試數據集。
- prepare_sample – 當新樣本進入我們的系統時,我們需要以與處理訓練數據相同的方式處理它。這就是為什么我們在進行進一步預測之前使用prepare_sample方法的原因。
3.3 模型訓練器
此組件負責訓練模型。通過構造函數,它接收有關用戶選擇的算法的信息,并從那里開始處理它。這是它的樣子:
from data_loader import DataLoader
from sklearn.svm import SVC
from sklearn.linear_model import LogisticRegression
from sklearn.tree import DecisionTreeClassifier
from sklearn.ensemble import RandomForestClassifier
from sklearn.metrics import accuracy_score
from penguin_sample import PenguinSample
class ModelTrainer():
def __init__(self, algoritm, test_size=0.2):
if(algoritm == 'svm'):
self.data_loader = DataLoader()
self.model = SVC(kernel="rbf", gamma=0.1, C=500, verbose=True)
if(algoritm == 'logistic regression'):
self.data_loader = DataLoader()
self.model = LogisticRegression(C=1e20, verbose=True)
if(algoritm == 'decision tree'):
self.data_loader = DataLoader(scale = False)
self.model = DecisionTreeClassifier(max_depth=5)
if(algoritm == 'random forest'):
self.data_loader = DataLoader(scale = False)
self.model = RandomForestClassifier(n_estimators=11, max_leaf_nodes=16, n_jobs=-1,
verbose=True)
def train(self, path):
X_train, X_test, y_train, y_test = self.data_loader.load_preprocess(path)
self.model.fit(X_train, y_train)
predictions = self.model.predict(X_test)
return accuracy_score(predictions, y_test)
def predict(self, data: PenguinSample):
prepared_sample = self.data_loader.prepare_sample(data)
return self.model.predict(prepared_sample)
在構造函數中,實例化了正確的模型。如您所見,使用了SciKit Learn中的類。除此之外,還創建了DataLoader的一個實例。在訓練方法中,檢索數據并訓練模型。完成此操作后,將計算準確性分數,并將該值返回給調用方。另一方面,預測方法接收新示例,并使用數據加載程序實例將其調整到模型。然后調用 predict 方法,并將結果返回給調用方。
3.4 REST API 模塊
最重要的文件是 main.py 文件。此文件將所有其他部分放在一起,并使用 FastAPI 構建 REST API。這是看起來的樣子:
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
from model_trainer import ModelTrainer
from train_parameters import TrainParameters
from penguin_sample import PenguinSample
origins = [
"http://localhost:8000",
"http://localhost:4200"
]
app = FastAPI()
app.add_middleware(
CORSMiddleware,
allow_origins=origins,
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
app.model = ModelTrainer('svm')
@app.post("/train")
async def train(params: TrainParameters):
print("Model Training Started")
app.model = ModelTrainer(params.model.lower(), params.testsize)
accuracy = app.model.train(params.path)
return accuracy
@app.post("/predict")
async def predict(data:PenguinSample):
print("Predicting")
spicies_map = {0: 'Adelie', 1: 'Chinstrap', 2: 'Gentoo'}
species = app.model.predict(data)
return spicies_map[species[0]]
首先,我們導入所有必要的庫。由于我們的服務器在 http://localhost:8000 和客戶端中以 http://localhost:4200 運行,因此我們需要處理 CORS 策略。這就是我們導入 CORSMiddleware 的原因。此類在應用初始化期間使用:
app = FastAPI()
app.add_middleware(
CORSMiddleware,
allow_origins=origins,
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
app.model = ModelTrainer('svm')
請注意,我們在那里添加了一個 ModelTrainer 的實例。此實例只是一個占位符,稍后將替換。有兩個終結點"/trian"和"/predict"都接受POST HTTP請求。這樣,我們有兩個函數來處理這些請求。訓練方法根據從客戶端收到的參數創建 ModelTrainer 的新實例。然后,它運行模型的訓練并返回模型的準確性:
@app.post("/train")
async def train(params: TrainParameters):
print("Model Training Started")
app.model = ModelTrainer(params.model.lower(), params.testsize)
accuracy = app.model.train(params.path)
return accuracy
預測方法是從 Web 應用程序的預測選項卡接收數據。它從 ModelTrainer 調用 predict 方法并返回預測值:
@app.post("/predict")
async def predict(data:PenguinSample):
print("Predicting")
spicies_map = {0: 'Adelie', 1: 'Chinstrap', 2: 'Gentoo'}
species = app.model.predict(data)
return spicies_map[species[0]]
3.5 測試服務器端
要運行此解決方案,請打開終端,然后轉到train_solutionserver 文件夾。運行以下命令:
uvicorn main:app --reload
INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
INFO: Started reloader process [28720]
INFO: Started server process [28722]
INFO: Waiting for application startup.
INFO: Application startup complete.
應用程序運行后,轉到瀏覽器中的localhost:8000 / docs。您應該看到如下內容:
首先,讓我們測試一下訓練終結點。展開它,然后單擊"試用"按鈕:
作為請求正文傳遞此 json 對象:
{
"model": "svm",
"path": "./data/penguins_size.csv",
"testsize": 0.2
}
然后點擊 執行 按鈕:
以下是我們得到的回應:
以類似的方式,我們可以測試預測端點。試試吧!
3.6 一起運行
要運行服務器端,您需要轉到train_solutionserver文件夾并使用以下命令:
uvicorn main:app --reload
INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
INFO: Started reloader process [28720]
INFO: Started server process [28722]
INFO: Waiting for application startup.
INFO: Application startup complete.
在另一個終端中,您需要轉到train_solutionclient文件夾并運行:
npm install
ng serve
√ Browser application bundle generation complete.
Initial Chunk Files | Names | Size
vendor.js | vendor | 3.02 MB
polyfills.js | polyfills | 481.27 kB
styles.css, styles.js | styles | 340.83 kB
main.js | main | 93.72 kB
runtime.js | runtime | 6.15 kB
| Initial Total | 3.92 MB
Build at: 2020-11-22T10:33:00.423Z - Hash: bf345d81dfd56983facb - Time: 10867ms
** Angular Live Development Server is listening on localhost:4200, open your browser on http://localhost:4200/ **
√ Compiled successfully.
完成此操作后,您可以轉到localhost:4200并測試應用程序:
4. 模型加載解決方案
現在,以前的解決方案真的很漂亮。我真的很喜歡你可以選擇不同的模型并玩轉參數。然而,即使它是底層的,它也不是一個現實世界的例子,它更像是一個虛榮的項目。在實際解決方案中,一個組件負責收集數據,另一個組件負責處理該數據,第三個組件負責定期訓練模型并將其存儲在某個位置。然后,REST API 會利用這些存儲的模型。現在,所有這些對于這個簡單的教程來說都太多了,但是,我仍然需要給出一些更接近現實世界的問題和解決方案的東西。因此,服務器端的體系結構更改為:
這些更改也會影響 UI。火車選項卡變成了"加載"選項卡,它更簡單一些。沒有測試大小定義,也沒有數據路徑定義。但是,用戶仍然可以選擇要使用的模型。該解決方案位于load_solution/客戶端路徑中,如下所示:
服務器端位于load_solution/服務器文件夾中,它的變化更多一些。讓我們來看看每個組件。
4.1 訓練和保存模型
您可能已經注意到解決方案文件夾中的新文件夾"models"。在此文件夾中,您可以找到以下文件:
這些是已經訓練過的模型。實際上,有一個腳本train_models_script.py,如果要再次重新訓練以下模型,則可以運行該腳本。它利用了先前實現中的零碎部分,但有一個主要區別。模型不存儲在內存中,而是存儲在硬設備中。腳本如下:
from sklearn.ensemble import RandomForestClassifier
from joblib import dump
def load_data(scale = True):
data = pd.read_csv('./data/penguins_size.csv')
data['culmen_length_mm'].fillna((data['culmen_length_mm'].mean()), inplace=True)
data['culmen_depth_mm'].fillna((data['culmen_depth_mm'].mean()), inplace=True)
data['flipper_length_mm'].fillna((data['flipper_length_mm'].mean()), inplace=True)
data['body_mass_g'].fillna((data['body_mass_g'].mean()), inplace=True)
data["species"] = data["species"].astype('category')
data["island"] = data["island"].astype('category')
data["sex"] = data["sex"].astype('category')
data["island_cat"] = data["island"].cat.codes
data["sex_cat"] = data["sex"].cat.codes
X = data.drop(['species', "island", "sex"], axis=1)
if(scale):
scaler = StandardScaler()
X = scaler.fit_transform(X)
y = data['species']
spicies = {'Adelie': 0, 'Chinstrap': 1, 'Gentoo': 2}
y = [spicies[item] for item in y]
y = np.array(y)
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=33)
return X_train, X_test, y_train, y_test
# Train SVM
X_train, X_test, y_train, y_test = load_data(scale=True)
model = SVC(kernel="rbf", gamma=0.1, C=500, verbose=True)
model.fit(X_train, y_train)
dump(model, './models/svm.joblib')
# Train Decision Tree
X_train, X_test, y_train, y_test = load_data(scale=False)
model = DecisionTreeClassifier(max_depth=5)
model.fit(X_train, y_train)
dump(model, './models/decision_tree.joblib')
# Train Random Forest
X_train, X_test, y_train, y_test = load_data(scale=False)
model = RandomForestClassifier(n_estimators=11, max_leaf_nodes=16, n_jobs=-1, verbose=True)
model.fit(X_train, y_train)
dump(model, './models/random_forest.joblib')
# Train Logistic Regression
X_train, X_test, y_train, y_test = load_data(scale=True)
model = LogisticRegression(C=1e20, verbose=True)
model.fit(X_train, y_train)
dump(model, './models/logistic_regression.joblib')
load_data函數加載數據并執行所有必要的預處理,就像 DataLoader 在上一個解決方案中所做的那樣。然后,我們使用 joblib 的轉儲函數訓練模型并將其保存在文件中。您可以按如下方式運行此腳本:
python train_models_script.py
4.2 模型加載器
此組件看起來類似于先前實現中的 ModelTrainer,但它更簡單。這是它的樣子:
from sklearn.svm import SVC
from sklearn.linear_model import LogisticRegression
from sklearn.tree import DecisionTreeClassifier
from sklearn.ensemble import RandomForestClassifier
from sklearn.metrics import accuracy_score
from penguin_sample import PenguinSample
from sklearn.preprocessing import StandardScaler
from joblib import load
import numpy as np
class ModelLoader():
def __init__(self, algoritm):
self.scaledData = True
self._island_map = {'Torgersen': 2, 'Biscoe': 0, 'Dream': 1}
self._sex_map = {'MALE': 2, 'FEMALE': 1}
if(algoritm == 'svm'):
self.model = load('./models/svm.joblib')
if(algoritm == 'logistic regression'):
self.model = load('./models/decision_tree.joblib')
if(algoritm == 'decision tree'):
self.scaledData = False
self.model = load('./models/random_forest.joblib')
if(algoritm == 'random forest'):
self.scaledData = False
self.model = load('./models/logistic_regression.joblib')
self.scaler = StandardScaler()
def prepare_sample(self, raw_sample: PenguinSample):
island = self._island_map[raw_sample.island]
sex = self._sex_map[raw_sample.sex]
sample = [raw_sample.culmenLength, raw_sample.culmenDepth, raw_sample.flipperLength,
raw_sample.bodyMass, island, sex]
sample = np.array([np.asarray(sample)]).reshape(-1, 1)
if(self.scaledData):
self.scaler.fit_transform(sample)
return sample.reshape(1, -1)
def predict(self, data: PenguinSample):
prepared_sample = self.prepare_sample(data)
return self.model.predict(prepared_sample)
同樣,根據我們從客戶端接收的參數,我們使用 joblib 的 load 函數加載正確的模型。這兩種方法用于執行預測。prepare_sample準備一個新樣本進行模型處理,而預測方法則在模型中運行樣本并檢索預測。
4.3 REST API 模塊
此模塊與之前的實現幾乎相同:
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
from model_loader import ModelLoader
from train_parameters import TrainParameters
from penguin_sample import PenguinSample
origins = [
"http://localhost:8000",
"http://127.0.0.1:8000/predict",
"http://127.0.0.1:8000/load",
"http://localhost:4200"
]
app = FastAPI()
app.add_middleware(
CORSMiddleware,
allow_origins=origins,
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
app.model = ModelLoader('svm')
@app.post("/train")
async def train(params: TrainParameters):
print("Model Loading Started")
app.model = ModelLoader(params.model.lower())
return True
@app.post("/predict")
async def predict(data:PenguinSample):
print("Predicting")
spicies_map = {0: 'Adelie', 1: 'Chinstrap', 2: 'Gentoo'}
species = app.model.predict(data)
return spicies_map[species[0]]
主要區別在于使用了 ModelLoader 類,并且沒有對模型進行任何訓練。
要運行服務器端,您需要轉到train_solutionserver文件夾并使用以下命令:
4.4 一起運行
要運行服務器端,您需要轉到load_solutionserver文件夾并使用以下命令:
uvicorn main:app --reload
INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
INFO: Started reloader process [28720]
INFO: Started server process [28722]
INFO: Waiting for application startup.
INFO: Application startup complete.
在另一個終端中,您需要轉到load_solutionclient文件夾并運行:
npm install
ng serve
√ Browser application bundle generation complete.
Initial Chunk Files | Names | Size
vendor.js | vendor | 3.02 MB
polyfills.js | polyfills | 481.27 kB
styles.css, styles.js | styles | 340.83 kB
main.js | main | 93.72 kB
runtime.js | runtime | 6.15 kB
| Initial Total | 3.92 MB
Build at: 2020-11-22T10:33:00.423Z - Hash: bf345d81dfd56983facb - Time: 10867ms
** Angular Live Development Server is listening on localhost:4200, open your browser on http://localhost:4200/ **
√ Compiled successfully.
完成此操作后,您可以轉到localhost:4200并測試應用程序:
結論
在本文中,我們能夠看到如何使用 FastAPI 和一些 JavaScript 框架(在此特定情況下為 Angular)部署機器學習算法。我們看到了什么是REST API以及如何使用FastAPI構建它。最后,我們在用戶界面的幫助下利用了這個API,完成了整個解決方案。如果你想更進一步,你可能想把它放到Docker實例中,并使用Kubernetes。
感謝您的閱讀!
原文標題:Deploying machine Learning Models with FastAPI and Angular
作者:AI, Angular, Machine Learning, Python
原文:
https://rubikscode.net/2020/11/23/deploying-machine-learning-models-with-fastapi-and-angular/
編譯:LCR