CPU推理|使用英特爾 Sapphire Rapids 加速 PyTorch Transformers

語言: CN / TW / HK

最近的一篇文章 中,我們介紹了代號為 Sapphire Rapids 的第四代英特爾至強 CPU 及其新的先進矩陣擴展 (AMX) 指令集。通過使用 Amazon EC2 上的 Sapphire Rapids 服務器集羣並結合相應的英特爾優化庫,如 英特爾 PyTorch 擴展 (IPEX),我們展示瞭如何使用 CPU 進行高效的分佈式大規模訓練,與上一代至強 (Ice Lake) 相比,Sapphire Rapids 實現了 8 倍的加速,取得了近線性的擴展比。

本文我們將重點關注推理。使用基於 PyTorch 的 Hugging Face transformers 模型,我們首先在 Ice Lake 服務器上分別測量它們在長、短兩種文本序列上的性能。然後,我們在 Sapphire Rapids 服務器和最新版本的 Hugging Face Optimum Intel 上執行相同的測試,並比較兩代 CPU 的性能。這裏,Optimum Intel 是一個專用於英特爾平台的硬件加速開源庫。

讓我們開始吧!

為什麼你應該考慮使用 CPU 推理

在決定使用 CPU 還是 GPU 進行深度學習推理時需要考慮多個因素。最重要的當然是模型的大小。一般來説,較大的模型能更多地受益於 GPU 提供的強大算力,而較小的模型可以在 CPU 上高效運行。

另一個需要考慮的因素是模型和推理任務本身的並行度。GPU 為大規模並行處理而設計,因此它們可能對那些可以高度並行化的任務更高效。而另一方面,如果模型或推理任務並沒有特別高的並行度,CPU 可能是更有效的選擇。

成本也是一個需要考慮的重要因素。GPU 可能很昂貴,而使用 CPU 可能是一種性價比更高的選擇,尤其是在業務應用並不需要極低延遲的情況下。此外,如果你需要能夠輕鬆擴縮推理實例的數量,或者如果你需要能夠在各種平台上進行推理,使用 CPU 可能是更靈活的選擇。

現在,讓我們開始配置我們的測試服務器。

配置我們的測試服務器

和上一篇文章一樣,我們將使用 Amazon EC2 實例:

  • 一個基於 Ice Lake 架構 c6i.16xlarge 實例,
  • 一個基於 Sapphire Rapids 架構的 r7iz.16xlarge-metal 實例。你可以在 AWS 網站上獲取有關新 r7iz 系列的更多信息。

兩個實例都有 32 個物理核 (因此有 64 個 vCPU)。我們將用相同的方式來設置它們:

  • Ubuntu 22.04 和 Linux 5.15.0 (ami-0574da719dca65348),
  • PyTorch 1.13 與 IPEX (Intel Extension for PyTorch) 1.13,
  • Transformers 4.25.1。

唯一的區別是在 r7iz 實例上我們多裝一個 Optimum Intel 庫。

以下是設置步驟。像往常一樣,我們建議使用虛擬環境來保證環境純淨。

sudo apt-get update

# 安裝 libtcmalloc,獲取更好性能
sudo apt install libgoogle-perftools-dev -y
export LD_PRELOAD="/usr/lib/x86_64-linux-gnu/libtcmalloc.so"

sudo apt-get install python3-pip -y
pip install pip --upgrade
export PATH=/home/ubuntu/.local/bin:$PATH
pip install virtualenv
virtualenv inference_env
source inference_env/bin/activate

pip3 install torch==1.13.0 -f http://download.pytorch.org/whl/cpu
pip3 install intel_extension_for_pytorch==1.13.0 -f http://developer.intel.com/ipex-whl-stable-cpu
pip3 install transformers

# 僅需在 r7iz 實例上安裝
pip3 install optimum [intel]

在兩個實例上完成上述步驟後,我們就可以開始運行測試了。

對流行的 NLP 模型進行基準測試

在這個例子中,我們將在文本分類任務上對幾個 NLP 模型進行基準測試: distilbert-base-uncased, bert-base-uncasedroberta-base。你可以在 Github 上找到 完整腳本。當然,你也可以用你自己的模型隨意嘗試!

models = ["distilbert-base-uncased", "bert-base-uncased", "roberta-base"]

我們使用序列長度分別為 16 和 128 的兩種句子來測試,同時我們也將在這兩種句子上分別測量單句推理和批量推理的平均預測延遲和 p99 預測延遲。該測試方案模擬了真實場景,因此可以較好地近似在真實場景中的預期加速比。

sentence_short = "This is a really nice pair of shoes, I am completely satisfied with my purchase"
sentence_short_array = [sentence_short] * 8

sentence_long = "These Adidas Lite Racer shoes hit a nice sweet spot for comfort shoes. Despite being a little snug in the toe box, these are very comfortable to wear and provide nice support while wearing. I would stop short of saying they are good running shoes or cross-trainers because they simply lack the ankle and arch support most would desire in those type of shoes and the treads wear fairly quickly, but they are definitely comfortable. I actually walked around Disney World all day in these without issue if that is any reference. Bottom line, I use these as the shoes they are best; versatile, inexpensive, and comfortable, without expecting the performance of a high-end athletic sneaker or expecting the comfort of my favorite pair of slippers."
sentence_long_array = [sentence_long] * 8

基準測試功能非常簡單。在幾次預熱迭代後,我們使用 pipeline API 運行 1000 次預測,把預測時間存下來,並計算它們的均值和 p99 值。

import time
import numpy as np

def benchmark (pipeline, data, iterations=1000):
    # 預熱 100 次
    for i in range (100):
        result = pipeline (data)
    times = []
    for i in range (iterations):
        tick = time.time ()
        result = pipeline (data)
        tock = time.time ()
        times.append (tock - tick)
    return "{:.2f}".format (np.mean (times) * 1000), "{:.2f}".format (
        np.percentile (times, 99) * 1000
    )

在 c6i (Ice Lake) 實例上,我們只使用普通的 Transformers pipeline

from transformers import pipeline

for model in models:
    print (f"Benchmarking {model}")
    pipe = pipeline ("sentiment-analysis", model=model)
    result = benchmark (pipe, sentence_short)
    print (f"Transformers pipeline, short sentence: {result}")
    result = benchmark (pipe, sentence_long)
    print (f"Transformers pipeline, long sentence: {result}")
    result = benchmark (pipe, sentence_short_array)
    print (f"Transformers pipeline, short sentence array: {result}")
    result = benchmark (pipe, sentence_long_array)
    print (f"Transformers pipeline, long sentence array: {result}")

在 r7iz (Sapphire Rapids) 實例上,我們同時使用普通 pipeline 和 Optimum pipeline。在 Optimum pipeline 中,我們啟用 bfloat16 模式以利用到 AMX 指令,並將 jit 設置為 True 以使用即時編譯進一步優化模型。

   import torch
	from optimum.intel import inference_mode
	
	with inference_mode (pipe, dtype=torch.bfloat16, jit=True) as opt_pipe:
	    result = benchmark (opt_pipe, sentence_short)
	    print (f"Optimum pipeline, short sentence: {result}")
	    result = benchmark (opt_pipe, sentence_long)
	    print (f"Optimum pipeline, long sentence: {result}")
	    result = benchmark (opt_pipe, sentence_short_array)
	    print (f"Optimum pipeline, short sentence array: {result}")
	    result = benchmark (opt_pipe, sentence_long_array)
	    print (f"Optimum pipeline, long sentence array: {result}")

為簡潔起見,我們先看下 distilbert-base-uncased 的 p99 結果。你可以在文章末尾找到所有測例的完整結果。

如上圖所示,與上一代至強 CPU 相比,Sapphire Rapids 上單個句子的預測延遲加速了 60-65%。也就是説,由於結合了英特爾 Sapphire Rapids 平台以及 Hugging Face Optimum 的優化,你只需對代碼進行很少改動就可將預測速度提高 3 倍。

這讓我們即使在長文本序列上也可以達到 個位數的預測延遲。在 Sapphire Rapids 之前,這樣的性能只有通過 GPU 才能實現。

結論

第四代英特爾至強 CPU 提供了出色的推理性能,尤其是在與 Hugging Face Optimum 結合使用時。這是深度學習在更易得和更具成本效益的道路上的又一個進步,我們期待與英特爾的朋友們在這條道路上繼續合作。

以下是一些可幫助你入門的其他資源:

如果你有任何問題或反饋,我們很樂意在 Hugging Face 論壇 上與你交流。

感謝閲讀!

附錄: 完整結果

基準測試軟件環境:

  • Ubuntu 22.04 with libtcmalloc
  • Linux 5.15.0 patched for Intel AMX support
  • PyTorch 1.13 with Intel Extension for PyTorch
  • Transformers 4.25.1
  • Optimum 1.6.1
  • Optimum Intel 1.7.0.dev0

英文原文: http://hf.co/blog/intel-sapphire-rapids-inference

譯者: Matrix Yao (姚偉峯),英特爾深度學習工程師,工作方向為 transformer-family 模型在各模態數據上的應用及大規模模型的訓練推理。

審校、排版: zhongdongy (阿東)