作者 |?Timothée Lacroix
選擇正確的 LLM 推理?xiàng)R馕吨x擇適合你的任務(wù)的正確模型,并配以適當(dāng)?shù)耐评泶a在適當(dāng)?shù)?a href="http://www.brongaenegriffin.com/v/tag/1751/" target="_blank">硬件上運(yùn)行。本文介紹了流行的 LLM 推理堆棧和設(shè)置,詳細(xì)說明其推理的成本構(gòu)成;并討論當(dāng)前的開源模型以及如何充分利用它們,同時(shí)還涉及當(dāng)前開源服務(wù)棧中仍然缺失的功能,以及未來模型將解鎖的新功能。
本文源自 Mistral AI 首席技術(shù)官 Timothée Lacroix 的演講。他于 2015 年在 Facebook AI Research 擔(dān)任工程師,于 2016 年至 2019 年間與école des Ponts 合作完成了關(guān)于推薦系統(tǒng)的張量分解的論文。2023 年他成為 Mistral AI 的聯(lián)合創(chuàng)始人。Mistral AI 于近期發(fā)布了業(yè)內(nèi)首個(gè)開源 MoE 大模型 Mixtral-8x7B。
本次演講的很多內(nèi)容都基于我在網(wǎng)上找到的信息或通過對(duì)第一個(gè) LLaMA 版本模型進(jìn)行實(shí)驗(yàn)時(shí)的發(fā)現(xiàn)。我認(rèn)為,現(xiàn)在的 Mistral 更關(guān)注推理成本,而非訓(xùn)練成本。因此,我將分享推理成本的構(gòu)成、吞吐、時(shí)延及其影響因素。
很多人想要部署語言大模型,我將分享如何使用開源工具部署自己的語言大模型。當(dāng)然,你也可以使用一些出色的公共 API,但我對(duì)開源工具更感興趣,所以接下來我將深入討論部署一個(gè) 70 億參數(shù)模型的重要細(xì)節(jié)。我將分享的許多內(nèi)容也同樣適用于更大規(guī)模的模型,但那需要更多 GPU。
影響推理的指標(biāo)
我們將首先討論有哪些重要指標(biāo),以及這些指標(biāo)的影響因素,包括硬件和軟件層面。接下來,我將介紹一些能夠改善性能的技巧,據(jù)我所知,其中一些技巧還未獲得廣泛實(shí)現(xiàn)。我嘗試在各種不同的硬件上運(yùn)行了一系列模型,并嘗試獲得性能曲線,我認(rèn)為實(shí)例非常重要,所以我將通過這些數(shù)據(jù)得出結(jié)論。
首先,我們?cè)撽P(guān)注哪些指標(biāo)?第一是吞吐量,以每秒查詢數(shù)(Query/second)表示,我們希望在批處理作業(yè)中將這一指標(biāo)最大化,或者希望允許更多用戶使用我們的服務(wù)。第二是時(shí)延,以每詞元每秒(seconds/token)表示,即輸出下一個(gè)詞元所需的時(shí)間,這決定了你的應(yīng)用程序的速度和靈敏度。在 ChatGPT 中,這一速度相當(dāng)快。對(duì)于較小的模型,可以更輕松地實(shí)現(xiàn)快速響應(yīng),因此我們希望將這個(gè)值最小化以提升用戶體驗(yàn)。較為優(yōu)秀的閾值是每分鐘輸出 250 個(gè)單詞,我認(rèn)為這是人類的平均閱讀速度,只要你的時(shí)延低于這個(gè)值,用戶就不會(huì)感到無聊。第三是成本,毫無疑問,這一數(shù)值越低越好。
影響推理指標(biāo)的因素
現(xiàn)在我將深入探討這些指標(biāo)的影響因素。我只會(huì)談?wù)撟曰貧w解碼,即基于一批批詞元通過神經(jīng)網(wǎng)絡(luò)確定下一批詞元,這部分不包括處理查詢的第一部分。提示處理有時(shí)被稱為預(yù)填充(prefill)部分,我們會(huì)一次性將大量詞元輸入到神經(jīng)網(wǎng)絡(luò)中,這部分處理通常已經(jīng)經(jīng)過充分優(yōu)化,挑戰(zhàn)性相對(duì)較低。
考慮到這一點(diǎn),我們對(duì)大小為 P 的模型的推理感興趣??梢约僭O(shè) P 是 7B,為執(zhí)行一步推理,大約需要 2xPxBatch_size 的 FLOPs(浮點(diǎn)運(yùn)算數(shù))。在進(jìn)行這些浮點(diǎn)運(yùn)算時(shí),我們需要將整個(gè)模型加載到實(shí)際運(yùn)行計(jì)算的 GPU,并且需要一次性加載整個(gè)模型,即大致上需要的內(nèi)存搬運(yùn)(memory movement)量等于模型的參數(shù)數(shù)量。
這兩個(gè)數(shù)量有趣的地方在于,第一個(gè)數(shù)量受硬件浮點(diǎn)運(yùn)算能力的限制,即 GPU 可以實(shí)現(xiàn)的浮點(diǎn)運(yùn)算次數(shù),并且與批大小呈線性關(guān)系,在上述圖表上呈增長趨勢。除非批大小特別大,內(nèi)存移動(dòng)量并不隨批大小而變化。但正如我所說,這種情況已經(jīng)得到了相當(dāng)程度的優(yōu)化,所以我們并不太關(guān)心內(nèi)存移動(dòng)量。我們還有一個(gè)常量,即模型大小除以內(nèi)存帶寬,這是一次性加載整個(gè)模型所需的最短時(shí)間,每次都需要重新執(zhí)行這個(gè)操作。
還有一個(gè)與批次大小有關(guān)的數(shù)量,它們?cè)谝粋€(gè)有趣的點(diǎn)上相交。這個(gè)點(diǎn)不取決于硬件之外的任何因素。舉例來說,在 A10G 和 A100 上,硬件可以實(shí)現(xiàn)的總浮點(diǎn)運(yùn)算次數(shù)的兩倍除以內(nèi)存帶寬為 400。
B*這個(gè)批大小非常有趣,因?yàn)榈陀谶@一批大小,基本上是在浪費(fèi) FLOPs,因?yàn)橛?jì)算受到了內(nèi)存限制,我們?cè)诘却?GPU 加載數(shù)據(jù),而計(jì)算速度太快,圖中某部分的時(shí)延是恒定的。如果超過這個(gè) B*這個(gè)閾值,時(shí)延就會(huì)開始增加,就變成了計(jì)算受限。
因此,B* 的真正優(yōu)勢在于,這個(gè)批大小的時(shí)延范圍是最優(yōu)的,因此用戶體驗(yàn)是最佳的,同時(shí)也沒有浪費(fèi)任何 FLOPs。
不管怎樣,我們理想的批大小 B* 是 400,這個(gè)值似乎相當(dāng)大,所以我們來計(jì)算一下 LLaMA 等模型規(guī)模的幾項(xiàng)指標(biāo)。LLaMA 模型有 4K 個(gè)維度,深度 32 層,模型大小很容易計(jì)算,在 FP16 中每個(gè)模型權(quán)重占兩個(gè)字節(jié),所以只需 2x7=14GB 內(nèi)存。
然后,我們用 KV 緩存存儲(chǔ)計(jì)算結(jié)果,這樣當(dāng)我們重新編碼一個(gè)新詞元時(shí),就不必重新從頭計(jì)算。KV 緩存的大小為 2,包括 K 緩存和 V 緩存,且使用 FP16 格式,每個(gè)都乘以 2,然后每層有一個(gè) KV 緩存,并且必須為批次中的每個(gè)元素保存數(shù)據(jù),每個(gè)位置在序列中表示一個(gè)詞元,然后乘以維度。
把實(shí)際數(shù)值代入這個(gè)公式發(fā)現(xiàn),每個(gè)批次元素需要約 2G 內(nèi)存才能支持最大長度 4K,因此,在 A10(24GB 內(nèi)存)上,我們的最大批大小約為 5,在更大的 A100(80GB 內(nèi)存)上,最大批大小只有 33 左右,這仍遠(yuǎn)低于理想值 400。
因此,對(duì)于所有實(shí)際用例,使用 70 億參數(shù)的模型進(jìn)行推理時(shí),解碼過程將嚴(yán)重受限于內(nèi)存帶寬。這也證明了 Mistral 從一開始就非常謹(jǐn)慎的一點(diǎn):模型和 KV 緩存所占內(nèi)存的大小確實(shí)影響了可允許的最大批大小,而最大批大小直接決定了效率的高低。
實(shí)用技巧
現(xiàn)在我將深入討論一些已經(jīng)存在但我個(gè)人很喜歡的技巧。其中一部分已經(jīng)為 Mistral 所用,其他一些尚未在 Mistral 中得到應(yīng)用,還有些則更多地涉及軟件部署層面。
分組查詢注意力
第一個(gè)技巧是分組查詢注意力。分組查詢注意力是通過每個(gè)查詢使用更少的鍵和值來減少 KV 緩存的方法。這在 LLaMA 2 中使用過,但只用于較大的模型尺寸,而非 70 億參數(shù)模型。在標(biāo)準(zhǔn)的多頭注意力中,有多少查詢,就有多少鍵和值。而在分組查詢注意力中,一對(duì)鍵值與一組查詢相關(guān)聯(lián)。在 Mistral,我們的每個(gè)鍵和值使用四個(gè)查詢,因此要執(zhí)行的浮點(diǎn)運(yùn)算量將保持不變,但內(nèi)存開銷只有原來的四分之一。這是一個(gè)簡單的技巧,不會(huì)對(duì)性能造成實(shí)質(zhì)性損害,這一做法很不錯(cuò)。
量化
第二個(gè)技巧是量化,對(duì)此我們并沒有進(jìn)行專門研究,但尤其在 LLaMA 發(fā)布后,這項(xiàng)技術(shù)發(fā)展得非常迅速。很多優(yōu)秀的現(xiàn)成解決方案為許多開源社區(qū)的人所使用,提供了模型的 int8 或 int4 版本。使用 int8 時(shí),模型尺寸會(huì)減半,在使用 int4 時(shí),會(huì)減少至四分之一。
這不會(huì)改變最優(yōu)批大小,因?yàn)檫@一比率只取決于硬件,與其他因素?zé)o關(guān)。就計(jì)算速度而言,量化后的速度為原來的兩倍,但我們發(fā)現(xiàn),對(duì)于 Mistral 模型規(guī)模以及其他模型,很難達(dá)到這個(gè)速度,如果以純浮點(diǎn)運(yùn)算量衡量,1.5 倍的速度更為合理。使用 int8 還會(huì)機(jī)械地增加 KV 緩存的可用內(nèi)存。
因此,如果你處于內(nèi)存受限的狀態(tài),一切操作都會(huì)快兩倍,這很不錯(cuò)。另一個(gè)好處是,int8 幾乎沒有或者只有極小的精度損失,而在 int4 下會(huì)有一些性能損失,但似乎可以通過 QLoRA 來恢復(fù),或者如果你只關(guān)心特定用例,那么我認(rèn)為這也可以正常運(yùn)作,且 serving 成本會(huì)低得多。
分頁注意力(Paged Attention)
第三個(gè)技巧是分頁注意力,由來自伯克利的 vLLM 專家提出。沒有分頁注意力的 KV 緩存是矩形的,需要分配一個(gè)大矩形內(nèi)存,其中一個(gè)維度是批大小,即模型一次可以處理的最大序列數(shù),另一個(gè)維度是,允許用戶使用的最大序列長度。當(dāng)一個(gè)新序列進(jìn)來時(shí),會(huì)為這個(gè)用戶分配一整行內(nèi)存,但這并不理想,因?yàn)橛脩糁泻芸赡苤挥?10% 會(huì)使用整行內(nèi)存,而大多數(shù)用戶可能只會(huì)發(fā)起短請(qǐng)求。因此,這最終會(huì)浪費(fèi)硬件內(nèi)存中的大量寶貴空間。
分頁注意力的作用是在 GPU 內(nèi)存中分配塊(block)。首先,加載模型以了解剩余空間大小,然后用內(nèi)存塊填充剩余部分。這些塊可以容納多達(dá) 16 到 32 個(gè)詞元,當(dāng)新序列到來時(shí),就可以為 prompt 分配所需的內(nèi)存塊,然后根據(jù)需要逐漸擴(kuò)展。
在上述示意圖中,可以看到序列并不一定分配在連續(xù)的內(nèi)存塊上,例如橙色、藍(lán)色或綠色并不在連續(xù)的塊上,這并不重要。這種方式能夠更精細(xì)地控制內(nèi)存分配,因此在示意圖中,右側(cè)完全空閑的部分可以用于新來的序列,一旦序列解碼完成,就可以釋放已使用的塊,非常高效。分頁注意力的提出者稱,與標(biāo)準(zhǔn)的實(shí)現(xiàn)方法相比,分頁注意力可以增加約 20 倍的吞吐量,這聽起來并不是那么遙不可及。
滑動(dòng)窗口注意力 (Sliding Window Attention)
我們?cè)?Mistral 中添加了一個(gè)技巧,即滑動(dòng)窗口注意力。通過這個(gè)技巧,我們可以訓(xùn)練模型在緩存中僅使用過去的 K 個(gè)詞元。這樣做的好處在于,我們可以使用一個(gè)固定的緩存大小。
眾所周知,一個(gè)序列一旦超過滑動(dòng)窗口的詞元數(shù)量,我們就可以在緩存中循環(huán)覆寫,從而重新開始,而這不會(huì)影響模型性能。
進(jìn)一步來說,通過這個(gè)技巧,我們可以使用比滑動(dòng)窗口更大的長下文長度。我們?cè)诓┛臀恼禄?GitHub 上對(duì)此進(jìn)行了簡要描述。
對(duì)于這個(gè)技巧的良好實(shí)現(xiàn)是將 KV 緩存看作是一個(gè)循環(huán)緩沖區(qū)。在上圖中的 t 時(shí)刻,我們?cè)诰彺娴淖詈笪恢貌迦?;?t+1 時(shí)刻,由于序列超出了滑動(dòng)窗口,所以只進(jìn)行了覆寫操作。這種實(shí)現(xiàn)非常簡單,因?yàn)榫彺嬷械奈恢貌⒉恢匾信c位置相關(guān)的信息都通過位置嵌入進(jìn)行編碼??傊@種方法兼具易可實(shí)現(xiàn)性和有效性。
連續(xù)批處理(Continuous Batching)
還有一個(gè)技巧是連續(xù)批處理。正如我在前面提到的,預(yù)填充階段同時(shí)處理的詞元數(shù)量要比解碼階段多得多。因此,我們可以嘗試將這些詞元與解碼詞元一起進(jìn)行批處理。我在 vLLM 和 TGI 中都注意到了同一個(gè)問題,即它們沒有嘗試對(duì)預(yù)填充階段進(jìn)行分塊處理。如果一個(gè)用戶向模型發(fā)送一個(gè)包含 4K 詞元的提示,這將增加所有用戶的時(shí)延,因?yàn)槲覀冃枰ㄙM(fèi)大量時(shí)間一次性處理這些詞元。
這其實(shí)是一種浪費(fèi),因?yàn)檫@時(shí)模型就不再處于既能實(shí)現(xiàn)低時(shí)延,又能充分利用計(jì)算資源的最佳狀態(tài)。因此,我建議在這些軟件中對(duì)預(yù)填充進(jìn)行分塊處理,這樣我們一次只處理 K 個(gè)詞元。這種方法能夠更加精細(xì)地分配資源,并且能夠更好地對(duì)解碼和預(yù)填充進(jìn)行批處理。
代碼
最后一種技巧是代碼。在處理這些規(guī)模的模型時(shí),代碼性能非常重要。通常,我們可以觀察到 Python 代碼的開銷很大。雖然我沒有詳細(xì)分析過 vLLM 和 TGI 的性能,但它們運(yùn)行的是 Python 代碼,根據(jù)經(jīng)驗(yàn),在這些規(guī)模下通常會(huì)存在一定的額外開銷。我們可以采取一些方法,在不影響 Python 大部分優(yōu)點(diǎn)的前提下緩解這一問題。
xFormers 庫就是一個(gè)很好的示例,它使用 CUDA 圖實(shí)現(xiàn)了零開銷。NVIDIA 的 TensorRT 可以通過追蹤推理并利用模式匹配來自動(dòng)提高性能。此外,我們還可以使用自定義內(nèi)核(如融合)來減少內(nèi)存帶寬,這樣可以避免在內(nèi)存中來回移動(dòng)數(shù)據(jù)。在數(shù)據(jù)已加載的情況下,我們可以執(zhí)行激活等操作,通??梢哉业郊せ詈瘮?shù)等優(yōu)化技巧,然后輕松地將它們插入到代碼中。
總之,驅(qū)動(dòng)這些性能指標(biāo)的因素主要是硬件中的固定浮點(diǎn)運(yùn)算與內(nèi)存帶寬之間的比率。這給出了最小批大小 B*,以充分利用硬件資源,避免浪費(fèi)不必要的浮點(diǎn)運(yùn)算。這個(gè)大小主要由硬件決定,不太受模型影響,除非你使用了 Transformer 之外的非傳統(tǒng)架構(gòu)。由于設(shè)備的內(nèi)存有限,因此要達(dá)到最佳批大小并不容易。
我檢查了兩個(gè)用于部署模型的開源庫,它們?nèi)栽谶\(yùn)行 Python 代碼,在這一規(guī)模下,模型會(huì)產(chǎn)生很多額外開銷。我還研究了 Faster Transformer 項(xiàng)目,它沒有額外開銷,但部署起來會(huì)比較困難。上述信息主要來自博文《語言大模型的推理演算》。
不同配置下的吞吐、時(shí)延與成本
現(xiàn)在讓我們談?wù)勍掏铝?- 時(shí)延平面圖,這通常是我評(píng)判這些指標(biāo)的方式。在這個(gè)平面中,x 軸表示時(shí)延,y 軸表示吞吐量,我們主要關(guān)注上方和左方,即更好的吞吐量和更低的時(shí)延。
如果購買更好的硬件,會(huì)改變這一吞吐量 - 時(shí)延性能曲線。對(duì)于固定硬件,左下角區(qū)域是固定時(shí)延,即內(nèi)存受限區(qū)域。隨著批大小增加,系統(tǒng)從內(nèi)存受限區(qū)域轉(zhuǎn)變?yōu)橛?jì)算受限區(qū)域。如果購買更先進(jìn)的硬件,成本會(huì)更高,但吞吐量 - 時(shí)延上的所有曲線會(huì)整體向左上方移動(dòng)。
改進(jìn)代碼或采用更好的模型會(huì)在低時(shí)延區(qū)域產(chǎn)生顯著影響,增加吞吐量,這對(duì)大型批大小的影響較小,因?yàn)檫@時(shí)候優(yōu)化已經(jīng)相對(duì)容易。
下面是一些性能測試結(jié)果及免責(zé)聲明,這個(gè)測試是我在短時(shí)間內(nèi)完成的,因?yàn)槭褂?Mistral 和 LLaMA 等配置工具比較容易,我運(yùn)行了 vLLM 基準(zhǔn)測試腳本。我不確定這些結(jié)果是否是我能取得的最佳結(jié)果,但至少整體方向是正確的,下面是我復(fù)制粘貼過來的 Matplotlib 圖,以供參考。
上圖是 Mistral 和 LLaMA 的性能比較。圖中黑線表示人類的閱讀速度。
上圖是在同一模型中,A10 和 H100 這兩種硬件之間的比較。可以看到,盡管 H100 價(jià)格更高,但由于其卓越的性能,更換硬件是一種更明智的選擇,而不是繼續(xù)使用老硬件。
總的來說,使用開源代碼在小型實(shí)例上部署小型模型非常容易,無需任何額外操作就能取得良好的運(yùn)行效果。僅需約 15 美元 / 天(并不算太高的費(fèi)用),我們就可以在 A10 上使用 Mistral-7B 模型處理上百萬個(gè)請(qǐng)求。改變模型精度可能使服務(wù)的請(qǐng)求數(shù)量翻倍。
開源部署解決方案在易用性方面表現(xiàn)出色,我認(rèn)為在實(shí)際的模型代碼部分還有很多工作要做。此外我認(rèn)為,未來模型的速度會(huì)越來越快。
答聽眾問 問題 1:如何選擇用于特定模型的最佳處理器?
Timothe?e Lacroix : 我還沒有測試過專用的 AI 硬件,主要測試過一系列 GPU。我甚至還沒有在 MacBook 上運(yùn)行過模型,因?yàn)槟壳皼]有找到合適的用途,但后續(xù)我可能會(huì)嘗試。對(duì)于用戶而言,如果只是想與模型聊天,直接在 MacBook 上運(yùn)行更經(jīng)濟(jì)。當(dāng)每天需要處理的請(qǐng)求達(dá)到一百萬次時(shí),使用 A10 會(huì)非常劃算,相當(dāng)于每天 15 美元的費(fèi)用,如果用戶能夠負(fù)擔(dān)這一費(fèi)用,那么我建議選擇 A10 處理器,它易于部署,而且效果很好。
關(guān)于選擇何種規(guī)模的硬件,由于硬件在任何地方都很容易部署,我們可以從最便宜的硬件開始,如果沒有達(dá)到所需的吞吐量或速度,再考慮升級(jí)。
我曾提到,在考慮成本的情況下,相比使用一堆 A10 處理器,H100 是更明智的選擇。然而,我們也經(jīng)常面臨可用性問題。因此,我建議按照處理器的成本和可用性順序逐個(gè)嘗試。如果你嘗試使用這些處理器大約 20 分鐘,這樣做的成本相對(duì)較低,并且這大致是運(yùn)行基準(zhǔn)測試所需的最長時(shí)間。通過這種方式,你可以在短時(shí)間內(nèi)獲得特定用例的準(zhǔn)確成本和性能數(shù)據(jù),從而更好地選擇適合自己需求的處理器。
問題 2: 是否推薦使用 Mojo 來減少 Python 開銷?你是否嘗試過使用 Mojo?
Timothe?e Lacroix:完全沒有。我首次嘗試減少開銷是通過使用 CUDA 圖,雖然在調(diào)試過程中有一些困難,但隨著時(shí)間推移,情況已經(jīng)好轉(zhuǎn)了,XFormers 就是一個(gè)很好的例子。在未來,torch.compile 也許能有效降低 Python 開銷,但我不清楚它們?cè)谔幚砜勺冃蛄虚L度等方面的進(jìn)展如何??傊?,我非常推薦 CUDA 圖,這是我目前降低開銷的首選方法。
問題 3:如果我們想要 LLM 具備多語理解能力,但目前數(shù)據(jù)集主要是英文,相比起來,使用非英文數(shù)據(jù)進(jìn)行微調(diào)的效果并不理想,對(duì)于這種情況,最有效的策略是什么?
Timothe?e Lacroix:LLM 的一切能力都源自數(shù)據(jù),所以我們首先需要獲取目標(biāo)語言數(shù)據(jù)。所有 LLM 都是在維基百科上訓(xùn)練的,這為模型掌握多語能力打下了良好基礎(chǔ),這也解釋了為何模型可以在未經(jīng)特別訓(xùn)練的情況下理解一些法語。我認(rèn)為,讓模型掌握多語能力存在一種權(quán)衡,例如,如果模型在法語方面取得了進(jìn)步,就會(huì)略微損失其他語言能力,但這種損失并不明顯,是可以接受的,因?yàn)檎w而言,在其他語言上的性能提升可能更為顯著。
OneDiff 是一個(gè)開箱即用的圖片 / 視頻生成推理引擎。開源版最新功能:1. 切換圖片尺寸無需重新編譯(即沒有時(shí)間消耗);2. 更快地保存和加載圖;3. 更小的靜態(tài)內(nèi)存。
?
審核編輯:黃飛
?
評(píng)論