NVIDIA大語言模型落地的全流程解析
包括三部分內(nèi)容:
- 第一部分是關(guān)于 NeMo Framework。NeMo Framework 是 NVIDIA 大語言模型的全棧解決方案,它通過不同組件完成生成式 AI 各個環(huán)節(jié)的任務(wù),包括數(shù)據(jù)預(yù)處理、分布式訓(xùn)練、模型微調(diào)、模型推理加速及部署(TensorRT-LLM及Triton)、檢索增強(qiáng)生成(RAG)、護(hù)欄技術(shù)(Guardrails)等等。
- 第二部分是關(guān)于 TensorRT-LLM。TensorRT 從 2016 年推出以來,一直在不斷發(fā)展。隨著進(jìn)入 AI 2.0 大語言模型時代,NVIDIA 隆重推出了 TensorRT-LLM。在 TensorRT 的基礎(chǔ)上,TensorRT-LLM 對 LLM 部分進(jìn)行了相應(yīng)的封裝和增強(qiáng)。一個令人振奮的消息是,TensorRT-LLM 是開源的,采用 Apache-2.0 開源協(xié)議,對用戶非常友好。
- 第三部分是關(guān)于 RAG(Retrieval Augmented Generation),即檢索增強(qiáng)生成。在使用大語言模型時,存在一個問題,即模型在某些未掌握領(lǐng)域可能出現(xiàn)胡言亂語。為了解決這個問題,NVIDIA 引入了 RAG 技術(shù),用于增強(qiáng)和輔助實(shí)際大模型的應(yīng)用。
一、NeMo Framework
首先來介紹 NeMo Framework。
1. NeMo Framework Overview
這一輪生成式 AI 浪潮涵蓋的范圍非常廣泛,除了大家熟知的大語言模型之外,還包括文生圖、科學(xué)研究,和最近備受關(guān)注的多模態(tài)及不同模態(tài)切換等方面。生成式AI 不僅僅局限于大語言模型,還涵蓋了與深度神經(jīng)網(wǎng)絡(luò)相關(guān)的部分以及更廣泛的內(nèi)容。
聚焦于大語言模型領(lǐng)域,NVIDIA 推出的 NeMo Framework 涵蓋了大語言模型的開發(fā)、部署以及使用的全流程。整體包括六大部分,如上圖所示,其中前三部分涉及模型開發(fā),后三部分涉及企業(yè)應(yīng)用部署。
- 第一,數(shù)據(jù)處理和清洗,以形成高質(zhì)量數(shù)據(jù)集。
- 第二,分布式訓(xùn)練,其中涉及諸多技術(shù),后文中會詳細(xì)討論。
- 第三,模型定制化,針對不同的場景或行業(yè)進(jìn)行相應(yīng)的適配工作。
- 第四,推理加速。在這方面,NVIDIA 有兩個非常重要的產(chǎn)品,一個是用于單個模型優(yōu)化加速的 TensorRT-LLM,另一個是用于直接進(jìn)行模型部署的 Triton。
- 第五,RAG(Retrieval Augmented Generation),召回和增強(qiáng)生成部分。涉及知識庫、向量庫以及 Agent,與大模型的最終落地息息相關(guān)。
- 第六,Guardrails,主要充當(dāng)守門員的角色。對于一些常見的問題,比如對時政等問題首先進(jìn)行過濾。
NeMo Framework 不僅僅用于大模型的訓(xùn)練和推理過程,也可以適配其他模態(tài),比如語音轉(zhuǎn)文字等不同場景。
2. NeMo Framework Key Components
接下來介紹 NeMo Framework 的核心組件。
首先是數(shù)據(jù)處理部分,這是一個關(guān)鍵環(huán)節(jié)。在實(shí)際場景中,原始數(shù)據(jù)集通常需要經(jīng)過多次處理。典型的處理步驟包括去重、清除一些低質(zhì)量的數(shù)據(jù)等。
對于去重,通常使用基于規(guī)則的方法。例如,如果發(fā)現(xiàn)某條數(shù)據(jù)中同一個詞語重復(fù)出現(xiàn)超過一定的頻率,我們就會將其視為不合適或低質(zhì)量的數(shù)據(jù),這就是基于規(guī)則的過濾方法。此外,我們還可以通過各種手段,包括訓(xùn)練模型的方式,來區(qū)分低質(zhì)量和高質(zhì)量的數(shù)據(jù)。通過這一系列手段對數(shù)據(jù)進(jìn)行處理的過程稱為數(shù)據(jù)預(yù)處理。
這一部分非常重要,因?yàn)閿?shù)據(jù)的質(zhì)量直接影響到最終模型的訓(xùn)練效果,我們必須注意確保數(shù)據(jù)預(yù)處理的質(zhì)量。
如上圖所示,經(jīng)過一些處理,比如去重、Quality filtering,通過對數(shù)據(jù)進(jìn)行高質(zhì)量的處理,模型效果得到了顯著的提升。右側(cè)梯度圖表示對數(shù)據(jù)進(jìn)行了不同處理后的效果,例如,經(jīng)過去重和基于規(guī)則的數(shù)據(jù)清洗,訓(xùn)練效果有了明顯的提升。目前,NeMo 提供了一個專門的工具 NeMo Data Curator,讓用戶評估并提高數(shù)據(jù)集的質(zhì)量。
完成數(shù)據(jù)處理后,接下來就要進(jìn)行模型訓(xùn)練。
NeMo 底層使用了 NVIDIA Megatron Core 技術(shù),對訓(xùn)練進(jìn)行了很多加速操作。因?yàn)樵诖笳Z言模型的預(yù)訓(xùn)練階段,性能要求相當(dāng)高,訓(xùn)練所需的資源和時間都是相當(dāng)龐大的,加速訓(xùn)練非常必要。在加速方面,有三種方法:
- 第一種是 Tensor 和 Pipeline 的并行。這意味著將模型的每一層 Tensor 做劃分,并讓每個 GPU 分別計(jì)算其中一部分 Tensor,以加速計(jì)算過程。
- 第二種是 Sequence 的并行,因?yàn)?nbsp;Dropout 和 LayerNorm 并不依賴完整 reduce 后的結(jié)果,所以可以拆分到不同 GPU 上運(yùn)行,以加速計(jì)算過程。
- 第三種是選擇性重計(jì)算或稱為選擇性激活重計(jì)算。對于激活函數(shù)并不需要每次都進(jìn)行重計(jì)算。激活層占用大量顯存,這一方法通過精心的選擇性計(jì)算一部分激活函數(shù),可以顯著減少顯存使用和重復(fù)計(jì)算,提高效率。
這些方法都是 Nemo 框架在訓(xùn)練中用來提升效率的手段。
NeMo Framework 還提供了一個自動選擇參數(shù)的工具,叫Auto-Configurator。
這個工具的使用非常簡便,用戶只需要提供一些訓(xùn)練時的限制,比如訓(xùn)練的時間限制、訓(xùn)練規(guī)模的設(shè)定等。將這些限制寫入一個文件中作為輸入,通過 Auto-Configurator 工具能夠自動生成合理的訓(xùn)練參數(shù)表。用戶可以使用輸出參數(shù)表進(jìn)行后續(xù)的訓(xùn)練,因此這個工具非常實(shí)用。
大模型訓(xùn)練可以分為不同的類型。在上圖中,從左到右分別是 Prompt Engineering、Prompt Learning、Parameter Efficient Fine-Tuning、Fine Tuning。這幾種訓(xùn)練類型在資源消耗和對參數(shù)的改變等方面都存在很大差異,效果也有很大的不同。
NeMo Framework 對這四種類型的 Tuning 都是支持的。這里特別提一下,SFT 和 RLHF 會對模型所有參數(shù)都進(jìn)行改變。在這四種微調(diào)中,SFT 和 RLHF 所需要的資源是最大的。
上圖中提到了 LoRA 技術(shù)。該技術(shù)通過矩陣的分解,將一個大的參數(shù)矩陣分解成兩個矩陣相乘。其中大矩陣保持不變,而小矩陣在訓(xùn)練時進(jìn)行改變。這樣就能以最小的參數(shù)代價(jià)達(dá)到最佳效果。
3. NeMo Framework Deployment Practice
下面介紹在使用 NeMo Framework 時的一些最佳實(shí)踐。
一般來說,訓(xùn)練一個模型需要經(jīng)過幾個步驟。
第一步,使用 NeMo Data Curator 對數(shù)據(jù)集進(jìn)行清洗。
第二步,配置參數(shù)。配置好參數(shù)后,可以直接使用 NeMo Framework Launcher。這個 Launcher 實(shí)際上是由許多腳本或者 E2E recipes 組成的。這些 recipes 中,訓(xùn)練參數(shù)可以進(jìn)行自定義設(shè)置,比如需要運(yùn)行多少步,每多少步保存一次 checkpoint 等。配置好這些參數(shù)后,只需啟動相應(yīng)的腳本,訓(xùn)練就會開始。模型訓(xùn)練過程,可參見上圖。中間的框圖描述了 NeMo 在整個訓(xùn)練過程中的步驟。尤其是最耗時的 Pre-Train 過程。
第三步,Pre-Train 結(jié)束后會進(jìn)入模型效果對齊(aligner)階段,比如與場景相關(guān)的 align,以及一些類似強(qiáng)化學(xué)習(xí)的模型調(diào)整,NeMo 平臺也會提供支持。不管是 SFT、PEFT、Reward Model 還是 RLHF,NeMo 都可以通過 NeMo Framework Launcher 啟動不同的腳本,使用不同的參數(shù)選擇不同的階段來完成訓(xùn)練或者微調(diào)。
這里提到了 NeMo Training Container,NeMo Framework Launcher 實(shí)際上是一套腳本,通過參數(shù)配置控制 NeMo Training Container 容器進(jìn)行訓(xùn)練。在容器化后,可以很方便地進(jìn)行單節(jié)點(diǎn)多卡、單節(jié)點(diǎn)多 Pod,甚至多節(jié)點(diǎn)多卡等擴(kuò)縮容。尤其在擴(kuò)容方面非常方便,因?yàn)橛?xùn)練通常需要大量資源,可以由 NeMo Framework Launcher 進(jìn)行調(diào)配組織和協(xié)調(diào)容器資源。
當(dāng)訓(xùn)練過程結(jié)束后,就涉及到 Inference 階段。對于 Inference,NeMo 也提供了 Inference Container。這個容器將運(yùn)行 Triton 和 TensorRT-LLM,完成推理工作。
通常情況下,NeMo Training Container 運(yùn)行在 Slurm 或者是 K8S 集群上。K8S 目前也是支持的,但可能會有一些限制,Slurm 集群是比較主流的選擇。但推理部署通常是基于 K8S 的。
通常來說,訓(xùn)練可以分為三種類型。首先是預(yù)訓(xùn)練(Pre-training),其次是監(jiān)督微調(diào)(Supervised Fine Tuning,SFT),最后是提示學(xué)習(xí)(Prompt Learning)。這三種訓(xùn)練需要的資源是大相徑庭的。
- 預(yù)訓(xùn)練(Pre-training)是一種從頭開始訓(xùn)練的方式 Training from scratch,因此它需要的資源是最多的。通常情況下,它涉及到多個節(jié)點(diǎn),每個節(jié)點(diǎn)可能有 8 張顯卡,使用大規(guī)模集群來進(jìn)行訓(xùn)練。
- 監(jiān)督微調(diào)(Supervised Fine Tuning,SFT)相對來說資源需求較少,通常情況下不需要多節(jié)點(diǎn),但會使用多張顯卡進(jìn)行訓(xùn)練。
- 提示學(xué)習(xí)(Prompt Learning),只需要較少的資源,一般單卡的或者較小規(guī)模的資源就能夠完成訓(xùn)練。
二、TensorRT-LLM
1. TensorRT-LLM Optimizing LLM Inference
訓(xùn)練完成后,將進(jìn)入一個非常重要的步驟,即推理部分。下面就來介紹TensorRT-LLM。
TensorRT-LLM 的作用主要在于推理加速。簡而言之,推理加速主要關(guān)注兩個方面:延遲和吞吐。延遲指的是輸入一定數(shù)量的字符后,返回字符所需的時間;吞吐則涉及一次性輸入多個句子并返回推理結(jié)果的能力。因此,推理加速的目標(biāo)是盡量減少延遲,同時提高吞吐能力。
目前,TensorRT-LLM 已經(jīng)在 Github 上開源,我們也提供了詳盡的文檔,大家可以通過相關(guān)鏈接學(xué)習(xí)和使用。
在 NeMo 生態(tài)中,Triton Inference Server 主要負(fù)責(zé)整個模型的部署,而TensorRT-LLM 主要負(fù)責(zé)模型推理的加速,使模型推理能夠更加高效。
這里介紹下 TensorRT-LLM 和 TensorRT 之間的關(guān)系。TensorRT-LLM 實(shí)際上是基于 TensorRT 的,它對 LLM(語言模型)相關(guān)的一些操作進(jìn)行了一些優(yōu)化,但是很多 CUDA kernel 仍然來自于 TensorRT。
TensorRT-LLM 增加的優(yōu)化部分包括:
KV Caching,每次計(jì)算中,KV Caching 始終是一個較大的部分,因?yàn)檫@部分有很多無需進(jìn)行重復(fù)計(jì)算的內(nèi)容,需要將之前的計(jì)算結(jié)果保存在 Caching 中,這是一個非常重要的優(yōu)化。
其次,對于 MHA(Multi-Head Attention)kernels,也就是多頭注意力的 CUDA kernel,TensorRT-LLM 也做了增強(qiáng)。
除此之外,TensorRT-LLM 有 Inflight Batching。語言模型接收的每句話長度都不同,對于比較短的話,很快就結(jié)束了,是否可以在運(yùn)行時插入后面一句話進(jìn)行推理呢?這是語言模型的一個特點(diǎn),與視覺不同,視覺推理的每個請求基本上都具有相對固定的維度,正因?yàn)檎Z言模型存在每句話的長度都不同的特點(diǎn),因此需要 Inflight Batching 特性。
對于大型語言模型,即使在推理階段,也可能涉及到多個 GPU、多個節(jié)點(diǎn)等,因此 TensorRT-LLM 還擁有 Multi-GPU、Multi-Node 等功能。
除了這些特性之外,TensorRT-LLM 剩下的部分與 TensorRT 是一致的,它也會借用到非常多 TensorRT 原有的 CUDA kernel。以矩陣運(yùn)算為例,MHA kernel 實(shí)際上最終會退化到矩陣運(yùn)算層,因此,最終它仍然會使用 TensorRT 中的GEMM 運(yùn)算,因?yàn)檫@些都是通用的。
TensorRT-LLM 使用的流程如下:
首先 TensorRT-LLM 將整個網(wǎng)絡(luò)模型以及參數(shù)加載進(jìn)來,然后進(jìn)行 engine 的構(gòu)建。構(gòu)建 engine 的過程實(shí)際上是為了選擇最快的計(jì)算過程,這包括對模型圖的重構(gòu),比如層的融合以及各種圖操作等。然后選擇運(yùn)算最快的 CUDA kernel。最終,將最優(yōu) CUDA kernels 固定為一個新的 engine,以后每次運(yùn)行這個模型都會通過這種方式來加載運(yùn)行。整個流程與 TensorRT 是一致的。
一般都會按照以下步驟進(jìn)行:首先是構(gòu)建,包括加載模型和加載權(quán)重;接著,建立一個 engine;然后嘗試運(yùn)行這個構(gòu)建好的 engine,并測試效果。
TensorRT-LLM 的工作流程借鑒了之前我們所采用的 Fast Transformer 的流程,隨著時間推移,這些 FT kernel 的數(shù)量會逐漸增多,包括我們自己編寫的一些 plugin,所有這些都會被集成到 TensorRT-LLM 中。此外,在 TensorRT 中有一個編譯器,它會從 pattern 的角度來進(jìn)行 kernel 優(yōu)化。
因?yàn)閷τ诖笮驼Z言模型,可能會牽涉到卡間通信,甚至節(jié)點(diǎn)間通信的問題,因此也需要將 NCCL 部分納入考慮。
2. Key Feature Overview
首先來看一下 Inflight Batching。在我們的語言模型中,每一句話的長度可能各不相同,為了解決這個問題,我們采取了一些措施,以確保整個計(jì)算過程不會因?yàn)殚L度的不同而導(dǎo)致計(jì)算不充分或浪費(fèi)資源。我們會將后續(xù)的內(nèi)容插入其中,使得整個計(jì)算塊都能夠被充分利用,如上圖所示。Inflight Batching 在運(yùn)行過程中實(shí)施了動態(tài)的 Batching,因此被稱為 Inflight Batching。
TensorRT-LLM 對多種 attention類型提供支持。我們知道,Multi-Head Attention 實(shí)際上還有其他類型的變體,例如有 Multi-Query Attention、Group-Query Attention 等,這些都是為了減少 attention 計(jì)算量而進(jìn)行的一些優(yōu)化,但這些方法也導(dǎo)致了 attention 計(jì)算過程的差異,因此針對這些不同的計(jì)算過程,需要有一些 CUDA kernel 來進(jìn)行優(yōu)化支持。這里的 optimize attention 主要指的是對不同類型的 attention 進(jìn)行支持的優(yōu)化。
TensorRT-LLM 目前已支持多種模型的量化過程,上圖中顯示了對不同模型的支持情況。
目前,TensorRT-LLM 正在迅速加入多 GPU 以及多節(jié)點(diǎn)的支持。一些尚未支持的部分正在不斷擴(kuò)展支持,在這方面需要處理的問題包括如何進(jìn)行 Tensor 并行以及各種并行操作、如何處理卡間通信比如 NCCL 相關(guān)的問題等。
三、RAG (Retrieval-Augmented Generation)
下面介紹 RAG (Retrieval-Augmented Generation)。
RAG 的主要目標(biāo)是處理大型語言模型由于專業(yè)領(lǐng)域知識不足以及其他一些原因?qū)е碌幕糜X問題。大語言模型不可能一直進(jìn)行訓(xùn)練和微調(diào),因?yàn)槟菢拥拇鷥r(jià)相當(dāng)大,所以訓(xùn)練和微調(diào)通常是周期性而不是一直進(jìn)行的。在這些周期之間,如何確保大語言模型能夠迅速適應(yīng)專業(yè)領(lǐng)域知識呢?RAG 就是用來解決這個問題的。
首先將我們的 Knowledge Base,也就是與專業(yè)領(lǐng)域相關(guān)的整個文獻(xiàn),輸入到系統(tǒng)中,作為其知識輸入。這樣,我們就為大語言模型提供了專業(yè)領(lǐng)域的知識。
然后,輸入查詢(query),經(jīng)過一系列的處理過程,在處理完畢后,再輸入我們的問題。可以輸入一個相對專業(yè)的問題,比如詢問 NVIDIA 的 Grace 芯片有多少個 CPU core,顯然,對于一個預(yù)訓(xùn)練模型來說,它的回答肯定是不準(zhǔn)確的,因?yàn)樗鼪]有這方面的知識。但由于剛剛輸入了 Knowledge Base 對其進(jìn)行了增強(qiáng),現(xiàn)在大語言模型就能夠更清楚地回答這個問題了,它有 200 個核心。這個示例直觀地展示了 RAG 的作用。
上圖展示了應(yīng)用 RAG 的過程,圖的右邊展示了 RAG 過程中涉及的模塊或參與的組件。左側(cè)展示了 RAG 的過程,這里將 Knowledge Base 文件作為輸入,經(jīng)過預(yù)處理模塊進(jìn)行處理,主要包括特征提取等操作,我們使用了 LlamaIndex,當(dāng)然也有很多其它工具可以在這個步驟中發(fā)揮作用。
處理完的數(shù)據(jù)進(jìn)入 Embedding model,在這個產(chǎn)品中我們使用了 E5 模型。將剛才提到的 Knowledge Base 作為模型的輸入,進(jìn)行微調(diào)。微調(diào)完成后,將 embedding 結(jié)果作為向量數(shù)據(jù)庫的輸入。向量數(shù)據(jù)庫使用的是 Milvus,而 RAFT 是用于加強(qiáng)當(dāng)前數(shù)據(jù)庫搜索過程的工具,主要引入了 GPU 加速的功能。
接下來是大語言模型 LLM,這里使用的是 Llama 2 模型。通過這樣的流程來回答我們的問題,相當(dāng)于在經(jīng)過大語言模型之前,先在 prompt 之外添加了一個上下文,使得大語言模型的反饋更準(zhǔn)確。這就是 RAG 的過程。
上圖完整展示了大語言模型 LLM 的服務(wù)過程以及 RAG 在其中的位置,以便大家更好地理解 RAG。
系統(tǒng)首先通過 Nvidia 相應(yīng)的工具來進(jìn)行 RAG 的處理,然后結(jié)合大型模型進(jìn)行推理。接著,將結(jié)果反饋給用戶的查詢或提示,這使得我們的大語言模型能夠真正朝著實(shí)用方向發(fā)展,不會出現(xiàn)幻覺或者在處理專業(yè)領(lǐng)域知識時無法精確匹配的問題。
下面概述一下 RAG 的過程:
- 第一步,對輸入的 Knowledge Base 進(jìn)行分塊分片,因?yàn)檩斎氲臄?shù)據(jù)通常有一定的格式,需要將其處理成類似于問答對或其他固定格式長短的數(shù)據(jù),以便進(jìn)一步處理。
- 第二步,對 Embedding 模型基于剛才輸入的 Knowledge Base 進(jìn)行 Fine-tune,并進(jìn)行其他處理,如索引等。
- 第三步,訪問數(shù)據(jù)庫。
- 第四步,進(jìn)行 Top K 的選擇,選出最符合條件的多條 chunk。
- 第五步,將這些 chunk 作為輸入,與 prompt 一起傳遞給大語言模型,得到更加精準(zhǔn)且匹配的反饋和回答。
這樣大語言模型就能夠真正實(shí)現(xiàn)落地,更貼近于當(dāng)前的使用場景。