告別冷啟動——LoRA推理速度提升300%的秘訣

釋出日期:2023年12月5日
在 GitHub 上更新

簡而言之:我們根據使用者請求交換Stable Diffusion LoRA介面卡,同時保持基礎模型“熱”啟動,從而實現在多個使用者之間快速進行LoRA推理。您可以透過瀏覽我們的LoRA目錄並使用推理小部件來體驗這一點。

Inference Widget Example

在本部落格中,我們將詳細介紹我們是如何實現這一目標的。

我們成功地大幅提升了基於公共Diffusion模型的公共LoRA在Hub上的推理速度。這使我們能夠節省計算資源,並提供更快、更好的使用者體驗。

在給定模型上執行推理分為兩個步驟:

  1. 預熱階段——包括下載模型和設定服務(25秒)。
  2. 然後是推理任務本身(10秒)。

透過這些改進,我們將預熱時間從25秒縮短到3秒。現在我們能夠用不到5個A10G GPU為數百個不同的LoRA提供推理服務,同時使用者請求的響應時間從35秒減少到13秒。

讓我們更深入地探討如何利用Diffusers庫中開發的一些最新功能,透過一個單一的服務以動態方式為許多不同的LoRA提供服務。

LoRA

LoRA是一種微調技術,屬於“引數高效”(PEFT)方法家族,旨在減少受微調過程影響的可訓練引數數量。它提高了微調速度,同時減小了微調檢查點的大小。

我們不透過對所有權重進行微小更改來微調模型,而是凍結大部分層,只訓練注意力塊中的少數特定層。此外,我們透過將兩個較小矩陣的乘積新增到原始權重來避免觸及這些層的引數。這些小矩陣的權重在微調過程中得到更新,然後儲存到磁碟。這意味著模型的所有原始引數都得以保留,我們可以使用適配方法在頂部載入LoRA權重。

LoRA(低秩適應)這個名稱來源於我們提到的小矩陣。有關該方法的更多資訊,請參閱這篇博文原始論文

LoRA decomposition

上圖顯示了兩個較小的橙色矩陣,它們作為LoRA介面卡的一部分被儲存。我們稍後可以載入LoRA介面卡並將其與藍色基礎模型合併,以獲得黃色的微調模型。關鍵在於,解除安裝介面卡也是可能的,因此我們可以在任何時候恢復到原始的基礎模型。

換句話說,LoRA介面卡就像基礎模型的附加元件,可以按需新增和移除。而且由於A和B的低秩特性,它與模型大小相比非常輕量。因此,載入速度比載入整個基礎模型快得多。

例如,如果你檢視Stable Diffusion XL Base 1.0模型庫(它被廣泛用作許多LoRA介面卡的基礎模型),你會發現它的尺寸約為**7 GB**。然而,像這個典型的LoRA介面卡僅佔用**24 MB**的空間!

Hub上“藍色”基礎模型的數量遠少於“黃色”模型的數量。如果我們能快速地從藍色模型切換到黃色模型,反之亦然,那麼我們就可以用少數幾個不同的藍色部署來服務許多不同的黃色模型。

有關LoRA的更詳盡介紹,請參閱以下部落格文章:使用 LoRA 進行高效 Stable Diffusion 微調,或直接查閱原始論文

優勢

我們Hub上有大約**2500**個不同的公共LoRA。其中絕大多數(**約92%**)是基於Stable Diffusion XL Base 1.0模型的LoRA。

在實現資源共享之前,這意味著要為所有這些模型(例如,上圖中所有黃色的合併矩陣)部署一個專用服務;釋放並預留至少一個新的GPU。啟動服務並使其準備好為特定模型提供服務的時間約為**25秒**,在此之上還有推理時間(在A10G上,1024x1024的SDXL推理擴散,25個推理步長,約為**10秒**)。如果某個介面卡只是偶爾被請求,其服務就會被停止以釋放被其他服務搶佔的資源。

如果你請求一個不那麼受歡迎的LoRA,即使它像目前Hub上絕大多數介面卡一樣基於SDXL模型,也需要**35秒**來預熱並在第一次請求時獲得響應(後續請求只需推理時間,例如**10秒**)。

現在:請求時間已從35秒減少到13秒,因為介面卡將只使用少數幾個不同的“藍色”基礎模型(例如,Diffusion模型只有兩個重要的)。即使你的介面卡不那麼受歡迎,它的“藍色”服務也很可能已經預熱。換句話說,你很有可能避免25秒的預熱時間,即使你不經常請求你的模型。藍色模型已經下載並準備就緒,我們所要做的就是解除安裝上一個介面卡並載入新的介面卡,這隻需要**3秒**,正如我們下面所看到的。

總的來說,這需要更少的GPU來服務所有不同的模型,儘管我們已經有辦法在部署之間共享GPU以最大限度地利用其計算能力。在**2分鐘**的時間內,大約有**10個**不同的LoRA權重被請求。我們不是啟動10個部署並保持它們熱啟動,而是簡單地用1到2個GPU(如果請求爆發,則更多)來服務所有這些請求。

實現

我們在推理API中實現了LoRA的共享。當我們的平臺上一個可用模型被請求時,我們首先判斷它是否為LoRA。然後我們識別LoRA的基礎模型,並將請求路由到一個公共後端叢集,該叢集能夠處理對所述模型的請求。推理請求透過保持基礎模型“熱”啟動並在執行時載入/解除安裝LoRA來得到處理。透過這種方式,我們最終能夠複用相同的計算資源來同時服務許多不同的模型。

LoRA結構

在Hub中,LoRA可以透過兩個屬性識別:

Hub

LoRA將具有`base_model`屬性。這僅僅是LoRA所構建的模型,在執行推理時應該應用於此模型。

由於LoRA並非唯一具有此類屬性的模型(任何複製的模型都將具有該屬性),因此LoRA還需要一個`lora`標籤才能被正確識別。

為Diffusers載入/解除安裝LoRA 🧨

請注意,使用peft庫可以更無縫地實現本節所述的功能。有關更多詳細資訊,請參閱文件。原理與下文相同(從上圖示意圖中的藍色框到黃色框,反之亦然)。


Diffusers庫中使用4個函式來載入和解除安裝不同的LoRA權重。

load_lora_weightsfuse_lora 用於載入權重並將其與主層合併。請注意,在執行推理之前將權重與主模型合併可以使推理時間減少30%。

unload_lora_weightsunfuse_lora 用於解除安裝。

下面我們提供了一個示例,展示如何利用Diffusers庫快速將多個LoRA權重載入到基礎模型之上。

import torch


from diffusers import (
    AutoencoderKL,
    DiffusionPipeline,
)

import time

base = "stabilityai/stable-diffusion-xl-base-1.0"

adapter1 = 'nerijs/pixel-art-xl'
weightname1 = 'pixel-art-xl.safetensors'

adapter2 = 'minimaxir/sdxl-wrong-lora'
weightname2 = None

inputs = "elephant"
kwargs = {}

if torch.cuda.is_available():
    kwargs["torch_dtype"] = torch.float16

start = time.time()

# Load VAE compatible with fp16 created by madebyollin
vae = AutoencoderKL.from_pretrained(
    "madebyollin/sdxl-vae-fp16-fix",
    torch_dtype=torch.float16,
)
kwargs["vae"] = vae
kwargs["variant"] = "fp16"

model = DiffusionPipeline.from_pretrained(
    base, **kwargs
)

if torch.cuda.is_available():
    model.to("cuda")

elapsed = time.time() - start

print(f"Base model loaded, elapsed {elapsed:.2f} seconds")


def inference(adapter, weightname):
    start = time.time()
    model.load_lora_weights(adapter, weight_name=weightname)
    # Fusing lora weights with the main layers improves inference time by 30 % !
    model.fuse_lora()
    elapsed = time.time() - start

    print(f"LoRA adapter loaded and fused to main model, elapsed {elapsed:.2f} seconds")

    start = time.time()
    data = model(inputs, num_inference_steps=25).images[0]
    elapsed = time.time() - start
    print(f"Inference time, elapsed {elapsed:.2f} seconds")

    start = time.time()
    model.unfuse_lora()
    model.unload_lora_weights()
    elapsed = time.time() - start
    print(f"LoRA adapter unfused/unloaded from base model, elapsed {elapsed:.2f} seconds")


inference(adapter1, weightname1)
inference(adapter2, weightname2)

載入資料

以下所有數字均以秒為單位

GPU T4 A10G
基礎模型載入——未快取 20 20
基礎模型載入——已快取 5.95 4.09
介面卡1載入 3.07 3.46
介面卡1解除安裝 0.52 0.28
介面卡2載入 1.44 2.71
介面卡2解除安裝 0.19 0.13
推理時間 20.7 8.5

每次推理增加2到4秒,我們就可以服務許多不同的LoRA。然而,在A10G GPU上,推理時間大幅減少,而介面卡載入時間變化不大,因此LoRA的載入/解除安裝相對更耗時。

處理請求

為了處理推理請求,我們使用了這個開源社群映象

您可以在 TextToImagePipeline 類中找到之前描述的機制。

當請求LoRA時,我們將檢查已載入的LoRA,並僅在需要時進行更改,然後像往常一樣執行推理。透過這種方式,我們能夠為基礎模型和許多不同的介面卡提供服務。

下面是如何測試和請求此映象的示例。

$ git clone https://github.com/huggingface/api-inference-community.git

$ cd api-inference-community/docker_images/diffusers

$ docker build -t test:1.0 -f Dockerfile .

$ cat > /tmp/env_file <<'EOF'
MODEL_ID=stabilityai/stable-diffusion-xl-base-1.0
TASK=text-to-image
HF_HUB_ENABLE_HF_TRANSFER=1
EOF

$ docker run --gpus all --rm --name test1 --env-file /tmp/env_file_minimal -p 8888:80 -it test:1.0

然後在另一個終端向基礎模型和/或在Hugging Face Hub上找到的各種LoRA介面卡發出請求。

# Request the base model
$ curl 0:8888 -d '{"inputs": "elephant", "parameters": {"num_inference_steps": 20}}' > /tmp/base.jpg

# Request one adapter
$ curl -H 'lora: minimaxir/sdxl-wrong-lora' 0:8888 -d '{"inputs": "elephant", "parameters": {"num_inference_steps": 20}}' > /tmp/adapter1.jpg

# Request another one
$ curl -H 'lora: nerijs/pixel-art-xl' 0:8888 -d '{"inputs": "elephant", "parameters": {"num_inference_steps": 20}}' > /tmp/adapter2.jpg

批次處理呢?

最近發表了一篇非常有趣的論文,描述瞭如何透過對LoRA模型執行批次推理來提高吞吐量。簡而言之,所有推理請求將收集在一個批次中,與通用基礎模型相關的計算將一次性完成,然後計算剩餘的介面卡特定乘積。我們沒有實現這種技術(接近於text-generation-inference為LLMs採用的方法)。相反,我們堅持使用單個順序推理請求。原因是,我們觀察到批次處理對擴散模型不感興趣:吞吐量不會隨著批次大小顯著增加。在我們進行的簡單影像生成基準測試中,對於批次大小為8,吞吐量僅增加了25%,但延遲增加了6倍!相比之下,批次處理對於LLMs更有趣,因為您可以獲得8倍的順序吞吐量,而延遲僅增加10%。這就是我們沒有為擴散模型實現批次處理的原因。

結論:時間

透過使用動態LoRA載入,我們成功節省了計算資源並提升了Hub推理API的使用者體驗。儘管解除安裝先前載入的介面卡並載入我們感興趣的介面卡會額外花費時間,但由於服務程序通常已經啟動並執行,整個推理響應時間大大縮短了。

請注意,若要讓某個LoRA在Hub上受益於此推理最佳化,它必須是公開的、非限制訪問的,並且基於非限制訪問的公共模型。如果您將相同的方法應用於您的部署,請務必告訴我們!

社群

註冊登入 發表評論

© . This site is unofficial and not affiliated with Hugging Face, Inc.