在企業級深度學習模型訓練中,如何有效管理計算資源並提升模型訓練效率至關重要。本文介紹的模型訓練服務,旨在解決資源分配、訓練效率、成本控制等問題,並提供一個可實際運作的服務範例。此服務透過 Docker 容器化訓練程式碼,實作了環境隔離和程式碼版本控制,同時透過 gRPC 提供統一 API 介面,簡化了模型訓練任務的提交和追蹤。服務內部採用佇列機制管理訓練任務,並支援分散式訓練,以提升資源利用率和訓練速度。此外,服務也強調模型訓練的可重現性,確保在相同輸入下產出一致的模型結果,提升模型的可靠性和穩定性。

模型訓練服務:設計綜覽

在企業環境中,深度學習模型訓練涉及兩個主要角色:資料科學家和平台工程師。資料科學家開發模型訓練演算法,而平台工程師則負責建立和維護執行模型訓練程式碼的系統,即模型訓練服務。

模型訓練服務的價值主張

模型訓練服務作為訓練基礎設施,在專用環境中執行模型訓練程式碼,處理訓練任務排程和計算資源管理。圖3.1展示了模型訓練服務執行模型訓練程式碼以生成模型的整體工作流程。

為什麼需要模型訓練服務?

許多人認為編寫一個簡單的bash指令碼在本地或遠端(如Amazon EC2例項)執行訓練程式碼就足夠了。然而,建立模型訓練服務的理由遠遠超出了僅僅啟動訓練計算。一個好的訓練服務可以使模型訓練更快、更可靠,並降低平均模型建構成本。當資料集或模型架構龐大時,使用訓練服務來管理分散式計算是唯一的選擇。

設計原則

本章首先探討了訓練服務的價值主張和設計原則,然後介紹了一個示例訓練服務。該服務不僅展示瞭如何在實踐中應用設計原則,還教導了訓練服務如何與任意訓練程式碼互動。接著,介紹了幾個可用於快速建立自己的訓練服務的開源訓練應用程式。最後,討論了何時使用公共雲端訓練系統。

深度學習訓練程式碼模式

為了理解本章中的訓練程式碼,讀者需要熟悉深度學習演算法程式碼模式。第3.2節對此進行了詳細介紹。訓練程式碼並非本章的重點;它僅用於演示目的,以便展示示例訓練服務。

示例程式碼

# 匯入必要的函式庫
import torch
import torch.nn as nn
import torch.optim as optim

# 定義模型架構
class Net(nn.Module):
    def __init__(self):
        super(Net, self).__init__()
        self.conv1 = nn.Conv2d(1, 10, kernel_size=5)
        self.conv2 = nn.Conv2d(10, 20, kernel_size=5)
        self.fc1 = nn.Linear(320, 50)
        self.fc2 = nn.Linear(50, 10)

    def forward(self, x):
        x = nn.functional.relu(nn.functional.max_pool2d(self.conv1(x), 2))
        x = nn.functional.relu(nn.functional.max_pool2d(self.conv2(x), 2))
        x = x.view(-1, 320)
        x = nn.functional.relu(self.fc1(x))
        x = self.fc2(x)
        return nn.functional.log_softmax(x, dim=1)

#### 內容解密:
# 這段程式碼定義了一個簡單的卷積神經網路(CNN)模型,用於手寫數字識別。
# Net類別繼承自PyTorch的nn.Module,具有卷積層和全連線層。
# `forward`方法定義了資料在網路中的前向傳播路徑。

# 初始化模型、損失函式和最佳化器
model = Net()
criterion = nn.NLLLoss()
optimizer = optim.SGD(model.parameters(), lr=0.01)

# 訓練模型
def train(model, device, train_loader, optimizer, epoch):
    model.train()
    for batch_idx, (data, target) in enumerate(train_loader):
        data, target = data.to(device), target.to(device)
        optimizer.zero_grad()
        output = model(data)
        loss = criterion(output, target)
        loss.backward()
        optimizer.step()

#### 內容解密:
# `train`函式定義了模型的訓練過程。
# 它遍歷訓練資料,載入資料到裝置上,進行前向傳播、計算損失、反向傳播和引數更新。

開源訓練服務與公共雲端訓練系統

本章還介紹了幾個開源訓練應用程式,例如Kubeflow,用於快速建立自己的訓練服務。並討論了何時使用公共雲端訓練系統。

模型訓練服務:設計綜覽

為何使用服務進行模型訓練?

假設您長官一個資料科學團隊,需要明智地將團隊寶貴的計算資源分配給團隊成員 Alex、Bob 和 Kevin。計算資源的分配方式必須確保所有團隊成員能夠在時間限制和預算內完成模型訓練任務。圖 3.2 展示了兩種分配計算資源的方法:專用和分享。

專用 vs. 分享

第一種選擇是專用,即為團隊中的每位成員專門分配一台強大的工作站。這是最簡單的方法,但顯然不是最經濟的方法,因為當 Alex 不執行他的訓練程式碼時,他的伺服器閒置,Bob 和 Kevin 都無法使用它。因此,在這種方法下,我們的預算利用率不高。

專用方法的另一個問題是它無法擴充套件。當 Alex 需要訓練一個大型模型或使用大型資料集訓練模型時,他需要多台機器。由於深度學習模型架構的複雜性,即使是一個中等大小的神經網路也需要具有大記憶體的 GPU。在這種情況下,我們必須為 Alex 分配更多的專用伺服器,這加劇了資源分配效率低的問題。

第二種選擇是分享計算資源,即建立一個彈性伺服器群組(群組大小可調整)並與所有成員分享。這種方法顯然更經濟,因為我們使用更少的伺服器來實作相同的結果,從而最大限度地提高了資源利用率。

模型訓練服務的工作流程

資料科學家向訓練服務提交訓練請求和訓練程式碼,服務在作業佇列中建立一個作業。然後,模型訓練服務分配計算資源來執行訓練作業(訓練程式碼)。當訓練執行完成時,作業生成一個模型。

為何選擇分享策略?

選擇分享策略並不是一個艱難的決定,因為它大大降低了訓練叢集的成本。但是,分享方法需要適當的管理,例如在突然湧現大量訓練請求時對使用者請求進行排隊,在必要時(訓練進度卡住)對每個訓練執行進行干預(重新啟動或中止),以及根據實時系統使用情況擴充套件或縮減叢集。

指令碼 vs. 服務

在模型訓練的背景下,訓練指令碼是指使用 shell 指令碼在分享伺服器叢集中協調不同的訓練活動。訓練服務是一個遠端程式,透過網路使用 HTTP 或 gRPC 進行通訊。作為資料科學家,Alex 和 Bob 將訓練請求傳送到服務,服務協調這些請求並管理分享伺服器上的訓練執行。

指令碼方法可能適用於單人場景,但在分享資源環境中會證明很困難。除了執行訓練程式碼外,我們還需要處理其他重要元素,例如設定環境、確保資料合規性以及故障排除模型效能。

模型訓練服務的優勢

從前面的討論中,我們可以想像出模型訓練服務的價值主張如下:

  • 使計算資源飽和,降低模型訓練成本
  • 透過以快速(更多資源可用)和可靠的方式構建模型來加快模型開發
  • 透過在受控環境中執行訓練來強制資料合規性
  • 便於模型效能故障排除

訓練服務設計原則

在檢視我們的示例訓練服務之前,讓我們看看可以用來評估模型訓練系統的四個設計原則。

原則 1:提供統一的 API,對實際訓練程式碼不可知

只有一個公共 API 來使用不同型別的訓練演算法訓練模型,使訓練服務易於使用。無論是目標檢測訓練、語音識別訓練還是文字意圖分類別訓練,我們都可以使用示例 API 觸發模型訓練執行。未來的演算法效能 A/B 測試也可以透過擁有單一的訓練 API 輕鬆實作。

原則 2:以高效能和低成本構建模型

一個好的訓練服務應該將成本效益作為優先事項。成本效益可以提供縮短模型訓練執行時間和提高計算資源利用率的方法。例如,現代訓練服務可以透過支援各種分散式訓練方法,提供良好的作業排程管理以使伺服器群組飽和,以及在訓練過程偏離原計劃時提醒使用者,以便提前終止,從而減少時間和硬體成本。

原則 3:支援模型的重現性

如果給定相同的輸入,服務應該產生相同的模型。這不僅對於除錯和效能故障排除很重要,而且還能建立系統的可信度。記住,我們將根據模型的預測結果構建業務邏輯。例如,我們可能會使用分類別模型來預測使用者的某種行為。

此圖示展示了模型訓練服務的高階工作流程。在步驟 1 中,資料科學家向訓練服務提交訓練請求和訓練程式碼,服務在作業佇列中建立一個作業。在步驟 2 中,模型訓練服務分配計算資源來執行訓練作業(訓練程式碼)。在步驟 3 中,當訓練執行完成時,作業生成一個模型。

內容解密:

  1. 圖表展示了資料科學家如何與模型訓練服務互動,以及服務如何管理計算資源來執行模型訓練。
  2. 該流程確保了資源的有效利用和模型的可靠生成。
  3. 透過這種方式,資料科學家可以專注於模型開發,而無需擔心底層基礎設施的管理。

深度學習訓練服務的設計與實作

在人工智慧領域中,深度學習模型的訓練是一個複雜且耗時的過程。為了確保模型的品質和訓練過程的可靠性,我們需要設計一個強壯的模型訓練服務。本章將介紹深度學習訓練程式碼的一般模式,並透過一個具體的示例來說明如何設計和實作一個模型訓練服務。

深度學習訓練程式碼模式

大多數深度學習模型都是透過迭代學習過程來訓練的。這個過程涉及重複執行相同的計算步驟,並在每次迭代中更新神經網路的權重和偏差,以使演算法的輸出結果更接近訓練目標。

模型訓練工作流程

圖 3.3 說明瞭一般的模型訓練步驟。由於神經網路無法一次性載入整個資料集,因此我們通常在訓練開始前將資料集重新分組為小批次(mini-batches)。在步驟 1 中,小批次的樣本被輸入到神經網路中,網路計算每個樣本的預測結果。在步驟 2 中,我們將預測結果和預期值(訓練標籤)傳遞給損失函式,以計算損失值,該值表示當前學習與目標資料模式之間的偏差。在步驟 3 中,一個名為反向傳播的過程根據損失值計算神經網路每個引數的梯度。這些梯度用於更新模型引數,以便在下一次訓練迴圈中獲得更好的預測準確性。在步驟 4 中,神經網路的引數(權重和偏差)由所選的最佳化演算法(如隨機梯度下降及其變體)更新。梯度(來自步驟 3)和學習率是最佳化演算法的輸入引數。模型準確性應該在這個模型更新步驟後得到改善。最後,在步驟 5 中,訓練完成,網路及其引數被儲存為最終的模型檔案。

將模型訓練程式碼 Docker 化

透過將訓練程式碼 Docker 化,我們可以將訓練程式碼及其依賴函式庫封裝到一個 Docker 映象中,並在容器中執行它(圖 3.4)。這樣,訓練服務可以透過簡單地啟動一個 Docker 容器來執行模型訓練。由於服務對容器內的內容不可見,因此訓練服務可以以標準化的方式執行所有不同的程式碼。

示例模型訓練服務

為了演示我們迄今為止介紹的概念和設計原則,我們構建了一個示例服務,它實作了模型訓練的基本生產場景——接收訓練請求、在 Docker 容器中啟動訓練執行、以及儲存訓練好的模型。

服務設計

我們的示例服務遵循以下設計原則:

  1. 可重複性:確保每次執行相同的訓練程式碼和資料時,都能生成相同品質的模型。
  2. 強壯性:處理訓練作業的強壯性(容錯移轉、故障還原)、資源隔離和彈性資源管理。
  3. 統一執行:透過 Docker 化訓練程式碼,使訓練服務能夠以統一的方式執行不同的模型訓練程式碼。

實作細節

我們的示例服務使用 Docker 容器來執行模型訓練。這樣可以確保每次訓練執行都在隔離的環境中進行,並且可以輕鬆地管理不同的依賴函式庫和環境變數。

import docker

# 建立 Docker 使用者端
client = docker.from_env()

# 定義訓練程式碼的 Docker 映象
image_name = "my_model_training_image"

# 定義訓練資料和超引數
training_data = "/path/to/training/data"
hyperparameters = {"learning_rate": 0.01, "batch_size": 32}

# 在 Docker 容器中執行模型訓練
container = client.containers.run(
    image_name,
    detach=True,
    environment={
        "TRAINING_DATA": training_data,
        "HYPERPARAMETERS": json.dumps(hyperparameters)
    }
)

# 等待容器完成
container.wait()

# 取得訓練好的模型
model_path = "/path/to/model/output"
with open(model_path, "rb") as f:
    model_data = f.read()

# 儲存訓練好的模型
with open("trained_model.bin", "wb") as f:
    f.write(model_data)

#### 內容解密:

  1. Docker 使用者端的建立:使用 docker.from_env() 建立一個 Docker 使用者端,以便與 Docker 守護程式進行通訊。
  2. 定義 Docker 映象:指定要使用的 Docker 映象名稱,該映象包含了模型訓練程式碼及其依賴函式庫。
  3. 定義訓練資料和超引數:指定訓練資料的路徑和超引數的值,這些值將被傳遞給 Docker 容器中的模型訓練程式碼。
  4. 在 Docker 容器中執行模型訓練:使用 client.containers.run() 方法在 Docker 容器中執行模型訓練,並將訓練資料和超引數傳遞給容器。
  5. 等待容器完成:使用 container.wait() 方法等待容器完成執行。
  6. 取得訓練好的模型:從容器中取得訓練好的模型,並將其儲存到本地檔案中。

3.3 模型訓練服務範例

模型訓練服務是機器學習(ML)系統中的核心元件,負責執行模型訓練工作並追蹤其執行進度。雖然這裡的範例相對簡單,只有幾百行程式碼,但它們展示了我們在前幾節中討論的關鍵概念,包括使用統一的API、容器化的訓練程式碼以及訓練服務與訓練容器之間的通訊協定。

注意事項

為了清晰地展示關鍵部分,本服務以簡潔的方式構建。模型訓練的後設資料(如執行中的任務和等待中的任務)被儲存在記憶體中,而不是資料函式庫中,並且訓練任務直接在本地的Docker引擎中執行。透過移除許多中間層,您將對兩個關鍵領域有直接的瞭解:訓練任務管理和訓練服務與訓練程式碼(Docker容器)之間的通訊。

3.3.1 與服務互動

在深入瞭解服務設計和實作之前,讓我們先看看如何與它互動。

注意事項

請按照GitHub上的指示執行此實驗。我們僅強調執行範例服務的主要步驟和關鍵命令,以避免冗長的程式碼和執行輸出頁面,從而清晰地展示概念。要執行此實驗,請按照orca3/MiniAutoML Git儲存函式庫中“single trainer demo”檔案(training-service/single_trainer_demo.md)中的指示進行操作,該檔案也包含了預期的輸出。

首先,我們使用scripts/ts-001-start-server.sh啟動服務:

docker build -t orca3/services:latest -f services.dockerfile .
docker run --name training-service -v /var/run/docker.sock:/var/run/docker.sock --network orca3 --rm -d -p "${TS_PORT}":51001 orca3/services:latest training-service.jar

啟動訓練服務Docker容器後,我們可以傳送gRPC請求以啟動模型訓練執行(scripts/ts-002-start-run.sh <dataset id>)。下面是一個範例gRPC請求。

grpcurl -plaintext -d "{
  \"metadata\": {
    \"algorithm\": \"intent-classification\",
    \"dataset_id\": \"1\",
    \"name\": \"test1\",
    \"train_data_version_hash\": \"hashBA==\",
    \"parameters\": {
      \"LR\": \"4\",
      \"EPOCHS\": \"15\",
      \"BATCH_SIZE\": \"64\",
      \"FC_SIZE\": \"128\"
    }
  }
}" "${TS_SERVER}:${TS_PORT}" training.TrainingService/Train

清單 3.1 呼叫訓練服務API:提交訓練任務

一旦成功提交任務,我們就可以使用從Train API傳回的任務ID來查詢訓練執行的進度(scripts/ts-003-check-run.sh <job id>);請參見以下範例:

grpcurl -plaintext -d "{\"job_id\": \"$job_id\"}" "${TS_SERVER}:${TS_PORT}" training.TrainingService/GetTrainingStatus

如您所見,透過呼叫兩個gRPC API,我們可以啟動深度學習訓練並追蹤其進度。現在,讓我們來看看這個範例訓練服務的設計和實作。

注意事項

如果您遇到任何問題,請檢視附錄A。A.2節中的指令碼自動執行資料集準備和模型訓練。如果您想檢視一個運作中的模型訓練範例,請閱讀該節的實驗部分。

3.3.2 服務設計概述

讓我們使用Alex(一位資料科學家)和Tang(一位開發者)來展示該服務的功能。要使用訓練服務來訓練模型,Alex需要編寫訓練程式碼(例如,一個神經網路演算法)並將其構建為Docker映像。此Docker映像需要釋出到構件儲存函式庫,以便訓練服務可以提取映像並將其作為容器執行。在Docker容器內部,訓練程式碼將由一個bash指令碼執行。

為了提供一個範例,我們用PyTorch編寫了一個意圖分類別訓練程式碼,將其構建為Docker映像,並將其推播到Docker Hub(https://hub.docker.com/u/orca3)。我們將在3.3.6節再次解釋它。

注意事項

在實際場景中,訓練Docker映像的建立、釋出和消費是自動完成的。一個範例場景可能是如下:步驟1,Alex將他的訓練程式碼提交到Git儲存函式庫;步驟2,一個預先組態好的程式——例如,一個Jenkins管道——被觸發,從這個儲存函式庫構建一個Docker映像;步驟3,管道還將Docker映像釋出到Docker映像構件函式庫,例如,JFrog Artifactory;步驟4,Alex傳送一個訓練請求,然後訓練服務從構件函式庫提取訓練映像並開始模型訓練。

當Alex完成訓練程式碼開發後,他可以開始使用該服務來執行他的訓練程式碼。整個工作流程如下:步驟1.a,Alex向我們的範例訓練服務提交一個訓練請求。該請求定義了訓練程式碼——一個Docker映像和標籤。當訓練服務接收到一個訓練請求時,它會在佇列中建立一個任務並將任務ID傳回給Alex,以便未來進行任務追蹤;步驟1.b,Alex可以查詢訓練服務以取得即時的訓練進度;步驟2,該服務在本地Docker引擎中啟動一個訓練任務作為Docker容器,以執行模型訓練;步驟3,Docker容器中的訓練程式碼使用由Train API傳回的任務ID。

程式碼解密:

此段落描述了使用Docker容器執行模型訓練的流程。主要步驟包括:

  1. 編寫並構建訓練程式碼為Docker映像。
  2. 將Docker映像釋出到構件儲存函式庫。
  3. 提交訓練請求給訓練服務。
  4. 查詢訓練進度。
  5. 執行模型訓練。

這些步驟展示瞭如何使用統一的API和容器化的訓練程式碼來管理和執行模型訓練任務。

模型訓練流程

@startuml
skinparam backgroundColor #FEFEFE
skinparam componentStyle rectangle

title 深度學習模型訓練服務設計綜覽

package "Docker 架構" {
    actor "開發者" as dev

    package "Docker Engine" {
        component [Docker Daemon] as daemon
        component [Docker CLI] as cli
        component [REST API] as api
    }

    package "容器運行時" {
        component [containerd] as containerd
        component [runc] as runc
    }

    package "儲存" {
        database [Images] as images
        database [Volumes] as volumes
        database [Networks] as networks
    }

    cloud "Registry" as registry
}

dev --> cli : 命令操作
cli --> api : API 呼叫
api --> daemon : 處理請求
daemon --> containerd : 容器管理
containerd --> runc : 執行容器
daemon --> images : 映像檔管理
daemon --> registry : 拉取/推送
daemon --> volumes : 資料持久化
daemon --> networks : 網路配置

@enduml

此圖示描述了從編寫訓練程式碼到執行模型訓練的整個流程。

詳細解說:

  1. 編寫訓練程式碼:資料科學家編寫用於模型訓練的程式碼。
  2. 構建為Docker映像:將訓練程式碼構建為Docker映像,以便於佈署和執行。
  3. 釋出到構件儲存函式庫:將Docker映像釋出到構件儲存函式庫,如Docker Hub或JFrog Artifactory。
  4. 提交訓練請求:向訓練服務提交一個包含所需Docker映像和引數的訓練請求。
  5. 查詢訓練進度:透過查詢API來取得模型的訓練進度。
  6. 執行模型訓練:在Docker容器中執行模型訓練,使用提交請求時提供的引數。

透過這個流程,可以實作自動化的模型訓練和管理,提高開發效率和模型的可靠性。