[翻譯] NVIDIA HugeCTR,GPU 版本引數伺服器 --(10)--- 推理架構

羅西的思考發表於2022-03-10

[翻譯] NVIDIA HugeCTR,GPU 版本引數伺服器 --(10)--- 推理架構

0x00 摘要

經過9篇文章之後,我們基本把 HugeCTR 的訓練過程梳理了以下,現在我們有必要看看HugeCTR如何進行推理,這樣可以讓我們從整體上有一個更好的把握。而且我們之前都是分析分散式訓練,此處恰好可以看看分散式推理。

本文翻譯自 https://github.com/triton-inference-server/hugectr_backend/blob/main/docs/architecture.md。

HugeCTR Backend (https://github.com/triton-inference-server/hugectr_backend/)是一個 GPU 加速的推薦模型部署框架,旨在通過解耦引數伺服器、嵌入快取和模型權重來有效地使用 GPU 記憶體來加速推理。HugeCTR 後端通過使用在多個模型例項之間共享的嵌入快取來支援跨多 GPU 的併發模型推理。

本系列其他文章如下:

[原始碼解析] NVIDIA HugeCTR,GPU 版本引數伺服器 --(1)

[原始碼解析] NVIDIA HugeCTR,GPU版本引數伺服器--- (2)

[原始碼解析] NVIDIA HugeCTR,GPU版本引數伺服器---(3)

[原始碼解析] NVIDIA HugeCTR,GPU版本引數伺服器--- (4)

[原始碼解析] NVIDIA HugeCTR,GPU版本引數伺服器--- (5) 嵌入式hash表

[原始碼解析] NVIDIA HugeCTR,GPU版本引數伺服器--- (6) --- Distributed hash表

[原始碼解析] NVIDIA HugeCTR,GPU 版本引數伺服器---(7) ---Distributed Hash之前向傳播

[原始碼解析] NVIDIA HugeCTR,GPU 版本引數伺服器---(8) ---Distributed Hash之後向傳播

[原始碼解析] NVIDIA HugeCTR,GPU 版本引數伺服器 --(9)--- Local hash表

0x01 設計

HugeCTR Backend採用分層框架,通過Parameter Server隔離嵌入表的載入,以此防止服務被部署在多個GPU上的多個模型影響,並通過嵌入快取來實現高服務可用性。GPU快取用於在推理過程中加速嵌入向量查詢效率。

HugeCTR 後端還提供以下功能:

  • 併發模型執行:多個模型和同一模型的多個例項可以在同一 GPU 或多個 GPU 上同時執行。
  • 可擴充套件的後端:HugeCTR 提供的推理介面可以很容易地與後端 API 整合,這允許使用 Python 或 C++ 使用任何執行邏輯擴充套件模型。
  • 輕鬆部署新模型:更新模型應儘可能透明,不應影響推理效能。這意味著無論需要部署多少個模型,只要這些模型是使用 HugeCTR 訓練的,它們都可以通過相同的 HugeCTR 後端 API 載入。注意:在某些情況下,可能需要針對每個模型相應更新其配置檔案。

0x02 HugeCTR後端框架

以下元件構成了 HugeCTR 後端框架:

  • 引數伺服器負責載入和管理屬於不同模型的大型嵌入表。嵌入表為嵌入快取提供同步和更新服務。它還確保嵌入表完全載入並定期更新。
  • 嵌入式快取可以直接載入到GPU記憶體之中。因此,它為模型提供了嵌入向量查詢功能,從而避免了從引數伺服器傳輸資料(CPU 和 GPU 之間傳輸)時產生的相對較高的延遲。它還提供了更新機制,以及時載入最新快取的嵌入向量,這樣確保了高命中率。
  • 模型比嵌入表小得多,因此它通常可以直接載入到GPU記憶體以加速推斷。該模型可以直接與 GPU 記憶體中的嵌入快取互動以獲得嵌入向量。基於分層設計結構,多個模型例項將共享 GPU 記憶體中的嵌入快取,以確保併發的模型執行。基於層級的依賴關係,嵌入表可以與模型的查詢操作解耦,轉而依靠嵌入快取實現高效低延遲的查詢操作。這使得使用逐個介面初始化和依賴注入來實現推理邏輯成為可能。

下面深入瞭解一下 HugeCTR Inference 介面的設計框架:

圖 1. HugeCTR 推理設計架構

在實際應用中,引數伺服器用於載入所有模型的嵌入表。由於不同的模型在不同的應用場景下通過訓練會得到不同的嵌入表,因此在推理過程中會產生很高的記憶體開銷。通過引入Parameter Server,嵌入表可以在嵌入表規模較小的情況下直接載入到GPU記憶體中,如果GPU資源耗盡,則載入到CPU的記憶體中,當嵌入表尺寸太大時甚至會載入到固態硬碟(SSD)中) 。這確保了不同模型和這些模型之間共享的嵌入表是隔離的。

每個嵌入表將在不同的 GPU 上建立單獨的嵌入快取。嵌入快取將嵌入表視為最小粒度,這意味著嵌入快取可以直接查詢並與相應的嵌入表同步。這種機制確保同一模型的多個模型例項可以在部署的 GPU 節點上共享相同的嵌入快取。

0x03 GPU 嵌入快取

3.1 啟用

當啟用 GPU 嵌入快取機制時,模型將從 GPU 嵌入快取中查詢嵌入向量。如果嵌入向量在 GPU 嵌入快取中不存在,它將返回預設嵌入向量。預設值為 0。

HugeCTR 後端需要在 config.pbtxt 檔案中設定以下引數:

parameters [
...
  {
 key: "gpucache"
 value: { string_value: "true" }
 },
 {
 key: "gpucacheper"
 value: { string_value: "0.5" }
 },
...
]
  • gpucache:使用此選項啟用 GPU 嵌入快取機制。
  • gpucacheper:確定將從嵌入表載入到 GPU 嵌入快取中的嵌入向量的百分比。預設值為 0.5。因此,在上面的示例中,嵌入表的 50% 將被載入到 GPU 嵌入快取中。
...
"inference": {
   "max_batchsize": 64,
   "hit_rate_threshold": 0.6,
   "dense_model_file": "/model/dcn/1/_dense_10000.model",
   "sparse_model_file": "/model/dcn/1/0_sparse_10000.model",
   "label": 1
 },

...
]
  • hit_rate_threshold:該選項根據命中率確定嵌入快取和引數伺服器的更新機制。如果嵌入向量查詢的命中率低於設定的閾值,GPU 嵌入快取將更新引數伺服器上缺失的向量。GPU 嵌入快取還會基於固定命中率來從引數伺服器讀取嵌入向量進行更新。必須在模型推理配置 JSON 檔案中設定命中率閾值。例如,請參閱dcn.jsondeepfm.json

3.2 禁用

當禁用 GPU 嵌入快取機制(即"gpucache"設定為false)時,模型將直接從引數伺服器查詢嵌入向量。在這種情況下,與 GPU 嵌入快取相關的所有其他設定都將被忽略。

0x04 本地化部署

Parameter Server 可以在同一個節點和叢集上實現本地化部署,即每個節點只有一個 GPU,Parameter Server 部署在同一節點上。以下是 HugeCTR 支援的幾種部署場景:

  • 場景1:一個GPU(Node 1)部署一個模型,通過啟動多個並行例項來最大化embedding cache的命中率。

  • 場景2:一個GPU(Node 2)部署多個模型來最大化GPU資源,這需要在併發例項數量和多個嵌入快取之間取得平衡,以確保有效使用 GPU 記憶體。每個嵌入快取和引數伺服器之間的資料傳輸使用一個獨立的 cuda 流。

    注意:在下面提到的示例中,在每個節點上部署了多個 GPU 和一個引數伺服器。

  • 場景3:多個 GPU(Node 3)部署單個模型,在這種情況下,引數伺服器可以幫助提高 GPU 之間嵌入快取的命中率。

  • 場景4:多個GPU(Node 4)部署多個模型,這是本地化部署最複雜的場景,需要保證不同的embedding cache可以共享同一個Parameter Server,不同的model可以共享同一節點上的embedding cache。

圖 2 HugeCTR 推理本地化部署架構

0x05 具有分層 HugeCTR 引數伺服器的分散式部署

HugeCTR 引入分散式Redis叢集作為CPU快取,用於儲存更大的嵌入表,並直接與GPU嵌入快取互動。本地 RocksDB 作為查詢引擎來支撐本地 SSD 上的完整嵌入表,以協助 Redis 叢集執行缺失的嵌入鍵查詢。要啟用這種分層查詢服務,您必須將"db_type"配置項新增到 ps.json 中"hierarchy"

{
    "supportlonglong": false,
    ...
    "db_type": "hierarchy",
    ...
    "models": [
      ...
    ]
}
  • 分散式Redis叢集
    同步查詢:每個Model例項從本地化的GPU快取中查詢需要的embedding key,同時也會將缺失的embedding key(Keys not found in the GPU cache)儲存到缺失 keys buffer中。缺失 keys buffer與 Redis 例項同步交換,Redis 例項依次對任何丟失的嵌入鍵執行查詢操作。從而,分散式Redis叢集充當了二級快取,這可以完全替代本地化引數伺服器來載入所有模型的完整嵌入表。

    使用者只需要設定各個節點的ip和埠,即可在HugeCTR分層引數伺服器之中啟用Redis叢集服務。但是Redis叢集作為分散式記憶體快取,仍然受到每個節點CPU記憶體大小的限制。換句話說,所有模型的嵌入表的大小仍然不能超過叢集的總 CPU 記憶體。因此,使用者可以使用"cache_size_percentage_redis"來控制載入到Redis叢集中的模型嵌入表的大小。

    要利用具有 HugeCTR 的 Redis 叢集,需要新增以下配置選項以新增到 ps.json:

    {
      "supportlonglong": false,
      ...
      "db_type": "hierarchy",
      "redis_ip": "node1_ip:port,node2_ip:port,node3_ip:port,...",
      "cache_size_percentage_redis": "0.5",
      ...
      "models": [
        ...
      ]
    }
    
  • Localized RocksDB(Key-Value Store)
    對於超大規模的嵌入表,仍然無法完全載入到Redis叢集中,我們將在每個節點上啟用本地鍵值儲存(key-value)。

    RocksDB的同步查詢:Redis叢集客戶端在分散式GPU快取中查詢embedding key時,會記錄丟失的embedding key(Keys not found in Redis cluster)並記錄到丟失key buffer中。丟失key buffer與本地 RocksDB 客戶端同步交換,然後將嘗試在本地 SSD 中查詢這些金鑰。最終,SSD 查詢引擎將對所有模型缺失的嵌入鍵執行第三次查詢操作。

    對於已經儲存在雲端的模型儲存庫(model repositories),RocksDB 將作為本地 SSD 快取,用於儲存 Redis 叢集無法載入的剩餘部分。因此,在實踐中,本地化的 RocksDB 例項充當了三級快取。

    本地化的 RocksDB 的配置需要新增到 ps.json 中,如下圖:

    {
      "supportlonglong":false,
      ...
      "db_type":"hierarchy",
      "rocksdb_path":"/current_node/rocksdb_path",
      ...
      "models":[
        ...
      ]
    }
    

圖 3. HugeCTR 推理分散式部署架構

0x06 Variant Compressed Sparse Row Input

(Variant Compressed Sparse Row (CSR) )資料格式通常用作 HugeCTR 模型的輸入。它允許高效地讀取資料,從原始資料中獲取資料語義資訊,並避免花費太多時間進行資料解析。NVTabular 必須輸出相應的槽資訊來指示分類資料的特徵檔案。通過使用變體CSR資料格式,模型可以在從請求中讀取資料時獲取特徵欄位資訊。此外,也可以通過避免過多的請求資料處理來加快推理過程。對於每個樣本,有三種主要型別的輸入資料:

  • Dense Feature:代表實際的數值資料。
  • Column Indices:上游預處理工具 NVTabular 對分類資料執行 one-hot 和 multi-hot 編碼並將其轉換為數值型資料。
  • Row ptr:包含每個插槽的分類特徵數。

圖 4. HugeCTR 推理 VCSR 輸入格式

VCSR 示例

每個模型的單個嵌入表

以上圖的第 0 行為例。輸入資料包含四個槽,HugeCTR根據“Row ptr”輸入解析Row 0槽資訊。所有嵌入向量都儲存在單個嵌入表中。

圖 5. 每個模型的單個嵌入表的 HugeCTR 推理 VCSR 示例

  • Slot 1:包含1 個分類特徵,嵌入鍵(embedding key) 為 1。
  • Slot 2:包含1 個分類特徵,嵌入鍵為 3。
  • Slot 3:包含0 個分類特徵。
  • Slot 4:包含2 個分類特徵,嵌入鍵為 8 和 9。 HugeCTR 將從 GPU 的嵌入快取或引數伺服器中查詢兩個嵌入向量,並最終得到一個用於 slot 4 的最終嵌入向量。

每個模型有多個嵌入表

同樣,我們以上圖中的Row 0為例。然而,這次我們假設輸入資料由四個槽組成,其中前兩個槽(槽 1 和槽 2)屬於第一個嵌入表,後兩個槽(槽 3 和槽 4)屬於第二個嵌入表。所以需要兩個獨立的Row prts才能在輸入資料中形成完整的 Row prts

圖 6. 每個模型的多個嵌入表的 HugeCTR 推理 VCSR 示例

  • Slot 1:包含1個分類特徵,embedding key為1,對應的embedding向量儲存在embedding table 1中。
  • Slot 2:包含1個分類特徵,embedding key為3,對應的embedding向量儲存在embedding table 1中。
  • Slot 3:包含0 個分類特徵。
  • Slot 4:包含2個分類特徵,embedding key分別為8和9。對應的embedding向量儲存在embedding table 2中。在這種情況下,HugeCTR會從GPU的embedding cache或者Parameter Server中查詢兩個embedding vector並最終得到槽 4 的最終嵌入向量。

至此,HugeCTR 全部分析完畢,下一個系列我們來看看 TensorFlow 的分散式訓練,敬請期待。

0xFF 參考

https://github.com/triton-inference-server/hugectr_backend/blob/main/docs/architecture.md

相關文章