在機器學習專案中,將訓練好的 PyTorch 模型佈署到生產環境是一個關鍵環節。Kubernetes 提供了一個理想的平台來管理和擴充套件模型服務。本文將逐步講解如何利用 Kubernetes 佈署 PyTorch 模型,涵蓋從叢集建立到服務擴充套件的完整流程,並探討 TorchScript 的應用及其在 C++ 環境中的整合。此方法能有效提升模型佈署效率,確保服務穩定性,並簡化模型更新流程。
透過 Kubernetes 佈署 PyTorch 模型,可以有效地管理和擴充套件模型服務。首先,我們需要建立 Kubernetes 叢集,並使用 kubectl 佈署應用程式。佈署過程中,模型引數檔案的位置會作為環境變數傳遞給容器。接著,我們需要建立一個 LoadBalancer 型別的 Service,以便從外部存取應用程式。透過 kubectl get service 命令可以檢視 Service 的外部 IP,即可透過該 IP 使用模型服務。此外,我們可以透過 kubectl logs 命令檢視 Pod 的日誌,並使用 kubectl scale 命令擴充套件服務以應付更多請求。當需要更新服務程式碼時,可以建立新的容器映像並更新 Deployment。最後,完成實驗後,記得清理資源以避免額外費用。
在Kubernetes上佈署PyTorch模型
建立Kubernetes叢集
首先,我們需要建立一個Kubernetes叢集。以下命令將建立一個包含兩個n1-standard-1節點的叢集,這是Google提供的最便宜和最低組態的例項。
gcloud container clusters create ml-cluster --num-nodes=2
這個過程可能需要幾分鐘來完全初始化新的叢集。一旦叢集準備就緒,我們就可以使用kubectl來佈署我們的應用程式。
佈署應用程式
kubectl run catfish-service \
--image=gcr.io/ml-k8s/catfish-service:v1 \
--port 5000 \
--env CATFISH_MODEL_LOCATION=[URL]
這裡,我們將模型引數檔案的位置作為環境變數傳遞給容器,就像我們在本地機器上使用docker run命令一樣。
內容解密:
kubectl run:用於在Kubernetes叢集中佈署一個新的應用程式。--image:指定要使用的Docker映像。--port:指定容器要暴露的埠。--env:設定容器的環境變數。
檢視執行中的Pod
使用kubectl get pods命令可以檢視叢集中正在執行的Pod。
kubectl get pods
輸出結果應該類別似於:
NAME READY STATUS RESTARTS AGE
catfish-service-<pod_id> 1/1 Running 0 4m15s
內容解密:
kubectl get pods:列出叢集中所有正在執行的Pod。NAME:Pod的名稱。READY:Pod中容器的就緒狀態。STATUS:Pod的狀態。RESTARTS:容器重啟的次數。AGE:Pod執行的時間。
暴露服務
為了能夠從外部存取我們的應用程式,需要建立一個Service。在這個例子中,我們將建立一個LoadBalancer型別的Service。
kubectl expose deployment catfish-service \
--type=LoadBalancer \
--port 80 \
--target-port 5000
內容解密:
kubectl expose:用於暴露一個Deployment或Pod為Service。--type=LoadBalancer:指定Service的型別為LoadBalancer。--port:指定Service要暴露的埠。--target-port:指定容器內部要對映到的埠。
檢視Service
使用kubectl get service命令可以檢視叢集中的Service及其外部IP。
kubectl get service
輸出結果應該類別似於:
NAME CLUSTER-IP EXTERNAL-IP PORT(S) AGE
catfish-service 10.3.251.122 203.0.113.0 80:30877/TCP 3d
內容解密:
kubectl get service:列出叢集中所有的Service。CLUSTER-IP:Service的叢集內部IP。EXTERNAL-IP:Service的外部IP,用於從叢集外部存取。PORT(S):Service暴露的埠。
現在,您應該能夠透過存取http://external-ip/predict來使用我們的模型服務,就像在本地機器上一樣。
日誌檢視
我們也可以在不登入Pod的情況下檢視其日誌:
kubectl logs catfish-service-<pod_id>
內容解密:
kubectl logs:用於檢視Pod的日誌。<pod_id>:Pod的ID,可以從kubectl get pods的輸出中獲得。
擴充套件服務
如果我們決定一個Pod不足以處理所有的請求,可以輕鬆地擴充套件我們的服務。例如,要執行三個副本:
kubectl scale deployment catfish-service --replicas=3
內容解密:
kubectl scale:用於擴充套件或縮減Deployment的副本數量。--replicas=3:指定要執行的副本數量。
更新和清理
當需要更新我們的服務程式碼時,我們可以建立一個新的容器映像並更新Deployment:
docker build -t gcr.io/ml-k8s/catfish-service:v2 .
docker push gcr.io/ml-k8s/catfish-service:v2
kubectl set image deployment/catfish-service catfish-service=gcr.io/ml-k8s/catfish-service:v2
內容解密:
docker build和docker push:用於建立和推播新的Docker映像。kubectl set image:用於更新Deployment使用的映像。
完成實驗後,記得清理資源以避免額外的費用:
kubectl delete service catfish-service
gcloud container clusters delete ml-cluster
內容解密:
kubectl delete service:刪除指定的Service。gcloud container clusters delete:刪除指定的Kubernetes叢集。
TorchScript介紹
PyTorch 1.0引入了TorchScript,它允許我們將PyTorch模型轉換為一個圖形表示,這樣可以進行最佳化和在C++等其他語言中使用。
Tracing示例
我們可以使用tracing將一個現有的PyTorch模組或函式轉換為TorchScript模組。例如,使用AlexNet模型:
import torch
import torchvision
model = torchvision.models.alexnet(pretrained=True)
example_input = torch.rand(1, 3, 224, 224)
traced_model = torch.jit.trace(model, example_input)
內容解密:
torch.jit.trace:用於透過跟蹤模型的前向傳遞來生成TorchScript模組。example_input:用於跟蹤模型的輸入範例。
TorchScript 的應用與限制
在 PyTorch 中,將模型轉換為 TorchScript 是生產環境佈署的重要步驟。TorchScript 提供了一種將 PyTorch 模型序列化的方法,使其能夠在沒有 Python 執行環境的情況下執行。然而,在使用 TorchScript 的過程中,我們可能會遇到一些限制和挑戰。
Tracing 的工作原理與限制
當我們使用 torch.jit.trace 將 PyTorch 模型轉換為 TorchScript 時,PyTorch 會記錄模型在給定輸入下的執行過程。這種方法對於大多數僅包含靜態計算圖的模型是有效的。然而,當模型包含動態行為(如 Dropout 層)時,Tracing 就會遇到問題。
程式碼範例:Tracing AlexNet
traced_model = torch.jit.trace(model, example_inputs)
內容解密:
torch.jit.trace函式用於將 PyTorch 模型轉換為 TorchScript。model是待轉換的 PyTorch 模型。example_inputs是用於 Tracing 的範例輸入。- Tracing 的結果是一個
TracedModule物件,可以像原始模型一樣呼叫。
處理非確定性行為
在 Tracing 過程中,如果模型包含非確定性行為(如 Dropout),PyTorch 會發出警告。這是因為 Tracing 無法捕捉模型的隨機行為。為瞭解決這個問題,我們可以將模型切換到評估模式(evaluation mode)。
程式碼範例:切換到評估模式
model.eval()
traced_model = torch.jit.trace(model, example_inputs)
內容解密:
model.eval()將模型切換到評估模式,關閉 Dropout 等訓練相關的層。- 在評估模式下重新進行 Tracing,可以避免非確定性行為帶來的問題。
Scripting 的應用
除了 Tracing,PyTorch 還提供了 Scripting 的方法來將 Python 程式碼轉換為 TorchScript。Scripting 允許我們直接編寫 TorchScript 程式碼,或將現有的 PyTorch 程式碼轉換為 TorchScript。
程式碼範例:使用 Scripting 定義模型元件
class FeaturesCNNNet(torch.jit.ScriptModule):
def __init__(self, num_classes=2):
super(FeaturesCNNNet, self).__init__()
self.features = torch.jit.trace(nn.Sequential(
nn.Conv2d(3, 64, kernel_size=11, stride=4, padding=2),
# 其他層...
))
內容解密:
torch.jit.ScriptModule是用於定義 TorchScript 模型的基礎類別。- 在
__init__方法中,我們可以使用torch.jit.trace將子模組轉換為 TorchScript。 - 這種混合使用 Tracing 和 Scripting 的方法,可以靈活地將現有的 PyTorch 模型轉換為 TorchScript。
PyTorch 在生產環境中的 TorchScript 應用
使用 TorchScript 最佳化 PyTorch 模型
TorchScript 是 PyTorch 中的一個重要元件,用於將 Python 模型轉換為可序列化和最佳化的形式。以下範例展示如何使用 TorchScript 將 AlexNet 的特徵提取部分轉換為 TorchScript 格式:
import torch
import torch.nn as nn
class AlexNetFeatures(nn.Module):
def __init__(self):
super(AlexNetFeatures, self).__init__()
self.features = nn.Sequential(
nn.Conv2d(3, 64, kernel_size=11, stride=4, padding=2),
nn.ReLU(),
nn.MaxPool2d(kernel_size=3, stride=2),
nn.Conv2d(64, 192, kernel_size=5, padding=2),
nn.ReLU(),
nn.MaxPool2d(kernel_size=3, stride=2),
nn.Conv2d(192, 384, kernel_size=3, padding=1),
nn.ReLU(),
nn.Conv2d(384, 256, kernel_size=3, padding=1),
nn.ReLU(),
nn.Conv2d(256, 256, kernel_size=3, padding=1),
nn.ReLU(),
nn.MaxPool2d(kernel_size=3, stride=2)
)
@torch.jit.script_method
def forward(self, x):
x = self.features(x)
return x
# 初始化模型並進行 tracing
model = AlexNetFeatures()
example_input = torch.rand(1, 3, 224, 224)
traced_model = torch.jit.trace(model, example_input)
內容解密:
- 模型定義:定義了一個名為
AlexNetFeatures的 PyTorch 模型類別,繼承自nn.Module。該模型類別包含了一個名為features的nn.Sequential層,用於定義 AlexNet 的特徵提取部分。 - TorchScript 方法標註:使用
@torch.jit.script_method標註forward方法,使其符合 TorchScript 的規範。 - 模型 tracing:使用
torch.jit.trace對模型進行 tracing,將模型的執行過程記錄下來,生成一個 TorchScript 模型。
TorchScript 的限制
TorchScript 相較於 Python,有一些限制,主要包括:
- 型別系統:TorchScript 的型別系統比 Python 更為嚴格。例如,不允許函式傳回不同型別的變數。
- 靜態型別:TorchScript 是靜態型別的,這意味著所有變數的型別在編譯時就已經確定。
TorchScript 支援的 Python 型別
| 型別 | 描述 |
|---|---|
| tensor | PyTorch 張量,支援任意 dtype、維度和後端 |
| tuple[T0, T1,…] | 元組,包含子型別 T0、T1 等 |
| boolean | 布林值 |
| str | 字串 |
| int | 整數 |
| float | 浮點數 |
| list[T] | 清單,包含型別 T 的元素 |
| optional[T] | 可選值,要麼是 None,要麼是型別 T |
| dict[K, V] | 字典,鍵的型別為 K,值的型別為 V;K 只能是 str、int 或 float |
在 C++ 中使用 libTorch
libTorch 是 PyTorch 的 C++ 函式庫,用於與 PyTorch 進行互動。以下是使用 libTorch 的基本步驟:
安裝必要的工具:安裝 C++ 編譯器和 CMake。
- 在 Debian-based 系統上,使用
apt install cmake g++。 - 在 Red Hat-based 系統上,使用
yum install cmake g++。
- 在 Debian-based 系統上,使用
下載 libTorch:下載 CPU 版本的 libTorch 以避免 CUDA 相依性問題。
wget https://download.pytorch.org/libtorch/cpu/libtorch-shared-with-deps-latest.zip
使用 libTorch 的注意事項
- C++ 前端:libTorch 提供了一個 C++ 前端,複製了 PyTorch 的 Python API。
- TorchScript 介面:libTorch 提供了與 TorchScript 的介面,用於載入和執行 TorchScript 模型。