使用WebAssembly提高模型部署的速度和可移植性
在最近幾個月中,我們已經幫助許多公司在各種環境中部署其AI / ML模型。 我們為醫療行業的模型部署做出了貢獻,在過去的幾個月中,我們已經幫助多家公司將經過訓練的模型轉移到不同類型的IoT設備上。 特別是在IoT設備情況下,要求通常很嚴格:計算周期數和可用內存通常都受到限制。
在本文中,我闡明了如何確保使用標準ML庫(例如PyTorch,Scikit-learn和Tensorflow)訓練的模型可以有效地部署在各種邊緣設備上。 為了使事情變得切實,我們將研究簡單的邏輯回歸模型的訓練和部署。 但是,我們在這里討論的大多數內容都直接轉移到更復雜的模型上。
模型訓練
為了說明模型訓練與部署之間的區別,讓我們首先模擬一些數據。 下面的代碼根據以下簡單模型生成1000個觀測值:圖片發布

- import numpy as np
- np.random.seed(66) # Set seed for replication# Simulate Data Generating Process
- n = 1000 # 1000 observations
- x1 = np.random.uniform(-2,2,n) # x_1 & x_2 between -2 and 2
- x2 = np.random.uniform(-2,2,n)
- p = 1 / (1 + np.exp( -1*(.75 + 1.5*x1 - .5*x2) )) # Implement DGPy = np.random.binomial(1, p, n) # Draw outcomes# Create dataset and print first few lines:
- data = np.column_stack((x1,x2,y))
- print(data[:10])
生成數據后,我們可以專注于擬合模型。 我們只需使用sklearn的LogisticRegression()函數即可:
- from sklearn.linear_model import LogisticRegression
- mod = LogisticRegression().fit(data[:,[0,1]], np.ravel(data[:,[2]]))
仔細看看
在這一點上,梳理并簡要考慮引擎蓋下正在發生的事情非常有用。與許多其他有趣的ML模型一樣,對邏輯回歸模型進行迭代訓練。為了訓練模型,sklearn(或提供類似功能的任何其他軟件包)將必須實現以下幾個功能:
1. 某種評分函數,指示模型的擬合度。這可能是誤差函數或最大似然函數。
2. 該函數可將擬合模型的參數從一次迭代更新到下一次迭代。
訓練過程將有效地重復使用這兩個功能:最初,模型的參數是隨機實例化的。接下來,檢查模型的分數。如果認為分數不夠(通常是因為與以前的迭代相比,分數有所提高),則將更新模型參數并重復該過程。
即使對于這個簡單的模型,sklearn仍需要遍歷數據集。以下代碼給出了迭代次數:
- # Print the number of iterations
- print(f'The number of iterations is: {mod.n_iter_}.'
因此,要訓練模型,我們需要訪問數據,還有幾個工具的函數,并且需要多次迭代/遍歷數據集。 總的來說,該訓練過程對計算的要求很高,這說明了為什么對于復雜的模型,我們求助于并行計算以及GPU或NPU加速,以在合理的時間內執行。 幸運的是,當訓練模型時,所需的相當復雜的邏輯已被我們使用的各種ML庫抽象化了。
生成預測
將其與從已經擬合的模型中生成預測進行比較(通常稱為推理,但由于統計中使用的后者不同,因此我發現這個術語令人困惑,因此我堅持使用預測)。 到模型擬合時,在這種情況下,我們實際上需要生成預測的全部就是邏輯回歸函數(與上面示例中用于生成數據的數學函數相同)以及擬合模型的三個參數。 這些很容易檢索:
- b = np.concatenate((mod.intercept_, mod.coef_.flatten()))
- print(b)
參數最終相對接近我們用于數據生成的值:[0.84576563 1.39541631 -0.47393112]。
此外,在大多數部署情況下,我們通常最終僅使用單個輸入來評估模型:在這種情況下,長度為2的數字向量。 如果我們要部署模型,則不需要擬合函數,不需要數據,也不需要迭代。 要生成預測,我們只需要簡單有效地實現所涉及的數學函數即可。
邊緣設備中部署模型
"所以呢?"你可能會問。當現代模型訓練工具抽象出所有這些細節時,為什么還要關心訓練和預測中涉及的細節呢?好吧,因為當您希望有效地部署模型時(例如,當您需要模型在小型設備上快速運行時),您可以更好地利用設備的差異。
為了便于討論,請對比以下兩種模型部署方法(即,將經過訓練的模型投入生產,以便可以使用其預測):
將sklearn作為REST服務部署在Docker容器上:這種方法很簡單并且經常使用:我們啟動一個包含python和用于訓練的工具的docker鏡像:對于上面的示例邏輯回歸模型sklearn。接下來,我們創建一個REST API服務,該服務使用擬合模型的mod.predict()函數來生成結果。
Scailable WebAssembly部署:除了上述方法以外,還可以將擬合模型轉換為WebAssembly(使用與Scailable提供的服務類似的服務),并部署.WASM二進制文件,其中僅包含在最小的WebAssembly運行時中進行預測所需的邏輯。 自動生成的二進制文件將僅包含必要的邏輯函數和估計的參數。二進制文件可能部署在服務器上因此也類似地通過REST調用使用,但是,它可以兼容可用的運行時,它也幾乎可以在任何邊緣設備上運行。
顯然,第一個部署過程接近數據科學家的"我們所知道的"。直接使用我們慣用的工具是非常方便的,并且在許多方面它都有效:我們可以使用對REST端點的調用來生成預測。第二種解決方案與我們的標準實踐相距甚遠,并且對于模型訓練毫無用處(即,沒有"WebAssembly軟件包來訓練模型……")。但是,我們仍然認為應該首選:第二種設置利用了訓練和預測之間的差異,從而在幾個方面使模型部署更好:
內存占用:上面兩個選項中的第一個選項將需要至少75Mb的容器(要使容器變小需要大量的工程設計,使容器的大小接近1Gb更為常見)。在這種情況下,存儲的模型本身很?。?#12316;2Kb),因此容器占部署內存占用的最大塊(請注意,例如大型神經網絡可能不正確)。相反,WebAssembly運行時可以降至64Kb以下。 WebAssembly二進制本身本身大于存儲的sklearn模型(〜50kb),但是現在它包含生成預測所必需的全部。因此,雖然第一個部署選項至少占用75Mb,但第二個部署選項占用不到0.1Mb。
速度:與高效的WebAssembly部署相比,消耗一個在Docker容器中運行的REST端點并不能在執行時間上取得優勢,因為Docker容器啟動了所有訓練所需的東西。下面是一些針對不同模型的速度比較,但是,不必說,利用訓練和預測之間的差異,并且僅僅將預測的基本需求投入生產,就可以通過一個數量級提高速度,從而生成這些預測。
因此,內存占用更小,執行速度更快。有幾個原因;其中一個原因是,我們可能希望有效地部署模型,而不會在每次做出預測時浪費能源。但是,一個小的內存占用和快速的執行也是很吸引人的,因為這正是我們在將模型投入生產的邊緣所需要的:好運部署你的Docker容器(例如,)在ESP32 MCU板上。使用WebAssembly,這是小菜一碟。
綜上所述,你一定對WebAssembly十分感興趣,那么看看這個代碼吧,它包含了本文的所有內容
https://github.com/scailable/sclbl-tutorials/tree/master/sclbl-train-vs-deploy