在機器學習領域,大多數關注點往往集中在演算法開發上,但模型最終目的並非僅止於建立,而是要投入實際生產環境中發揮價值。當我們談論將模型「推向生產環境」時,通常指的是執行推論(inference)流程。一個完整的推論解決方案應該包含三個核心功能:模型服務(serving)、監控(monitoring)和更新(updating)。
讓我們先來看一個實際案例,瞭解如何透過Anchor技術解釋模型預測結果,然後深入討論模型推論的各個核心元件。
預測結果解釋例項
當模型做出預測後,瞭解預測背後的原因對於建立可信任的AI系統至關重要。以下是一個使用Anchor技術解釋預測結果的例項:
# 預測結果
prediction = "<=50K"
# 設定精確度閾值為0.95
# 這表示在錨點條件成立的觀測值上,預測結果至少有95%的時間與解釋例項的預測相同
explanation = explainer.explain(X_test[idx], threshold=0.95)
print('Anchor: %s' % (' AND '.join(explanation.anchor)))
print('Precision: %.2f' % explanation.precision)
print('Coverage: %.2f' % explanation.coverage)
這段程式碼使用Anchor直譯器來解釋模型的特定預測結果。我們設定了0.95的精確度閾值,意味著我們希望找到一組特徵條件(錨點),當這些條件滿足時,至少有95%的情況下模型會做出相同的預測。explanation.anchor
是找到的錨點條件,precision
表示這些條件的準確率,而coverage
則表示這些條件覆寫了多少比例的資料點。
執行結果顯示:
Anchor: Marital Status = Separated AND Sex = Female
Precision: 0.95
Coverage: 0.18
這個結果告訴我們,對於這個預測案例,模型主要根據婚姻狀態(分居)和性別(女性)這兩個因素做出決策。這些錨點條件具有95%的精確度,但只覆寫了18%的資料點。
讓我們嘗試解釋另一個預測結果為">50K"的觀測值:
idx = 6
class_names = adult.target_names
print('Prediction: ', class_names[explainer.predictor(X_test[idx].reshape(1, -1))[0]])
explanation = explainer.explain(X_test[idx], threshold=0.95)
print('Anchor: %s' % (' AND '.join(explanation.anchor)))
print('Precision: %.2f' % explanation.precision)
print('Coverage: %.2f' % explanation.coverage)
執行結果:
Prediction: >50K
Could not find a result satisfying the 0.95 precision constraint.
Now returning the best non-eligible result.
Anchor: Capital Loss > 0.00 AND Relationship = Husband AND
Marital Status = Married AND Age > 37.00 AND
Race = White AND Country = United-States AND Sex = Male
Precision: 0.71
Coverage: 0.05
在這個案例中,直譯器無法找到滿足95%精確度要求的錨點條件,因此回傳了最佳的非合格結果。這主要是由於資料集不平衡(高收入:低收入比例約為25:75),導致在取樣階段,對應於低收入者的特徵範圍被過度取樣。這種情況實際上是一個特性,因為它可以指出資料集的不平衡問題,但也可以透過產生平衡的資料集來修復,使得兩個類別都能找到合適的錨點。
模型匯出與佈署
為了將建立的模型用於服務,我們需要匯出模型。這可以使用Scikit-learn的功能來完成:
from joblib import dump
dump(clf, '/tmp/job/income.joblib')
這行程式碼將訓練好的模型匯出為Scikit-learn格式,儲存在指定路徑下。這個匯出的模型可以被Scikit-learn伺服器等工具用於推論。這是將模型從開發環境轉移到生產環境的關鍵步驟。
模型服務的核心概念
模型推論的第一步是模型服務(Model Serving),即將模型託管在一個可以介接的服務後面。模型服務有兩種基本方法:
- 嵌入式模型(Embedded):模型直接佈署到應用程式中
- 模型即服務(MaaS, Model as a Service):一個專用於模型服務的獨立服務,可被企業內的任何應用程式使用
這兩種方法各有優缺點,讓我們來比較一下:
嵌入式模型
優點:
- 提供最佳效能
- 擁有最簡單的基礎架構
- 無需規劃異常使用者行為
缺點:
- 模型必須佈署在每個使用它的應用程式中
- 當模型型別變更時需要更新應用程式
- 所有佈署策略(如藍綠佈署)必須明確實作
模型即服務(MaaS)
優點:
- 簡化與其他技術和組織流程的整合
- 在多個流處理應用程式中重用模型佈署
- 允許在低功耗裝置(如手機)上提供模型服務
- 支援來自多個客戶端的請求的小批次處理
- 更容易提供內建功能,包括模型更新、可解釋性、漂移檢測等
- 啟用需要與應用程式解耦的進階模型佈署策略(如整合和多臂老虎機)
- 允許應用程式和模型伺服器之間的獨立擴充套件,或在不同裝置(如CPU和GPU)上執行
缺點:
- 額外的網路跳躍降低效能
- 與模型伺服器的緊密時間耦合可能影響整體服務級別協定
Kubeflow僅支援MaaS方法,因此在本文中我不會討論嵌入式模型。
實作MaaS有兩種主要方法:
- 模型即程式碼(Model as Code):直接在服務實作中使用模型程式碼
- 模型即資料(Model as Data):使用由中間模型格式(如PMML、PFA、ONNX或TensorFlow原生格式)驅動的通用實作
Kubeflow中的不同模型伺服器實作都使用這兩種方法。在決定使用哪種實作時,我建議使用模型即資料方法,因為它允許模型在服務例項之間的交換標準化,從而提供跨系統的可移植性並啟用通用模型服務功能。
模型服務系統的關鍵需求
在選擇模型服務解決方案時,需要考慮多個關鍵需求。這些需求可以分為功能性需求和非功能性需求兩大類別。
功能性需求
功能性需求定義了系統應該做什麼,包括:
- 支援多種模型格式:能夠服務使用不同框架(TensorFlow、PyTorch、Scikit-learn等)訓練的模型
- 推論API:提供標準化的推論API,支援單一預測和批次預測
- 模型管理:支援模型版本控制、A/B測試和金絲雀佈署
- 模型組合:能夠組合多個模型以建立更複雜的推論管道
- 可解釋性:提供解釋模型預測的機制
非功能性需求
非功能性需求定義了系統的品質特性,包括:
- 效能:低延遲和高吞吐量,尤其是在高流量場景
- 可靠性:系統應該能夠處理錯誤並繼續執行
- 可擴充套件性:能夠處理不斷增長的請求量
- 安全性:保護模型和資料免受未授權存取
- 可觀察性:提供監控和日誌記錄功能,以便了解系統狀態
模型監控的重要性
模型佈署到生產環境後,持續監控其效能至關重要。模型監控涉及多個方面:
- 效能監控:追蹤推論請求的延遲和吞吐量
- 準確性監控:檢測模型準確性隨時間的變化
- 資料漂移監控:識別輸入資料分佈的變化
- 概念漂移監控:檢測目標變數與特徵之間關係的變化
- 異常檢測:識別異常的輸入或輸出
有效的模型監控可以幫助及早發現問題,並觸發模型更新或重訓練。
模型更新策略
隨著時間推移,模型效能可能會下降,需要更新。模型更新策略包括:
- 完全重新佈署:用新模型完全替換舊模型
- 藍綠佈署:平行執行新舊模型,然後切換流量
- 金絲雀佈署:將少量流量引導到新模型,逐漸增加
- 影子佈署:將請求同時傳送到舊模型和新模型,但只使用舊模型的回應
選擇適當的更新策略取決於業務需求、風險承受能力和技術基礎設施。
Kubeflow支援的推論解決方案
Kubeflow提供多種推論解決方案,讓我們來看它們的特點:
TensorFlow Serving
TensorFlow Serving是Google開發的高效能模型服務系統,專為TensorFlow模型設計。它提供:
- 高效能服務能力,支援CPU和GPU
- 模型版本控制和熱載入
- gRPC和REST API
- 批次預測支援
KFServing
KFServing是Kubeflow的原生推論解決方案,提供:
- 多框架支援(TensorFlow、PyTorch、Scikit-learn等)
- 自動擴充套件功能,包括擴充套件到零
- 金絲雀佈署和A/B測試
- 預處理和後處理步驟
- 模型解釋
Seldon Core
Seldon Core是一個強大的開放原始碼平台,用於佈署機器學習模型,特點包括:
- 支援多種ML框架
- 複雜推論圖支援
- A/B測試和多臂老虎機
- 自定義佈署
- 與監控工具的整合
NVIDIA Triton Inference Server
專為高效能推論設計,特別是在GPU上,Triton提供:
- 多框架支援
- 動態批處理
- 模型整合
- 平行模型執行
整合模型到管道中
無論使用哪種Python機器學習函式庫,如果Kubeflow沒有相應的運算元,你都可以將程式碼容器化。要將筆記本用作管道階段,可以使用file_output
將結果模型上載到成品追蹤系統,也可以使用永續性儲存區機制。
讓我們以Scikit-learn模型為例,說明如何將其整合到Kubeflow管道中:
from kfp import dsl
from kfp.components import func_to_container_op
@func_to_container_op
def train_and_export_model(data_path: str) -> str:
import pandas as pd
from sklearn.ensemble import RandomForestClassifier
from joblib import dump
import os
# 載入資料
data = pd.read_csv(data_path)
X = data.drop('target', axis=1)
y = data['target']
# 訓練模型
model = RandomForestClassifier(n_estimators=100, random_state=42)
model.fit(X, y)
# 匯出模型
model_path = '/tmp/model.joblib'
dump(model, model_path)
return model_path
@dsl.pipeline(
name='Model Training Pipeline',
description='A pipeline that trains and exports a Scikit-learn model'
)
def model_training_pipeline(data_path: str):
train_task = train_and_export_model(data_path)
# 可以增加更多步驟,如模型評估、佈署等
這段程式碼展示瞭如何將Scikit-learn模型訓練和匯出的過程整合到Kubeflow管道中。我們使用func_to_container_op
將Python函式轉換為容器操作,然後在管道中使用該操作。這種方法允許我們輕鬆地將任何Python機器學習程式碼整合到Kubeflow中,而不需要特定的運算元。
建立完整的模型推論流程
一個完整的模型推論流程應該包括以下步驟:
- 模型匯出:將訓練好的模型匯出為標準格式
- 模型佈署:將模型佈署到服務平台
- 推論API設定:設定標準化的API以接收預測請求
- 監控設定:設定監控系統以追蹤模型效能
- 更新機制:建立模型更新的流程和策略
以下是一個使用KFServing佈署TensorFlow模型的範例:
apiVersion: "serving.kubeflow.org/v1alpha2"
kind: "InferenceService"
metadata:
name: "income-predictor"
spec:
default:
predictor:
tensorflow:
storageUri: "gs://my-bucket/models/income/1"
這是一個KFServing設定檔案,用於佈署TensorFlow模型。InferenceService
是KFServing的自定義資源,它定義了一個推論服務。storageUri
指定了模型的儲存位置,KFServing會從該位置載入模型。這種宣告式設定使得模型佈署變得簡單與可重複。
模型推論的最佳實踐
根據我在各種專案中的經驗,以下是一些模型推論的最佳實踐:
- 標準化模型格式:使用標準格式(如ONNX、SavedModel)儲存模型,以提高可移植性
- 容器化佈署:將模型和依賴項封裝到容器中,確保一致的執行環境
- 自動擴充套件:設定自動擴充套件以處理流量波動
- 批次處理:實作批次處理以提高吞吐量
- 監控與告警:設定全面的監控和告警系統
- 漸進式佈署:使用金絲雀或藍綠佈署策略減少風險
- 特徵儲存:使用特徵儲存確保訓練和推論之間的一致性
- 模型檔案:記錄模型的假設、限制和使用方式
結語
在本文中,我們探討了機器學習模型推論的核心元件:模型服務、模型監控和模型更新。我們瞭解瞭如何使用Anchor技術解釋模型預測,如何匯出模型以用於服務,以及Kubeflow支援的各種推論解決方案。
機器學習模型的價值不僅在於其準確性,還在於能夠可靠地將其佈署到生產環境中並持續監控其效能。透過選擇適當的推論解決方案並遵循最佳實踐,你可以建立一個強大、可靠與可擴充套件的機器學習系統。
隨著機器學習技術的不斷發展,推論解決方案也在不斷演進。保持對新工具和技術的關注,並根據你的特定需求選擇最適合的解決方案,將幫助你在競爭激烈的AI領域保持領先地位。
模型服務框架的分歧與統一趨勢
在機器學習實踐中,將訓練好的模型佈署上線提供服務是一個關鍵環節,然而目前業介面臨著模型服務框架分歧的挑戰。TFServing、ONNX Runtime、Triton 以及 TorchServe 等主流服務實作方案都採用「模型即資料」的方法,並利用中間模型格式進行處理。
當前模型服務框架的碎片化問題
在實作機器學習系統時,我發現模型服務框架的碎片化已經成為一個顯著問題。各大框架間存在明顯差異:
- 框架支援限制:某些實作僅支援單一框架,而其他則支援多種框架
- 格式與API不一致:每個解決方案使用不同的模型格式並暴露獨特的專有服務API
- 使用者經驗割裂:API介面的複雜性和差異導致不同的使用者經驗,無法有效分享功能
- 框架切換困難:由於這些實作背後的介面不同,切換模型框架時會遇到額外阻力
這種情況讓我想起早期網頁開發時各瀏覽器實作標準不一的混亂局面,需要為不同框架維護多套佈署流程,極大增加了維運成本。
業界統一努力的方向
值得注意的是,一些有影響力的業界玩家正在嘗試統一開放原始碼社群的模型伺服器,並減少模型框架間切換的摩擦:
- Seldon:以Seldon Core為基礎,開創圖推理服務
- Bloomberg和IBM:使用Knative等解決方案研究無伺服器模型服務
- Google:進一步強化其TensorFlow模型的服務實作
在我參與的多個企業專案中,發現這些統一化努力確實能顯著降低維護成本,尤其是在需要頻繁切換或比較不同框架效能的場景中。不過,完全統一仍然面臨著巨大挑戰,特別是在處理各框架特有最佳化時。
模型服務的關鍵需求
在設計和選擇模型服務解決方案時,需要全面考慮開發維運(DevOps)管理以及模型的分析、實驗和治理。以下是我認為模型服務解決方案應該滿足的幾個關鍵需求:
框架靈活性
理想的模型服務方案應該與具體實作無關。比如,當構建一個影像分類別推理服務時,無論底層模型是使用PyTorch、Scikit-learn還是TensorFlow訓練的,服務介面都應保持一致,確保API的一致性。
我曾在一個大型零售分析專案中遇到的問題是,團隊內不同成員偏好使用不同框架進行開發,導致佈署時需要維護多套服務系統。後來我們採用了統一的服務層,大幅降低了維護成本,同時也方便了模型A/B測試的進行。
硬體最佳化器利用能力
深度神經網路在評估階段也可從硬體最佳化器中受益。服務框架應支援根據演算法需求比對適合的硬體加速器,如GPU或TPU。
# 在Kubernetes環境中請求GPU資源的佈署範例
apiVersion: apps/v1
kind: Deployment
metadata:
name: model-server
spec:
replicas: 1
template:
spec:
containers:
- name: model-server
image: model-server:latest
resources:
limits:
nvidia.com/gpu: 1 # 請求一個GPU資源
上面的YAML設定展示瞭如何在Kubernetes環境中為模型伺服器請求GPU資源。這是透過在container規格的resources.limits
部分指定nvidia.com/gpu: 1
來實作的。這告訴Kubernetes排程器,該Pod需要分配一個NVIDIA GPU資源。在實際佈署中,這可以確保計算密集型模型(如深度神經網路)能夠獲得硬體加速,大幅提升推理效能。需要注意的是,在使用此設定前,叢集必須正確設定GPU驅動和相關的裝置外掛。
推理圖中的無縫互動
模型伺服器應與推理圖中的其他元件無縫互動。這些元件可能包括特徵轉換器、預測器、直譯器和漂移檢測器等。
在處理複雜的推理流程時,我發現模組化設計至關重要。例如,在一個金融風險評估系統中,我們將特徵處理、模型推理和結果解釋拆分為獨立微服務,不僅提高了系統彈性,還使各元件能獨立最佳化和擴充套件。
靈活的擴充套件選項
服務例項應支援顯式擴充套件和自動擴充套件,無論底層硬體如何。這對於控制推理成本和延遲尤為重要。
GPU自動擴充套件特別複雜,因為它依賴多種因素,包括GPU/CPU利用率指標、工作週期等。我在實施大規模影像處理系統時發現,找到合適的自動擴充套件指標並非顯而易見,需要根據具體工作負載特性進行調整。
多樣化的通訊介面
理想的服務例項應暴露REST請求或gRPC介面。如果有流式輸入需求,可能還需要支援Kafka等流式介面。
# 使用gRPC呼叫模型服務的Python客戶端範例
import grpc
import inference_pb2
import inference_pb2_grpc
# 建立gRPC通道
channel = grpc.insecure_channel('localhost:8500')
stub = inference_pb2_grpc.PredictionServiceStub(channel)
# 準備請求
request = inference_pb2.PredictRequest()
request.model_spec.name = "image_classifier"
request.model_spec.signature_name = "serving_default"
# 設定輸入資料
input_tensor = request.inputs["input"].tensor_content = image_bytes
# 傳送請求並取得回應
response = stub.Predict(request, 10.0) # 10秒超時
predictions = response.outputs["predictions"]
這段Python程式碼演示瞭如何使用gRPC協定呼叫模型服務。首先建立一個gRPC通道連線到伺服器端點,然後建立一個預測服務存根(stub)。接著構建包含模型規格和輸入資料的請求,最後傳送請求並接收預測結果。gRPC相比REST API的優勢在於:(1)使用Protocol Buffers進行序列化,資料傳輸更高效;(2)支援雙向流式通訊;(3)自動生成客戶端程式碼,減少錯誤。這使得gRPC特別適合高吞吐量、低延遲的模型服務場景,尤其是在微服務架構中。
模型監控的重要性與方法
將模型佈署到生產環境後,必須對模型伺服器進行監控。這不僅包括模型服務的洞察,還包括用於任何根據Kubernetes的應用程式的常規監控,如記憶體、CPU、網路等。
模型精確度、漂移和可解釋性
在生成模型服務洞察時,最常見的ML屬性是模型精確度、模型漂移和可解釋性:
- 模型精確度:指訓練資料的驗證精確度
- 模型漂移:當實時資料分佈開始偏離原始訓練資料時,會導致模型漂移
- 模型可解釋性:解釋為何產生特定結果的能力
模型漂移發生在傳送到模型的資料特徵分佈開始與訓練模型時使用的資料顯著不同時,導致模型表現不佳。ML洞察系統實施有效技術來分析和檢測輸入資料可能發生的變化(概念漂移),這些漂移的檢測對生產系統中執行的模型至關重要。
在我參與的一個使用者行為預測系統中,我們在佈署後三個月發現模型準確率顯著下降。透過實施漂移檢測,我們識別出使用者行為模式因季節性因素發生了變化,及時調整了模型,避免了更大的損失。
模型可解釋性的關鍵問題
模型可解釋性是越來越受到關注的模型洞察形式,它能夠回答以下關鍵問題:
- 模型認為資料中的哪些特徵最重要?
- 對於模型的任何單一預測,資料中的每個特徵如何影響該特定預測?
- 特徵之間的哪些互動對模型的預測有最大影響?
# 使用SHAP解釋模型預測的範例程式碼
import shap
import numpy as np
# 載入預訓練模型
model = load_model("my_model.h5")
# 建立直譯器
explainer = shap.KernelExplainer(model.predict, background_data)
# 計算SHAP值
shap_values = explainer.shap_values(test_instance)
# 視覺化特徵影響
shap.force_plot(explainer.expected_value, shap_values, test_instance)
這段程式碼展示瞭如何使用SHAP(SHapley Additive exPlanations)函式庫為機器學習模型提供可解釋性。SHAP是根據博弈論的方法,能夠計算每個特徵對模型預測的貢獻。程式碼首先載入預訓練模型,然後建立一個KernelExplainer,它可以處理任何黑盒模型。接著計算測試例項的SHAP值,最後生成視覺化圖表展示各特徵對預測結果的影響程度和方向。這種解釋對於理解模型決策過程、識別潛在偏見、滿足監管要求以及建立使用者對AI系統的信任都至關重要。在金融、醫療等高風險領域,可解釋性不僅是技術需求,更是法規要求。
應用監控的傳統方面
除了模型洞察外,應用監控傳統上與網路可觀察性(遙測)、日誌聚合以及服務網格相關的指標收集有關。這些工具有助於從實時服務例項捕捉資料,提供足夠的可查詢訊息,用於故障排除和警示,以防出現可達性、利用率或延遲問題。
模型監控需求
監控模型精確度和模型漂移並不容易,幸運的是,這是一個非常活躍的研究領域,有各種開放原始碼解決方案。理想的推理解決方案應該讓你能夠插入提供所需功能的解決方案。
開箱即用的ML洞察
推理服務應該開箱即用地提供ML洞察,並在微服務架構中執行,以簡化漂移檢測和模型解釋解決方案的實驗。這種方式使資料科學家能專注於模型效能改進,而不必擔心基礎設施問題。
全面的可觀察性支援
服務應支援監控、日誌記錄和追蹤功能,並支援Prometheus、Kibana和Zipkin等解決方案,同時也能無縫支援它們的替代方案。
在設計企業級機器學習平台時,我經常採用「可觀察性優先」的策略,確保從一開始就內建完整的監控能力。這不僅方便了問題診斷,也為後續系統最佳化提供了資料支援。
模型更新策略
當你希望更新模型並推出較新版本或回復到先前版本時,你需要佈署並執行此更新版本。然而,當前佈署與新佈署之間的關係可以有多種定義方式。
影子模型與競爭模型
當推理系統引入模型服務例項的多個版本時,可以使用影子模型或競爭模型:
影子模型(Shadow models): 在考慮替換生產中的模型時很有用。你可以將新模型佈署在當前模型旁邊,並傳送相同的生產流量,以收集有關影子模型表現的資料,然後再進行升級。
競爭模型(Competing models): 這是一個稍微複雜的場景,你在生產中嘗試模型的多個版本,透過A/B測試等工具找出哪個更好。
三種主要佈署策略
讓我們討論三種主要的佈署策略:
藍綠佈署(Blue-green deployments)
藍綠佈署透過只有一個實時環境來減少與版本發布相關的停機時間和風險,該環境服務所有生產流量。
# 藍綠佈署的Kubernetes設定範例
apiVersion: v1
kind: Service
metadata:
name: model-service
spec:
selector:
app: model-server
version: blue # 控制流量導向哪個版本
ports:
- port: 80
targetPort: 8080
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: model-server-blue
spec:
replicas: 3
selector:
matchLabels:
app: model-server
version: blue
template:
metadata:
labels:
app: model-server
version: blue
spec:
containers:
- name: model-server
image: model-server:v1
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: model-server-green
spec:
replicas: 3
selector:
matchLabels:
app: model-server
version: green
template:
metadata:
labels:
app: model-server
version: green
spec:
containers:
- name: model-server
image: model-server:v2
這個YAML設定展示了Kubernetes中藍綠佈署的實作方式。它包含三個主要部分:一個服務(Service)和兩個佈署(Deployment)。服務透過標籤選擇器(selector
)控制流量路由,指向標記為version: blue
的Pod。兩個佈署分別代表藍版本(當前生產版本)和綠版本(新版本),它們執行不同版本的模型伺服器映像。藍綠佈署的關鍵在於切換過程:當需要升級時,只需修改服務的選擇器從blue
切換到green
,即可實作零停機切換。這種方式的優勢在於可以快速回復(只需將選擇器改回),並且在切換前可以完全測試新版本。