在機器學習專案中,有效管理大量的訓練資料至關重要。本範例資料集管理服務旨在提供一個穩健的平台,讓資料工程師和資料科學家能輕鬆地匯入、更新、查詢和使用資料集。服務採用三層架構,並利用雲端儲存的優勢,簡化資料的處理流程。透過提供資料 URL,而非直接上傳檔案,有效降低了伺服器端的負擔,並提升了系統的擴充套件性。此外,版本控制機制確保了資料集的完整性和可追溯性,方便模型訓練和實驗的進行。

2.2 探索範例資料集管理服務

在本文中,我們將帶您瞭解一個範例資料集管理(DM)服務。我們建立這個範例是為了讓您瞭解第2.1.2節中提出的原則如何被實施。首先,我們將在本機執行該服務,並與之互動,然後檢視其API設計和內部實作。

2.2.1 與範例服務互動

為了讓您更輕鬆地體驗,我們編寫了七個shell指令碼來自動化整個DM實驗室。這些shell指令碼是體驗本文中演示場景的推薦方法,因為它們不僅自動化了服務的本機設定,還負責設定環境變數、準備範例資料和初始化本機網路。

您可以在 https://github.com/orca3/MiniAutoML/tree/main/scripts 中找到這些指令碼,搜尋關鍵字“dm”。我們的GitHub倉函式庫中的“function demo”檔案(https://github.com/orca3/MiniAutoML/tree/main/data-management)提供瞭如何完成實驗室的詳細說明以及這些指令碼的範例輸出。

設定本機服務

範例服務使用Java 11編寫,並使用MinIO作為檔案blob伺服器來模擬雲端物件儲存(例如Amazon S3),因此我們可以在本機執行所有內容,而無需任何遠端依賴。如果您已經按照附錄A中的說明設定了實驗室,則可以在指令碼資料夾的根目錄下執行以下命令(列表2.1)來啟動服務。

# (1) 啟動minio伺服器
./scripts/dm-001-start-minio.sh
# (2) 啟動資料集管理服務,它將構建dm映像並執行容器
./scripts/dm-002-start-server.sh

列表2.1 在本機啟動服務

建立並更新語言意圖資料集

我們的範例DM服務提供了三個API方法,供使用者建立/更新資料集並檢查結果。這些API方法是CreateDataset、UpdateDataset和GetDatasetSummary。我們將在接下來的幾節中詳細討論它們。

在此範例場景中,首先我們呼叫資料管理服務上的CreateDataset API方法來建立新的語言意圖資料集;然後,我們使用UpdateDataset API方法將更多資料附加到資料集中。最後,我們使用GetDatasetSummary API方法取得資料集的統計資訊和提交(資料變更)歷史記錄。

mc -q cp data-management/src/test/resources/datasets/test.csv myminio/"${MINIO_DM_BUCKET}"/upload/001.csv
grpcurl -v -plaintext \
-d '{"name": "dataset-1", \
"dataset_type": "LANGUAGE_INTENT", \
"bucket": "mini-automl", \
"path": "{DATA_URL_IN_MINIO}"}' \
${DM_SERVER}:${DM_PORT} \
data_management.DataManagementService/CreateDataset

列表2.2 建立語言意圖資料集

更新語言意圖資料集

建立資料集後,您可以透過附加更多資料來持續更新它;請參閱以下資料集更新gRPC請求。

mc -q cp data-management/src/test/resources/datasets/train.csv myminio/"${MINIO_DM_BUCKET}"/upload/002.csv
grpcurl -v -plaintext \
-d '{"dataset_id": "1", \
"commit_message": "More training data", \
"bucket": "mini-automl", \
"path": "upload/002.csv", \
"tags": [{ \
"tag_key": "category", \
"tag_value": "training set"}]}' \
${DM_SERVER}:${DM_PORT} \
data_management.DataManagementService/UpdateDataset

列表2.3 更新語言意圖資料集

程式碼解密:

  1. mc -q cp data-management/src/test/resources/datasets/test.csv myminio/"${MINIO_DM_BUCKET}"/upload/001.csv:此命令將測試資料集test.csv上傳到MinIO伺服器的指定儲存桶中的upload/001.csv路徑。

  2. grpcurl -v -plaintext ... data_management.DataManagementService/CreateDataset:此gRPC請求用於建立新的資料集。它提供了資料集的名稱、型別、儲存桶名稱和資料在MinIO中的路徑。

  3. grpcurl -v -plaintext ... data_management.DataManagementService/UpdateDataset:此gRPC請求用於更新現有的資料集。它指定了要更新的資料集ID、提交訊息、儲存桶名稱、資料在MinIO中的新路徑,以及用於過濾提交的標籤。

  4. 標籤(tags):在更新資料集的請求中,標籤用於對提交進行分類別,例如區分訓練集和測試集。

  5. 提交歷史(commits):每次對資料集的變更都會被記錄下來,包括變更的時間、變更的訊息以及變更後的資料統計資訊。

此圖示呈現了整個流程:

@startuml
skinparam backgroundColor #FEFEFE
skinparam sequenceArrowThickness 2

title 資料集管理服務設計

actor "客戶端" as client
participant "API Gateway" as gateway
participant "認證服務" as auth
participant "業務服務" as service
database "資料庫" as db
queue "訊息佇列" as mq

client -> gateway : HTTP 請求
gateway -> auth : 驗證 Token
auth --> gateway : 認證結果

alt 認證成功
    gateway -> service : 轉發請求
    service -> db : 查詢/更新資料
    db --> service : 回傳結果
    service -> mq : 發送事件
    service --> gateway : 回應資料
    gateway --> client : HTTP 200 OK
else 認證失敗
    gateway --> client : HTTP 401 Unauthorized
end

@enduml

資料集管理服務總覽

在前一節中,我們瞭解瞭如何使用資料集管理服務(Dataset Management Service)來上傳和管理資料。現在,讓我們進一步探討如何使用該服務來建立訓練資料集。

取得資料集摘要

您可以使用 GetDatasetSummary API 來取得資料集的摘要和提交歷史。以下是一個 gRPC 請求的範例:

grpcurl -v -plaintext -d '{"datasetId": "1"}' ${DM_SERVER}:${DM_PORT} data_management.DataManagementService/GetDatasetSummary

建立訓練資料集

現在,我們已經建立了一個包含原始資料的資料集(ID = 1)。讓我們試著從中建立一個訓練資料集。在我們的範例服務中,這是一個兩步驟的過程。

首先,我們呼叫 PrepareTrainingDataset API 來啟動資料集建立程式。然後,我們使用 FetchTrainingDataset API 來查詢資料集準備的進度,直到請求完成。

使用 PrepareTrainingDataset API

要使用 PrepareTrainingDataset API,我們只需要提供一個資料集 ID。如果您只想要一部分資料在訓練資料集中,您可以使用標籤作為篩選器在請求中。以下是一個範例請求:

grpcurl -plaintext -d '{"dataset_id": "1"}' ${DM_SERVER}:${DM_PORT} data_management.DataManagementService/PrepareTrainingDataset

或者,您也可以使用標籤來篩選資料:

grpcurl -plaintext -d '{"dataset_id": "1", "Tags": [{"tag_key": "category", "tag_value": "training set"}]}' ${DM_SERVER}:${DM_PORT} data_management.DataManagementService/PrepareTrainingDataset

使用 FetchTrainingDataset API

一旦資料準備 gRPC 請求成功,它將傳回一個 JSON 物件,其中包含 version_hash 字串。我們可以使用這個雜湊作為 ID,呼叫 FetchTrainingDataset API 來追蹤建立訓練資料集的進度。以下是一個範例:

grpcurl -plaintext -d '{"dataset_id": "1", "version_hash": "hashDg=="}' ${DM_SERVER}:${DM_PORT} data_management.DataManagementService/FetchTrainingDataset

FetchTrainingDataset API 將傳回一個 JSON 物件,描述訓練資料集的狀態。如果訓練資料已準備好使用,回應物件將顯示可下載的訓練資料 URL。

程式碼解說

PrepareTrainingDataset API 請求

{
  "dataset_id": "1"
}

這個請求物件包含了要建立訓練資料集的資料集 ID。

FetchTrainingDataset API 回應

{
  "dataset_id": "1",
  "version_hash": "hashDg==",
  "state": "READY",
  "parts": [
    {
      "name": "examples.csv",
      "bucket": "mini-automl-dm",
      "path": "versionedDatasets/1/hashDg==/examples.csv"
    },
    {
      "name": "labels.csv",
      "bucket": "mini-automl-dm",
      "path": "versionedDatasets/1/hashDg==/labels.csv"
    }
  ],
  "statistics": {
    "numExamples": "16200",
    "numLabels": "151"
  }
}

這個回應物件包含了訓練資料集的狀態、資料 URL 和統計資訊。

使用者場景和整體架構

在設計後端服務時,我們發現從外部開始思考非常有用。首先,找出使用者是誰,他們將如何與服務互動,以及服務將提供什麼價值。然後,內部邏輯和儲存佈局應該自然而然地浮現出來。

我們的範例 DM 服務是為兩個虛擬使用者設計的:Jianguo,一位資料工程師,和 Julia,一位資料科學家。他們一起合作訓練一個語言意圖分類別模型。Jianguo 負責收集訓練資料,而 Julia 則使用這些資料來訓練模型。

使用者和使用者場景

我們的範例 DM 服務是為兩個虛擬使用者設計的:Jianguo 和 Julia。他們的工作流程如下:

  • Jianguo 從不同的資料來源收集資料,並將其上傳到 DM 服務。
  • Julia 使用 DM 服務提供的 API 來建立訓練資料集,並使用這些資料來訓練模型。

透過瞭解使用者場景和整體架構,我們可以設計出更符合使用者需求的服務。

2.2 資料集管理服務例項導覽

本文將介紹一個範例資料集管理服務(Dataset Management Service,簡稱DM服務)的架構和實作細節。該服務旨在為資料工程師和資料科學家提供一個統一的資料集管理平台,以支援資料的匯入、儲存、檢索和版本控制。

服務整體架構

範例DM服務採用三層架構:資料匯入層、資料集擷取層和資料集內部儲存層。資料匯入API允許資料工程師上傳新的訓練資料並查詢資料集狀態,而資料集擷取API則允許資料科學家取得訓練資料集。如圖2.6和2.7所示,展示了該服務的整體架構。

圖2.6:範例資料集管理服務的系統總覽

該圖顯示了範例DM服務的三個主要元件:資料匯入API、內部儲存和資料集擷取API。內部儲存系統支援強型別的schema資料集(文字和影像型別)和非schema資料集(GENERIC型別)。

圖2.7:內部儲存結構

圖2.7顯示了範例DM服務用於儲存資料集的整體資料結構。透過資料匯入API建立的提交(commits)會建立版本化的快照(versioned snapshots),而這些快照是由資料集擷取API所建立。引入提交和版本化快照的概念是為了滿足資料集動態和靜態性質的需求。

資料匯入API

資料匯入API允許在範例DM服務中建立、更新和查詢資料集。如圖2.8所示,灰色方框中定義了四個服務方法,分別支援將資料匯入DM服務。以下列出其gRPC方法定義,如清單2.6所示。

# 建立新的資料集並儲存資料
rpc CreateDataset (CreateDatasetRequest) returns (DatasetSummary);
# 將新資料新增至現有的資料集
rpc UpdateDataset (CreateCommitRequest) returns (DatasetSummary);
# 取得指定資料集的摘要和歷史記錄
rpc GetDatasetSummary (DatasetPointer) returns (DatasetSummary);
# 列出所有現有資料集的摘要
rpc ListDatasets (ListQueryOptions) returns (stream DatasetSummary);

message CreateDatasetRequest {
  string name = 1;
  string description = 2;
  DatasetType dataset_type = 3;
  string bucket = 4;
  string path = 5;
  repeated Tag tags = 6;
}

清單2.6:資料匯入API服務定義

在API設計中,需要使用者提供資料URL作為原始資料輸入,而不是直接上傳檔案至服務。在第2.2.4節中,我們將進一步討論這種設計的優缺點。

資料URL與資料串流

在設計API時,我們選擇要求使用者提供資料URL,而不是直接上傳檔案。這種設計可以讓伺服器端從指定的URL下載資料,而不需要處理檔案上傳的複雜邏輯。

圖2.8:支援資料匯入的四個方法

圖2.8展示了四個用於支援資料匯入的方法:建立資料集、更新資料集、取得資料集摘要和列出所有資料集。

程式碼解析

message CreateDatasetRequest {
  string name = 1; // 定義資料集名稱
  string description = 2; // 定義資料集描述
  DatasetType dataset_type = 3; // 定義資料集型別(例如:"TEXT_INTENT" 或 "GENERIC")
  string bucket = 4; // 定義儲存桶名稱
  string path = 5; // 定義檔案路徑
  repeated Tag tags = 6; // 定義標籤,用於篩選資料
}

#### 內容解密:

  1. CreateDatasetRequest訊息定義了建立新資料集所需的引數。
  2. name欄位用於指定資料集的名稱。
  3. description欄位用於提供對資料集的描述。
  4. dataset_type欄位決定了資料集的型別,例如文字意圖分類別或一般型別。
  5. bucketpath欄位共同指定了儲存資料的位置。
  6. tags欄位允許為資料集新增標籤,以便進行篩選。

綜上所述,範例DM服務提供了一個統一的平台,用於管理不同型別的資料集,並支援多種操作,如建立、更新、查詢等。透過gRPC API,開發者可以輕鬆地與DM服務進行互動,實作對資料集的有效管理。

資料集管理服務的核心功能實作

在深度學習模型開發過程中,資料集的管理扮演著至關重要的角色。一個完善的資料集管理服務能夠有效地支援資料的儲存、更新以及檢索。本章節將探討一個範例資料集管理服務(Dataset Management Service)的設計與實作細節。

為何選擇傳回資料URL而非直接串流檔案

在設計資料集管理服務時,一個重要的決策是選擇傳回資料的URL作為訓練資料集,而不是直接透過串流端點傳回檔案。主要原因有兩個:首先,這樣做可以將檔案傳輸的工作解除安裝到雲端物件儲存服務(如Amazon S3或Azure Blob),節省網路頻寬;其次,這減少了程式碼的複雜性,因為保持資料串流在高用性方面的運作對於大檔案和高API使用率的情況可能會變得非常複雜。

建立新資料集的流程

要建立一個新的資料集,使用者(例如Jianguo)需要先準備好資料的可下載URL(步驟1和2)。這個URL可以是雲端物件儲存服務中的一個可下載連結。我們的範例服務使用MinIO伺服器在本機模擬Amazon S3。Jianguo還可以在資料集建立請求中命名資料集並分配標籤。

public void createDataset(CreateDatasetRequest request) {
    int datasetId = store.datasetIdSeed.incrementAndGet();
    Dataset dataset = new Dataset(
        datasetId, request.getName(),
        request.getDescription(),
        request.getDatasetType());
    int commitId = dataset.getNextCommitId();
    CommitInfo.Builder builder = DatasetIngestion
        .ingest(minioClient, datasetId, commitId,
            request.getDatasetType(), request.getBucket(),
            request.getPath(), config.minioBucketName);
    store.datasets.put(Integer.toString(datasetId), dataset);
    dataset.commits.put(commitId, builder
        .setCommitMessage("Initial commit")
        .addAllTags(request.getTagsList()).build());
    responseObserver.onNext(dataset.toDatasetSummary());
    responseObserver.onCompleted();
}

內容解密:

  1. createDataset 方法接收建立資料集的請求:首先,系統為新的資料集分配一個唯一的ID。
  2. 建立資料集物件:根據使用者請求中的後設資料(如名稱、描述和資料集型別)建立一個新的Dataset物件。
  3. DatasetIngestion.ingest 方法處理資料下載和上傳:從使用者提供的URL下載資料,並將其上傳到DM的雲端儲存。
  4. 儲存資料集和初始提交:將建立的Dataset物件和初始的CommitInfo儲存到系統中。
  5. 傳回資料集摘要給客戶端:完成建立後,向客戶端傳回新建立的資料集摘要。

更新現有資料集

在深度學習模型開發過程中,資料集需要不斷更新以適應模型訓練的需求。為此,我們提供了UpdateDataset API來支援資料集的更新。

public void updateDataset(CreateCommitRequest request) {
    String datasetId = request.getDatasetId();
    Dataset dataset = store.datasets.get(datasetId);
    String commitId = Integer.toString(dataset.getNextCommitId());
    // 後續程式碼與 Listing 2.7 相同
    // ...
}

內容解密:

  1. updateDataset 方法接收更新資料集的請求:根據請求中的datasetId找到對應的Dataset物件。
  2. 建立新的提交物件:為更新操作生成新的提交ID,並執行與建立新資料集類別似的資料下載、上傳和儲存邏輯。
  3. 儲存更新後的資料集狀態:將新的提交物件新增到資料集的提交歷史中。

資料集查詢和管理API

除了建立和更新資料集之外,我們還提供了ListDatasetsGetDatasetSummary API來支援資料集的查詢和管理。

  • ListDatasets API:列出所有現有的資料集。
  • GetDatasetSummary API:提供某個特定資料集的詳細資訊,如提交歷史、範例和標籤數量等。

訓練資料集擷取API

為了支援模型訓練,我們設計了兩種API:PrepareTrainingDataset和用於擷取訓練資料的API。資料科學家(Julia)首先呼叫PrepareTrainingDataset API發出訓練資料準備請求;我們的DM服務將啟動後台執行緒開始構建訓練資料,並傳回一個版本字串作為訓練資料的參考控制程式碼。