#AIGC創新先鋒者征文大賽# 部署 LLMs 前如何計算與優化 GPU 內存需求? 原創 精華
??【本文正在參與 AI.x社區AIGC創新先鋒者征文大賽】??
??http://www.ekrvqnd.cn/aigc/2223.html??
編者按:想要部署大語言模型(LLMs),卻不知該如何估算所需的 GPU 內存?在項目預算有限的情況下,是否曾因為 GPU 內存估算不準而導致資源浪費或性能不足?這些問題不僅影響項目進度,還可能直接導致成本超支或服務質量下降。
本文作者憑借豐富的 LLM 部署經驗,深入剖析了 GPU 內存需求的計算方法。從模型參數到 KV 緩存,從激活值到系統開銷,文章全面而詳細地講解了各個組成部分的內存占用。文章還討論了內存管理面臨的挑戰,如內存碎片、過度分配和高級解碼算法帶來的額外需求。為解決這些問題,作者介紹了 PagedAttention 和 vLLM 等優化技術,當 GPU 內存不足時,還可以采用 Swapping 和 Recomputation 等優化策略。
作者 | Muhammad Saad Uddin
編譯 | 岳揚
將 LLMs 投入生產環境使用,會遇到諸多挑戰,尤其是想要為 LLMs 高效提供所需的計算資源時。有過此類經驗的人可能深有體會,GPU 內存是支持 LLMs 運行的一項關鍵資源。由于這些模型體積巨大,且推理過程具有動態性質,因此對 GPU 內存使用的規劃和優化提出了更高的要求。
Image by Author via DallE
出于以下幾個原因,準確估算 GPU 內存的需求至關重要:
- 成本效益:GPU資源成本高昂。高估內存需求會導致不必要的硬件支出,而低估內存需求則會導致系統故障或性能下降。
- 性能優化:合理的內存管理能夠保障模型的高效運行,從而快速響應用戶需求,并提高并發處理能力。
- 可擴展性:隨著業務需求的增長,準確掌握內存需求對于在不影響性能和不產生過高成本的情況下擴展服務至關重要。
然而,計算 LLMs 服務所需的 GPU 內存并非一件簡單的事。模型的大?。╩odel size)、序列長度(sequence lengths)、批處理數量(batch sizes)以及解碼算法(decoding algorithms)等多種因素,都會以復雜的方式影響內存使用。而且,傳統的內存分配方法常因內存碎片和鍵值(KV)緩存等動態內存組件的低效管理而造成大量浪費。
在本文中,我將盡可能詳細地解釋如何計算 LLMs 服務所需的 GPU 內存。我將分析影響內存使用的各部分,并根據模型參數和工作負載特征(workload characteristics),逐步介紹如何估算內存占用大小。同時,我還會探討 Paged Attention 和 vLLM 等先進的優化技術,這些技術能顯著降低內存消耗并提升處理能力。通過閱讀本文,你將能夠全面了解如何規劃和優化 LLMs 的 GPU 內存使用,從而在實際應用中實現高效且低成本的 LLMs 部署。
01 了解 LLM 推理過程中,主要消耗 GPU 內存的幾部分
要想掌握 GPU 內存的計算方法,最關鍵的是了解各部分如何占用 GPU 內存的。了解內存的使用去向有助于我們更好地規劃與優化資源。在 LLMs 推理過程中,主要消耗 GPU 內存的幾部分包括權重(模型參數)、鍵值緩存內存(Key-Value Cache Memory)、激活值(Activations)與臨時緩沖區(Temporary Buffers),以及系統開銷(Overheads)(如果你對并行處理或分布式計算有所研究,可能對這個概念已有一定的認識)。
1.1 模型參數(權重)
模型參數是神經網絡在訓練過程中學到的數值(權重(weights)和偏置(biases))。這些參數定義了模型如何處理輸入數據生成輸出。
模型大小對 GPU 內存的影響
- 直接關系:模型越大(參數越多),存儲這些權重所需的 GPU 內存就越多。
- 內存計算:在使用半精度(FP16)格式時,每個參數通常需要 2 個字節,這在推理過程中很常見,可以節省內存而不會明顯損失精度。
讓我們看看這些模型:
- 擁有 34.5 億參數的小型 LLM:
- 所需內存:34.5 億 × 2 字節 = 69 MB。單 GPU 即可輕松支持。
- 現在如果使用 llama2-13b 模型:
- 擁有 130 億參數,所需內存將是:130 億 × 2 字節 = 26 GB。這種情況下,需要一個擁有 40 GB內存的 A100 GPU。
- 如果我們看看據說擁有 1750 億參數的 GPT-3 模型:
- 所需內存:1750 億 × 2 字節 = 350 GB,我們至少需要 9 個 GPU 來僅存放模型權重。
請記住,對于 GPT-3 及其之后的模型,使用模型并行化(model parallelism)將模型參數分布到多個 GPU 上是十分必要的。
1.2 鍵值(KV)緩存內存
KV緩存存儲生成序列中每個 token 所需的中間表示。簡單來說,當模型每次生成一個 token 時,它需要記住之前的 tokens 以保持上下文。KV緩存存儲了目前為止生成的每個 token 的鍵(key)和值(value)向量,使模型能夠高效地處理過去的 tokens ,而無需重新計算。
工作原理:
- Key 和 Values:在注意力機制中,模型為每個 token 計算一個鍵向量和一個值向量。
- Storage:這些向量存儲在 KV 緩存中,并在后續步驟中用于生成新 tokens 。
序列長度(Sequence Length)和并發請求(Concurrent Requests)的影響:
- Longer Sequences:tokens 越多,KV緩存中的條目就越多,內存使用量增加。
- Multiple Users:同時服務多個請求會成倍增加所需的KV緩存內存。
計算每個 token 的 KV 緩存大小
讓我們來分析一下如何得出每個 token 的 KV 緩存大?。?/p>
- 每個 token 的 KV 緩存組件:
鍵向量(每層一個鍵向量)和值向量(每層一個值向量)
- 每個 token 的向量總數:
模型層數(L)× 隱藏層大?。℉):模型的深度 × 每個向量的維度。
再次以 llama-13b 模型為例,假設模型具有以下特征:
- 模型層數(L):40層
- 隱藏層大?。℉):5120維度(這種大小的模型中的常見維度)
- 計算每個 token 占用的內存:
i. 鍵向量:
- 總數量:L層 × H維度 = 40 × 5120 = 204,800 個
- 內存大?。?04,800 個 × 2字節(FP16)= 409,600字節(或400 KB)
ii. 值向量:
- 與鍵向量相同:也是400 KB
iii. 每個 token 的總KV緩存:
- 鍵向量 + 值向量:400 KB + 400 KB = 800 KB
現在考慮輸出內容為2000個 tokens 的情況:
800 KB/token × 2000 tokens = 每個輸出序列 1.6 GB
假如有 10 個并發請求(模型同時為 10 個用戶服務):1.6 GB/輸出序列 × 10 輸出序列 = 16 GB 的 KV 緩存內存
KV緩存隨著序列長度和并發請求數量的增加而線性增長。我從一篇論文[1]中了解到,KV緩存可以消耗多達 30% 甚至更多的GPU內存。
1.3 激活值和臨時緩沖區
激活值(Activations)是指推理過程中神經網絡層的輸出,而臨時緩沖區(temporary buffers)用于中間計算。激活值和緩沖區通常消耗的內存比模型權重和 KV 緩存要少。
它們可能使用大約 5-10% 的總 GPU 內存。
盡管它們的容量較小,但激活值對于模型計算每一層的輸出是十分必要的。它們在前向傳遞過程(forward pass)中被創建和丟棄,但仍需要足夠的內存分配。
1.4 內存開銷
額外的內存使用開銷來自于內存分配和使用的低效率。下面是對其的簡要介紹:
內存碎片:
- Internal Fragmentation:當分配的內存塊沒有被完全利用時產生。
- External Fragmentation:隨著時間的推移,空閑內存被分割成小塊,使得在需要時難以分配較大的連續內存塊。
計算過程中產生的中間步驟:
- 臨時數據:像矩陣乘法這樣的操作可能會創建消耗內存的臨時張量。
低效內存管理的影響:
- 性能降低:浪費的內存可能會限制系統可以處理的并發請求數量。
- 吞吐量降低:低效的內存管理可能會導致延遲并降低向用戶提供服務響應的整體速度。
示例:如果內存碎片在 40 GB GPU 上浪費了 20 %的內存,那么就有 8 GB 的內存本可以用來處理更多請求,但是現在被浪費了。
02 計算 GPU 內存需求
既然我們已經對關鍵內容有了足夠的了解,那么就不再拖延,直接計算完整的 GPU 內存需求!
逐步計算:
要計算任何模型的內存需求,幾乎以下內容都需要:了解模型權重、KV緩存、激活值和臨時緩沖區以及系統內存開銷。以 llama-2 13B 模型為例,公式為:
所需內存總量:模型權重 + KV緩存 + 激活值和系統內存開銷
對于 13 B 模型來說:
模型權重 = 參數數量 × 每個參數的字節數
總 KV 緩存內存 = 每個 token 的 KV 緩存內存 × 輸出序列長度 × 輸出序列數量
激活值和系統內存開銷 = GPU總內存的 5–10 %
激活值和系統內存開銷通常消耗模型參數和 KV 緩存使用的 GPU 總內存的大約 5–10 %。你可以額外分配目前計算出的總內存的 10 %作為這部分的內存消耗預留量。
模型權重 = 130 億 × 2 字節 = 26 GB
總 KV 緩存內存 = 800 KB × 8192* tokens × 10* 并發請求 = 66 GB
激活值和系統內存開銷 = 0.1 × (26 GB + 66GB) = 9.2 GB
*假設模型的輸出系列長度為 8192,有 10 個并行請求。
所需內存總量:26 GB + 66 GB + 9.2 GB = 101.2 GB
所以,運行 llama-2 7B 模型至少需要 3 個 A100 40GB GPU。
如果我想要托管一個 GPT-3 模型(我知道這很瘋狂;D),計算方法與此類似,但這次我會假設每次只處理一個請求,并使用 OPT-175B[2] 模型的大小( 96 層和每層 12288 維度)作為參考。
模型權重 = 1750 億 × 2 字節 = 350 GB
總 KV 緩存內存 = 4.5 MB × 8192 token × 1 并發請求 = 36 GB
激活值和系統內存開銷 = 0.1 × (350 GB + 36GB) = 38.6 GB
所需總內存:350 GB + 36 GB + 38.6 GB = 424.6 GB 幾乎需要 11 個 A100 ??。
如果假設 GPT-4 是一個擁有 1 萬億參數的模型,那么將需要 2.3 TB的內存。
根據有關模型大小和參數的公開信息,計算出的內存計算表如下所示:
Table calculated by Author
同樣,如果我將模型部署給許多用戶(比如 10 個)同時使用,計算出的內存計算表如下所示:
Table calculated by Author
在處理多個請求時,內存消耗明顯增加。主要是KV緩存大量增加,因為模型權重和系統內存開銷保持不變,KV 緩存會隨著 tokens 數量和并發請求的增加而大幅增加,矩陣的行數就會越多,從而直接增加內存消耗。
現在想象一下 OpenAI[3] 或 Anthropic[4] 的大模型擁有數百萬用戶的情況吧!!
03 使用 GPU 內存過程中遇到的挑戰及其優化策略
經過上述計算,我意識到如果不探討在部署大語言模型(LLMs)時遇到的一些挑戰,以及目前的研究是如何針對這些問題進行優化的,那么這篇文章將顯得不夠完整。對于我們許多人來說,了解、掌握高效的 GPU 內存管理技巧至關重要。下面我們簡要分析一下。
3.1 挑戰一:內存碎片與內存過度分配
在部署過程中,我們通常會靜態地為 KV cache 分配內存,為每個請求預留盡可能大的內存空間。這樣往往會導致內存過度分配,因為盡管實際的輸出序列往往更短,系統會為可能的最長輸出序列預留內存空間。
此外,內存碎片會降低有效的可用內存,從而限制系統同時處理的請求數量。內存碎片分為內部(Internal)和外部(External)兩種。內部內存碎片是指分配的內存塊未被充分利用,留下未使用的內存空間。而外部內存碎片則是指隨著時間推移,空閑內存被分割成多個小的且不連續的內存塊,這樣就難以在需要時分配足夠大的連續內存塊。
內存使用效率低下意味著 GPU 的計算資源并未得到充分利用。結果,系統受內存限制而非計算能力的限制,浪費了處理器性能。(這也是我們在并行或分布式系統中力求避免的問題)
3.2 挑戰二:解碼算法
大量的 LLM 應用都傾向于采用先進的解碼算法來優化輸出質量或是產生多樣化的輸出結果。盡管這些方法效果顯著,但它們也對內存管理提出了新的挑戰。以束搜索(Beam Search)為例,該算法會生成多個備選輸出序列(即“束(beams)”),并根據評分標準保留得分最高的輸出序列。這意味著,每個“束(beams)”都需要專屬的 KV 緩存空間,從而增加了內存使用量。同樣,Parallel Sampling 通過從模型生成的概率分布(probability distribution)中抽取樣本,一次性生成多個獨立輸出,每個輸出同樣需要獨立的 KV 緩存,這無疑進一步增加了內存消耗。
在動態內存分配這種情況下,解碼過程中“束(beams)”或樣本的數量可能會發生變化,從而導致不可預測的內存需求。在不產生內存碎片和過度內存開銷的情況下,動態地分配和釋放內存,成為一項技術挑戰。此外,這些解碼方法可能會成倍增加內存需求,有時甚至超出了 GPU 的處理能力。如果 GPU 內存不足,系統可能不得不將數據轉移到速度較慢的 CPU 內存或硬盤上,這無疑會延長處理時間。
面對這些問題,我們可能會思考:
我們該如何突破這些限制?
3.3 PagedAttention
受操作系統內存管理方式的啟發,PagedAttention 技術將虛擬內存的分頁原理應用于 KV 緩存的管理。這種方法使得 KV 緩存數據不必占據一大塊連續的內存空間,而是可以分散存儲于多個不連續的內存頁面上。PagedAttention 采用動態內存分配策略,即根據實際需求為 KV 緩存分配內存,無需提前預留出最大輸出序列長度所需的內存。這樣的注意力機制能夠順暢地從不同內存地址中檢索 KV 緩存數據。
PagedAttention 的優勢在于,通過使用較小的內存塊,有效減少了內存碎片,降低了因內存碎片導致的內存浪費,從而提升了內存的整體使用率。
3.4 vLLM
簡單來說,vLLM 是一個基于 PagedAttention 構建的高吞吐量 LLM 服務系統。其核心目的是在推理過程中高效管理 GPU 內存,尤其是 KV 緩存。從理論上看,vLLM 幾乎實現了零內存浪費的解決方案。通過內存的動態分配和非連續存儲,它幾乎消除了內存浪費。理論上,它還支持在單個請求內部和跨請求之間共享 KV 緩存數據,這對于高級解碼方法尤其有用。在此基礎上,vLLM 能夠處理更大的批處理數量和更多的并發請求,從而提升整體性能。
即使進行了優化,有時也可能出現 GPU 內存不足的情況。vLLM 可通過 swapping 和 recomputation 來應對這個問題。讓我們進一步了解這一機制。
3.5 Swapping KV Cache to CPU Memory
- Swapping:當 GPU 內存滿載時,系統會將 KV 緩存數據從 GPU 內存臨時轉移到 CPU 內存中。
- 優點:
- 內存釋放(Memory Relief):通過將數據移出 GPU 內存,可以為新的請求騰出空間,確保 GPU 內存不會因為資源不足而阻礙新任務的執行。
- 代價:
- 延遲增加:由于 CPU 內存的訪問速度通常低于 GPU 內存,因此從 CPU 內存讀取數據會比從 GPU 內存讀取數據更加耗時。
- 數據傳輸開銷:在 GPU 內存和 CPU 內存之間轉移數據需要消耗帶寬和處理器時間。
3.6 Recomputation
不存儲所有 KV 緩存數據,而是在需要時按需重新計算。
- 優點:
- 減少內存使用:在內存中需要存儲的數據量減少。
- 代價:
- 增加計算量:重新計算數據需要額外的處理能力。
- 延遲影響:由于增加的額外計算量,可能會導致響應時間變長。
Swapping 和 Recomputation 兩種方法比較表
Table by Author
單獨使用 Swapping 或 Recomputation 可能各有優缺點,但是將兩者結合起來使用,可以相互彌補對方的不足,從而在節省內存、減少計算量、降低延遲等方面達到一個較為理想的平衡狀態。
Thanks for reading!
Hope you have enjoyed and learned new things from this blog!
About the authors
Muhammad Saad Uddin
Data Science is poetry of math where data is your love and its on you how you write your verses to show world your poetic expressions with utmost clarity?.
END
本期互動內容 ??
?你是否遇到過因為模型過大而導致的 GPU 內存不足問題?
??文中鏈接??
[1]??https://arxiv.org/pdf/2309.06180??
[2]??https://medium.com/@plienhar/llm-inference-series-4-kv-caching-a-deeper-look-4ba9a77746c8??
[4]??https://www.anthropic.com/??
原文鏈接:
