選擇正確的LLM推理棧意味著選擇適合你的任務的正確模型,並配以適當的推理程式碼在適當的硬件上執行。本文介紹了流行的LLM推理堆疊和設定,詳細說明其推理的成本構成;並討論當前的開源模型以及如何充分利用它們,同時還涉及當前開源服務棧中仍然缺失的功能,以及未來模型將解鎖的新功能。
本文源自Mistral AI技術長Timothée Lacroix的演講。他於2015年在Facebook AI Research擔任工程師,於2016年至2019年間與École des Ponts合作完成了關於推薦系統的張量分解的論文。2023年他成為Mistral AI的聯合創始人。Mistral AI於近期釋出了業內首個開源MoE大模型Mixtral-8x7B。
(以下內容由OneFlow編譯釋出,轉載請聯系授權。地址:https://www.youtube.com/watch?v=mYRqvB1_gRk)
作者 | Timothée Lacroix
OneFlow編譯
轉譯|宛子琳、楊婷
本次演講的很多內容都基於我在網上找到的資訊或透過對第一個LLaMA版本模型進行實驗時的發現。我認為,現在的Mistral更關註推理成本,而非訓練成本。因此,我將分享推理成本的構成、吞吐、時延及其影響因素。
很多人想要部署語言大模型,我將分享如何使用開源工具部署自己的語言大模型。當然,你也可以使用一些出色的公共API,但我對開源工具更感興趣,所以接下來我將深入討論部署一個70億參數模型的重要細節。我將分享的許多內容也同樣適用於更大規模的模型,但那需要更多GPU。
1
影響推理的指標
我們將首先討論有哪些重要指標,以及這些指標的影響因素,包括硬件和軟件層面。接下來,我將介紹一些能夠改善效能的技巧,據我所知,其中一些技巧還未獲得廣泛實作。我嘗試在各種不同的硬件上執行了一系列模型,並嘗試獲得效能曲線,我認為例項非常重要,所以我將透過這些數據得出結論。
首先,我們該關註哪些指標?第一是吞吐量,以每秒查詢數(Query/second)表示,我們希望在批次處理作業中將這一指標最大化,或者希望允許更多使用者使用我們的服務。第二是時延,以每詞元每秒(seconds/token)表示,即輸出下一個詞元所需的時間,這決定了你的應用程式的速度和靈敏度。在ChatGPT中,這一速度相當快。對於較小的模型,可以更輕松地實作快速響應,因此我們希望將這個值最小化以提升使用者體驗。較為優秀的閾值是每分鐘輸出250個單詞,我認為這是人類的平均閱讀速度,只要你的時延低於這個值,使用者就不會感到無聊。第三是成本,毫無疑問,這一數值越低越好。
2
影響推理指標的因素
現在我將深入探討這些指標的影響因素。我只會談論自回歸解碼,即基於一批批詞元透過神經網絡確定下一批詞元,這部份不包括處理查詢的第一部份。提示處理有時被稱為預填充(prefill)部份,我們會一次性將大量詞元輸入到神經網絡中,這部份處理通常已經經過充分最佳化,挑戰性相對較低。
考慮到這一點,我們對大小為P的模型的推理感興趣。可以假設P是7B,為執行一步推理,大約需要2xPxBatch_size的FLOPs(浮點運算數)。在進行這些浮點運算時,我們需要將整個模型載入到實際執行計算的GPU,並且需要一次性載入整個模型,即大致上需要的記憶體搬運(memory movement)量等於模型的參數數量。
這兩個數量有趣的地方在於,第一個數量受硬件浮點運算能力的限制,即GPU可以實作的浮點運算次數,並且與批大小呈線性關系,在上述圖表上呈增長趨勢。除非批大小特別大,記憶體移動量並不隨批大小而變化。但正如我所說,這種情況已經得到了相當程度的最佳化,所以我們並不太關心記憶體移動量。我們還有一個常量,即模型大小除以記憶體頻寬,這是一次性載入整個模型所需的最短時間,每次都需要重新執行這個操作。
還有一個與批次大小有關的數量,它們在一個有趣的點上相交。這個點不取決於硬件之外的任何因素。舉例來說,在A10G和A100上,硬件可以實作的總浮點運算次數的兩倍除以記憶體頻寬為400。
B*這個批大小非常有趣,因為低於這一批大小,基本上是在浪費FLOPs,因為計算受到了記憶體限制,我們在等待GPU載入數據,而計算速度太快,圖中某部份的時延是恒定的。如果超過這個B*這個閾值,時延就會開始增加,就變成了計算受限。
因此,B*的真正優勢在於,這個批大小的時延範圍是最優的,因此使用者體驗是最佳的,同時也沒有浪費任何FLOPs。
不管怎樣,我們理想的批大小B*是400,這個值似乎相當大,所以我們來計算一下LLaMA等模型規模的幾項指標。LLaMA模型有4K個維度,深度32層,模型大小很容易計算,在FP16中每個模型權重占兩個字節,所以只需2x7=14GB記憶體。
然後,我們用KV緩存儲存計算結果,這樣當我們重新編碼一個新詞元時,就不必重新從頭計算。KV緩存的大小為2,包括K緩存和V緩存,且使用FP16格式,每個都乘以2,然後每層有一個KV緩存,並且必須為批次中的每個元素保存數據,每個位置在序列中表示一個詞元,然後乘以維度。
把實際數值代入這個公式發現,每個批次元素需要約2G記憶體才能支持最大長度4K,因此,在A10(24GB記憶體)上,我們的最大批大小約為5,在更大的A100(80GB記憶體)上,最大批大小只有33左右,這仍遠低於理想值400。
因此,對於所有實際用例,使用70億參數的模型進行推理時,解碼過程將嚴重受限於記憶體頻寬。這也證明了Mistral從一開始就非常謹慎的一點: 模型和KV緩存所占記憶體的大小確實影響了可允許的最大批大小,而最大批大小直接決定了效率的高低。
3
實用技巧
現在我將深入討論一些已經存在但我個人很喜歡的技巧。其中一部份已經為Mistral所用,其他一些尚未在Mistral中得到套用,還有些則更多地涉及軟件部署層面。
分組查詢註意力
第一個技巧是分組查詢註意力。分組查詢註意力是透過每個查詢使用更少的鍵和值來減少KV緩存的方法。這在LLaMA 2中使用過,但只用於較大的模型尺寸,而非70億參數模型。在標準的多頭註意力中,有多少查詢,就有多少鍵和值。而在分組查詢註意力中,一對鍵值與一組查詢相關聯。在Mistral,我們的每個鍵和值使用四個查詢,因此要執行的浮點運算量將保持不變,但記憶體開銷只有原來的四分之一。這是一個簡單的技巧,不會對效能造成實質性損害,這一做法很不錯。
量化
第二個技巧是量化,對此我們並沒有進行專門研究,但尤其在LLaMA釋出後,這項技術發展得非常迅速。很多優秀的現成解決方案為許多開源社區的人所使用,提供了模型的int8或int4版本。使用int8時,模型尺寸會減半,在使用int4時,會減少至四分之一。
這不會改變最優批大小,因為這一比率只取決於硬件,與其他因素無關。就計算速度而言,量化後的速度為原來的兩倍,但我們發現,對於Mistral模型規模以及其他模型,很難達到這個速度,如果以純浮點運算量衡量,1.5倍的速度更為合理。使用int8還會機械地增加KV緩存的可用記憶體。
因此,如果你處於記憶體受限的狀態,一切操作都會快兩倍,這很不錯。另一個好處是,int8幾乎沒有或者只有極小的精度損失,而在int4下會有一些效能損失,但似乎可以透過QLoRA來恢復,或者如果你只關心特定用例,那麽我認為這也可以正常運作,且serving成本會低得多。
分頁註意力(Paged Attention)
第三個技巧是分頁註意力,由來自柏克萊的vLLM專家提出。沒有分頁註意力的KV緩存是矩形的,需要分配一個大矩形記憶體,其中一個維度是批大小,即模型一次可以處理的最大序列數,另一個維度是,允許使用者使用的最大序列長度。當一個新序列進來時,會為這個使用者分配一整行記憶體,但這並不理想,因為使用者中很可能只有10%會使用整行記憶體,而大多數使用者可能只會發起短請求。因此,這最終會浪費硬件記憶體中的大量寶貴空間。
分頁註意力的作用是在GPU記憶體中分配塊(block)。首先,載入模型以了解剩余空間大小,然後用記憶體塊填充剩余部份。這些塊可以容納多達16到32個詞元,當新序列到來時,就可以為prompt分配所需的記憶體塊,然後根據需要逐漸擴充套件。
在上述示意圖中,可以看到序列並不一定分配在連續的記憶體塊上,例如橙色、藍色或綠色並不在連續的塊上,這並不重要。這種方式能夠更精細地控制記憶體分配,因此在示意圖中,右側完全空閑的部份可以用於新來的序列,一旦序列解碼完成,就可以釋放已使用的塊,非常高效。分頁註意力的提出者稱,與標準的實作方法相比,分頁註意力可以增加約20倍的吞吐量,這聽起來並不是那麽遙不可及。
滑動視窗註意力(Sliding Window Attention)
我們在Mistral中添加了一個技巧,即滑動視窗註意力。透過這個技巧,我們可以訓練模型在緩存中僅使用過去的K個詞元。這樣做的好處在於,我們可以使用一個固定的緩存大小。
眾所周知,一個序列一旦超過滑動視窗的詞元數量,我們就可以在緩存中迴圈覆寫,從而重新開始,而這不會影響模型效能。
進一步來說,透過這個技巧,我們可以使用比滑動視窗更大的長下文長度。我們在網誌文章或GitHub上對此進行了簡要描述。
對於這個技巧的良好實作是將KV緩存看作是一個迴圈緩沖區。在上圖中的t時刻,我們在緩存的最後位置插入;在t+1時刻,由於序列超出了滑動視窗,所以只進行了覆寫操作。這種實作非常簡單,因為緩存中的位置並不重要,所有與位置相關的資訊都透過位置嵌入進行編碼。總之,這種方法兼具易可實作性和有效性。
連續批次處理(Continuous Batching)
還有一個技巧是連續批次處理。正如我在前面提到的,預填充階段同時處理的詞元數量要比解碼階段多得多。因此,我們可以嘗試將這些詞元與解碼詞元一起進行批次處理。我在vLLM和TGI中都註意到了同一個問題,即它們沒有嘗試對預填充階段進行分塊處理。如果一個使用者向模型發送一個包含4K詞元的提示,這將增加所有使用者的時延,因為我們需要花費大量時間一次性處理這些詞元。
這其實是一種浪費,因為這時模型就不再處於既能實作低時延,又能充分利用計算資源的最佳狀態。因此,我建議在這些軟件中對預填充進行分塊處理,這樣我們一次只處理K個詞元。這種方法能夠更加精細地分配資源,並且能夠更好地對解碼和預填充進行批次處理。
程式碼
最後一種技巧是程式碼。在處理這些規模的模型時,程式碼效能非常重要。通常,我們可以觀察到Python程式碼的開銷很大。雖然我沒有詳細分析過vLLM和TGI的效能,但它們執行的是Python程式碼,根據經驗,在這些規模下通常會存在一定的額外開銷。我們可以采取一些方法,在不影響Python大部份優點的前提下緩解這一問題。
xFormers庫就是一個很好的範例,它使用CUDA圖實作了零開銷。NVIDIA的TensorRT可以透過追蹤推理並利用模式匹配來自動提高效能。此外,我們還可以使用自訂內核(如融合)來減少記憶體頻寬,這樣可以避免在記憶體中來回流動數據。在數據已載入的情況下,我們可以執行啟用等操作,通常可以找到啟用函數等最佳化技巧,然後輕松地將它們插入到程式碼中。
總之,驅動這些效能指標的因素主要是硬件中的固定浮點運算與記憶體頻寬之間的比率。這給出了最小批大小B*,以充分利用硬件資源,避免浪費不必要的浮點運算。這個大小主要由硬件決定,不太受模型影響,除非你使用了Transformer之外的非傳統架構。由於器材的記憶體有限,因此要達到最佳批大小並不容易。
我檢查了兩個用於部署模型的開源庫,它們仍在執行Python程式碼,在這一規模下,模型會產生很多額外開銷。我還研究了Faster Transformer專案,它沒有額外開銷,但部署起來會比較困難。上述資訊主要來自博文【 語言大模型的推理演算 】。
3
不同配置下的吞吐、時延與成本
現在讓我們談談吞吐量-時延平面圖,這通常是我評判這些指標的方式。在這個平面中,x軸表示時延,y軸表示吞吐量,我們主要關註上方和左方,即更好的吞吐量和更低的時延。
如果購買更好的硬件,會改變這一吞吐量-時延效能曲線。對於固定硬件,左下角區域是固定時延,即記憶體受限區域。隨著批大小增加,系統從記憶體受限區域轉變為計算受限區域。如果購買更先進的硬件,成本會更高,但吞吐量-時延上的所有曲線會整體向左上方移動。
改進程式碼或采用更好的模型會在低時延區域產生顯著影響,增加吞吐量,這對大型批大小的影響較小,因為這時候最佳化已經相對容易。
下面是一些效能測試結果及免責聲明,這個測試是我在短時間內完成的,因為使用Mistral和LLaMA等配置工具比較容易,我執行了vLLM基準測試指令碼。我不確定這些結果是否是我能取得的最佳結果,但至少整體方向是正確的,下面是我復制貼上過來的Matplotlib圖,以供參考。
上圖是Mistral和LLaMA的效能比較。圖中黑線表示人類的閱讀速度。
上圖是在同一模型中,A10和p00這兩種硬件之間的比較。可以看到,盡管p00價格更高,但由於其卓越的效能,更換硬件是一種更明智的選擇,而不是繼續使用老硬件。
總的來說,使用開原始碼在小型例項上部署小型模型非常容易,無需任何額外操作就能取得良好的執行效果。僅需約15美元/天(並不算太高的費用),我們就可以在A10上使用Mistral-7B模型處理上百萬個請求。改變模型精度可能使服務的請求數量翻倍。
開源部署解決方案在易用性方面表現出色,我認為在實際的模型程式碼部份還有很多工作要做。此外我認為,未來模型的速度會越來越快。
4
答聽眾問
問題1:如何選擇用於特定模型的最佳處理器?
Timothée Lacroix : 我還沒有測試過專用的AI硬件,主要測試過一系列GPU。我甚至還沒有在MacBook上執行過模型,因為目前沒有找到合適的用途,但後續我可能會嘗試。對於使用者而言,如果只是想與模型聊天,直接在MacBook上執行更經濟。當每天需要處理的請求達到一百萬次時,使用A10會非常劃算,相當於每天15美元的費用,如果使用者能夠負擔這一費用,那麽我建議選擇A10處理器,它易於部署,而且效果很好。
關於選擇何種規模的硬件,由於硬件在任何地方都很容易部署,我們可以從最便宜的硬件開始,如果沒有達到所需的吞吐量或速度,再考慮升級。
我曾提到,在考慮成本的情況下,相比使用一堆A10處理器,p00是更明智的選擇。然而,我們也經常面臨可用性問題。因此,我建議按照處理器的成本和可用性順序逐個嘗試。如果你嘗試使用這些處理器大約20分鐘,這樣做的成本相對較低,並且這大致是執行基準測試所需的最長時間。透過這種方式,你可以在短時間內獲得特定用例的準確成本和效能數據,從而更好地選擇適合自己需求的處理器。
問題2: 是否推薦使用Mojo來減少Python開銷?你是否嘗試過使用Mojo?
Timothée Lacroix: 完全沒有。我首次嘗試減少開銷是透過使用CUDA圖,雖然在偵錯過程中有一些困難,但隨著時間推移,情況已經好轉了,XFormers就是一個很好的例子。在未來,torch.compile也許能有效降低Python開銷,但我不清楚它們在處理可變序列長度等方面的進展如何。總之,我非常推薦CUDA圖,這是我目前降低開銷的首選方法。
問題3:如果我們想要LLM具備多語理解能力,但目前數據集主要是英文,相比起來,使用非英文數據進行微調的效果並不理想,對於這種情況,最有效的策略是什麽?
Timothée Lacroix: LLM的一切能力都源自數據,所以我們首先需要獲取目標語言數據。所有LLM都是在維基百科上訓練的,這為模型掌握多語能力打下了良好基礎,這也解釋了為何模型可以在未經特別訓練的情況下理解一些法語。我認為,讓模型掌握多語能力存在一種權衡,例如,如果模型在法語方面取得了進步,就會略微損失其他語言能力,但這種損失並不明顯,是可以接受的,因為整體而言,在其他語言上的效能提升可能更為顯著。
OneDiff是一個開箱即用的圖片/影片生成推理引擎。開源版最新功能:1.切換圖片尺寸無需重新編譯(即沒有時間消耗);2.更快地保存和載入圖;3.更小的靜態記憶體。
地址:https://github.com/siliconflow/onediff
使用方法:https://github.com/siliconflow/onediff/releases/tag/0.12.0
歡迎 Star、試用 OneFlow 最新版本:
https://github.com/Oneflow-Inc/oneflow/